diff --git a/404.html b/404.html index c632d1fb8..f0ec615de 100644 --- a/404.html +++ b/404.html @@ -4,13 +4,13 @@ Page Not Found | Jitsi Meet - +
Skip to main content

Page Not Found

We could not find what you were looking for.

Please contact the owner of the site that linked you to the original URL and let them know their link is broken.

- + \ No newline at end of file diff --git a/assets/js/00635824.9d5f0db6.js b/assets/js/00635824.97518960.js similarity index 98% rename from assets/js/00635824.9d5f0db6.js rename to assets/js/00635824.97518960.js index 8f0058fe8..705bef6cf 100644 --- a/assets/js/00635824.9d5f0db6.js +++ b/assets/js/00635824.97518960.js @@ -1 +1 @@ -"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[7387],{3905:(e,t,o)=>{o.d(t,{Zo:()=>c,kt:()=>m});var r=o(7294);function a(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}function n(e,t){var o=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),o.push.apply(o,r)}return o}function i(e){for(var t=1;t=0||(a[o]=e[o]);return a}(e,t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,o)&&(a[o]=e[o])}return a}var d=r.createContext({}),p=function(e){var t=r.useContext(d),o=t;return e&&(o="function"==typeof e?e(t):i(i({},t),e)),o},c=function(e){var t=p(e.components);return r.createElement(d.Provider,{value:t},e.children)},s={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},g=r.forwardRef((function(e,t){var o=e.components,a=e.mdxType,n=e.originalType,d=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),g=p(o),m=a,u=g["".concat(d,".").concat(m)]||g[m]||s[m]||n;return o?r.createElement(u,i(i({ref:t},c),{},{components:o})):r.createElement(u,i({ref:t},c))}));function m(e,t){var o=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var n=o.length,i=new Array(n);i[0]=g;var l={};for(var d in t)hasOwnProperty.call(t,d)&&(l[d]=t[d]);l.originalType=e,l.mdxType="string"==typeof e?e:a,i[1]=l;for(var p=2;p{o.r(t),o.d(t,{assets:()=>c,contentTitle:()=>d,default:()=>m,frontMatter:()=>l,metadata:()=>p,toc:()=>s});var r=o(7462),a=o(3366),n=(o(7294),o(3905)),i=["components"],l={id:"dev-guide-web-integrations",title:"Web integrations",sidebar_label:"Integrations"},d=void 0,p={unversionedId:"dev-guide/dev-guide-web-integrations",id:"dev-guide/dev-guide-web-integrations",title:"Web integrations",description:"Creating the Google API client for Google Calendar and YouTube integration",source:"@site/docs/dev-guide/web-integrations.md",sourceDirName:"dev-guide",slug:"/dev-guide/dev-guide-web-integrations",permalink:"/handbook/docs/dev-guide/dev-guide-web-integrations",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/dev-guide/web-integrations.md",tags:[],version:"current",lastUpdatedAt:1694635654,formattedLastUpdatedAt:"Sep 13, 2023",frontMatter:{id:"dev-guide-web-integrations",title:"Web integrations",sidebar_label:"Integrations"},sidebar:"docs",previous:{title:"Modifying lib-jitsi-meet",permalink:"/handbook/docs/dev-guide/dev-guide-ljm"},next:{title:"IFrame API",permalink:"/handbook/docs/dev-guide/dev-guide-iframe"}},c={},s=[{value:"Creating the Google API client for Google Calendar and YouTube integration",id:"creating-the-google-api-client-for-google-calendar-and-youtube-integration",level:2},{value:"Creating the Microsoft app for Microsoft Outlook integration",id:"creating-the-microsoft-app-for-microsoft-outlook-integration",level:2},{value:"Creating the Dropbox app for Dropbox recording integration",id:"creating-the-dropbox-app-for-dropbox-recording-integration",level:2}],g={toc:s};function m(e){var t=e.components,o=(0,a.Z)(e,i);return(0,n.kt)("wrapper",(0,r.Z)({},g,o,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("h2",{id:"creating-the-google-api-client-for-google-calendar-and-youtube-integration"},"Creating the Google API client for Google Calendar and YouTube integration"),(0,n.kt)("ol",null,(0,n.kt)("li",{parentName:"ol"},"Log into a Google admin account."),(0,n.kt)("li",{parentName:"ol"},"Go to Google cloud platform dashboard. ",(0,n.kt)("a",{parentName:"li",href:"https://console.cloud.google.com/apis/dashboard"},"https://console.cloud.google.com/apis/dashboard")),(0,n.kt)("li",{parentName:"ol"},"In the Select a Project dropdown, click New Project."),(0,n.kt)("li",{parentName:"ol"},"Give the project a name."),(0,n.kt)("li",{parentName:"ol"},"Proceed to the Credentials settings of the new project."),(0,n.kt)("li",{parentName:"ol"},"In the Credentials tab of the Credentials settings, click Create Credentials and select the type OAuth client ID."),(0,n.kt)("li",{parentName:"ol"},"Proceed with creating a Web application and add the domains (origins) on which the application will be hosted. Local development environments (http://localhost:8000 for example) can be added here."),(0,n.kt)("li",{parentName:"ol"},"While still in the Google cloud platform dashboard, click the Library settings for the calendar project."),(0,n.kt)("li",{parentName:"ol"},"Search for the Google Calendar API (used for calendar accessing), click its result, and enable it."),(0,n.kt)("li",{parentName:"ol"},"Do the same for YouTube Data API v3")),(0,n.kt)("h2",{id:"creating-the-microsoft-app-for-microsoft-outlook-integration"},"Creating the Microsoft app for Microsoft Outlook integration"),(0,n.kt)("ol",null,(0,n.kt)("li",{parentName:"ol"},"Go to ",(0,n.kt)("a",{parentName:"li",href:"https://apps.dev.microsoft.com/"},"https://apps.dev.microsoft.com/")),(0,n.kt)("li",{parentName:"ol"},'Proceed through the "Add an app" flow. Once created, a page with several Graph Permissions fields should display.'),(0,n.kt)("li",{parentName:"ol"},'Under "Platforms" add "Web"'),(0,n.kt)("li",{parentName:"ol"},"Add a redirect URL for the Microsoft auth flow to visit once a user has confirmed authentication. Target domain if available is just 'yourdomain.com' (the deployment address) and the redirect URL is ",(0,n.kt)("inlineCode",{parentName:"li"},"https://yourdomain.com/static/msredirect.html"),"."),(0,n.kt)("li",{parentName:"ol"},"Add Microsoft Graph delegated permissions, if this option is available: Calendars.Read, Calendars.ReadWrite, Calendars.Read.Shared, Calendars.ReadWrite.Shared."),(0,n.kt)("li",{parentName:"ol"},"Check ",(0,n.kt)("inlineCode",{parentName:"li"},"Allow Implicit Flow")," (and ",(0,n.kt)("inlineCode",{parentName:"li"},"Restrict token issuing to this app")," if available)."),(0,n.kt)("li",{parentName:"ol"},"Save the changes.")),(0,n.kt)("h2",{id:"creating-the-dropbox-app-for-dropbox-recording-integration"},"Creating the Dropbox app for Dropbox recording integration"),(0,n.kt)("ol",null,(0,n.kt)("li",{parentName:"ol"},"You need a Dropbox account (If you don't already have one, you can sign up for a free account ",(0,n.kt)("a",{parentName:"li",href:"https://www.dropbox.com/register"},"here"),".)"),(0,n.kt)("li",{parentName:"ol"},"Create new App as described in ",(0,n.kt)("a",{parentName:"li",href:"https://www.dropbox.com/developers/reference/getting-started?_tk=guides_lp&_ad=guides2&_camp=get_started#app%20console"},"Getting Started Guide")," in App Console section."),(0,n.kt)("li",{parentName:"ol"},"Choose",(0,n.kt)("ol",{parentName:"li"},(0,n.kt)("li",{parentName:"ol"},"'Dropbox API - For apps that need to access files in Dropbox.' "),(0,n.kt)("li",{parentName:"ol"},"'App folder\u2013 Access to a single folder created specifically for your app.'"),(0,n.kt)("li",{parentName:"ol"},"Fill in the name of your app"))),(0,n.kt)("li",{parentName:"ol"},"You need only, the newly created App key, goes in ",(0,n.kt)("inlineCode",{parentName:"li"},"/etc/jitsi/meet/yourdeployment.com-config.js")," in ",(0,n.kt)("pre",{parentName:"li"},(0,n.kt)("code",{parentName:"pre",className:'language-title="/etc/jitsi/meet/yourdeployment.com-config.js"'}," dropbox: {\n appKey: '__dropbox_app_key__',\n redirectURI: 'https://yourdeployment.com/static/oauth.html'\n }\n"))),(0,n.kt)("li",{parentName:"ol"},"Add your Dropbox Redirect URIs in the Dropbox form ",(0,n.kt)("inlineCode",{parentName:"li"},"https://yourdeployment.com/static/oauth.html")),(0,n.kt)("li",{parentName:"ol"},"Fill in Branding")))}m.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[7387],{3905:(e,t,o)=>{o.d(t,{Zo:()=>c,kt:()=>m});var r=o(7294);function a(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}function n(e,t){var o=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),o.push.apply(o,r)}return o}function i(e){for(var t=1;t=0||(a[o]=e[o]);return a}(e,t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,o)&&(a[o]=e[o])}return a}var d=r.createContext({}),p=function(e){var t=r.useContext(d),o=t;return e&&(o="function"==typeof e?e(t):i(i({},t),e)),o},c=function(e){var t=p(e.components);return r.createElement(d.Provider,{value:t},e.children)},s={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},g=r.forwardRef((function(e,t){var o=e.components,a=e.mdxType,n=e.originalType,d=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),g=p(o),m=a,u=g["".concat(d,".").concat(m)]||g[m]||s[m]||n;return o?r.createElement(u,i(i({ref:t},c),{},{components:o})):r.createElement(u,i({ref:t},c))}));function m(e,t){var o=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var n=o.length,i=new Array(n);i[0]=g;var l={};for(var d in t)hasOwnProperty.call(t,d)&&(l[d]=t[d]);l.originalType=e,l.mdxType="string"==typeof e?e:a,i[1]=l;for(var p=2;p{o.r(t),o.d(t,{assets:()=>c,contentTitle:()=>d,default:()=>m,frontMatter:()=>l,metadata:()=>p,toc:()=>s});var r=o(7462),a=o(3366),n=(o(7294),o(3905)),i=["components"],l={id:"dev-guide-web-integrations",title:"Web integrations",sidebar_label:"Integrations"},d=void 0,p={unversionedId:"dev-guide/dev-guide-web-integrations",id:"dev-guide/dev-guide-web-integrations",title:"Web integrations",description:"Creating the Google API client for Google Calendar and YouTube integration",source:"@site/docs/dev-guide/web-integrations.md",sourceDirName:"dev-guide",slug:"/dev-guide/dev-guide-web-integrations",permalink:"/handbook/docs/dev-guide/dev-guide-web-integrations",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/dev-guide/web-integrations.md",tags:[],version:"current",lastUpdatedAt:1695203964,formattedLastUpdatedAt:"Sep 20, 2023",frontMatter:{id:"dev-guide-web-integrations",title:"Web integrations",sidebar_label:"Integrations"},sidebar:"docs",previous:{title:"Modifying lib-jitsi-meet",permalink:"/handbook/docs/dev-guide/dev-guide-ljm"},next:{title:"IFrame API",permalink:"/handbook/docs/dev-guide/dev-guide-iframe"}},c={},s=[{value:"Creating the Google API client for Google Calendar and YouTube integration",id:"creating-the-google-api-client-for-google-calendar-and-youtube-integration",level:2},{value:"Creating the Microsoft app for Microsoft Outlook integration",id:"creating-the-microsoft-app-for-microsoft-outlook-integration",level:2},{value:"Creating the Dropbox app for Dropbox recording integration",id:"creating-the-dropbox-app-for-dropbox-recording-integration",level:2}],g={toc:s};function m(e){var t=e.components,o=(0,a.Z)(e,i);return(0,n.kt)("wrapper",(0,r.Z)({},g,o,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("h2",{id:"creating-the-google-api-client-for-google-calendar-and-youtube-integration"},"Creating the Google API client for Google Calendar and YouTube integration"),(0,n.kt)("ol",null,(0,n.kt)("li",{parentName:"ol"},"Log into a Google admin account."),(0,n.kt)("li",{parentName:"ol"},"Go to Google cloud platform dashboard. ",(0,n.kt)("a",{parentName:"li",href:"https://console.cloud.google.com/apis/dashboard"},"https://console.cloud.google.com/apis/dashboard")),(0,n.kt)("li",{parentName:"ol"},"In the Select a Project dropdown, click New Project."),(0,n.kt)("li",{parentName:"ol"},"Give the project a name."),(0,n.kt)("li",{parentName:"ol"},"Proceed to the Credentials settings of the new project."),(0,n.kt)("li",{parentName:"ol"},"In the Credentials tab of the Credentials settings, click Create Credentials and select the type OAuth client ID."),(0,n.kt)("li",{parentName:"ol"},"Proceed with creating a Web application and add the domains (origins) on which the application will be hosted. Local development environments (http://localhost:8000 for example) can be added here."),(0,n.kt)("li",{parentName:"ol"},"While still in the Google cloud platform dashboard, click the Library settings for the calendar project."),(0,n.kt)("li",{parentName:"ol"},"Search for the Google Calendar API (used for calendar accessing), click its result, and enable it."),(0,n.kt)("li",{parentName:"ol"},"Do the same for YouTube Data API v3")),(0,n.kt)("h2",{id:"creating-the-microsoft-app-for-microsoft-outlook-integration"},"Creating the Microsoft app for Microsoft Outlook integration"),(0,n.kt)("ol",null,(0,n.kt)("li",{parentName:"ol"},"Go to ",(0,n.kt)("a",{parentName:"li",href:"https://apps.dev.microsoft.com/"},"https://apps.dev.microsoft.com/")),(0,n.kt)("li",{parentName:"ol"},'Proceed through the "Add an app" flow. Once created, a page with several Graph Permissions fields should display.'),(0,n.kt)("li",{parentName:"ol"},'Under "Platforms" add "Web"'),(0,n.kt)("li",{parentName:"ol"},"Add a redirect URL for the Microsoft auth flow to visit once a user has confirmed authentication. Target domain if available is just 'yourdomain.com' (the deployment address) and the redirect URL is ",(0,n.kt)("inlineCode",{parentName:"li"},"https://yourdomain.com/static/msredirect.html"),"."),(0,n.kt)("li",{parentName:"ol"},"Add Microsoft Graph delegated permissions, if this option is available: Calendars.Read, Calendars.ReadWrite, Calendars.Read.Shared, Calendars.ReadWrite.Shared."),(0,n.kt)("li",{parentName:"ol"},"Check ",(0,n.kt)("inlineCode",{parentName:"li"},"Allow Implicit Flow")," (and ",(0,n.kt)("inlineCode",{parentName:"li"},"Restrict token issuing to this app")," if available)."),(0,n.kt)("li",{parentName:"ol"},"Save the changes.")),(0,n.kt)("h2",{id:"creating-the-dropbox-app-for-dropbox-recording-integration"},"Creating the Dropbox app for Dropbox recording integration"),(0,n.kt)("ol",null,(0,n.kt)("li",{parentName:"ol"},"You need a Dropbox account (If you don't already have one, you can sign up for a free account ",(0,n.kt)("a",{parentName:"li",href:"https://www.dropbox.com/register"},"here"),".)"),(0,n.kt)("li",{parentName:"ol"},"Create new App as described in ",(0,n.kt)("a",{parentName:"li",href:"https://www.dropbox.com/developers/reference/getting-started?_tk=guides_lp&_ad=guides2&_camp=get_started#app%20console"},"Getting Started Guide")," in App Console section."),(0,n.kt)("li",{parentName:"ol"},"Choose",(0,n.kt)("ol",{parentName:"li"},(0,n.kt)("li",{parentName:"ol"},"'Dropbox API - For apps that need to access files in Dropbox.' "),(0,n.kt)("li",{parentName:"ol"},"'App folder\u2013 Access to a single folder created specifically for your app.'"),(0,n.kt)("li",{parentName:"ol"},"Fill in the name of your app"))),(0,n.kt)("li",{parentName:"ol"},"You need only, the newly created App key, goes in ",(0,n.kt)("inlineCode",{parentName:"li"},"/etc/jitsi/meet/yourdeployment.com-config.js")," in ",(0,n.kt)("pre",{parentName:"li"},(0,n.kt)("code",{parentName:"pre",className:'language-title="/etc/jitsi/meet/yourdeployment.com-config.js"'}," dropbox: {\n appKey: '__dropbox_app_key__',\n redirectURI: 'https://yourdeployment.com/static/oauth.html'\n }\n"))),(0,n.kt)("li",{parentName:"ol"},"Add your Dropbox Redirect URIs in the Dropbox form ",(0,n.kt)("inlineCode",{parentName:"li"},"https://yourdeployment.com/static/oauth.html")),(0,n.kt)("li",{parentName:"ol"},"Fill in Branding")))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/0480b142.3add4571.js b/assets/js/0480b142.577769de.js similarity index 99% rename from assets/js/0480b142.3add4571.js rename to assets/js/0480b142.577769de.js index fa4856447..57ac6b806 100644 --- a/assets/js/0480b142.3add4571.js +++ b/assets/js/0480b142.577769de.js @@ -1 +1 @@ -"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[836],{3905:(e,t,o)=>{o.d(t,{Zo:()=>p,kt:()=>d});var n=o(7294);function r(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}function a(e,t){var o=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),o.push.apply(o,n)}return o}function i(e){for(var t=1;t=0||(r[o]=e[o]);return r}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,o)&&(r[o]=e[o])}return r}var l=n.createContext({}),u=function(e){var t=n.useContext(l),o=t;return e&&(o="function"==typeof e?e(t):i(i({},t),e)),o},p=function(e){var t=u(e.components);return n.createElement(l.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var o=e.components,r=e.mdxType,a=e.originalType,l=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),m=u(o),d=r,h=m["".concat(l,".").concat(d)]||m[d]||c[d]||a;return o?n.createElement(h,i(i({ref:t},p),{},{components:o})):n.createElement(h,i({ref:t},p))}));function d(e,t){var o=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var a=o.length,i=new Array(a);i[0]=m;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:r,i[1]=s;for(var u=2;u{o.r(t),o.d(t,{assets:()=>p,contentTitle:()=>l,default:()=>d,frontMatter:()=>s,metadata:()=>u,toc:()=>c});var n=o(7462),r=o(3366),a=(o(7294),o(3905)),i=["components"],s={id:"faq",title:"FAQ"},l=void 0,u={unversionedId:"faq",id:"faq",title:"FAQ",description:"How to tell if my server instance is behind NAT?",source:"@site/docs/faq.md",sourceDirName:".",slug:"/faq",permalink:"/handbook/docs/faq",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/faq.md",tags:[],version:"current",lastUpdatedAt:1694635654,formattedLastUpdatedAt:"Sep 13, 2023",frontMatter:{id:"faq",title:"FAQ"},sidebar:"docs",previous:{title:"Security",permalink:"/handbook/docs/security"},next:{title:"Our community",permalink:"/handbook/docs/community/community-intro"}},p={},c=[{value:"How to tell if my server instance is behind NAT?",id:"how-to-tell-if-my-server-instance-is-behind-nat",level:2},{value:"Clients could communicate well in room created at meet.jit.si. The same clients still could connect to my self-hosted instance but can neither hear nor see one another. What's wrong?",id:"clients-could-communicate-well-in-room-created-at-meetjitsi-the-same-clients-still-could-connect-to-my-self-hosted-instance-but-can-neither-hear-nor-see-one-another-whats-wrong",level:2},{value:"It works with two participants, but crashes or does not work properly when a third joins",id:"it-works-with-two-participants-but-crashes-or-does-not-work-properly-when-a-third-joins",level:2},{value:"Can I mute and unmute other participants?",id:"can-i-mute-and-unmute-other-participants",level:2},{value:"How can I protect my meetings with Jitsi?",id:"how-can-i-protect-my-meetings-with-jitsi",level:2},{value:"1. Create a "strong" room name.",id:"1-create-a-strong-room-name",level:3},{value:"2. Use a different room name for each meeting / conference you have.",id:"2-use-a-different-room-name-for-each-meeting--conference-you-have",level:3},{value:"3. Add a password to the room.",id:"3-add-a-password-to-the-room",level:3},{value:"4. Enable "secure domain" if you are using your own instance of Jitsi.",id:"4-enable-secure-domain-if-you-are-using-your-own-instance-of-jitsi",level:3},{value:"It's working when I connect from a browser, but not from the iOS or Android apps",id:"its-working-when-i-connect-from-a-browser-but-not-from-the-ios-or-android-apps",level:2},{value:"Can I record and save the video?",id:"can-i-record-and-save-the-video",level:2},{value:"I set the password in meeting but it is not working the next time",id:"i-set-the-password-in-meeting-but-it-is-not-working-the-next-time",level:2},{value:"How to limit the number of participants?",id:"how-to-limit-the-number-of-participants",level:2}],m={toc:c};function d(e){var t=e.components,o=(0,r.Z)(e,i);return(0,a.kt)("wrapper",(0,n.Z)({},m,o,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h2",{id:"how-to-tell-if-my-server-instance-is-behind-nat"},"How to tell if my server instance is behind NAT?"),(0,a.kt)("p",null,"In general, if the tool ifconfig (or ipconfig) shows the assigned IPv4 address to be a private / local address (10.x.x.x, or 172.16.x.x - 172.31.x.x, or 192.168.x.x) but you know that its public IPv4 address is different from that, the server is most probably behind NAT."),(0,a.kt)("p",null,"If you are hosting your server on a VPS, and you are not sure, ask your VPS provider's support team."),(0,a.kt)("h2",{id:"clients-could-communicate-well-in-room-created-at-meetjitsi-the-same-clients-still-could-connect-to-my-self-hosted-instance-but-can-neither-hear-nor-see-one-another-whats-wrong"},"Clients could communicate well in room created at ",(0,a.kt)("inlineCode",{parentName:"h2"},"meet.jit.si"),". The same clients still could connect to my self-hosted instance but can neither hear nor see one another. What's wrong?"),(0,a.kt)("p",null,"Most probably, the server is behind NAT, but you haven't added the NAT-specific configuration. See this ",(0,a.kt)("a",{parentName:"p",href:"https://community.jitsi.org/t/cannot-see-video-or-hear-audio-on-self-hosted-instance/"},"resolved question"),". You need to follow the steps detailed ",(0,a.kt)("a",{parentName:"p",href:"devops-guide/devops-guide-quickstart#advanced-configuration"},"here"),"."),(0,a.kt)("h2",{id:"it-works-with-two-participants-but-crashes-or-does-not-work-properly-when-a-third-joins"},"It works with two participants, but crashes or does not work properly when a third joins"),(0,a.kt)("p",null,"P2P mode is working, but it fails when you are trying to pass traffic via jitsi-videobridge2."),(0,a.kt)("p",null,"Check you've got your firewall / NAT set up correctly \u2014 especially UDP 10000. For more information, see ",(0,a.kt)("a",{parentName:"p",href:"devops-guide/devops-guide-quickstart#setup-and-configure-your-firewall"},"here"),"."),(0,a.kt)("h2",{id:"can-i-mute-and-unmute-other-participants"},"Can I mute and unmute other participants?"),(0,a.kt)("p",null,"If you are the moderator of a conference, you can mute everyone's microphone. You cannot unmute other people's microphones, and they can unmute their microphone at any time."),(0,a.kt)("p",null,'You may want to set some "ground rules" for who can talk and when, just as with any physical meeting or classroom.'),(0,a.kt)("p",null,'If you would like to limit who can become a moderator, you need to set up your own instance of Jitsi and enable "secure domain". Please see ',(0,a.kt)("a",{parentName:"p",href:"#4-enable-secure-domain-if-you-are-using-your-own-instance-of-jitsi"},"here")," for more information."),(0,a.kt)("h2",{id:"how-can-i-protect-my-meetings-with-jitsi"},"How can I protect my meetings with Jitsi?"),(0,a.kt)("h3",{id:"1-create-a-strong-room-name"},(0,a.kt)("em",{parentName:"h3"},'1. Create a "strong" room name.')),(0,a.kt)("p",null,'Use a strong room name, which no-one else is likely to be using. Use the name generator on the welcome page, or else generate your own "strong" name.'),(0,a.kt)("p",null,"For example, on macOS, in terminal, you can use ",(0,a.kt)("inlineCode",{parentName:"p"},"uuidgen")," to generate a string of letters of numbers (e.g. B741B63E-C5E6-4D82-BAC4-048BE25D8CC7)."),(0,a.kt)("p",null,"Your room name would be ",(0,a.kt)("inlineCode",{parentName:"p"},"meet.jit.si/B741B63E-C5E6-4D82-BAC4-048BE25D8CC7")," on the hosted ",(0,a.kt)("inlineCode",{parentName:"p"},"meet.jit.si")," platform."),(0,a.kt)("p",null,'If you use "test" or "LucysMeeting" or "pilates" or similar, then it\'s highly likely that other users will have had the same idea.'),(0,a.kt)("h3",{id:"2-use-a-different-room-name-for-each-meeting--conference-you-have"},(0,a.kt)("em",{parentName:"h3"},"2. Use a different room name for each meeting / conference you have.")),(0,a.kt)("p",null,"If you are going to have multiple meetings, ideally use a different room name for each one."),(0,a.kt)("p",null,"If that is not practical, at least use a different room name for each group of people."),(0,a.kt)("h3",{id:"3-add-a-password-to-the-room"},(0,a.kt)("em",{parentName:"h3"},"3. Add a password to the room.")),(0,a.kt)("p",null,"Once you have started your room, set a password for it. Only people who have the password can join from that point on, but it does not affect people who have already joined."),(0,a.kt)("p",null,"You will need to tell everyone the password."),(0,a.kt)("p",null,"If they give the password to others, those other people can also join."),(0,a.kt)("h3",{id:"4-enable-secure-domain-if-you-are-using-your-own-instance-of-jitsi"},(0,a.kt)("em",{parentName:"h3"},'4. Enable "secure domain" if you are using your own instance of Jitsi.')),(0,a.kt)("p",null,"In addition to the tips above, consider enabling the ",(0,a.kt)("a",{parentName:"p",href:"https://jitsi.github.io/handbook/docs/devops-guide/secure-domain"},'"secure domain" configuration'),". This requires you (or someone else) to enter a username and password to open a room. It also allows you to become a moderator."),(0,a.kt)("h2",{id:"its-working-when-i-connect-from-a-browser-but-not-from-the-ios-or-android-apps"},"It's working when I connect from a browser, but not from the iOS or Android apps"),(0,a.kt)("p",null,"This probably means that you are not serving the fullchain for your TLS certificate. You can check if your cert chain\nis properly configured ",(0,a.kt)("a",{parentName:"p",href:"https://whatsmychaincert.com/"},"here"),"."),(0,a.kt)("p",null,"In nginx, if you are using Let's Encrypt, you should have a line like this:"),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"ssl_certificate /etc/letsencrypt/live/jitsi.example.com/fullchain.pem;")),(0,a.kt)("h2",{id:"can-i-record-and-save-the-video"},"Can I record and save the video?"),(0,a.kt)("p",null,"Yes. There are multiple methods (using external software or services):"),(0,a.kt)("p",null,(0,a.kt)("em",{parentName:"p"},"Note"),": If you want to use a privacy-friendly method, use method 1 or 2."),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("strong",{parentName:"p"},"OBS"),": Use ",(0,a.kt)("a",{parentName:"p",href:"https://obsproject.com/"},"OBS")," to record your Session (e.g. your browser window).")),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("strong",{parentName:"p"},"RTMP-Server"),": For this you have to setup your own RTMP-Server and then use your RTMP URL + Stream key instead of the Youtube Stream key as described ",(0,a.kt)("a",{parentName:"p",href:"https://jitsi.org/blog/live-streaming-with-jitsi-and-youtube/"},"here"),". Self-installed Jitsi Meet deployments will need to setup Jibri to do this.")),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("strong",{parentName:"p"},"Dropbox"),": ",(0,a.kt)("a",{parentName:"p",href:"/handbook/docs/dev-guide/dev-guide-web-integrations#creating-the-dropbox-app-for-dropbox-recording-integration"},"Connect to Dropbox with Jitsi Meet")," and save the video in the Dropbox. ")),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("strong",{parentName:"p"},"Video Services/Websites"),": Stream your conference to YouTube or other sites (e.g. Twitch) and access the recording there (see ",(0,a.kt)("a",{parentName:"p",href:"https://jitsi.org/blog/live-streaming-with-jitsi-and-youtube/"},"howto"),"). Self-installed Jitsi Meet deployments will need to setup Jibri to do this. "))),(0,a.kt)("p",null,"More methods might be implemented in the future, but are not ready yet (e.g. ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet/issues/6014"},"Local Recording"),"."),(0,a.kt)("h2",{id:"i-set-the-password-in-meeting-but-it-is-not-working-the-next-time"},"I set the password in meeting but it is not working the next time"),(0,a.kt)("p",null,"Once the meeting ends it's password also gets removed, so you need to set the password again for next meeting."),(0,a.kt)("h2",{id:"how-to-limit-the-number-of-participants"},"How to limit the number of participants?"),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},"Use the command ",(0,a.kt)("inlineCode",{parentName:"li"},"prosodyctl about")," to view the version of prosody and plug directory, similar to the output below.")),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"Prosody 0.11.6\n\n# Prosody directories\n\nData directory: /var/lib/prosody\n\nConfig directory: /etc/prosody\n\nSource directory: /usr/lib/prosody\n\nPlugin directories:\n\n/usr/share/jitsi-meet/prosody-plugins/\n\n/usr/lib/prosody/modules/\n")),(0,a.kt)("ol",{start:2},(0,a.kt)("li",{parentName:"ol"},"Check if there is a ",(0,a.kt)("inlineCode",{parentName:"li"},"mod_muc_max_occupants.lua")," file in your plugin directory.")),(0,a.kt)("p",null,"If not, please create a new file ",(0,a.kt)("inlineCode",{parentName:"p"},"mod_muc_max_occupants.lua")," in the plugin directory And copy everything from ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet/blob/master/resources/prosody-plugins/mod_muc_max_occupants.lua"},"here")," to paste."),(0,a.kt)("p",null,"If it exists, please ignore this step."),(0,a.kt)("p",null,"3.Edit your ",(0,a.kt)("inlineCode",{parentName:"p"},"/etc/prosody/conf.avail/meet.example.com.cfg.lua")," file and add ",(0,a.kt)("inlineCode",{parentName:"p"},"muc_max_occupants"),' as a module_enabled in the conference.meet.example.com "muc" section.'),(0,a.kt)("p",null,"Then, add the options below that. You need both ",(0,a.kt)("inlineCode",{parentName:"p"},"muc_max_occupants")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"muc_access_whitelist")," defined."),(0,a.kt)("p",null,"Example:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},'Component "conference.meet.example.com" "muc"\n storage = "memory"\n modules_enabled = {\n "muc_meeting_id";\n "muc_domain_mapper";\n "muc_max_occupants"; \n }\n muc_max_occupants = "5"\n muc_access_whitelist = { "focus@auth.meet.example.com" }\n admins = { "focus@auth.meet.example.com" }\n muc_room_locking = false\n muc_room_default_public_jids = true\n')),(0,a.kt)("p",null,'Note: the relationship between storage = "" and your prosody version, and you need to modify all storage="" .'),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},'Prosody nightly747 storage = "null"'),(0,a.kt)("li",{parentName:"ul"},'Prosody 0.10 storage = "none"'),(0,a.kt)("li",{parentName:"ul"},'Prosody 0.11 storage = "memory"')),(0,a.kt)("ol",{start:4},(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},"You need to use the command ",(0,a.kt)("inlineCode",{parentName:"p"},"prosodyctl restart")," to see the effect.")),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},"If you want to update to use prosody, you can check ",(0,a.kt)("a",{parentName:"p",href:"https://community.jitsi.org/t/how-to-how-do-i-update-prosody/72205"},"here"),"."))))}d.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[836],{3905:(e,t,o)=>{o.d(t,{Zo:()=>p,kt:()=>d});var n=o(7294);function r(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}function a(e,t){var o=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),o.push.apply(o,n)}return o}function i(e){for(var t=1;t=0||(r[o]=e[o]);return r}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,o)&&(r[o]=e[o])}return r}var l=n.createContext({}),u=function(e){var t=n.useContext(l),o=t;return e&&(o="function"==typeof e?e(t):i(i({},t),e)),o},p=function(e){var t=u(e.components);return n.createElement(l.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var o=e.components,r=e.mdxType,a=e.originalType,l=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),m=u(o),d=r,h=m["".concat(l,".").concat(d)]||m[d]||c[d]||a;return o?n.createElement(h,i(i({ref:t},p),{},{components:o})):n.createElement(h,i({ref:t},p))}));function d(e,t){var o=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var a=o.length,i=new Array(a);i[0]=m;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:r,i[1]=s;for(var u=2;u{o.r(t),o.d(t,{assets:()=>p,contentTitle:()=>l,default:()=>d,frontMatter:()=>s,metadata:()=>u,toc:()=>c});var n=o(7462),r=o(3366),a=(o(7294),o(3905)),i=["components"],s={id:"faq",title:"FAQ"},l=void 0,u={unversionedId:"faq",id:"faq",title:"FAQ",description:"How to tell if my server instance is behind NAT?",source:"@site/docs/faq.md",sourceDirName:".",slug:"/faq",permalink:"/handbook/docs/faq",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/faq.md",tags:[],version:"current",lastUpdatedAt:1695203964,formattedLastUpdatedAt:"Sep 20, 2023",frontMatter:{id:"faq",title:"FAQ"},sidebar:"docs",previous:{title:"Security",permalink:"/handbook/docs/security"},next:{title:"Our community",permalink:"/handbook/docs/community/community-intro"}},p={},c=[{value:"How to tell if my server instance is behind NAT?",id:"how-to-tell-if-my-server-instance-is-behind-nat",level:2},{value:"Clients could communicate well in room created at meet.jit.si. The same clients still could connect to my self-hosted instance but can neither hear nor see one another. What's wrong?",id:"clients-could-communicate-well-in-room-created-at-meetjitsi-the-same-clients-still-could-connect-to-my-self-hosted-instance-but-can-neither-hear-nor-see-one-another-whats-wrong",level:2},{value:"It works with two participants, but crashes or does not work properly when a third joins",id:"it-works-with-two-participants-but-crashes-or-does-not-work-properly-when-a-third-joins",level:2},{value:"Can I mute and unmute other participants?",id:"can-i-mute-and-unmute-other-participants",level:2},{value:"How can I protect my meetings with Jitsi?",id:"how-can-i-protect-my-meetings-with-jitsi",level:2},{value:"1. Create a "strong" room name.",id:"1-create-a-strong-room-name",level:3},{value:"2. Use a different room name for each meeting / conference you have.",id:"2-use-a-different-room-name-for-each-meeting--conference-you-have",level:3},{value:"3. Add a password to the room.",id:"3-add-a-password-to-the-room",level:3},{value:"4. Enable "secure domain" if you are using your own instance of Jitsi.",id:"4-enable-secure-domain-if-you-are-using-your-own-instance-of-jitsi",level:3},{value:"It's working when I connect from a browser, but not from the iOS or Android apps",id:"its-working-when-i-connect-from-a-browser-but-not-from-the-ios-or-android-apps",level:2},{value:"Can I record and save the video?",id:"can-i-record-and-save-the-video",level:2},{value:"I set the password in meeting but it is not working the next time",id:"i-set-the-password-in-meeting-but-it-is-not-working-the-next-time",level:2},{value:"How to limit the number of participants?",id:"how-to-limit-the-number-of-participants",level:2}],m={toc:c};function d(e){var t=e.components,o=(0,r.Z)(e,i);return(0,a.kt)("wrapper",(0,n.Z)({},m,o,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h2",{id:"how-to-tell-if-my-server-instance-is-behind-nat"},"How to tell if my server instance is behind NAT?"),(0,a.kt)("p",null,"In general, if the tool ifconfig (or ipconfig) shows the assigned IPv4 address to be a private / local address (10.x.x.x, or 172.16.x.x - 172.31.x.x, or 192.168.x.x) but you know that its public IPv4 address is different from that, the server is most probably behind NAT."),(0,a.kt)("p",null,"If you are hosting your server on a VPS, and you are not sure, ask your VPS provider's support team."),(0,a.kt)("h2",{id:"clients-could-communicate-well-in-room-created-at-meetjitsi-the-same-clients-still-could-connect-to-my-self-hosted-instance-but-can-neither-hear-nor-see-one-another-whats-wrong"},"Clients could communicate well in room created at ",(0,a.kt)("inlineCode",{parentName:"h2"},"meet.jit.si"),". The same clients still could connect to my self-hosted instance but can neither hear nor see one another. What's wrong?"),(0,a.kt)("p",null,"Most probably, the server is behind NAT, but you haven't added the NAT-specific configuration. See this ",(0,a.kt)("a",{parentName:"p",href:"https://community.jitsi.org/t/cannot-see-video-or-hear-audio-on-self-hosted-instance/"},"resolved question"),". You need to follow the steps detailed ",(0,a.kt)("a",{parentName:"p",href:"devops-guide/devops-guide-quickstart#advanced-configuration"},"here"),"."),(0,a.kt)("h2",{id:"it-works-with-two-participants-but-crashes-or-does-not-work-properly-when-a-third-joins"},"It works with two participants, but crashes or does not work properly when a third joins"),(0,a.kt)("p",null,"P2P mode is working, but it fails when you are trying to pass traffic via jitsi-videobridge2."),(0,a.kt)("p",null,"Check you've got your firewall / NAT set up correctly \u2014 especially UDP 10000. For more information, see ",(0,a.kt)("a",{parentName:"p",href:"devops-guide/devops-guide-quickstart#setup-and-configure-your-firewall"},"here"),"."),(0,a.kt)("h2",{id:"can-i-mute-and-unmute-other-participants"},"Can I mute and unmute other participants?"),(0,a.kt)("p",null,"If you are the moderator of a conference, you can mute everyone's microphone. You cannot unmute other people's microphones, and they can unmute their microphone at any time."),(0,a.kt)("p",null,'You may want to set some "ground rules" for who can talk and when, just as with any physical meeting or classroom.'),(0,a.kt)("p",null,'If you would like to limit who can become a moderator, you need to set up your own instance of Jitsi and enable "secure domain". Please see ',(0,a.kt)("a",{parentName:"p",href:"#4-enable-secure-domain-if-you-are-using-your-own-instance-of-jitsi"},"here")," for more information."),(0,a.kt)("h2",{id:"how-can-i-protect-my-meetings-with-jitsi"},"How can I protect my meetings with Jitsi?"),(0,a.kt)("h3",{id:"1-create-a-strong-room-name"},(0,a.kt)("em",{parentName:"h3"},'1. Create a "strong" room name.')),(0,a.kt)("p",null,'Use a strong room name, which no-one else is likely to be using. Use the name generator on the welcome page, or else generate your own "strong" name.'),(0,a.kt)("p",null,"For example, on macOS, in terminal, you can use ",(0,a.kt)("inlineCode",{parentName:"p"},"uuidgen")," to generate a string of letters of numbers (e.g. B741B63E-C5E6-4D82-BAC4-048BE25D8CC7)."),(0,a.kt)("p",null,"Your room name would be ",(0,a.kt)("inlineCode",{parentName:"p"},"meet.jit.si/B741B63E-C5E6-4D82-BAC4-048BE25D8CC7")," on the hosted ",(0,a.kt)("inlineCode",{parentName:"p"},"meet.jit.si")," platform."),(0,a.kt)("p",null,'If you use "test" or "LucysMeeting" or "pilates" or similar, then it\'s highly likely that other users will have had the same idea.'),(0,a.kt)("h3",{id:"2-use-a-different-room-name-for-each-meeting--conference-you-have"},(0,a.kt)("em",{parentName:"h3"},"2. Use a different room name for each meeting / conference you have.")),(0,a.kt)("p",null,"If you are going to have multiple meetings, ideally use a different room name for each one."),(0,a.kt)("p",null,"If that is not practical, at least use a different room name for each group of people."),(0,a.kt)("h3",{id:"3-add-a-password-to-the-room"},(0,a.kt)("em",{parentName:"h3"},"3. Add a password to the room.")),(0,a.kt)("p",null,"Once you have started your room, set a password for it. Only people who have the password can join from that point on, but it does not affect people who have already joined."),(0,a.kt)("p",null,"You will need to tell everyone the password."),(0,a.kt)("p",null,"If they give the password to others, those other people can also join."),(0,a.kt)("h3",{id:"4-enable-secure-domain-if-you-are-using-your-own-instance-of-jitsi"},(0,a.kt)("em",{parentName:"h3"},'4. Enable "secure domain" if you are using your own instance of Jitsi.')),(0,a.kt)("p",null,"In addition to the tips above, consider enabling the ",(0,a.kt)("a",{parentName:"p",href:"https://jitsi.github.io/handbook/docs/devops-guide/secure-domain"},'"secure domain" configuration'),". This requires you (or someone else) to enter a username and password to open a room. It also allows you to become a moderator."),(0,a.kt)("h2",{id:"its-working-when-i-connect-from-a-browser-but-not-from-the-ios-or-android-apps"},"It's working when I connect from a browser, but not from the iOS or Android apps"),(0,a.kt)("p",null,"This probably means that you are not serving the fullchain for your TLS certificate. You can check if your cert chain\nis properly configured ",(0,a.kt)("a",{parentName:"p",href:"https://whatsmychaincert.com/"},"here"),"."),(0,a.kt)("p",null,"In nginx, if you are using Let's Encrypt, you should have a line like this:"),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"ssl_certificate /etc/letsencrypt/live/jitsi.example.com/fullchain.pem;")),(0,a.kt)("h2",{id:"can-i-record-and-save-the-video"},"Can I record and save the video?"),(0,a.kt)("p",null,"Yes. There are multiple methods (using external software or services):"),(0,a.kt)("p",null,(0,a.kt)("em",{parentName:"p"},"Note"),": If you want to use a privacy-friendly method, use method 1 or 2."),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("strong",{parentName:"p"},"OBS"),": Use ",(0,a.kt)("a",{parentName:"p",href:"https://obsproject.com/"},"OBS")," to record your Session (e.g. your browser window).")),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("strong",{parentName:"p"},"RTMP-Server"),": For this you have to setup your own RTMP-Server and then use your RTMP URL + Stream key instead of the Youtube Stream key as described ",(0,a.kt)("a",{parentName:"p",href:"https://jitsi.org/blog/live-streaming-with-jitsi-and-youtube/"},"here"),". Self-installed Jitsi Meet deployments will need to setup Jibri to do this.")),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("strong",{parentName:"p"},"Dropbox"),": ",(0,a.kt)("a",{parentName:"p",href:"/handbook/docs/dev-guide/dev-guide-web-integrations#creating-the-dropbox-app-for-dropbox-recording-integration"},"Connect to Dropbox with Jitsi Meet")," and save the video in the Dropbox. ")),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("strong",{parentName:"p"},"Video Services/Websites"),": Stream your conference to YouTube or other sites (e.g. Twitch) and access the recording there (see ",(0,a.kt)("a",{parentName:"p",href:"https://jitsi.org/blog/live-streaming-with-jitsi-and-youtube/"},"howto"),"). Self-installed Jitsi Meet deployments will need to setup Jibri to do this. "))),(0,a.kt)("p",null,"More methods might be implemented in the future, but are not ready yet (e.g. ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet/issues/6014"},"Local Recording"),"."),(0,a.kt)("h2",{id:"i-set-the-password-in-meeting-but-it-is-not-working-the-next-time"},"I set the password in meeting but it is not working the next time"),(0,a.kt)("p",null,"Once the meeting ends it's password also gets removed, so you need to set the password again for next meeting."),(0,a.kt)("h2",{id:"how-to-limit-the-number-of-participants"},"How to limit the number of participants?"),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},"Use the command ",(0,a.kt)("inlineCode",{parentName:"li"},"prosodyctl about")," to view the version of prosody and plug directory, similar to the output below.")),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"Prosody 0.11.6\n\n# Prosody directories\n\nData directory: /var/lib/prosody\n\nConfig directory: /etc/prosody\n\nSource directory: /usr/lib/prosody\n\nPlugin directories:\n\n/usr/share/jitsi-meet/prosody-plugins/\n\n/usr/lib/prosody/modules/\n")),(0,a.kt)("ol",{start:2},(0,a.kt)("li",{parentName:"ol"},"Check if there is a ",(0,a.kt)("inlineCode",{parentName:"li"},"mod_muc_max_occupants.lua")," file in your plugin directory.")),(0,a.kt)("p",null,"If not, please create a new file ",(0,a.kt)("inlineCode",{parentName:"p"},"mod_muc_max_occupants.lua")," in the plugin directory And copy everything from ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet/blob/master/resources/prosody-plugins/mod_muc_max_occupants.lua"},"here")," to paste."),(0,a.kt)("p",null,"If it exists, please ignore this step."),(0,a.kt)("p",null,"3.Edit your ",(0,a.kt)("inlineCode",{parentName:"p"},"/etc/prosody/conf.avail/meet.example.com.cfg.lua")," file and add ",(0,a.kt)("inlineCode",{parentName:"p"},"muc_max_occupants"),' as a module_enabled in the conference.meet.example.com "muc" section.'),(0,a.kt)("p",null,"Then, add the options below that. You need both ",(0,a.kt)("inlineCode",{parentName:"p"},"muc_max_occupants")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"muc_access_whitelist")," defined."),(0,a.kt)("p",null,"Example:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},'Component "conference.meet.example.com" "muc"\n storage = "memory"\n modules_enabled = {\n "muc_meeting_id";\n "muc_domain_mapper";\n "muc_max_occupants"; \n }\n muc_max_occupants = "5"\n muc_access_whitelist = { "focus@auth.meet.example.com" }\n admins = { "focus@auth.meet.example.com" }\n muc_room_locking = false\n muc_room_default_public_jids = true\n')),(0,a.kt)("p",null,'Note: the relationship between storage = "" and your prosody version, and you need to modify all storage="" .'),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},'Prosody nightly747 storage = "null"'),(0,a.kt)("li",{parentName:"ul"},'Prosody 0.10 storage = "none"'),(0,a.kt)("li",{parentName:"ul"},'Prosody 0.11 storage = "memory"')),(0,a.kt)("ol",{start:4},(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},"You need to use the command ",(0,a.kt)("inlineCode",{parentName:"p"},"prosodyctl restart")," to see the effect.")),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},"If you want to update to use prosody, you can check ",(0,a.kt)("a",{parentName:"p",href:"https://community.jitsi.org/t/how-to-how-do-i-update-prosody/72205"},"here"),"."))))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/04af5f46.e1480eb8.js b/assets/js/04af5f46.25a16dea.js similarity index 98% rename from assets/js/04af5f46.e1480eb8.js rename to assets/js/04af5f46.25a16dea.js index b871a2395..33f8b484b 100644 --- a/assets/js/04af5f46.e1480eb8.js +++ b/assets/js/04af5f46.25a16dea.js @@ -1 +1 @@ -"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[8029],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>m});var o=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function r(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var l=o.createContext({}),c=function(e){var t=o.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},u=function(e){var t=c(e.components);return o.createElement(l.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},d=o.forwardRef((function(e,t){var n=e.components,i=e.mdxType,a=e.originalType,l=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),d=c(n),m=i,f=d["".concat(l,".").concat(m)]||d[m]||p[m]||a;return n?o.createElement(f,r(r({ref:t},u),{},{components:n})):o.createElement(f,r({ref:t},u))}));function m(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=n.length,r=new Array(a);r[0]=d;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:i,r[1]=s;for(var c=2;c{n.r(t),n.d(t,{assets:()=>u,contentTitle:()=>l,default:()=>m,frontMatter:()=>s,metadata:()=>c,toc:()=>p});var o=n(7462),i=n(3366),a=(n(7294),n(3905)),r=["components"],s={id:"secure-domain",title:"Secure Domain setup",sidebar_label:"Authentication (Secure Domain)"},l=void 0,c={unversionedId:"devops-guide/secure-domain",id:"devops-guide/secure-domain",title:"Secure Domain setup",description:"It is possible to allow only authenticated users to create new conference",source:"@site/docs/devops-guide/secure-domain.md",sourceDirName:"devops-guide",slug:"/devops-guide/secure-domain",permalink:"/handbook/docs/devops-guide/secure-domain",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/devops-guide/secure-domain.md",tags:[],version:"current",lastUpdatedAt:1694635654,formattedLastUpdatedAt:"Sep 13, 2023",frontMatter:{id:"secure-domain",title:"Secure Domain setup",sidebar_label:"Authentication (Secure Domain)"},sidebar:"docs",previous:{title:"Configuration",permalink:"/handbook/docs/category/configuration"},next:{title:"LDAP Authentication",permalink:"/handbook/docs/devops-guide/ldap-authentication"}},u={},p=[{value:"Prosody configuration",id:"prosody-configuration",level:2},{value:"Enable authentication",id:"enable-authentication",level:3},{value:"Enable anonymous login for guests",id:"enable-anonymous-login-for-guests",level:3},{value:"Jitsi Meet configuration",id:"jitsi-meet-configuration",level:2},{value:"Jicofo configuration",id:"jicofo-configuration",level:2},{value:"Create users in Prosody (internal auth)",id:"create-users-in-prosody-internal-auth",level:2},{value:"Optional: Jigasi configuration",id:"optional-jigasi-configuration",level:2},{value:"Enable Authentication",id:"enable-authentication-1",level:3},{value:"Debugging",id:"debugging",level:3}],d={toc:p};function m(e){var t=e.components,n=(0,i.Z)(e,r);return(0,a.kt)("wrapper",(0,o.Z)({},d,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("p",null,"It is possible to allow only authenticated users to create new conference\nrooms. Whenever a new room is about to be created, Jitsi Meet will prompt for\na user name and password. After the room is created, others will be able to join\nfrom anonymous domain. Here's what has to be configured:"),(0,a.kt)("h2",{id:"prosody-configuration"},"Prosody configuration"),(0,a.kt)("p",null,"If you have installed Jitsi Meet from the Debian package, these changes should be made in ",(0,a.kt)("inlineCode",{parentName:"p"},"/etc/prosody/conf.avail/[your-hostname].cfg.lua")),(0,a.kt)("h3",{id:"enable-authentication"},"Enable authentication"),(0,a.kt)("p",null,"Inside the ",(0,a.kt)("inlineCode",{parentName:"p"},'VirtualHost "[your-hostname]"')," block, replace anonymous authentication with hashed password authentication:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},'VirtualHost "jitsi-meet.example.com"\n authentication = "internal_hashed"\n')),(0,a.kt)("p",null,"Replace ",(0,a.kt)("inlineCode",{parentName:"p"},"jitsi-meet.example.com")," with your hostname."),(0,a.kt)("h3",{id:"enable-anonymous-login-for-guests"},"Enable anonymous login for guests"),(0,a.kt)("p",null,"Add this block ",(0,a.kt)("strong",{parentName:"p"},"after the previous VirtualHost")," to enable the anonymous login method for guests:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},'VirtualHost "guest.jitsi-meet.example.com"\n authentication = "anonymous"\n c2s_require_encryption = false\n')),(0,a.kt)("p",null,(0,a.kt)("em",{parentName:"p"},"Note that ",(0,a.kt)("inlineCode",{parentName:"em"},"guest.jitsi-meet.example.com")," is internal to Jitsi, and you do not need to (and should not) create a DNS record for it, or generate an SSL/TLS certificate, or do any web server configuration. While it is internal, you should still replace ",(0,a.kt)("inlineCode",{parentName:"em"},"jitsi-meet.example.com")," with your hostname.")),(0,a.kt)("h2",{id:"jitsi-meet-configuration"},"Jitsi Meet configuration"),(0,a.kt)("p",null,"In config.js, the ",(0,a.kt)("inlineCode",{parentName:"p"},"anonymousdomain")," options has to be set."),(0,a.kt)("p",null,"If you have installed jitsi-meet from the Debian package, these changes should be made in ",(0,a.kt)("inlineCode",{parentName:"p"},"/etc/jitsi/meet/[your-hostname]-config.js"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"var config = {\n hosts: {\n domain: 'jitsi-meet.example.com',\n anonymousdomain: 'guest.jitsi-meet.example.com',\n ...\n },\n ...\n}\n")),(0,a.kt)("h2",{id:"jicofo-configuration"},"Jicofo configuration"),(0,a.kt)("p",null,"When running Jicofo, specify your main domain in an additional configuration\nproperty. Jicofo will accept conference allocation requests only from the\nauthenticated domain. This should go as a new 'authentication' section in ",(0,a.kt)("inlineCode",{parentName:"p"},"/etc/jitsi/jicofo/jicofo.conf"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"jicofo {\n authentication: {\n enabled: true\n type: XMPP\n login-url: jitsi-meet.example.com\n }\n ...\n")),(0,a.kt)("p",null,"When using token based authentication, the type must use ",(0,a.kt)("inlineCode",{parentName:"p"},"JWT")," as the scheme instead:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"jicofo {\n authentication: {\n enabled: true\n type: JWT\n login-url: jitsi-meet.example.com\n }\n ...\n")),(0,a.kt)("h2",{id:"create-users-in-prosody-internal-auth"},"Create users in Prosody (internal auth)"),(0,a.kt)("p",null,"Finally, run ",(0,a.kt)("inlineCode",{parentName:"p"},"prosodyctl")," to create a user in Prosody:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"sudo prosodyctl register jitsi-meet.example.com \n")),(0,a.kt)("p",null,"and then restart prosody, jicofo and jitsi-videobridge2"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"systemctl restart prosody\nsystemctl restart jicofo\nsystemctl restart jitsi-videobridge2\n")),(0,a.kt)("admonition",{type:"note"},(0,a.kt)("p",{parentName:"admonition"},"Docker users may require an alternate config path. Users of the official ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/jitsi/docker-jitsi-meet"},(0,a.kt)("inlineCode",{parentName:"a"},"jitsi/prosody"))," image should invoke ",(0,a.kt)("inlineCode",{parentName:"p"},"prosodyctl")," as follows."),(0,a.kt)("pre",{parentName:"admonition"},(0,a.kt)("code",{parentName:"pre"},"prosodyctl --config /config/prosody.cfg.lua register meet.jitsi \n")),(0,a.kt)("p",{parentName:"admonition"},"Full documentation for ",(0,a.kt)("inlineCode",{parentName:"p"},"prosodyctl")," can be found on ",(0,a.kt)("a",{parentName:"p",href:"https://prosody.im/doc/prosodyctl"},"the official site"),".")),(0,a.kt)("h2",{id:"optional-jigasi-configuration"},"Optional: Jigasi configuration"),(0,a.kt)("h3",{id:"enable-authentication-1"},"Enable Authentication"),(0,a.kt)("p",null,"If you are using Jigasi, set it to authenticate by editing the following lines in ",(0,a.kt)("inlineCode",{parentName:"p"},"/etc/jitsi/jigasi/sip-communicator.properties"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"org.jitsi.jigasi.xmpp.acc.USER_ID=SOME_USER@SOME_DOMAIN\norg.jitsi.jigasi.xmpp.acc.PASS=SOME_PASS\norg.jitsi.jigasi.xmpp.acc.ANONYMOUS_AUTH=false\n")),(0,a.kt)("p",null,"Note that the password is the actual plaintext password, not a base64 encoding."),(0,a.kt)("h3",{id:"debugging"},"Debugging"),(0,a.kt)("p",null,"If you experience problems with a certificate chain, you may need to uncomment the following line, also in ",(0,a.kt)("inlineCode",{parentName:"p"},"sip-communicator.properties"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"net.java.sip.communicator.service.gui.ALWAYS_TRUST_MODE_ENABLED=true\n")),(0,a.kt)("admonition",{type:"note"},(0,a.kt)("p",{parentName:"admonition"},"This should only be used for testing/debugging purposes, or in controlled environments. If you confirm that this is the problem, you should then solve it in another way (e.g. get a signed certificate for Prosody, or add the particular certificate to Jigasi\u2019s trust store).")))}m.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[8029],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>m});var o=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function r(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var l=o.createContext({}),c=function(e){var t=o.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},u=function(e){var t=c(e.components);return o.createElement(l.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},d=o.forwardRef((function(e,t){var n=e.components,i=e.mdxType,a=e.originalType,l=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),d=c(n),m=i,f=d["".concat(l,".").concat(m)]||d[m]||p[m]||a;return n?o.createElement(f,r(r({ref:t},u),{},{components:n})):o.createElement(f,r({ref:t},u))}));function m(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=n.length,r=new Array(a);r[0]=d;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:i,r[1]=s;for(var c=2;c{n.r(t),n.d(t,{assets:()=>u,contentTitle:()=>l,default:()=>m,frontMatter:()=>s,metadata:()=>c,toc:()=>p});var o=n(7462),i=n(3366),a=(n(7294),n(3905)),r=["components"],s={id:"secure-domain",title:"Secure Domain setup",sidebar_label:"Authentication (Secure Domain)"},l=void 0,c={unversionedId:"devops-guide/secure-domain",id:"devops-guide/secure-domain",title:"Secure Domain setup",description:"It is possible to allow only authenticated users to create new conference",source:"@site/docs/devops-guide/secure-domain.md",sourceDirName:"devops-guide",slug:"/devops-guide/secure-domain",permalink:"/handbook/docs/devops-guide/secure-domain",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/devops-guide/secure-domain.md",tags:[],version:"current",lastUpdatedAt:1695203964,formattedLastUpdatedAt:"Sep 20, 2023",frontMatter:{id:"secure-domain",title:"Secure Domain setup",sidebar_label:"Authentication (Secure Domain)"},sidebar:"docs",previous:{title:"Configuration",permalink:"/handbook/docs/category/configuration"},next:{title:"LDAP Authentication",permalink:"/handbook/docs/devops-guide/ldap-authentication"}},u={},p=[{value:"Prosody configuration",id:"prosody-configuration",level:2},{value:"Enable authentication",id:"enable-authentication",level:3},{value:"Enable anonymous login for guests",id:"enable-anonymous-login-for-guests",level:3},{value:"Jitsi Meet configuration",id:"jitsi-meet-configuration",level:2},{value:"Jicofo configuration",id:"jicofo-configuration",level:2},{value:"Create users in Prosody (internal auth)",id:"create-users-in-prosody-internal-auth",level:2},{value:"Optional: Jigasi configuration",id:"optional-jigasi-configuration",level:2},{value:"Enable Authentication",id:"enable-authentication-1",level:3},{value:"Debugging",id:"debugging",level:3}],d={toc:p};function m(e){var t=e.components,n=(0,i.Z)(e,r);return(0,a.kt)("wrapper",(0,o.Z)({},d,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("p",null,"It is possible to allow only authenticated users to create new conference\nrooms. Whenever a new room is about to be created, Jitsi Meet will prompt for\na user name and password. After the room is created, others will be able to join\nfrom anonymous domain. Here's what has to be configured:"),(0,a.kt)("h2",{id:"prosody-configuration"},"Prosody configuration"),(0,a.kt)("p",null,"If you have installed Jitsi Meet from the Debian package, these changes should be made in ",(0,a.kt)("inlineCode",{parentName:"p"},"/etc/prosody/conf.avail/[your-hostname].cfg.lua")),(0,a.kt)("h3",{id:"enable-authentication"},"Enable authentication"),(0,a.kt)("p",null,"Inside the ",(0,a.kt)("inlineCode",{parentName:"p"},'VirtualHost "[your-hostname]"')," block, replace anonymous authentication with hashed password authentication:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},'VirtualHost "jitsi-meet.example.com"\n authentication = "internal_hashed"\n')),(0,a.kt)("p",null,"Replace ",(0,a.kt)("inlineCode",{parentName:"p"},"jitsi-meet.example.com")," with your hostname."),(0,a.kt)("h3",{id:"enable-anonymous-login-for-guests"},"Enable anonymous login for guests"),(0,a.kt)("p",null,"Add this block ",(0,a.kt)("strong",{parentName:"p"},"after the previous VirtualHost")," to enable the anonymous login method for guests:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},'VirtualHost "guest.jitsi-meet.example.com"\n authentication = "anonymous"\n c2s_require_encryption = false\n')),(0,a.kt)("p",null,(0,a.kt)("em",{parentName:"p"},"Note that ",(0,a.kt)("inlineCode",{parentName:"em"},"guest.jitsi-meet.example.com")," is internal to Jitsi, and you do not need to (and should not) create a DNS record for it, or generate an SSL/TLS certificate, or do any web server configuration. While it is internal, you should still replace ",(0,a.kt)("inlineCode",{parentName:"em"},"jitsi-meet.example.com")," with your hostname.")),(0,a.kt)("h2",{id:"jitsi-meet-configuration"},"Jitsi Meet configuration"),(0,a.kt)("p",null,"In config.js, the ",(0,a.kt)("inlineCode",{parentName:"p"},"anonymousdomain")," options has to be set."),(0,a.kt)("p",null,"If you have installed jitsi-meet from the Debian package, these changes should be made in ",(0,a.kt)("inlineCode",{parentName:"p"},"/etc/jitsi/meet/[your-hostname]-config.js"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"var config = {\n hosts: {\n domain: 'jitsi-meet.example.com',\n anonymousdomain: 'guest.jitsi-meet.example.com',\n ...\n },\n ...\n}\n")),(0,a.kt)("h2",{id:"jicofo-configuration"},"Jicofo configuration"),(0,a.kt)("p",null,"When running Jicofo, specify your main domain in an additional configuration\nproperty. Jicofo will accept conference allocation requests only from the\nauthenticated domain. This should go as a new 'authentication' section in ",(0,a.kt)("inlineCode",{parentName:"p"},"/etc/jitsi/jicofo/jicofo.conf"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"jicofo {\n authentication: {\n enabled: true\n type: XMPP\n login-url: jitsi-meet.example.com\n }\n ...\n")),(0,a.kt)("p",null,"When using token based authentication, the type must use ",(0,a.kt)("inlineCode",{parentName:"p"},"JWT")," as the scheme instead:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"jicofo {\n authentication: {\n enabled: true\n type: JWT\n login-url: jitsi-meet.example.com\n }\n ...\n")),(0,a.kt)("h2",{id:"create-users-in-prosody-internal-auth"},"Create users in Prosody (internal auth)"),(0,a.kt)("p",null,"Finally, run ",(0,a.kt)("inlineCode",{parentName:"p"},"prosodyctl")," to create a user in Prosody:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"sudo prosodyctl register jitsi-meet.example.com \n")),(0,a.kt)("p",null,"and then restart prosody, jicofo and jitsi-videobridge2"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"systemctl restart prosody\nsystemctl restart jicofo\nsystemctl restart jitsi-videobridge2\n")),(0,a.kt)("admonition",{type:"note"},(0,a.kt)("p",{parentName:"admonition"},"Docker users may require an alternate config path. Users of the official ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/jitsi/docker-jitsi-meet"},(0,a.kt)("inlineCode",{parentName:"a"},"jitsi/prosody"))," image should invoke ",(0,a.kt)("inlineCode",{parentName:"p"},"prosodyctl")," as follows."),(0,a.kt)("pre",{parentName:"admonition"},(0,a.kt)("code",{parentName:"pre"},"prosodyctl --config /config/prosody.cfg.lua register meet.jitsi \n")),(0,a.kt)("p",{parentName:"admonition"},"Full documentation for ",(0,a.kt)("inlineCode",{parentName:"p"},"prosodyctl")," can be found on ",(0,a.kt)("a",{parentName:"p",href:"https://prosody.im/doc/prosodyctl"},"the official site"),".")),(0,a.kt)("h2",{id:"optional-jigasi-configuration"},"Optional: Jigasi configuration"),(0,a.kt)("h3",{id:"enable-authentication-1"},"Enable Authentication"),(0,a.kt)("p",null,"If you are using Jigasi, set it to authenticate by editing the following lines in ",(0,a.kt)("inlineCode",{parentName:"p"},"/etc/jitsi/jigasi/sip-communicator.properties"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"org.jitsi.jigasi.xmpp.acc.USER_ID=SOME_USER@SOME_DOMAIN\norg.jitsi.jigasi.xmpp.acc.PASS=SOME_PASS\norg.jitsi.jigasi.xmpp.acc.ANONYMOUS_AUTH=false\n")),(0,a.kt)("p",null,"Note that the password is the actual plaintext password, not a base64 encoding."),(0,a.kt)("h3",{id:"debugging"},"Debugging"),(0,a.kt)("p",null,"If you experience problems with a certificate chain, you may need to uncomment the following line, also in ",(0,a.kt)("inlineCode",{parentName:"p"},"sip-communicator.properties"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"net.java.sip.communicator.service.gui.ALWAYS_TRUST_MODE_ENABLED=true\n")),(0,a.kt)("admonition",{type:"note"},(0,a.kt)("p",{parentName:"admonition"},"This should only be used for testing/debugging purposes, or in controlled environments. If you confirm that this is the problem, you should then solve it in another way (e.g. get a signed certificate for Prosody, or add the particular certificate to Jigasi\u2019s trust store).")))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/095cc5b8.7829563c.js b/assets/js/095cc5b8.3a71f2ab.js similarity index 99% rename from assets/js/095cc5b8.7829563c.js rename to assets/js/095cc5b8.3a71f2ab.js index d2151267b..5c225cd9f 100644 --- a/assets/js/095cc5b8.7829563c.js +++ b/assets/js/095cc5b8.3a71f2ab.js @@ -1 +1 @@ -"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[3981],{3905:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>m});var o=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function a(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var p=o.createContext({}),l=function(e){var t=o.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},d=function(e){var t=l(e.components);return o.createElement(p.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},u=o.forwardRef((function(e,t){var n=e.components,r=e.mdxType,i=e.originalType,p=e.parentName,d=s(e,["components","mdxType","originalType","parentName"]),u=l(n),m=r,f=u["".concat(p,".").concat(m)]||u[m]||c[m]||i;return n?o.createElement(f,a(a({ref:t},d),{},{components:n})):o.createElement(f,a({ref:t},d))}));function m(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var i=n.length,a=new Array(i);a[0]=u;var s={};for(var p in t)hasOwnProperty.call(t,p)&&(s[p]=t[p]);s.originalType=e,s.mdxType="string"==typeof e?e:r,a[1]=s;for(var l=2;l{n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>p,default:()=>m,frontMatter:()=>s,metadata:()=>l,toc:()=>c});var o=n(7462),r=n(3366),i=(n(7294),n(3905)),a=["components"],s={id:"videosipgw",title:"Configuring a video SIP gateway",sidebar_label:"Video SIP gateway"},p=void 0,l={unversionedId:"devops-guide/videosipgw",id:"devops-guide/videosipgw",title:"Configuring a video SIP gateway",description:"This document describes how you can configure jitsi-meet to use sipgw jibri and enable rooms in 'Add people dialog'",source:"@site/docs/devops-guide/video-sipgw.md",sourceDirName:"devops-guide",slug:"/devops-guide/videosipgw",permalink:"/handbook/docs/devops-guide/videosipgw",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/devops-guide/video-sipgw.md",tags:[],version:"current",lastUpdatedAt:1694635654,formattedLastUpdatedAt:"Sep 13, 2023",frontMatter:{id:"videosipgw",title:"Configuring a video SIP gateway",sidebar_label:"Video SIP gateway"},sidebar:"docs",previous:{title:"Speaker Stats",permalink:"/handbook/docs/devops-guide/speakerstats"},next:{title:"Cloud API",permalink:"/handbook/docs/devops-guide/cloud-api"}},d={},c=[{value:"People search service",id:"people-search-service",level:2}],u={toc:c};function m(e){var t=e.components,n=(0,r.Z)(e,a);return(0,i.kt)("wrapper",(0,o.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"This document describes how you can configure jitsi-meet to use sipgw jibri and enable rooms in 'Add people dialog'\nYou will need a working deployment of jibri configured to use a regular sip video device, for more info check out the ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jibri/blob/master/README.md"},"jibri documentation"),"."),(0,i.kt)("p",null,"This feature is available for non-guests of the system, so this relies on setting in config.js ",(0,i.kt)("inlineCode",{parentName:"p"},"enableUserRolesBasedOnToken: true")," and providing a jwt token when accessing the conference."),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Jicofo configuration:\nedit /etc/jitsi/jicofo/sip-communicator.properties (or similar), set the appropriate MUC to look for the Jibri Controllers. This should be the same MUC as is referenced in jibri's config.json file. Restart Jicofo after setting this property.")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"}," org.jitsi.jicofo.jibri.SIP_BREWERY=TheSipBrewery@conference.yourdomain.com\n")),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Jitsi Meet configuration:")),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"config.js: add ")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"}," enableUserRolesBasedOnToken: true,\n peopleSearchQueryTypes: ['conferenceRooms'],\n peopleSearchUrl: 'https://api.yourdomain.com/testpath/searchpeople',\n")),(0,i.kt)("p",null,"The combination of the above settings and providing a jwt token will enable a button under invite option which will show the dialog 'Add people'."),(0,i.kt)("h2",{id:"people-search-service"},"People search service"),(0,i.kt)("p",null,"When searching in the dialog, a request for results is made to the ",(0,i.kt)("inlineCode",{parentName:"p"},"peopleSearchUrl")," service."),(0,i.kt)("p",null,"The request is in the following format:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"https://api.yourdomain.com/testpath/searchpeople?query=testroomname&queryTypes=[%22conferenceRooms%22]&jwt=somejwt\n")),(0,i.kt)("p",null,"The parameters are:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"query - The text entered by the user."),(0,i.kt)("li",{parentName:"ul"},"queryTypes - What type of results we want people, rooms, conferenceRooms. This is the value from config.js ",(0,i.kt)("inlineCode",{parentName:"li"},"peopleSearchQueryTypes")),(0,i.kt)("li",{parentName:"ul"},"jwt - The token used by the user to access the conference.")),(0,i.kt)("p",null,"The response of the service is a json in the following format:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'[\n {\n "id": "address@sip.domain.com",\n "name": "Some room name",\n "type": "videosipgw"\n },\n {\n "id": "address2@sip.domain.com",\n "name": "Some room name2",\n "type": "videosipgw"\n }\n]\n')),(0,i.kt)("p",null,"Type should be ",(0,i.kt)("inlineCode",{parentName:"p"},"videosipgw"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"name")," is the name shown to the user and ",(0,i.kt)("inlineCode",{parentName:"p"},"id")," is the sip address to be called by the sipgw jibri."))}m.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[3981],{3905:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>m});var o=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function a(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var p=o.createContext({}),l=function(e){var t=o.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},d=function(e){var t=l(e.components);return o.createElement(p.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},u=o.forwardRef((function(e,t){var n=e.components,r=e.mdxType,i=e.originalType,p=e.parentName,d=s(e,["components","mdxType","originalType","parentName"]),u=l(n),m=r,f=u["".concat(p,".").concat(m)]||u[m]||c[m]||i;return n?o.createElement(f,a(a({ref:t},d),{},{components:n})):o.createElement(f,a({ref:t},d))}));function m(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var i=n.length,a=new Array(i);a[0]=u;var s={};for(var p in t)hasOwnProperty.call(t,p)&&(s[p]=t[p]);s.originalType=e,s.mdxType="string"==typeof e?e:r,a[1]=s;for(var l=2;l{n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>p,default:()=>m,frontMatter:()=>s,metadata:()=>l,toc:()=>c});var o=n(7462),r=n(3366),i=(n(7294),n(3905)),a=["components"],s={id:"videosipgw",title:"Configuring a video SIP gateway",sidebar_label:"Video SIP gateway"},p=void 0,l={unversionedId:"devops-guide/videosipgw",id:"devops-guide/videosipgw",title:"Configuring a video SIP gateway",description:"This document describes how you can configure jitsi-meet to use sipgw jibri and enable rooms in 'Add people dialog'",source:"@site/docs/devops-guide/video-sipgw.md",sourceDirName:"devops-guide",slug:"/devops-guide/videosipgw",permalink:"/handbook/docs/devops-guide/videosipgw",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/devops-guide/video-sipgw.md",tags:[],version:"current",lastUpdatedAt:1695203964,formattedLastUpdatedAt:"Sep 20, 2023",frontMatter:{id:"videosipgw",title:"Configuring a video SIP gateway",sidebar_label:"Video SIP gateway"},sidebar:"docs",previous:{title:"Speaker Stats",permalink:"/handbook/docs/devops-guide/speakerstats"},next:{title:"Cloud API",permalink:"/handbook/docs/devops-guide/cloud-api"}},d={},c=[{value:"People search service",id:"people-search-service",level:2}],u={toc:c};function m(e){var t=e.components,n=(0,r.Z)(e,a);return(0,i.kt)("wrapper",(0,o.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"This document describes how you can configure jitsi-meet to use sipgw jibri and enable rooms in 'Add people dialog'\nYou will need a working deployment of jibri configured to use a regular sip video device, for more info check out the ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jibri/blob/master/README.md"},"jibri documentation"),"."),(0,i.kt)("p",null,"This feature is available for non-guests of the system, so this relies on setting in config.js ",(0,i.kt)("inlineCode",{parentName:"p"},"enableUserRolesBasedOnToken: true")," and providing a jwt token when accessing the conference."),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Jicofo configuration:\nedit /etc/jitsi/jicofo/sip-communicator.properties (or similar), set the appropriate MUC to look for the Jibri Controllers. This should be the same MUC as is referenced in jibri's config.json file. Restart Jicofo after setting this property.")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"}," org.jitsi.jicofo.jibri.SIP_BREWERY=TheSipBrewery@conference.yourdomain.com\n")),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Jitsi Meet configuration:")),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"config.js: add ")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"}," enableUserRolesBasedOnToken: true,\n peopleSearchQueryTypes: ['conferenceRooms'],\n peopleSearchUrl: 'https://api.yourdomain.com/testpath/searchpeople',\n")),(0,i.kt)("p",null,"The combination of the above settings and providing a jwt token will enable a button under invite option which will show the dialog 'Add people'."),(0,i.kt)("h2",{id:"people-search-service"},"People search service"),(0,i.kt)("p",null,"When searching in the dialog, a request for results is made to the ",(0,i.kt)("inlineCode",{parentName:"p"},"peopleSearchUrl")," service."),(0,i.kt)("p",null,"The request is in the following format:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"https://api.yourdomain.com/testpath/searchpeople?query=testroomname&queryTypes=[%22conferenceRooms%22]&jwt=somejwt\n")),(0,i.kt)("p",null,"The parameters are:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"query - The text entered by the user."),(0,i.kt)("li",{parentName:"ul"},"queryTypes - What type of results we want people, rooms, conferenceRooms. This is the value from config.js ",(0,i.kt)("inlineCode",{parentName:"li"},"peopleSearchQueryTypes")),(0,i.kt)("li",{parentName:"ul"},"jwt - The token used by the user to access the conference.")),(0,i.kt)("p",null,"The response of the service is a json in the following format:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'[\n {\n "id": "address@sip.domain.com",\n "name": "Some room name",\n "type": "videosipgw"\n },\n {\n "id": "address2@sip.domain.com",\n "name": "Some room name2",\n "type": "videosipgw"\n }\n]\n')),(0,i.kt)("p",null,"Type should be ",(0,i.kt)("inlineCode",{parentName:"p"},"videosipgw"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"name")," is the name shown to the user and ",(0,i.kt)("inlineCode",{parentName:"p"},"id")," is the sip address to be called by the sipgw jibri."))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/0e384e19.a23b1f5c.js b/assets/js/0e384e19.3a326472.js similarity index 98% rename from assets/js/0e384e19.a23b1f5c.js rename to assets/js/0e384e19.3a326472.js index ee2a407ef..5fcdc2916 100644 --- a/assets/js/0e384e19.a23b1f5c.js +++ b/assets/js/0e384e19.3a326472.js @@ -1 +1 @@ -"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[9671],{3905:(e,t,r)=>{r.d(t,{Zo:()=>c,kt:()=>h});var n=r(7294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var p=n.createContext({}),l=function(e){var t=n.useContext(p),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},c=function(e){var t=l(e.components);return n.createElement(p.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,p=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),d=l(r),h=o,f=d["".concat(p,".").concat(h)]||d[h]||u[h]||a;return r?n.createElement(f,i(i({ref:t},c),{},{components:r})):n.createElement(f,i({ref:t},c))}));function h(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,i=new Array(a);i[0]=d;var s={};for(var p in t)hasOwnProperty.call(t,p)&&(s[p]=t[p]);s.originalType=e,s.mdxType="string"==typeof e?e:o,i[1]=s;for(var l=2;l{r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>p,default:()=>h,frontMatter:()=>s,metadata:()=>l,toc:()=>u});var n=r(7462),o=r(3366),a=(r(7294),r(3905)),i=["components"],s={id:"intro",title:"Introduction"},p=void 0,l={unversionedId:"intro",id:"intro",title:"Introduction",description:"What is Jitsi?",source:"@site/docs/intro.md",sourceDirName:".",slug:"/intro",permalink:"/handbook/docs/intro",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/intro.md",tags:[],version:"current",lastUpdatedAt:1694635654,formattedLastUpdatedAt:"Sep 13, 2023",frontMatter:{id:"intro",title:"Introduction"},sidebar:"docs",next:{title:"Architecture",permalink:"/handbook/docs/architecture"}},c={},u=[{value:"What is Jitsi?",id:"what-is-jitsi",level:2},{value:"About this handbook",id:"about-this-handbook",level:2},{value:"JaaS customers",id:"jaas-customers",level:2}],d={toc:u};function h(e){var t=e.components,r=(0,o.Z)(e,i);return(0,a.kt)("wrapper",(0,n.Z)({},d,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h2",{id:"what-is-jitsi"},"What is Jitsi?"),(0,a.kt)("p",null,"Jitsi is a ",(0,a.kt)("a",{parentName:"p",href:"/handbook/docs/architecture"},"collection of Open Source projects")," which provide state-of-the-art video conferencing\ncapabilities that are secure, easy to use and easy to self-host."),(0,a.kt)("h2",{id:"about-this-handbook"},"About this handbook"),(0,a.kt)("p",null,"This handbook aims to be the one-stop-shop for all Jitsi documentation."),(0,a.kt)("admonition",{title:"It's work in progress.",type:"note"},(0,a.kt)("p",{parentName:"admonition"},"If you want to help out please create a ",(0,a.kt)("strong",{parentName:"p"},"Pull Request")," in our ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/jitsi/handbook"},"GitHub repository"),"!")),(0,a.kt)("p",null,"The content is divided in 3 main areas:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"/docs/category/user-guide"},"User guide"),": Designed to help users of the service, to better\nunderstand all the available features and how to use them.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"/docs/category/developer-guide"},"Developer guide"),": Designed to help developers who want to either\nintegrate the Jitsi Meet API / SDK in their products or want to improve Jitsi Meet\nitself by developing new features or fixing bugs.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"/handbook/docs/devops-guide/"},"Self-Hosting guide"),": Designed for folks wanting to self-host, system administrators\nor anyone who wishes to deploy and operate their own Jitsi Meet instance."))),(0,a.kt)("h2",{id:"jaas-customers"},"JaaS customers"),(0,a.kt)("p",null,"Are you a JaaS customer? If so, please start ",(0,a.kt)("a",{parentName:"p",href:"https://developer.8x8.com/jaas/docs/jaas-onboarding"},"here"),"."))}h.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[9671],{3905:(e,t,r)=>{r.d(t,{Zo:()=>c,kt:()=>h});var n=r(7294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var p=n.createContext({}),l=function(e){var t=n.useContext(p),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},c=function(e){var t=l(e.components);return n.createElement(p.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,p=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),d=l(r),h=o,f=d["".concat(p,".").concat(h)]||d[h]||u[h]||a;return r?n.createElement(f,i(i({ref:t},c),{},{components:r})):n.createElement(f,i({ref:t},c))}));function h(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,i=new Array(a);i[0]=d;var s={};for(var p in t)hasOwnProperty.call(t,p)&&(s[p]=t[p]);s.originalType=e,s.mdxType="string"==typeof e?e:o,i[1]=s;for(var l=2;l{r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>p,default:()=>h,frontMatter:()=>s,metadata:()=>l,toc:()=>u});var n=r(7462),o=r(3366),a=(r(7294),r(3905)),i=["components"],s={id:"intro",title:"Introduction"},p=void 0,l={unversionedId:"intro",id:"intro",title:"Introduction",description:"What is Jitsi?",source:"@site/docs/intro.md",sourceDirName:".",slug:"/intro",permalink:"/handbook/docs/intro",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/intro.md",tags:[],version:"current",lastUpdatedAt:1695203964,formattedLastUpdatedAt:"Sep 20, 2023",frontMatter:{id:"intro",title:"Introduction"},sidebar:"docs",next:{title:"Architecture",permalink:"/handbook/docs/architecture"}},c={},u=[{value:"What is Jitsi?",id:"what-is-jitsi",level:2},{value:"About this handbook",id:"about-this-handbook",level:2},{value:"JaaS customers",id:"jaas-customers",level:2}],d={toc:u};function h(e){var t=e.components,r=(0,o.Z)(e,i);return(0,a.kt)("wrapper",(0,n.Z)({},d,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h2",{id:"what-is-jitsi"},"What is Jitsi?"),(0,a.kt)("p",null,"Jitsi is a ",(0,a.kt)("a",{parentName:"p",href:"/handbook/docs/architecture"},"collection of Open Source projects")," which provide state-of-the-art video conferencing\ncapabilities that are secure, easy to use and easy to self-host."),(0,a.kt)("h2",{id:"about-this-handbook"},"About this handbook"),(0,a.kt)("p",null,"This handbook aims to be the one-stop-shop for all Jitsi documentation."),(0,a.kt)("admonition",{title:"It's work in progress.",type:"note"},(0,a.kt)("p",{parentName:"admonition"},"If you want to help out please create a ",(0,a.kt)("strong",{parentName:"p"},"Pull Request")," in our ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/jitsi/handbook"},"GitHub repository"),"!")),(0,a.kt)("p",null,"The content is divided in 3 main areas:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"/docs/category/user-guide"},"User guide"),": Designed to help users of the service, to better\nunderstand all the available features and how to use them.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"/docs/category/developer-guide"},"Developer guide"),": Designed to help developers who want to either\nintegrate the Jitsi Meet API / SDK in their products or want to improve Jitsi Meet\nitself by developing new features or fixing bugs.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"/handbook/docs/devops-guide/"},"Self-Hosting guide"),": Designed for folks wanting to self-host, system administrators\nor anyone who wishes to deploy and operate their own Jitsi Meet instance."))),(0,a.kt)("h2",{id:"jaas-customers"},"JaaS customers"),(0,a.kt)("p",null,"Are you a JaaS customer? If so, please start ",(0,a.kt)("a",{parentName:"p",href:"https://developer.8x8.com/jaas/docs/jaas-onboarding"},"here"),"."))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/1107b5bf.b87ff78b.js b/assets/js/1107b5bf.3e4c3466.js similarity index 99% rename from assets/js/1107b5bf.b87ff78b.js rename to assets/js/1107b5bf.3e4c3466.js index fcf9a9949..38969c1cb 100644 --- a/assets/js/1107b5bf.b87ff78b.js +++ b/assets/js/1107b5bf.3e4c3466.js @@ -1 +1 @@ -"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[4227],{3905:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>m});var r=n(7294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function a(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var s=r.createContext({}),p=function(e){var t=r.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},d=function(e){var t=p(e.components);return r.createElement(s.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},c=r.forwardRef((function(e,t){var n=e.components,o=e.mdxType,i=e.originalType,s=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),c=p(n),m=o,f=c["".concat(s,".").concat(m)]||c[m]||u[m]||i;return n?r.createElement(f,a(a({ref:t},d),{},{components:n})):r.createElement(f,a({ref:t},d))}));function m(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=n.length,a=new Array(i);a[0]=c;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:o,a[1]=l;for(var p=2;p{n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>s,default:()=>m,frontMatter:()=>l,metadata:()=>p,toc:()=>u});var r=n(7462),o=n(3366),i=(n(7294),n(3905)),a=["components"],l={id:"faq",title:"FAQ"},s=void 0,p={unversionedId:"devops-guide/faq",id:"devops-guide/faq",title:"FAQ",description:"How to migrate away from multiplexing and enable bridge websockets",source:"@site/docs/devops-guide/faq.md",sourceDirName:"devops-guide",slug:"/devops-guide/faq",permalink:"/handbook/docs/devops-guide/faq",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/devops-guide/faq.md",tags:[],version:"current",lastUpdatedAt:1694635654,formattedLastUpdatedAt:"Sep 13, 2023",frontMatter:{id:"faq",title:"FAQ"},sidebar:"docs",previous:{title:"Video Tutorials",permalink:"/handbook/docs/devops-guide/devops-guide-videotutorials"}},d={},u=[{value:"How to migrate away from multiplexing and enable bridge websockets",id:"how-to-migrate-away-from-multiplexing-and-enable-bridge-websockets",level:2}],c={toc:u};function m(e){var t=e.components,n=(0,o.Z)(e,a);return(0,i.kt)("wrapper",(0,r.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h2",{id:"how-to-migrate-away-from-multiplexing-and-enable-bridge-websockets"},"How to migrate away from multiplexing and enable bridge websockets"),(0,i.kt)("p",null,"For a while, we were using nginx multiplexing to serve Jitsi Meet's content on https(port 443) and use the same port for running a turn server.\nThis proved to be problematic(you cannot use websockets with this setup) and we moved away from it.\nHere is how to remove multiplexing and enable websockets in favor of WebRTC Data Channels."),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},"Dropping multiplexing in nginx")),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"delete ",(0,i.kt)("inlineCode",{parentName:"li"},"/etc/nginx/modules-enabled/60-jitsi-meet.conf")),(0,i.kt)("li",{parentName:"ul"},"Then go to ",(0,i.kt)("inlineCode",{parentName:"li"},"/etc/nginx/sites-available/your-conf")," and change your virtual host to listen on 443 instead of 4444.")),(0,i.kt)("ol",{start:2},(0,i.kt)("li",{parentName:"ol"},"Edit turnserver config")),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"make sure your turnserver is listening on standard port tls port ",(0,i.kt)("inlineCode",{parentName:"li"},"5349"),". Make sure in ",(0,i.kt)("inlineCode",{parentName:"li"},"/etc/turnserver.conf")," you have the following: ",(0,i.kt)("inlineCode",{parentName:"li"},"tls-listening-port=5349")),(0,i.kt)("li",{parentName:"ul"},"In ",(0,i.kt)("inlineCode",{parentName:"li"},"/etc/prosody/conf.avail/your-conf.cfg.lua")," prosody is instructed to announce ",(0,i.kt)("inlineCode",{parentName:"li"},"turns")," turn server on port ",(0,i.kt)("inlineCode",{parentName:"li"},"5349")," by having this line:\n",(0,i.kt)("inlineCode",{parentName:"li"},'{ type = "turns", host = "your-domain", port = "5349", transport = "tcp" }'),". Make sure you replace ",(0,i.kt)("inlineCode",{parentName:"li"},"your-domain")," with the DNS of your deployment.")),(0,i.kt)("ol",{start:3},(0,i.kt)("li",{parentName:"ol"},"Add the bridge websocket location in your nginx config (add it after the ",(0,i.kt)("inlineCode",{parentName:"li"},"location = /xmpp-websocket")," section):",(0,i.kt)("pre",{parentName:"li"},(0,i.kt)("code",{parentName:"pre"},' # colibri (JVB) websockets for jvb1\n location ~ ^/colibri-ws/default-id/(.*) {\n proxy_pass http://127.0.0.1:9090/colibri-ws/default-id/$1$is_args$args;\n proxy_http_version 1.1;\n proxy_set_header Upgrade $http_upgrade;\n proxy_set_header Connection "upgrade";\n tcp_nodelay on;\n }\n'))),(0,i.kt)("li",{parentName:"ol"},"Enable the websockets in Jitsi Videobridge. Make sure in ",(0,i.kt)("inlineCode",{parentName:"li"},"/etc/jitsi/videobridge/jvb.conf")," you have:",(0,i.kt)("pre",{parentName:"li"},(0,i.kt)("code",{parentName:"pre"},'videobridge {\n http-servers {\n public {\n port = 9090\n }\n }\n websockets {\n enabled = true\n domain = "your-domain:443"\n tls = true\n }\n}\n'))," Make sure you replace ",(0,i.kt)("inlineCode",{parentName:"li"},"your-domain")," with the DNS of your deployment."),(0,i.kt)("li",{parentName:"ol"},"After restarting the bridge (",(0,i.kt)("inlineCode",{parentName:"li"},"systemctl restart jitsi-videobridge2"),") and nginx (",(0,i.kt)("inlineCode",{parentName:"li"},"systemctl restart nginx"),") you are good to go!")))}m.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[4227],{3905:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>m});var r=n(7294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function a(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var s=r.createContext({}),p=function(e){var t=r.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},d=function(e){var t=p(e.components);return r.createElement(s.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},c=r.forwardRef((function(e,t){var n=e.components,o=e.mdxType,i=e.originalType,s=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),c=p(n),m=o,f=c["".concat(s,".").concat(m)]||c[m]||u[m]||i;return n?r.createElement(f,a(a({ref:t},d),{},{components:n})):r.createElement(f,a({ref:t},d))}));function m(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=n.length,a=new Array(i);a[0]=c;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:o,a[1]=l;for(var p=2;p{n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>s,default:()=>m,frontMatter:()=>l,metadata:()=>p,toc:()=>u});var r=n(7462),o=n(3366),i=(n(7294),n(3905)),a=["components"],l={id:"faq",title:"FAQ"},s=void 0,p={unversionedId:"devops-guide/faq",id:"devops-guide/faq",title:"FAQ",description:"How to migrate away from multiplexing and enable bridge websockets",source:"@site/docs/devops-guide/faq.md",sourceDirName:"devops-guide",slug:"/devops-guide/faq",permalink:"/handbook/docs/devops-guide/faq",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/devops-guide/faq.md",tags:[],version:"current",lastUpdatedAt:1695203964,formattedLastUpdatedAt:"Sep 20, 2023",frontMatter:{id:"faq",title:"FAQ"},sidebar:"docs",previous:{title:"Video Tutorials",permalink:"/handbook/docs/devops-guide/devops-guide-videotutorials"}},d={},u=[{value:"How to migrate away from multiplexing and enable bridge websockets",id:"how-to-migrate-away-from-multiplexing-and-enable-bridge-websockets",level:2}],c={toc:u};function m(e){var t=e.components,n=(0,o.Z)(e,a);return(0,i.kt)("wrapper",(0,r.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h2",{id:"how-to-migrate-away-from-multiplexing-and-enable-bridge-websockets"},"How to migrate away from multiplexing and enable bridge websockets"),(0,i.kt)("p",null,"For a while, we were using nginx multiplexing to serve Jitsi Meet's content on https(port 443) and use the same port for running a turn server.\nThis proved to be problematic(you cannot use websockets with this setup) and we moved away from it.\nHere is how to remove multiplexing and enable websockets in favor of WebRTC Data Channels."),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},"Dropping multiplexing in nginx")),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"delete ",(0,i.kt)("inlineCode",{parentName:"li"},"/etc/nginx/modules-enabled/60-jitsi-meet.conf")),(0,i.kt)("li",{parentName:"ul"},"Then go to ",(0,i.kt)("inlineCode",{parentName:"li"},"/etc/nginx/sites-available/your-conf")," and change your virtual host to listen on 443 instead of 4444.")),(0,i.kt)("ol",{start:2},(0,i.kt)("li",{parentName:"ol"},"Edit turnserver config")),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"make sure your turnserver is listening on standard port tls port ",(0,i.kt)("inlineCode",{parentName:"li"},"5349"),". Make sure in ",(0,i.kt)("inlineCode",{parentName:"li"},"/etc/turnserver.conf")," you have the following: ",(0,i.kt)("inlineCode",{parentName:"li"},"tls-listening-port=5349")),(0,i.kt)("li",{parentName:"ul"},"In ",(0,i.kt)("inlineCode",{parentName:"li"},"/etc/prosody/conf.avail/your-conf.cfg.lua")," prosody is instructed to announce ",(0,i.kt)("inlineCode",{parentName:"li"},"turns")," turn server on port ",(0,i.kt)("inlineCode",{parentName:"li"},"5349")," by having this line:\n",(0,i.kt)("inlineCode",{parentName:"li"},'{ type = "turns", host = "your-domain", port = "5349", transport = "tcp" }'),". Make sure you replace ",(0,i.kt)("inlineCode",{parentName:"li"},"your-domain")," with the DNS of your deployment.")),(0,i.kt)("ol",{start:3},(0,i.kt)("li",{parentName:"ol"},"Add the bridge websocket location in your nginx config (add it after the ",(0,i.kt)("inlineCode",{parentName:"li"},"location = /xmpp-websocket")," section):",(0,i.kt)("pre",{parentName:"li"},(0,i.kt)("code",{parentName:"pre"},' # colibri (JVB) websockets for jvb1\n location ~ ^/colibri-ws/default-id/(.*) {\n proxy_pass http://127.0.0.1:9090/colibri-ws/default-id/$1$is_args$args;\n proxy_http_version 1.1;\n proxy_set_header Upgrade $http_upgrade;\n proxy_set_header Connection "upgrade";\n tcp_nodelay on;\n }\n'))),(0,i.kt)("li",{parentName:"ol"},"Enable the websockets in Jitsi Videobridge. Make sure in ",(0,i.kt)("inlineCode",{parentName:"li"},"/etc/jitsi/videobridge/jvb.conf")," you have:",(0,i.kt)("pre",{parentName:"li"},(0,i.kt)("code",{parentName:"pre"},'videobridge {\n http-servers {\n public {\n port = 9090\n }\n }\n websockets {\n enabled = true\n domain = "your-domain:443"\n tls = true\n }\n}\n'))," Make sure you replace ",(0,i.kt)("inlineCode",{parentName:"li"},"your-domain")," with the DNS of your deployment."),(0,i.kt)("li",{parentName:"ol"},"After restarting the bridge (",(0,i.kt)("inlineCode",{parentName:"li"},"systemctl restart jitsi-videobridge2"),") and nginx (",(0,i.kt)("inlineCode",{parentName:"li"},"systemctl restart nginx"),") you are good to go!")))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/129210bc.6082975c.js b/assets/js/129210bc.64be09f7.js similarity index 97% rename from assets/js/129210bc.6082975c.js rename to assets/js/129210bc.64be09f7.js index 4529856df..39c5638d4 100644 --- a/assets/js/129210bc.6082975c.js +++ b/assets/js/129210bc.64be09f7.js @@ -1 +1 @@ -"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[4383],{3905:(e,t,r)=>{r.d(t,{Zo:()=>c,kt:()=>k});var n=r(7294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function l(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var i=n.createContext({}),s=function(e){var t=n.useContext(i),r=t;return e&&(r="function"==typeof e?e(t):l(l({},t),e)),r},c=function(e){var t=s(e.components);return n.createElement(i.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},p=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,i=e.parentName,c=u(e,["components","mdxType","originalType","parentName"]),p=s(r),k=o,b=p["".concat(i,".").concat(k)]||p[k]||d[k]||a;return r?n.createElement(b,l(l({ref:t},c),{},{components:r})):n.createElement(b,l({ref:t},c))}));function k(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,l=new Array(a);l[0]=p;var u={};for(var i in t)hasOwnProperty.call(t,i)&&(u[i]=t[i]);u.originalType=e,u.mdxType="string"==typeof e?e:o,l[1]=u;for(var s=2;s{r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>i,default:()=>k,frontMatter:()=>u,metadata:()=>s,toc:()=>d});var n=r(7462),o=r(3366),a=(r(7294),r(3905)),l=["components"],u={id:"keyboard-shortcuts",title:"Keyboard shortcuts"},i=void 0,s={unversionedId:"user-guide/keyboard-shortcuts",id:"user-guide/keyboard-shortcuts",title:"Keyboard shortcuts",description:"* F - Show or hide video thumbnails",source:"@site/docs/user-guide/keyboard-shortcuts.md",sourceDirName:"user-guide",slug:"/user-guide/keyboard-shortcuts",permalink:"/handbook/docs/user-guide/keyboard-shortcuts",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/user-guide/keyboard-shortcuts.md",tags:[],version:"current",lastUpdatedAt:1694635654,formattedLastUpdatedAt:"Sep 13, 2023",frontMatter:{id:"keyboard-shortcuts",title:"Keyboard shortcuts"},sidebar:"docs",previous:{title:"Jitsi Meet for Google Calendar",permalink:"/handbook/docs/user-guide/user-guide-jitsi-meet-for-google-calendar"},next:{title:"Basic options",permalink:"/handbook/docs/user-guide/user-guide-basic"}},c={},d=[],p={toc:d};function k(e){var t=e.components,r=(0,o.Z)(e,l);return(0,a.kt)("wrapper",(0,n.Z)({},p,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("kbd",null,"F")," - Show or hide video thumbnails"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("kbd",null,"M")," - Mute or unmute your microphone"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("kbd",null,"V")," - Start or stop your camera"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("kbd",null,"A")," - Manage video quality"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("kbd",null,"C")," - Open or close the chat"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("kbd",null,"D")," - Switch between camera and screen sharing"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("kbd",null,"P")," - Show or hide the participants pane"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("kbd",null,"R")," - Raise or lower your hand"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("kbd",null,"S")," - View or exit full screen"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("kbd",null,"W")," - Toggle tile view"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("kbd",null,"?")," - Show or hide keyboard shortcuts"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("kbd",null,"SPACE")," - Push to talk"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("kbd",null,"T")," - Show speaker stats"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("kbd",null,"0")," - Focus on your video"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("kbd",null,"1"),"-",(0,a.kt)("kbd",null,"9")," - Focus on another person's video")))}k.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[4383],{3905:(e,t,r)=>{r.d(t,{Zo:()=>c,kt:()=>k});var n=r(7294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function l(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var i=n.createContext({}),s=function(e){var t=n.useContext(i),r=t;return e&&(r="function"==typeof e?e(t):l(l({},t),e)),r},c=function(e){var t=s(e.components);return n.createElement(i.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},p=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,i=e.parentName,c=u(e,["components","mdxType","originalType","parentName"]),p=s(r),k=o,b=p["".concat(i,".").concat(k)]||p[k]||d[k]||a;return r?n.createElement(b,l(l({ref:t},c),{},{components:r})):n.createElement(b,l({ref:t},c))}));function k(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,l=new Array(a);l[0]=p;var u={};for(var i in t)hasOwnProperty.call(t,i)&&(u[i]=t[i]);u.originalType=e,u.mdxType="string"==typeof e?e:o,l[1]=u;for(var s=2;s{r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>i,default:()=>k,frontMatter:()=>u,metadata:()=>s,toc:()=>d});var n=r(7462),o=r(3366),a=(r(7294),r(3905)),l=["components"],u={id:"keyboard-shortcuts",title:"Keyboard shortcuts"},i=void 0,s={unversionedId:"user-guide/keyboard-shortcuts",id:"user-guide/keyboard-shortcuts",title:"Keyboard shortcuts",description:"* F - Show or hide video thumbnails",source:"@site/docs/user-guide/keyboard-shortcuts.md",sourceDirName:"user-guide",slug:"/user-guide/keyboard-shortcuts",permalink:"/handbook/docs/user-guide/keyboard-shortcuts",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/user-guide/keyboard-shortcuts.md",tags:[],version:"current",lastUpdatedAt:1695203964,formattedLastUpdatedAt:"Sep 20, 2023",frontMatter:{id:"keyboard-shortcuts",title:"Keyboard shortcuts"},sidebar:"docs",previous:{title:"Jitsi Meet for Google Calendar",permalink:"/handbook/docs/user-guide/user-guide-jitsi-meet-for-google-calendar"},next:{title:"Basic options",permalink:"/handbook/docs/user-guide/user-guide-basic"}},c={},d=[],p={toc:d};function k(e){var t=e.components,r=(0,o.Z)(e,l);return(0,a.kt)("wrapper",(0,n.Z)({},p,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("kbd",null,"F")," - Show or hide video thumbnails"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("kbd",null,"M")," - Mute or unmute your microphone"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("kbd",null,"V")," - Start or stop your camera"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("kbd",null,"A")," - Manage video quality"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("kbd",null,"C")," - Open or close the chat"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("kbd",null,"D")," - Switch between camera and screen sharing"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("kbd",null,"P")," - Show or hide the participants pane"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("kbd",null,"R")," - Raise or lower your hand"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("kbd",null,"S")," - View or exit full screen"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("kbd",null,"W")," - Toggle tile view"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("kbd",null,"?")," - Show or hide keyboard shortcuts"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("kbd",null,"SPACE")," - Push to talk"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("kbd",null,"T")," - Show speaker stats"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("kbd",null,"0")," - Focus on your video"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("kbd",null,"1"),"-",(0,a.kt)("kbd",null,"9")," - Focus on another person's video")))}k.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/1a2ccd47.65ba650f.js b/assets/js/1a2ccd47.90d43c8e.js similarity index 99% rename from assets/js/1a2ccd47.65ba650f.js rename to assets/js/1a2ccd47.90d43c8e.js index 540402ce2..4a33b13d5 100644 --- a/assets/js/1a2ccd47.65ba650f.js +++ b/assets/js/1a2ccd47.90d43c8e.js @@ -1 +1 @@ -"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[6667],{3905:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>m});var i=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function r(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=i.createContext({}),p=function(e){var t=i.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},d=function(e){var t=p(e.components);return i.createElement(s.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},u=i.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,s=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),u=p(n),m=a,h=u["".concat(s,".").concat(m)]||u[m]||c[m]||o;return n?i.createElement(h,r(r({ref:t},d),{},{components:n})):i.createElement(h,r({ref:t},d))}));function m(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,r=new Array(o);r[0]=u;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:a,r[1]=l;for(var p=2;p{n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>s,default:()=>m,frontMatter:()=>l,metadata:()=>p,toc:()=>c});var i=n(7462),a=n(3366),o=(n(7294),n(3905)),r=["components"],l={id:"devops-guide-manual",title:"Self-Hosting Guide - Manual installation",sidebar_label:"Manual installation"},s=void 0,p={unversionedId:"devops-guide/devops-guide-manual",id:"devops-guide/devops-guide-manual",title:"Self-Hosting Guide - Manual installation",description:"We recommend following the quick-install document. The current document describes the steps that are needed to install a working deployment, but steps are easy to mess up, and the debian packages are more up-to-date, where this document is sometimes not updated to reflect latest changes.",source:"@site/docs/devops-guide/manual.md",sourceDirName:"devops-guide",slug:"/devops-guide/devops-guide-manual",permalink:"/handbook/docs/devops-guide/devops-guide-manual",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/devops-guide/manual.md",tags:[],version:"current",lastUpdatedAt:1694635654,formattedLastUpdatedAt:"Sep 13, 2023",frontMatter:{id:"devops-guide-manual",title:"Self-Hosting Guide - Manual installation",sidebar_label:"Manual installation"},sidebar:"docs",previous:{title:"Docker",permalink:"/handbook/docs/devops-guide/devops-guide-docker"},next:{title:"Configuration",permalink:"/handbook/docs/category/configuration"}},d={},c=[{value:"Network description",id:"network-description",level:2},{value:"Install prosody",id:"install-prosody",level:2},{value:"Configure prosody",id:"configure-prosody",level:2},{value:"Install Nginx",id:"install-nginx",level:2},{value:"Install Jitsi Videobridge",id:"install-jitsi-videobridge",level:2},{value:"Install Jitsi Conference Focus (jicofo)",id:"install-jitsi-conference-focus-jicofo",level:2},{value:"Deploy Jitsi Meet",id:"deploy-jitsi-meet",level:2},{value:"Running behind NAT",id:"running-behind-nat",level:2},{value:"Hold your first conference",id:"hold-your-first-conference",level:2},{value:"Enabling recording",id:"enabling-recording",level:2}],u={toc:c};function m(e){var t=e.components,n=(0,a.Z)(e,r);return(0,o.kt)("wrapper",(0,i.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("admonition",{title:"Manual installation is not recommended",type:"warning"},(0,o.kt)("p",{parentName:"admonition"},"We recommend following the ",(0,o.kt)("a",{parentName:"p",href:"https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-quickstart/"},"quick-install")," document. The current document describes the steps that are needed to install a working deployment, but steps are easy to mess up, and the debian packages are more up-to-date, where this document is sometimes not updated to reflect latest changes.")),(0,o.kt)("p",null,"This describes configuring a server ",(0,o.kt)("inlineCode",{parentName:"p"},"jitsi.example.com")," on a Debian-based distribution.",(0,o.kt)("br",{parentName:"p"}),"\n",(0,o.kt)("strong",{parentName:"p"},"For other distributions")," you can adapt the steps (especially changing the dependencies package installations (e.g. for nginx) and paths accordingly) so that it matches your host's distribution.",(0,o.kt)("br",{parentName:"p"}),"\n","You will also need to generate some passwords for ",(0,o.kt)("inlineCode",{parentName:"p"},"YOURSECRET1"),", ",(0,o.kt)("inlineCode",{parentName:"p"},"YOURSECRET2")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"YOURSECRET3"),"."),(0,o.kt)("p",null,"There are also some complete ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet/tree/master/doc/debian/"},"example config files")," available, mentioned in each section."),(0,o.kt)("p",null,"There are additional configurations to be done for a ",(0,o.kt)("a",{parentName:"p",href:"devops-guide-scalable"},"scalable installation"),"."),(0,o.kt)("h2",{id:"network-description"},"Network description"),(0,o.kt)("p",null,"This is how the network looks:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"}," + +\n | |\n | |\n v |\n 443 |\n +-------+ |\n | | |\n | Nginx | |\n | | |\n +--+-+--+ |\n | | |\n+------------+ | | +--------------+ |\n| | | | | | |\n| Jitsi Meet +<---+ +---\x3e+ prosody/xmpp | |\n| |files 5280 | | |\n+------------+ +--------------+ v\n 5222 ^ ^ 5222 10000\n +--------+ | | +-------------+\n | | | | | |\n | jicofo +----^ ^----+ videobridge |\n | | | |\n +--------+ +-------------+\n")),(0,o.kt)("h2",{id:"install-prosody"},"Install prosody"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"apt-get install prosody\n")),(0,o.kt)("h2",{id:"configure-prosody"},"Configure prosody"),(0,o.kt)("p",null,"Add config file in ",(0,o.kt)("inlineCode",{parentName:"p"},"/etc/prosody/conf.avail/jitsi.example.com.cfg.lua")," :"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"add your domain virtual host section:")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'VirtualHost "jitsi.example.com"\n authentication = "anonymous"\n ssl = {\n key = "/var/lib/prosody/jitsi.example.com.key";\n certificate = "/var/lib/prosody/jitsi.example.com.crt";\n }\n modules_enabled = {\n "bosh";\n "pubsub";\n }\n c2s_require_encryption = false\n')),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"add domain with authentication for conference focus user:")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'VirtualHost "auth.jitsi.example.com"\n ssl = {\n key = "/var/lib/prosody/auth.jitsi.example.com.key";\n certificate = "/var/lib/prosody/auth.jitsi.example.com.crt";\n }\n authentication = "internal_hashed"\n')),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"add focus user to server admins:")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'admins = { "focus@auth.jitsi.example.com" }\n')),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"and finally configure components:")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'Component "conference.jitsi.example.com" "muc"\nComponent "jitsi-videobridge.jitsi.example.com"\n component_secret = "YOURSECRET1"\nComponent "focus.jitsi.example.com"\n component_secret = "YOURSECRET2"\n')),(0,o.kt)("p",null,"Add link for the added configuration"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"ln -s /etc/prosody/conf.avail/jitsi.example.com.cfg.lua /etc/prosody/conf.d/jitsi.example.com.cfg.lua\n")),(0,o.kt)("p",null,"Generate certs for the domain:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"prosodyctl cert generate jitsi.example.com\nprosodyctl cert generate auth.jitsi.example.com\n")),(0,o.kt)("p",null,"Add auth.jitsi.example.com to the trusted certificates on the local machine:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"ln -sf /var/lib/prosody/auth.jitsi.example.com.crt /usr/local/share/ca-certificates/auth.jitsi.example.com.crt\nupdate-ca-certificates -f\n")),(0,o.kt)("p",null,"Note that the ",(0,o.kt)("inlineCode",{parentName:"p"},"-f")," flag is necessary if there are symlinks left from a previous installation."),(0,o.kt)("p",null,"If you are using a JDK package not provided by Debian, as the ones from adoptjdk, you should also make your JDK aware of the new debian certificate keystore replacing or linking the JDK ",(0,o.kt)("inlineCode",{parentName:"p"},"cacerts"),". Example, if you use JDK from adoptjdk:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"cd /usr/lib/jvm/adoptopenjdk-8-hotspot-amd64/jre\nln -sf /etc/ssl/certs/java/cacerts lib/security/cacerts\n")),(0,o.kt)("p",null,"Create conference focus user:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"prosodyctl register focus auth.jitsi.example.com YOURSECRET3\n")),(0,o.kt)("p",null,"Restart prosody XMPP server with the new config"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"prosodyctl restart\n")),(0,o.kt)("h2",{id:"install-nginx"},"Install Nginx"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"apt-get install nginx\n")),(0,o.kt)("p",null,"Add a new file ",(0,o.kt)("inlineCode",{parentName:"p"},"jitsi.example.com")," in ",(0,o.kt)("inlineCode",{parentName:"p"},"/etc/nginx/sites-available")," (see also the example config file):"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"server_names_hash_bucket_size 64;\n\nserver {\n listen 0.0.0.0:443 ssl http2;\n listen [::]:443 ssl http2;\n # tls configuration that is not covered in this guide\n # we recommend the use of https://certbot.eff.org/\n server_name jitsi.example.com;\n # set the root\n root /srv/jitsi-meet;\n index index.html;\n location ~ ^/([a-zA-Z0-9=\\?]+)$ {\n rewrite ^/(.*)$ / break;\n }\n location / {\n ssi on;\n }\n # BOSH, Bidirectional-streams Over Synchronous HTTP\n # https://en.wikipedia.org/wiki/BOSH_(protocol)\n location /http-bind {\n proxy_pass http://localhost:5280/http-bind;\n proxy_set_header X-Forwarded-For $remote_addr;\n proxy_set_header Host $http_host;\n }\n # external_api.js must be accessible from the root of the\n # installation for the electron version of Jitsi Meet to work\n # https://github.com/jitsi/jitsi-meet-electron\n location /external_api.js {\n alias /srv/jitsi-meet/libs/external_api.min.js;\n }\n}\n")),(0,o.kt)("p",null,"Add link for the added configuration"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"cd /etc/nginx/sites-enabled\nln -s ../sites-available/jitsi.example.com jitsi.example.com\n")),(0,o.kt)("h2",{id:"install-jitsi-videobridge"},"Install Jitsi Videobridge"),(0,o.kt)("admonition",{type:"warning"},(0,o.kt)("p",{parentName:"admonition"},"This method is no longer supported.\nYou can either install the JVB from ",(0,o.kt)("a",{parentName:"p",href:"https://download.jitsi.org/stable/"},"https://download.jitsi.org/stable/")," and follow these ",(0,o.kt)("a",{parentName:"p",href:"https://jitsi.org/downloads/ubuntu-debian-installations-instructions/"},"Instructions")," or ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-videobridge"},"clone the repo")," and build it manually.")),(0,o.kt)("p",null,"Visit ",(0,o.kt)("a",{parentName:"p",href:"https://download.jitsi.org/jitsi-videobridge/linux"},"https://download.jitsi.org/jitsi-videobridge/linux")," to determine the current build number, download and unzip it:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"wget https://download.jitsi.org/jitsi-videobridge/linux/jitsi-videobridge-linux-{arch-buildnum}.zip\nunzip jitsi-videobridge-linux-{arch-buildnum}.zip\n")),(0,o.kt)("p",null,"Install JRE if missing:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"apt-get install openjdk-8-jre\n")),(0,o.kt)("p",null,(0,o.kt)("em",{parentName:"p"},"NOTE: When installing on older Debian releases keep in mind that you need JRE >= 1.7.")),(0,o.kt)("p",null,"Create ",(0,o.kt)("inlineCode",{parentName:"p"},"~/.sip-communicator/sip-communicator.properties")," in the home folder of the user that will be starting Jitsi Videobridge:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"mkdir -p ~/.sip-communicator\ncat > ~/.sip-communicator/sip-communicator.properties << EOF\norg.jitsi.impl.neomedia.transform.srtp.SRTPCryptoContext.checkReplay=false\n# The videobridge uses 443 by default with 4443 as a fallback, but since we're already\n# running nginx on 443 in this example doc, we specify 4443 manually to avoid a race condition\norg.jitsi.videobridge.TCP_HARVESTER_PORT=4443\nEOF\n")),(0,o.kt)("p",null,"Start the videobridge with:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"./jvb.sh --host=localhost --domain=jitsi.example.com --secret=YOURSECRET1 &\n")),(0,o.kt)("p",null,"Or autostart it by adding the line in ",(0,o.kt)("inlineCode",{parentName:"p"},"/etc/rc.local"),":"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"/bin/bash /root/jitsi-videobridge-linux-{arch-buildnum}/jvb.sh --host=localhost --domain=jitsi.example.com --secret=YOURSECRET1 > /var/log/jvb.log 2>&1\n")),(0,o.kt)("h2",{id:"install-jitsi-conference-focus-jicofo"},"Install Jitsi Conference Focus (jicofo)"),(0,o.kt)("p",null,"Install JDK and Maven if missing:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"apt-get install openjdk-8-jdk maven\n")),(0,o.kt)("p",null,(0,o.kt)("em",{parentName:"p"},"NOTE: When installing on older Debian releases keep in mind that you need JDK >= 1.7.")),(0,o.kt)("p",null,"Clone source from Github repo:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"git clone https://github.com/jitsi/jicofo.git\n")),(0,o.kt)("p",null,"Build the package."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"cd jicofo\nmvn package -DskipTests -Dassembly.skipAssembly=false\n")),(0,o.kt)("p",null,"Run jicofo:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"=======\nunzip target/jicofo-1.1-SNAPSHOT-archive.zip\ncd jicofo-1.1-SNAPSHOT-archive'\n./jicofo.sh --host=localhost --domain=jitsi.example.com --secret=YOURSECRET2 --user_domain=auth.jitsi.example.com --user_name=focus --user_password=YOURSECRET3\n")),(0,o.kt)("h2",{id:"deploy-jitsi-meet"},"Deploy Jitsi Meet"),(0,o.kt)("p",null,"Checkout and configure Jitsi Meet:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"cd /srv\ngit clone https://github.com/jitsi/jitsi-meet.git\ncd jitsi-meet\nnpm install\nmake\n")),(0,o.kt)("p",null,(0,o.kt)("em",{parentName:"p"},"NOTE: When installing on older distributions keep in mind that you need Node.js >= 12 and npm >= 6.")),(0,o.kt)("p",null,"Edit host names in ",(0,o.kt)("inlineCode",{parentName:"p"},"/srv/jitsi-meet/config.js")," (see also the example config file):"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"var config = {\n hosts: {\n domain: 'jitsi.example.com',\n muc: 'conference.jitsi.example.com',\n bridge: 'jitsi-videobridge.jitsi.example.com',\n focus: 'focus.jitsi.example.com'\n },\n useNicks: false,\n bosh: '//jitsi.example.com/http-bind', // FIXME: use xep-0156 for that\n //chromeExtensionId: 'diibjkoicjeejcmhdnailmkgecihlobk', // Id of desktop streamer Chrome extension\n //minChromeExtVersion: '0.1' // Required version of Chrome extension\n};\n")),(0,o.kt)("p",null,"Verify that nginx config is valid and reload nginx:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"nginx -t && nginx -s reload\n")),(0,o.kt)("h2",{id:"running-behind-nat"},"Running behind NAT"),(0,o.kt)("p",null,"Jitsi Videobridge can run behind a NAT, provided that both required ports are routed (forwarded) to the machine that it runs on. By default these ports are ",(0,o.kt)("inlineCode",{parentName:"p"},"TCP/4443")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"UDP/10000"),"."),(0,o.kt)("p",null,"If you do not route these two ports, Jitsi Meet will only work with video for two people, breaking upon 3 or more people trying to show video."),(0,o.kt)("p",null,(0,o.kt)("inlineCode",{parentName:"p"},"TCP/443")," is required for the webserver which can be running on another machine than the Jitsi Videobrige is running on."),(0,o.kt)("p",null,"The following extra lines need to be added to the file ",(0,o.kt)("inlineCode",{parentName:"p"},"~/.sip-communicator/sip-communicator.properties")," (in the home directory of the user running the videobridge):"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"org.ice4j.ice.harvest.NAT_HARVESTER_LOCAL_ADDRESS=\norg.ice4j.ice.harvest.NAT_HARVESTER_PUBLIC_ADDRESS=\n")),(0,o.kt)("h2",{id:"hold-your-first-conference"},"Hold your first conference"),(0,o.kt)("p",null,"You are now all set and ready to have your first meet by going to ",(0,o.kt)("a",{parentName:"p",href:"http://jitsi.example.com"},"http://jitsi.example.com")),(0,o.kt)("h2",{id:"enabling-recording"},"Enabling recording"),(0,o.kt)("p",null,(0,o.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jibri"},"Jibri")," is a set of tools for recording and/or streaming a Jitsi Meet conference."))}m.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[6667],{3905:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>m});var i=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function r(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=i.createContext({}),p=function(e){var t=i.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},d=function(e){var t=p(e.components);return i.createElement(s.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},u=i.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,s=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),u=p(n),m=a,h=u["".concat(s,".").concat(m)]||u[m]||c[m]||o;return n?i.createElement(h,r(r({ref:t},d),{},{components:n})):i.createElement(h,r({ref:t},d))}));function m(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,r=new Array(o);r[0]=u;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:a,r[1]=l;for(var p=2;p{n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>s,default:()=>m,frontMatter:()=>l,metadata:()=>p,toc:()=>c});var i=n(7462),a=n(3366),o=(n(7294),n(3905)),r=["components"],l={id:"devops-guide-manual",title:"Self-Hosting Guide - Manual installation",sidebar_label:"Manual installation"},s=void 0,p={unversionedId:"devops-guide/devops-guide-manual",id:"devops-guide/devops-guide-manual",title:"Self-Hosting Guide - Manual installation",description:"We recommend following the quick-install document. The current document describes the steps that are needed to install a working deployment, but steps are easy to mess up, and the debian packages are more up-to-date, where this document is sometimes not updated to reflect latest changes.",source:"@site/docs/devops-guide/manual.md",sourceDirName:"devops-guide",slug:"/devops-guide/devops-guide-manual",permalink:"/handbook/docs/devops-guide/devops-guide-manual",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/devops-guide/manual.md",tags:[],version:"current",lastUpdatedAt:1695203964,formattedLastUpdatedAt:"Sep 20, 2023",frontMatter:{id:"devops-guide-manual",title:"Self-Hosting Guide - Manual installation",sidebar_label:"Manual installation"},sidebar:"docs",previous:{title:"Docker",permalink:"/handbook/docs/devops-guide/devops-guide-docker"},next:{title:"Configuration",permalink:"/handbook/docs/category/configuration"}},d={},c=[{value:"Network description",id:"network-description",level:2},{value:"Install prosody",id:"install-prosody",level:2},{value:"Configure prosody",id:"configure-prosody",level:2},{value:"Install Nginx",id:"install-nginx",level:2},{value:"Install Jitsi Videobridge",id:"install-jitsi-videobridge",level:2},{value:"Install Jitsi Conference Focus (jicofo)",id:"install-jitsi-conference-focus-jicofo",level:2},{value:"Deploy Jitsi Meet",id:"deploy-jitsi-meet",level:2},{value:"Running behind NAT",id:"running-behind-nat",level:2},{value:"Hold your first conference",id:"hold-your-first-conference",level:2},{value:"Enabling recording",id:"enabling-recording",level:2}],u={toc:c};function m(e){var t=e.components,n=(0,a.Z)(e,r);return(0,o.kt)("wrapper",(0,i.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("admonition",{title:"Manual installation is not recommended",type:"warning"},(0,o.kt)("p",{parentName:"admonition"},"We recommend following the ",(0,o.kt)("a",{parentName:"p",href:"https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-quickstart/"},"quick-install")," document. The current document describes the steps that are needed to install a working deployment, but steps are easy to mess up, and the debian packages are more up-to-date, where this document is sometimes not updated to reflect latest changes.")),(0,o.kt)("p",null,"This describes configuring a server ",(0,o.kt)("inlineCode",{parentName:"p"},"jitsi.example.com")," on a Debian-based distribution.",(0,o.kt)("br",{parentName:"p"}),"\n",(0,o.kt)("strong",{parentName:"p"},"For other distributions")," you can adapt the steps (especially changing the dependencies package installations (e.g. for nginx) and paths accordingly) so that it matches your host's distribution.",(0,o.kt)("br",{parentName:"p"}),"\n","You will also need to generate some passwords for ",(0,o.kt)("inlineCode",{parentName:"p"},"YOURSECRET1"),", ",(0,o.kt)("inlineCode",{parentName:"p"},"YOURSECRET2")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"YOURSECRET3"),"."),(0,o.kt)("p",null,"There are also some complete ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet/tree/master/doc/debian/"},"example config files")," available, mentioned in each section."),(0,o.kt)("p",null,"There are additional configurations to be done for a ",(0,o.kt)("a",{parentName:"p",href:"devops-guide-scalable"},"scalable installation"),"."),(0,o.kt)("h2",{id:"network-description"},"Network description"),(0,o.kt)("p",null,"This is how the network looks:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"}," + +\n | |\n | |\n v |\n 443 |\n +-------+ |\n | | |\n | Nginx | |\n | | |\n +--+-+--+ |\n | | |\n+------------+ | | +--------------+ |\n| | | | | | |\n| Jitsi Meet +<---+ +---\x3e+ prosody/xmpp | |\n| |files 5280 | | |\n+------------+ +--------------+ v\n 5222 ^ ^ 5222 10000\n +--------+ | | +-------------+\n | | | | | |\n | jicofo +----^ ^----+ videobridge |\n | | | |\n +--------+ +-------------+\n")),(0,o.kt)("h2",{id:"install-prosody"},"Install prosody"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"apt-get install prosody\n")),(0,o.kt)("h2",{id:"configure-prosody"},"Configure prosody"),(0,o.kt)("p",null,"Add config file in ",(0,o.kt)("inlineCode",{parentName:"p"},"/etc/prosody/conf.avail/jitsi.example.com.cfg.lua")," :"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"add your domain virtual host section:")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'VirtualHost "jitsi.example.com"\n authentication = "anonymous"\n ssl = {\n key = "/var/lib/prosody/jitsi.example.com.key";\n certificate = "/var/lib/prosody/jitsi.example.com.crt";\n }\n modules_enabled = {\n "bosh";\n "pubsub";\n }\n c2s_require_encryption = false\n')),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"add domain with authentication for conference focus user:")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'VirtualHost "auth.jitsi.example.com"\n ssl = {\n key = "/var/lib/prosody/auth.jitsi.example.com.key";\n certificate = "/var/lib/prosody/auth.jitsi.example.com.crt";\n }\n authentication = "internal_hashed"\n')),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"add focus user to server admins:")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'admins = { "focus@auth.jitsi.example.com" }\n')),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"and finally configure components:")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'Component "conference.jitsi.example.com" "muc"\nComponent "jitsi-videobridge.jitsi.example.com"\n component_secret = "YOURSECRET1"\nComponent "focus.jitsi.example.com"\n component_secret = "YOURSECRET2"\n')),(0,o.kt)("p",null,"Add link for the added configuration"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"ln -s /etc/prosody/conf.avail/jitsi.example.com.cfg.lua /etc/prosody/conf.d/jitsi.example.com.cfg.lua\n")),(0,o.kt)("p",null,"Generate certs for the domain:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"prosodyctl cert generate jitsi.example.com\nprosodyctl cert generate auth.jitsi.example.com\n")),(0,o.kt)("p",null,"Add auth.jitsi.example.com to the trusted certificates on the local machine:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"ln -sf /var/lib/prosody/auth.jitsi.example.com.crt /usr/local/share/ca-certificates/auth.jitsi.example.com.crt\nupdate-ca-certificates -f\n")),(0,o.kt)("p",null,"Note that the ",(0,o.kt)("inlineCode",{parentName:"p"},"-f")," flag is necessary if there are symlinks left from a previous installation."),(0,o.kt)("p",null,"If you are using a JDK package not provided by Debian, as the ones from adoptjdk, you should also make your JDK aware of the new debian certificate keystore replacing or linking the JDK ",(0,o.kt)("inlineCode",{parentName:"p"},"cacerts"),". Example, if you use JDK from adoptjdk:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"cd /usr/lib/jvm/adoptopenjdk-8-hotspot-amd64/jre\nln -sf /etc/ssl/certs/java/cacerts lib/security/cacerts\n")),(0,o.kt)("p",null,"Create conference focus user:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"prosodyctl register focus auth.jitsi.example.com YOURSECRET3\n")),(0,o.kt)("p",null,"Restart prosody XMPP server with the new config"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"prosodyctl restart\n")),(0,o.kt)("h2",{id:"install-nginx"},"Install Nginx"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"apt-get install nginx\n")),(0,o.kt)("p",null,"Add a new file ",(0,o.kt)("inlineCode",{parentName:"p"},"jitsi.example.com")," in ",(0,o.kt)("inlineCode",{parentName:"p"},"/etc/nginx/sites-available")," (see also the example config file):"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"server_names_hash_bucket_size 64;\n\nserver {\n listen 0.0.0.0:443 ssl http2;\n listen [::]:443 ssl http2;\n # tls configuration that is not covered in this guide\n # we recommend the use of https://certbot.eff.org/\n server_name jitsi.example.com;\n # set the root\n root /srv/jitsi-meet;\n index index.html;\n location ~ ^/([a-zA-Z0-9=\\?]+)$ {\n rewrite ^/(.*)$ / break;\n }\n location / {\n ssi on;\n }\n # BOSH, Bidirectional-streams Over Synchronous HTTP\n # https://en.wikipedia.org/wiki/BOSH_(protocol)\n location /http-bind {\n proxy_pass http://localhost:5280/http-bind;\n proxy_set_header X-Forwarded-For $remote_addr;\n proxy_set_header Host $http_host;\n }\n # external_api.js must be accessible from the root of the\n # installation for the electron version of Jitsi Meet to work\n # https://github.com/jitsi/jitsi-meet-electron\n location /external_api.js {\n alias /srv/jitsi-meet/libs/external_api.min.js;\n }\n}\n")),(0,o.kt)("p",null,"Add link for the added configuration"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"cd /etc/nginx/sites-enabled\nln -s ../sites-available/jitsi.example.com jitsi.example.com\n")),(0,o.kt)("h2",{id:"install-jitsi-videobridge"},"Install Jitsi Videobridge"),(0,o.kt)("admonition",{type:"warning"},(0,o.kt)("p",{parentName:"admonition"},"This method is no longer supported.\nYou can either install the JVB from ",(0,o.kt)("a",{parentName:"p",href:"https://download.jitsi.org/stable/"},"https://download.jitsi.org/stable/")," and follow these ",(0,o.kt)("a",{parentName:"p",href:"https://jitsi.org/downloads/ubuntu-debian-installations-instructions/"},"Instructions")," or ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-videobridge"},"clone the repo")," and build it manually.")),(0,o.kt)("p",null,"Visit ",(0,o.kt)("a",{parentName:"p",href:"https://download.jitsi.org/jitsi-videobridge/linux"},"https://download.jitsi.org/jitsi-videobridge/linux")," to determine the current build number, download and unzip it:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"wget https://download.jitsi.org/jitsi-videobridge/linux/jitsi-videobridge-linux-{arch-buildnum}.zip\nunzip jitsi-videobridge-linux-{arch-buildnum}.zip\n")),(0,o.kt)("p",null,"Install JRE if missing:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"apt-get install openjdk-8-jre\n")),(0,o.kt)("p",null,(0,o.kt)("em",{parentName:"p"},"NOTE: When installing on older Debian releases keep in mind that you need JRE >= 1.7.")),(0,o.kt)("p",null,"Create ",(0,o.kt)("inlineCode",{parentName:"p"},"~/.sip-communicator/sip-communicator.properties")," in the home folder of the user that will be starting Jitsi Videobridge:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"mkdir -p ~/.sip-communicator\ncat > ~/.sip-communicator/sip-communicator.properties << EOF\norg.jitsi.impl.neomedia.transform.srtp.SRTPCryptoContext.checkReplay=false\n# The videobridge uses 443 by default with 4443 as a fallback, but since we're already\n# running nginx on 443 in this example doc, we specify 4443 manually to avoid a race condition\norg.jitsi.videobridge.TCP_HARVESTER_PORT=4443\nEOF\n")),(0,o.kt)("p",null,"Start the videobridge with:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"./jvb.sh --host=localhost --domain=jitsi.example.com --secret=YOURSECRET1 &\n")),(0,o.kt)("p",null,"Or autostart it by adding the line in ",(0,o.kt)("inlineCode",{parentName:"p"},"/etc/rc.local"),":"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"/bin/bash /root/jitsi-videobridge-linux-{arch-buildnum}/jvb.sh --host=localhost --domain=jitsi.example.com --secret=YOURSECRET1 > /var/log/jvb.log 2>&1\n")),(0,o.kt)("h2",{id:"install-jitsi-conference-focus-jicofo"},"Install Jitsi Conference Focus (jicofo)"),(0,o.kt)("p",null,"Install JDK and Maven if missing:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"apt-get install openjdk-8-jdk maven\n")),(0,o.kt)("p",null,(0,o.kt)("em",{parentName:"p"},"NOTE: When installing on older Debian releases keep in mind that you need JDK >= 1.7.")),(0,o.kt)("p",null,"Clone source from Github repo:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"git clone https://github.com/jitsi/jicofo.git\n")),(0,o.kt)("p",null,"Build the package."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"cd jicofo\nmvn package -DskipTests -Dassembly.skipAssembly=false\n")),(0,o.kt)("p",null,"Run jicofo:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"=======\nunzip target/jicofo-1.1-SNAPSHOT-archive.zip\ncd jicofo-1.1-SNAPSHOT-archive'\n./jicofo.sh --host=localhost --domain=jitsi.example.com --secret=YOURSECRET2 --user_domain=auth.jitsi.example.com --user_name=focus --user_password=YOURSECRET3\n")),(0,o.kt)("h2",{id:"deploy-jitsi-meet"},"Deploy Jitsi Meet"),(0,o.kt)("p",null,"Checkout and configure Jitsi Meet:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"cd /srv\ngit clone https://github.com/jitsi/jitsi-meet.git\ncd jitsi-meet\nnpm install\nmake\n")),(0,o.kt)("p",null,(0,o.kt)("em",{parentName:"p"},"NOTE: When installing on older distributions keep in mind that you need Node.js >= 12 and npm >= 6.")),(0,o.kt)("p",null,"Edit host names in ",(0,o.kt)("inlineCode",{parentName:"p"},"/srv/jitsi-meet/config.js")," (see also the example config file):"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"var config = {\n hosts: {\n domain: 'jitsi.example.com',\n muc: 'conference.jitsi.example.com',\n bridge: 'jitsi-videobridge.jitsi.example.com',\n focus: 'focus.jitsi.example.com'\n },\n useNicks: false,\n bosh: '//jitsi.example.com/http-bind', // FIXME: use xep-0156 for that\n //chromeExtensionId: 'diibjkoicjeejcmhdnailmkgecihlobk', // Id of desktop streamer Chrome extension\n //minChromeExtVersion: '0.1' // Required version of Chrome extension\n};\n")),(0,o.kt)("p",null,"Verify that nginx config is valid and reload nginx:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"nginx -t && nginx -s reload\n")),(0,o.kt)("h2",{id:"running-behind-nat"},"Running behind NAT"),(0,o.kt)("p",null,"Jitsi Videobridge can run behind a NAT, provided that both required ports are routed (forwarded) to the machine that it runs on. By default these ports are ",(0,o.kt)("inlineCode",{parentName:"p"},"TCP/4443")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"UDP/10000"),"."),(0,o.kt)("p",null,"If you do not route these two ports, Jitsi Meet will only work with video for two people, breaking upon 3 or more people trying to show video."),(0,o.kt)("p",null,(0,o.kt)("inlineCode",{parentName:"p"},"TCP/443")," is required for the webserver which can be running on another machine than the Jitsi Videobrige is running on."),(0,o.kt)("p",null,"The following extra lines need to be added to the file ",(0,o.kt)("inlineCode",{parentName:"p"},"~/.sip-communicator/sip-communicator.properties")," (in the home directory of the user running the videobridge):"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"org.ice4j.ice.harvest.NAT_HARVESTER_LOCAL_ADDRESS=\norg.ice4j.ice.harvest.NAT_HARVESTER_PUBLIC_ADDRESS=\n")),(0,o.kt)("h2",{id:"hold-your-first-conference"},"Hold your first conference"),(0,o.kt)("p",null,"You are now all set and ready to have your first meet by going to ",(0,o.kt)("a",{parentName:"p",href:"http://jitsi.example.com"},"http://jitsi.example.com")),(0,o.kt)("h2",{id:"enabling-recording"},"Enabling recording"),(0,o.kt)("p",null,(0,o.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jibri"},"Jibri")," is a set of tools for recording and/or streaming a Jitsi Meet conference."))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/24f4a563.3941363b.js b/assets/js/24f4a563.1b78af49.js similarity index 98% rename from assets/js/24f4a563.3941363b.js rename to assets/js/24f4a563.1b78af49.js index 64b6a32d3..124edc739 100644 --- a/assets/js/24f4a563.3941363b.js +++ b/assets/js/24f4a563.1b78af49.js @@ -1 +1 @@ -"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[3311],{3905:(e,t,r)=>{r.d(t,{Zo:()=>c,kt:()=>m});var o=r(7294);function i(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function n(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,o)}return r}function a(e){for(var t=1;t=0||(i[r]=e[r]);return i}(e,t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(i[r]=e[r])}return i}var s=o.createContext({}),u=function(e){var t=o.useContext(s),r=t;return e&&(r="function"==typeof e?e(t):a(a({},t),e)),r},c=function(e){var t=u(e.components);return o.createElement(s.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},p=o.forwardRef((function(e,t){var r=e.components,i=e.mdxType,n=e.originalType,s=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),p=u(r),m=i,v=p["".concat(s,".").concat(m)]||p[m]||d[m]||n;return r?o.createElement(v,a(a({ref:t},c),{},{components:r})):o.createElement(v,a({ref:t},c))}));function m(e,t){var r=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var n=r.length,a=new Array(n);a[0]=p;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:i,a[1]=l;for(var u=2;u{r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>s,default:()=>m,frontMatter:()=>l,metadata:()=>u,toc:()=>d});var o=r(7462),i=r(3366),n=(r(7294),r(3905)),a=["components"],l={id:"devops-guide-videotutorials",title:"Video Tutorials"},s=void 0,u={unversionedId:"devops-guide/devops-guide-videotutorials",id:"devops-guide/devops-guide-videotutorials",title:"Video Tutorials",description:"Installing Jitsi Meet on your own Linux Server",source:"@site/docs/devops-guide/videotutorials.md",sourceDirName:"devops-guide",slug:"/devops-guide/devops-guide-videotutorials",permalink:"/handbook/docs/devops-guide/devops-guide-videotutorials",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/devops-guide/videotutorials.md",tags:[],version:"current",lastUpdatedAt:1694635654,formattedLastUpdatedAt:"Sep 13, 2023",frontMatter:{id:"devops-guide-videotutorials",title:"Video Tutorials"},sidebar:"docs",previous:{title:"Cloud API",permalink:"/handbook/docs/devops-guide/cloud-api"},next:{title:"FAQ",permalink:"/handbook/docs/devops-guide/faq"}},c={},d=[{value:"Installing Jitsi Meet on your own Linux Server",id:"installing-jitsi-meet-on-your-own-linux-server",level:2},{value:"How to Load Balance Jitsi Meet",id:"how-to-load-balance-jitsi-meet",level:2},{value:"Scaling Jitsi Meet in the Cloud",id:"scaling-jitsi-meet-in-the-cloud",level:2}],p={toc:d};function m(e){var t=e.components,r=(0,i.Z)(e,a);return(0,n.kt)("wrapper",(0,o.Z)({},p,r,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("h2",{id:"installing-jitsi-meet-on-your-own-linux-server"},(0,n.kt)("a",{parentName:"h2",href:"https://jitsi.org/news/new-tutorial-installing-jitsi-meet-on-your-own-linux-server/"},"Installing Jitsi Meet on your own Linux Server")),(0,n.kt)("figure",{class:"video-container"},(0,n.kt)("iframe",{src:"https://www.youtube.com/embed/8KR0AhDZF2A",frameborder:"0",allow:"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0})),(0,n.kt)("h2",{id:"how-to-load-balance-jitsi-meet"},(0,n.kt)("a",{parentName:"h2",href:"https://jitsi.org/blog/tutorial-video-how-to-load-balance-jitsi-meet/"},"How to Load Balance Jitsi Meet")),(0,n.kt)("figure",{class:"video-container"},(0,n.kt)("iframe",{src:"https://www.youtube.com/embed/LyGV4uW8km8",frameborder:"0",allow:"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0})),(0,n.kt)("h2",{id:"scaling-jitsi-meet-in-the-cloud"},(0,n.kt)("a",{parentName:"h2",href:"https://jitsi.org/blog/new-tutorial-video-scaling-jitsi-meet-in-the-cloud/"},"Scaling Jitsi Meet in the Cloud")),(0,n.kt)("figure",{class:"video-container"},(0,n.kt)("iframe",{src:"https://www.youtube.com/embed/Jj8a6ZRgehI",frameborder:"0",allow:"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0})))}m.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[3311],{3905:(e,t,r)=>{r.d(t,{Zo:()=>c,kt:()=>m});var o=r(7294);function i(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function n(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,o)}return r}function a(e){for(var t=1;t=0||(i[r]=e[r]);return i}(e,t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(i[r]=e[r])}return i}var s=o.createContext({}),u=function(e){var t=o.useContext(s),r=t;return e&&(r="function"==typeof e?e(t):a(a({},t),e)),r},c=function(e){var t=u(e.components);return o.createElement(s.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},p=o.forwardRef((function(e,t){var r=e.components,i=e.mdxType,n=e.originalType,s=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),p=u(r),m=i,v=p["".concat(s,".").concat(m)]||p[m]||d[m]||n;return r?o.createElement(v,a(a({ref:t},c),{},{components:r})):o.createElement(v,a({ref:t},c))}));function m(e,t){var r=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var n=r.length,a=new Array(n);a[0]=p;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:i,a[1]=l;for(var u=2;u{r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>s,default:()=>m,frontMatter:()=>l,metadata:()=>u,toc:()=>d});var o=r(7462),i=r(3366),n=(r(7294),r(3905)),a=["components"],l={id:"devops-guide-videotutorials",title:"Video Tutorials"},s=void 0,u={unversionedId:"devops-guide/devops-guide-videotutorials",id:"devops-guide/devops-guide-videotutorials",title:"Video Tutorials",description:"Installing Jitsi Meet on your own Linux Server",source:"@site/docs/devops-guide/videotutorials.md",sourceDirName:"devops-guide",slug:"/devops-guide/devops-guide-videotutorials",permalink:"/handbook/docs/devops-guide/devops-guide-videotutorials",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/devops-guide/videotutorials.md",tags:[],version:"current",lastUpdatedAt:1695203964,formattedLastUpdatedAt:"Sep 20, 2023",frontMatter:{id:"devops-guide-videotutorials",title:"Video Tutorials"},sidebar:"docs",previous:{title:"Cloud API",permalink:"/handbook/docs/devops-guide/cloud-api"},next:{title:"FAQ",permalink:"/handbook/docs/devops-guide/faq"}},c={},d=[{value:"Installing Jitsi Meet on your own Linux Server",id:"installing-jitsi-meet-on-your-own-linux-server",level:2},{value:"How to Load Balance Jitsi Meet",id:"how-to-load-balance-jitsi-meet",level:2},{value:"Scaling Jitsi Meet in the Cloud",id:"scaling-jitsi-meet-in-the-cloud",level:2}],p={toc:d};function m(e){var t=e.components,r=(0,i.Z)(e,a);return(0,n.kt)("wrapper",(0,o.Z)({},p,r,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("h2",{id:"installing-jitsi-meet-on-your-own-linux-server"},(0,n.kt)("a",{parentName:"h2",href:"https://jitsi.org/news/new-tutorial-installing-jitsi-meet-on-your-own-linux-server/"},"Installing Jitsi Meet on your own Linux Server")),(0,n.kt)("figure",{class:"video-container"},(0,n.kt)("iframe",{src:"https://www.youtube.com/embed/8KR0AhDZF2A",frameborder:"0",allow:"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0})),(0,n.kt)("h2",{id:"how-to-load-balance-jitsi-meet"},(0,n.kt)("a",{parentName:"h2",href:"https://jitsi.org/blog/tutorial-video-how-to-load-balance-jitsi-meet/"},"How to Load Balance Jitsi Meet")),(0,n.kt)("figure",{class:"video-container"},(0,n.kt)("iframe",{src:"https://www.youtube.com/embed/LyGV4uW8km8",frameborder:"0",allow:"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0})),(0,n.kt)("h2",{id:"scaling-jitsi-meet-in-the-cloud"},(0,n.kt)("a",{parentName:"h2",href:"https://jitsi.org/blog/new-tutorial-video-scaling-jitsi-meet-in-the-cloud/"},"Scaling Jitsi Meet in the Cloud")),(0,n.kt)("figure",{class:"video-container"},(0,n.kt)("iframe",{src:"https://www.youtube.com/embed/Jj8a6ZRgehI",frameborder:"0",allow:"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0})))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/25b664f5.68e78fbb.js b/assets/js/25b664f5.0ed72de6.js similarity index 99% rename from assets/js/25b664f5.68e78fbb.js rename to assets/js/25b664f5.0ed72de6.js index 085d9347e..8d5e65d3a 100644 --- a/assets/js/25b664f5.68e78fbb.js +++ b/assets/js/25b664f5.0ed72de6.js @@ -1 +1 @@ -"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[3634],{3905:(t,e,n)=>{n.d(e,{Zo:()=>m,kt:()=>k});var a=n(7294);function r(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function l(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);e&&(a=a.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,a)}return n}function i(t){for(var e=1;e=0||(r[n]=t[n]);return r}(t,e);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(t);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(t,n)&&(r[n]=t[n])}return r}var p=a.createContext({}),d=function(t){var e=a.useContext(p),n=e;return t&&(n="function"==typeof t?t(e):i(i({},e),t)),n},m=function(t){var e=d(t.components);return a.createElement(p.Provider,{value:e},t.children)},s={inlineCode:"code",wrapper:function(t){var e=t.children;return a.createElement(a.Fragment,{},e)}},u=a.forwardRef((function(t,e){var n=t.components,r=t.mdxType,l=t.originalType,p=t.parentName,m=o(t,["components","mdxType","originalType","parentName"]),u=d(n),k=r,N=u["".concat(p,".").concat(k)]||u[k]||s[k]||l;return n?a.createElement(N,i(i({ref:e},m),{},{components:n})):a.createElement(N,i({ref:e},m))}));function k(t,e){var n=arguments,r=e&&e.mdxType;if("string"==typeof t||r){var l=n.length,i=new Array(l);i[0]=u;var o={};for(var p in e)hasOwnProperty.call(e,p)&&(o[p]=e[p]);o.originalType=t,o.mdxType="string"==typeof t?t:r,i[1]=o;for(var d=2;d{n.r(e),n.d(e,{assets:()=>m,contentTitle:()=>p,default:()=>k,frontMatter:()=>o,metadata:()=>d,toc:()=>s});var a=n(7462),r=n(3366),l=(n(7294),n(3905)),i=["components"],o={id:"devops-guide-docker",title:"Self-Hosting Guide - Docker",sidebar_label:"Docker"},p=void 0,d={unversionedId:"devops-guide/devops-guide-docker",id:"devops-guide/devops-guide-docker",title:"Self-Hosting Guide - Docker",description:"Starting with release stable-7289-1 our images are provided with amd64 and arm64 architecture.",source:"@site/docs/devops-guide/docker.md",sourceDirName:"devops-guide",slug:"/devops-guide/devops-guide-docker",permalink:"/handbook/docs/devops-guide/devops-guide-docker",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/devops-guide/docker.md",tags:[],version:"current",lastUpdatedAt:1694635654,formattedLastUpdatedAt:"Sep 13, 2023",frontMatter:{id:"devops-guide-docker",title:"Self-Hosting Guide - Docker",sidebar_label:"Docker"},sidebar:"docs",previous:{title:"openSUSE",permalink:"/handbook/docs/devops-guide/devops-guide-opensuse"},next:{title:"Manual installation",permalink:"/handbook/docs/devops-guide/devops-guide-manual"}},m={},s=[{value:"Quick start",id:"quick-start",level:2},{value:"Testing development / unstable builds",id:"testing-development--unstable-builds",level:3},{value:"Building your own images",id:"building-your-own-images",level:3},{value:"Security note",id:"security-note",level:3},{value:"Architecture",id:"architecture",level:2},{value:"External Ports",id:"external-ports",level:3},{value:"Images",id:"images",level:3},{value:"Design considerations",id:"design-considerations",level:3},{value:"Configuration",id:"configuration",level:2},{value:"TLS Configuration",id:"tls-configuration",level:3},{value:"Let's Encrypt configuration",id:"lets-encrypt-configuration",level:4},{value:"Using existing TLS certificate and key",id:"using-existing-tls-certificate-and-key",level:4},{value:"Features configuration (config.js)",id:"features-configuration-configjs",level:3},{value:"Jigasi SIP gateway (audio only) configuration",id:"jigasi-sip-gateway-audio-only-configuration",level:3},{value:"Display Dial-In information",id:"display-dial-in-information",level:4},{value:"Recording / live streaming configuration with Jibri",id:"recording--live-streaming-configuration-with-jibri",level:3},{value:"Jitsi Meet configuration",id:"jitsi-meet-configuration",level:3},{value:"Authentication",id:"authentication",level:3},{value:"Internal authentication",id:"internal-authentication",level:4},{value:"Authentication using LDAP",id:"authentication-using-ldap",level:4},{value:"Authentication using JWT tokens",id:"authentication-using-jwt-tokens",level:4},{value:"Authentication using Matrix",id:"authentication-using-matrix",level:4},{value:"Authentication using Hybrid Matrix Token",id:"authentication-using-hybrid-matrix-token",level:4},{value:"External authentication",id:"external-authentication",level:4},{value:"Shared document editing using Etherpad",id:"shared-document-editing-using-etherpad",level:3},{value:"Transcription configuration",id:"transcription-configuration",level:3},{value:"Sentry logging configuration",id:"sentry-logging-configuration",level:3},{value:"TURN server configuration",id:"turn-server-configuration",level:3},{value:"Advanced configuration",id:"advanced-configuration",level:3},{value:"Advanced Prosody options",id:"advanced-prosody-options",level:4},{value:"Advanced Jicofo options",id:"advanced-jicofo-options",level:4},{value:"Advanced JVB options",id:"advanced-jvb-options",level:4},{value:"Advanced Jigasi options",id:"advanced-jigasi-options",level:4},{value:"Running behind NAT or on a LAN environment",id:"running-behind-nat-or-on-a-lan-environment",level:3},{value:"Split horizon",id:"split-horizon",level:4},{value:"Offline / airgapped installation",id:"offline--airgapped-installation",level:4},{value:"Accessing server logs",id:"accessing-server-logs",level:2},{value:"Build Instructions",id:"build-instructions",level:2},{value:"Running behind a reverse proxy",id:"running-behind-a-reverse-proxy",level:2},{value:"Disabling WebSocket connections",id:"disabling-websocket-connections",level:3}],u={toc:s};function k(t){var e=t.components,o=(0,r.Z)(t,i);return(0,l.kt)("wrapper",(0,a.Z)({},u,o,{components:e,mdxType:"MDXLayout"}),(0,l.kt)("admonition",{type:"note"},(0,l.kt)("p",{parentName:"admonition"},"Starting with release ",(0,l.kt)("inlineCode",{parentName:"p"},"stable-7289-1")," our images are provided with ",(0,l.kt)("inlineCode",{parentName:"p"},"amd64")," and ",(0,l.kt)("inlineCode",{parentName:"p"},"arm64")," architecture.")),(0,l.kt)("h2",{id:"quick-start"},"Quick start"),(0,l.kt)("p",null,"In order to quickly run Jitsi Meet on a machine running Docker and Docker Compose,\nfollow these steps:"),(0,l.kt)("ol",null,(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("p",{parentName:"li"},"Download and extract the ",(0,l.kt)("a",{parentName:"p",href:"https://github.com/jitsi/docker-jitsi-meet/releases/latest"},"latest release"),". ",(0,l.kt)("strong",{parentName:"p"},"DO NOT")," clone the git repository. See below if you are interested in running test images.")),(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("p",{parentName:"li"},"Create a ",(0,l.kt)("inlineCode",{parentName:"p"},".env")," file by copying and adjusting ",(0,l.kt)("inlineCode",{parentName:"p"},"env.example"),":"),(0,l.kt)("pre",{parentName:"li"},(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"cp env.example .env\n"))),(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("p",{parentName:"li"},"Set strong passwords in the security section options of ",(0,l.kt)("inlineCode",{parentName:"p"},".env")," file by running the following bash script"),(0,l.kt)("pre",{parentName:"li"},(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"./gen-passwords.sh\n"))),(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("p",{parentName:"li"},"Create required ",(0,l.kt)("inlineCode",{parentName:"p"},"CONFIG")," directories"),(0,l.kt)("ul",{parentName:"li"},(0,l.kt)("li",{parentName:"ul"},"For linux: ")),(0,l.kt)("pre",{parentName:"li"},(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"mkdir -p ~/.jitsi-meet-cfg/{web,transcripts,prosody/config,prosody/prosody-plugins-custom,jicofo,jvb,jigasi,jibri}\n")),(0,l.kt)("ul",{parentName:"li"},(0,l.kt)("li",{parentName:"ul"},"For Windows: ")),(0,l.kt)("pre",{parentName:"li"},(0,l.kt)("code",{parentName:"pre",className:"language-bash"},'echo web,transcripts,prosody/config,prosody/prosody-plugins-custom,jicofo,jvb,jigasi,jibri | % { mkdir "~/.jitsi-meet-cfg/$_" }\n'))),(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("p",{parentName:"li"},"Run ",(0,l.kt)("inlineCode",{parentName:"p"},"docker-compose up -d"))),(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("p",{parentName:"li"},"Access the web UI at ",(0,l.kt)("a",{parentName:"p",href:"https://localhost:8443"},(0,l.kt)("inlineCode",{parentName:"a"},"https://localhost:8443"))," (or a different port, in case you edited the ",(0,l.kt)("inlineCode",{parentName:"p"},".env")," file)."))),(0,l.kt)("admonition",{type:"note"},(0,l.kt)("p",{parentName:"admonition"},"HTTP (not HTTPS) is also available (on port 8000, by default), but that's e.g. for a reverse proxy setup;\ndirect access via HTTP instead HTTPS leads to WebRTC errors such as\n",(0,l.kt)("em",{parentName:"p"},"Failed to access your microphone/camera: Cannot use microphone/camera for an unknown reason. Cannot read property 'getUserMedia' of undefined"),"\nor ",(0,l.kt)("em",{parentName:"p"},"navigator.mediaDevices is undefined"),".")),(0,l.kt)("p",null,"If you want to use jigasi too, first configure your env file with SIP credentials\nand then run Docker Compose as follows: "),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"docker-compose -f docker-compose.yml -f jigasi.yml up\n")),(0,l.kt)("p",null,"If you want to enable document sharing via ",(0,l.kt)("a",{parentName:"p",href:"https://github.com/ether/etherpad-lite"},"Etherpad"),",\nconfigure it and run Docker Compose as follows: "),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"docker-compose -f docker-compose.yml -f etherpad.yml up\n")),(0,l.kt)("p",null,"If you want to use jibri too, first configure a host as described in JItsi BRoadcasting Infrastructure configuration section\nand then run Docker Compose as follows:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"docker-compose -f docker-compose.yml -f jibri.yml up -d\n")),(0,l.kt)("p",null,"or to use jigasi too:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"docker-compose -f docker-compose.yml -f jigasi.yml -f jibri.yml up -d\n")),(0,l.kt)("h3",{id:"testing-development--unstable-builds"},"Testing development / unstable builds"),(0,l.kt)("p",null,"Download the latest code:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"git clone https://github.com/jitsi/docker-jitsi-meet && cd docker-jitsi-meet\n")),(0,l.kt)("admonition",{type:"note"},(0,l.kt)("p",{parentName:"admonition"},"The code in ",(0,l.kt)("inlineCode",{parentName:"p"},"master")," is designed to work with the unstable images. Do not run it with release images.")),(0,l.kt)("p",null,"Run ",(0,l.kt)("inlineCode",{parentName:"p"},"docker-compose up")," as usual."),(0,l.kt)("p",null,'Every day a new "unstable" image build is uploaded.'),(0,l.kt)("h3",{id:"building-your-own-images"},"Building your own images"),(0,l.kt)("p",null,"Download the latest code:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"git clone https://github.com/jitsi/docker-jitsi-meet && cd docker-jitsi-meet\n")),(0,l.kt)("p",null,"The provided ",(0,l.kt)("inlineCode",{parentName:"p"},"Makefile")," provides a comprehensive way of building the whole stack or individual images."),(0,l.kt)("p",null,"To build all images:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"make\n")),(0,l.kt)("p",null,"To build a specific image (the web image for example):"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"make build_web\n")),(0,l.kt)("p",null,"Once your local build is ready make sure to add ",(0,l.kt)("inlineCode",{parentName:"p"},"JITSI_IMAGE_VERSION=latest")," to your ",(0,l.kt)("inlineCode",{parentName:"p"},".env")," file."),(0,l.kt)("h3",{id:"security-note"},"Security note"),(0,l.kt)("p",null,"This setup used to have default passwords for internal accounts used across components.\nIn order to make the default setup secure by default these have been removed and the respective containers won't start without having a password set."),(0,l.kt)("p",null,"Strong passwords may be generated as follows: ",(0,l.kt)("inlineCode",{parentName:"p"},"./gen-passwords.sh"),"\nThis will modify your ",(0,l.kt)("inlineCode",{parentName:"p"},".env")," file (a backup is saved in ",(0,l.kt)("inlineCode",{parentName:"p"},".env.bak"),") and set strong passwords for each of the\nrequired options. Passwords are generated using ",(0,l.kt)("inlineCode",{parentName:"p"},"openssl rand -hex 16")," ."),(0,l.kt)("p",null,"DO NOT reuse any of the passwords."),(0,l.kt)("h2",{id:"architecture"},"Architecture"),(0,l.kt)("p",null,"A Jitsi Meet installation can be broken down into the following components:"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},"A web interface"),(0,l.kt)("li",{parentName:"ul"},"An XMPP server"),(0,l.kt)("li",{parentName:"ul"},"A conference focus component"),(0,l.kt)("li",{parentName:"ul"},"A video router (could be more than one)"),(0,l.kt)("li",{parentName:"ul"},"A SIP gateway for audio calls"),(0,l.kt)("li",{parentName:"ul"},"A Broadcasting Infrastructure for recording or streaming a conference.")),(0,l.kt)("p",null,(0,l.kt)("img",{src:n(168).Z,width:"882",height:"505"})),(0,l.kt)("p",null,"The diagram shows a typical deployment in a host running Docker. This project\nseparates each of the components above into interlinked containers. To this end,\nseveral container images are provided."),(0,l.kt)("h3",{id:"external-ports"},"External Ports"),(0,l.kt)("p",null,"The following external ports must be opened on a firewall:"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"80/tcp")," for Web UI HTTP (really just to redirect, after uncommenting ",(0,l.kt)("inlineCode",{parentName:"li"},"ENABLE_HTTP_REDIRECT=1")," in ",(0,l.kt)("inlineCode",{parentName:"li"},".env"),")"),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"443/tcp")," for Web UI HTTPS"),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"10000/udp")," for RTP media over UDP")),(0,l.kt)("p",null,"Also ",(0,l.kt)("inlineCode",{parentName:"p"},"20000-20050/udp")," for jigasi, in case you choose to deploy that to facilitate SIP access."),(0,l.kt)("p",null,"E.g. on a CentOS/Fedora server this would be done like this (without SIP access):"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"sudo firewall-cmd --permanent --add-port=80/tcp\nsudo firewall-cmd --permanent --add-port=443/tcp\nsudo firewall-cmd --permanent --add-port=10000/udp\nsudo firewall-cmd --reload\n")),(0,l.kt)("p",null,"See ",(0,l.kt)("a",{parentName:"p",href:"https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-quickstart#setup-and-configure-your-firewall"},"the corresponding section in the manual setup guide"),"."),(0,l.kt)("h3",{id:"images"},"Images"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("strong",{parentName:"li"},"base"),": Debian stable base image with the ",(0,l.kt)("a",{parentName:"li",href:"https://github.com/just-containers/s6-overlay"},"S6 Overlay")," for process control and the\n",(0,l.kt)("a",{parentName:"li",href:"https://jitsi.org/downloads/"},"Jitsi repositories")," enabled. All other images are based on this one."),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("strong",{parentName:"li"},"base-java"),": Same as the above, plus Java (OpenJDK)."),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("strong",{parentName:"li"},"web"),": Jitsi Meet web UI, served with nginx."),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("strong",{parentName:"li"},"prosody"),": ",(0,l.kt)("a",{parentName:"li",href:"https://prosody.im/"},"Prosody"),", the XMPP server."),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("strong",{parentName:"li"},"jicofo"),": ",(0,l.kt)("a",{parentName:"li",href:"https://github.com/jitsi/jicofo"},"Jicofo"),", the XMPP focus component."),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("strong",{parentName:"li"},"jvb"),": ",(0,l.kt)("a",{parentName:"li",href:"https://github.com/jitsi/jitsi-videobridge"},"Jitsi Videobridge"),", the video router."),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("strong",{parentName:"li"},"jigasi"),": ",(0,l.kt)("a",{parentName:"li",href:"https://github.com/jitsi/jigasi"},"Jigasi"),", the SIP (audio only) gateway."),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("strong",{parentName:"li"},"jibri"),": ",(0,l.kt)("a",{parentName:"li",href:"https://github.com/jitsi/jibri"},"Jibri"),", the broadcasting infrastructure.")),(0,l.kt)("h3",{id:"design-considerations"},"Design considerations"),(0,l.kt)("p",null,"Jitsi Meet uses XMPP for signaling, thus the need for the XMPP server.\nThe setup provided by these containers does not expose the XMPP server to the outside world.\nInstead, it's kept completely sealed, and routing of XMPP traffic only happens on a user-defined network."),(0,l.kt)("p",null,"The XMPP server can be exposed to the outside world,\nbut that's out of the scope of this project."),(0,l.kt)("h2",{id:"configuration"},"Configuration"),(0,l.kt)("p",null,"The configuration is performed via environment variables contained in a ",(0,l.kt)("inlineCode",{parentName:"p"},".env")," file.\nYou can copy the provided ",(0,l.kt)("inlineCode",{parentName:"p"},"env.example")," file as a reference."),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Example"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"CONFIG")),(0,l.kt)("td",{parentName:"tr",align:null},"Directory where all configuration will be stored"),(0,l.kt)("td",{parentName:"tr",align:null},"/opt/jitsi-meet-cfg")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"TZ")),(0,l.kt)("td",{parentName:"tr",align:null},"System Time Zone"),(0,l.kt)("td",{parentName:"tr",align:null},"Europe/Amsterdam")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"HTTP_PORT")),(0,l.kt)("td",{parentName:"tr",align:null},"Exposed port for HTTP traffic"),(0,l.kt)("td",{parentName:"tr",align:null},"8000")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"HTTPS_PORT")),(0,l.kt)("td",{parentName:"tr",align:null},"Exposed port for HTTPS traffic"),(0,l.kt)("td",{parentName:"tr",align:null},"8443")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JVB_ADVERTISE_IPS")),(0,l.kt)("td",{parentName:"tr",align:null},"IP addresses of the Docker host (comma separated), needed for LAN environments"),(0,l.kt)("td",{parentName:"tr",align:null},"192.168.1.1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"PUBLIC_URL")),(0,l.kt)("td",{parentName:"tr",align:null},"Public URL for the web service"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"https://meet.example.com"},"https://meet.example.com"))))),(0,l.kt)("admonition",{type:"note"},(0,l.kt)("p",{parentName:"admonition"},"The mobile apps won't work with self-signed certificates (the default).\nSee below for instructions on how to obtain a proper certificate with Let's Encrypt.")),(0,l.kt)("h3",{id:"tls-configuration"},"TLS Configuration"),(0,l.kt)("h4",{id:"lets-encrypt-configuration"},"Let's Encrypt configuration"),(0,l.kt)("p",null,"If you want to expose your Jitsi Meet instance to the outside traffic directly, but don't own a proper TLS certificate, you are in luck\nbecause Let's Encrypt support is built right in. Here are the required options:"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Example"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_LETSENCRYPT")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable Let's Encrypt certificate generation"),(0,l.kt)("td",{parentName:"tr",align:null},"1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"LETSENCRYPT_DOMAIN")),(0,l.kt)("td",{parentName:"tr",align:null},"Domain for which to generate the certificate"),(0,l.kt)("td",{parentName:"tr",align:null},"meet.example.com")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"LETSENCRYPT_EMAIL")),(0,l.kt)("td",{parentName:"tr",align:null},"E-Mail for receiving important account notifications (mandatory)"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"mailto:alice@atlanta.net"},"alice@atlanta.net"))))),(0,l.kt)("p",null,"In addition, you will need to set ",(0,l.kt)("inlineCode",{parentName:"p"},"HTTP_PORT")," to 80 and ",(0,l.kt)("inlineCode",{parentName:"p"},"HTTPS_PORT")," to 443 and PUBLIC_URL to your domain.\nYou might also consider to redirect HTTP traffic to HTTPS by setting ",(0,l.kt)("inlineCode",{parentName:"p"},"ENABLE_HTTP_REDIRECT=1"),"."),(0,l.kt)("p",null,(0,l.kt)("strong",{parentName:"p"},"Let's Encrypt rate limit warning"),": Let's Encrypt has a limit to how many times you can submit a request\nfor a new certificate for your domain name. At the time of writing, the current limit is five new (duplicate)\ncertificates for the same domain name every seven days. Because of this, it is recommended that you disable the\nLet's Encrypt enviroment variables from ",(0,l.kt)("inlineCode",{parentName:"p"},".env")," if you plan on deleting the ",(0,l.kt)("inlineCode",{parentName:"p"},".jitsi-meet-cfg")," folder.\nOtherwise, you might want to consider moving the ",(0,l.kt)("inlineCode",{parentName:"p"},".jitsi-meet-cfg")," folder to a different location so you have a safe place to find\nthe certificate that already Let's Encrypt issued. Or do initial testing with Let's Encrypt disabled, then re-enable\nLet's Encrypt once you are done testing."),(0,l.kt)("admonition",{type:"note"},(0,l.kt)("p",{parentName:"admonition"},"When you move away from ",(0,l.kt)("inlineCode",{parentName:"p"},"LETSENCRYPT_USE_STAGING"),",\nyou will have to manually clear the certificates from ",(0,l.kt)("inlineCode",{parentName:"p"},".jitsi-meet-cfg/web"),".")),(0,l.kt)("p",null,"For more information on Let's Encrypt's rate limits, visit:\n",(0,l.kt)("a",{parentName:"p",href:"https://letsencrypt.org/docs/rate-limits/"},"https://letsencrypt.org/docs/rate-limits/")),(0,l.kt)("h4",{id:"using-existing-tls-certificate-and-key"},"Using existing TLS certificate and key"),(0,l.kt)("p",null,"If you own a proper TLS certificate and don't need a Let's Encrypt certificate, you can configure Jitsi Meet container\nto use it. "),(0,l.kt)("p",null,"Unlike Let's Encrypt certificates, this is not configured through the ",(0,l.kt)("inlineCode",{parentName:"p"},".env"),"file, but by telling Jitsi Meet's ",(0,l.kt)("inlineCode",{parentName:"p"},"web")," service\nto mount the following two volumes: "),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},"mount ",(0,l.kt)("inlineCode",{parentName:"li"},"/path/to/your/cert.key")," file to ",(0,l.kt)("inlineCode",{parentName:"li"},"/config/keys/cert.key")," mount point"),(0,l.kt)("li",{parentName:"ul"},"mount ",(0,l.kt)("inlineCode",{parentName:"li"},"/path/to/your/cert.fullchain")," file to the ",(0,l.kt)("inlineCode",{parentName:"li"},"/config/keys/cert.crt")," mount point.")),(0,l.kt)("p",null,"Doing it in ",(0,l.kt)("inlineCode",{parentName:"p"},"docker-compose.yml")," file should look like this:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"services:\n web:\n ...\n volumes:\n ...\n - /path/to/your/cert.fullchain:/config/keys/cert.crt\n - /path/to/your/cert.key:/config/keys/cert.key\n")),(0,l.kt)("h3",{id:"features-configuration-configjs"},"Features configuration (config.js)"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Example"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"TOOLBAR_BUTTONS")),(0,l.kt)("td",{parentName:"tr",align:null},"Configure toolbar buttons. Add the buttons name separated with comma(no spaces between comma)"),(0,l.kt)("td",{parentName:"tr",align:null})),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"HIDE_PREMEETING_BUTTONS")),(0,l.kt)("td",{parentName:"tr",align:null},"Hide the buttons at pre-join screen. Add the buttons name separated with comma"),(0,l.kt)("td",{parentName:"tr",align:null})),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_LOBBY")),(0,l.kt)("td",{parentName:"tr",align:null},"Control whether the lobby feature should be enabled or not"),(0,l.kt)("td",{parentName:"tr",align:null},"1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_AV_MODERATION")),(0,l.kt)("td",{parentName:"tr",align:null},"Control whether the A/V moderation should be enabled or not"),(0,l.kt)("td",{parentName:"tr",align:null},"1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_PREJOIN_PAGE")),(0,l.kt)("td",{parentName:"tr",align:null},"Show a prejoin page before entering a conference"),(0,l.kt)("td",{parentName:"tr",align:null},"1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_WELCOME_PAGE")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable the welcome page"),(0,l.kt)("td",{parentName:"tr",align:null},"1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_CLOSE_PAGE")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable the close page"),(0,l.kt)("td",{parentName:"tr",align:null},"0")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"DISABLE_AUDIO_LEVELS")),(0,l.kt)("td",{parentName:"tr",align:null},"Disable measuring of audio levels"),(0,l.kt)("td",{parentName:"tr",align:null},"0")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_NOISY_MIC_DETECTION")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable noisy mic detection"),(0,l.kt)("td",{parentName:"tr",align:null},"1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_BREAKOUT_ROOMS")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable breakout rooms"),(0,l.kt)("td",{parentName:"tr",align:null},"1")))),(0,l.kt)("h3",{id:"jigasi-sip-gateway-audio-only-configuration"},"Jigasi SIP gateway (audio only) configuration"),(0,l.kt)("p",null,"If you want to enable the SIP gateway, these options are required:"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Example"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_SIP_URI")),(0,l.kt)("td",{parentName:"tr",align:null},"SIP URI for incoming / outgoing calls"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"mailto:test@sip2sip.info"},"test@sip2sip.info"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_SIP_PASSWORD")),(0,l.kt)("td",{parentName:"tr",align:null},"Password for the specified SIP account"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},""))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_SIP_SERVER")),(0,l.kt)("td",{parentName:"tr",align:null},"SIP server (use the SIP account domain if in doubt)"),(0,l.kt)("td",{parentName:"tr",align:null},"sip2sip.info")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_SIP_PORT")),(0,l.kt)("td",{parentName:"tr",align:null},"SIP server port"),(0,l.kt)("td",{parentName:"tr",align:null},"5060")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_SIP_TRANSPORT")),(0,l.kt)("td",{parentName:"tr",align:null},"SIP transport"),(0,l.kt)("td",{parentName:"tr",align:null},"UDP")))),(0,l.kt)("h4",{id:"display-dial-in-information"},"Display Dial-In information"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Example"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"DIALIN_NUMBERS_URL")),(0,l.kt)("td",{parentName:"tr",align:null},"URL to the JSON with all Dial-In numbers"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"https://meet.example.com/dialin.json"},"https://meet.example.com/dialin.json"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"CONFCODE_URL")),(0,l.kt)("td",{parentName:"tr",align:null},"URL to the API for checking/generating Dial-In codes"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"https://jitsi-api.jitsi.net/conferenceMapper"},"https://jitsi-api.jitsi.net/conferenceMapper"))))),(0,l.kt)("p",null,"The JSON with the Dial-In numbers should look like this:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-json"},'{"message":"Dial-In numbers:","numbers":{"DE": ["+49-721-0000-0000"]},"numbersEnabled":true}\n')),(0,l.kt)("h3",{id:"recording--live-streaming-configuration-with-jibri"},"Recording / live streaming configuration with Jibri"),(0,l.kt)("details",null,(0,l.kt)("summary",null,"If you are using a release older than 7439 some extra setup is necessary."),"Before running Jibri **on releases older than 7439**, you need to set up an ALSA loopback device on the host. This **will not** work on a non-Linux host.",(0,l.kt)("p",null,"For CentOS 7, the module is already compiled with the kernel, so just run:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},'# configure 5 capture/playback interfaces\necho "options snd-aloop enable=1,1,1,1,1 index=0,1,2,3,4" > /etc/modprobe.d/alsa-loopback.conf\n# setup autoload the module\necho "snd_aloop" > /etc/modules-load.d/snd_aloop.conf\n# load the module\nmodprobe snd-aloop\n# check that the module is loaded\nlsmod | grep snd_aloop\n')),(0,l.kt)("p",null,"For Ubuntu:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},'# install the module\napt update && apt install linux-image-extra-virtual\n# configure 5 capture/playback interfaces\necho "options snd-aloop enable=1,1,1,1,1 index=0,1,2,3,4" > /etc/modprobe.d/alsa-loopback.conf\n# setup autoload the module\necho "snd-aloop" >> /etc/modules\n# check that the module is loaded\nlsmod | grep snd_aloop\n')),(0,l.kt)("admonition",{type:"note"},(0,l.kt)("p",{parentName:"admonition"},'If you are running on AWS you may need to reboot your machine to use the generic kernel instead\nof the "aws" kernel. If after reboot, your machine is still using the "aws" kernel, you\'ll need to manually update the grub file. So just run:')),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},'# open the grub file in editor\nnano /etc/default/grub\n# Modify the value of GRUB_DEFAULT from "0" to "1>2"\n# Save and exit from file\n\n# Update grub\nupdate-grub\n# Reboot the machine\nreboot now\n')),(0,l.kt)("p",null,"For using multiple Jibri instances, you have to select different loopback interfaces for each instance manually."),(0,l.kt)("p",null," Default the first instance has:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},'...\nslave.pcm "hw:Loopback,0,0"\n...\nslave.pcm "hw:Loopback,0,1"\n...\nslave.pcm "hw:Loopback,1,1"\n...\nslave.pcm "hw:Loopback,1,0"\n...\n')),(0,l.kt)("p",null," To setup the second instance, run container with changed ",(0,l.kt)("inlineCode",{parentName:"p"},"/home/jibri/.asoundrc"),":"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},'...\nslave.pcm "hw:Loopback_1,0,0"\n...\nslave.pcm "hw:Loopback_1,0,1"\n...\nslave.pcm "hw:Loopback_1,1,1"\n...\nslave.pcm "hw:Loopback_1,1,0"\n...\n')),(0,l.kt)("p",null," Also you can use numbering id for set loopback interface. The third instance will have ",(0,l.kt)("inlineCode",{parentName:"p"},".asoundrc")," that looks like:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},'...\nslave.pcm "hw:2,0,0"\n...\nslave.pcm "hw:2,0,1"\n...\nslave.pcm "hw:2,1,1"\n...\nslave.pcm "hw:2,1,0"\n...\n\n'))),(0,l.kt)("p",null,"If you want to enable Jibri these options are required:"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Example"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_RECORDING")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable recording / live streaming"),(0,l.kt)("td",{parentName:"tr",align:null},"1")))),(0,l.kt)("p",null,"Extended Jibri configuration:"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Example"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIBRI_RECORDER_USER")),(0,l.kt)("td",{parentName:"tr",align:null},"Internal recorder user for Jibri client connections"),(0,l.kt)("td",{parentName:"tr",align:null},"recorder")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIBRI_RECORDER_PASSWORD")),(0,l.kt)("td",{parentName:"tr",align:null},"Internal recorder password for Jibri client connections"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},""))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIBRI_RECORDING_DIR")),(0,l.kt)("td",{parentName:"tr",align:null},"Directory for recordings inside Jibri container"),(0,l.kt)("td",{parentName:"tr",align:null},"/config/recordings")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIBRI_FINALIZE_RECORDING_SCRIPT_PATH")),(0,l.kt)("td",{parentName:"tr",align:null},"The finalizing script. Will run after recording is complete"),(0,l.kt)("td",{parentName:"tr",align:null},"/config/finalize.sh")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIBRI_XMPP_USER")),(0,l.kt)("td",{parentName:"tr",align:null},"Internal user for Jibri client connections."),(0,l.kt)("td",{parentName:"tr",align:null},"jibri")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIBRI_STRIP_DOMAIN_JID")),(0,l.kt)("td",{parentName:"tr",align:null},"Prefix domain for strip inside Jibri (please see env.example for details)"),(0,l.kt)("td",{parentName:"tr",align:null},"muc")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIBRI_BREWERY_MUC")),(0,l.kt)("td",{parentName:"tr",align:null},"MUC name for the Jibri pool"),(0,l.kt)("td",{parentName:"tr",align:null},"jibribrewery")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIBRI_PENDING_TIMEOUT")),(0,l.kt)("td",{parentName:"tr",align:null},"MUC connection timeout"),(0,l.kt)("td",{parentName:"tr",align:null},"90")))),(0,l.kt)("h3",{id:"jitsi-meet-configuration"},"Jitsi Meet configuration"),(0,l.kt)("admonition",{title:"This section partly contains duplicate settings",type:"tip"},(0,l.kt)("p",{parentName:"admonition"},"There are settings within your ",(0,l.kt)("inlineCode",{parentName:"p"},"docker-compose.yml")," like ",(0,l.kt)("inlineCode",{parentName:"p"},"START_AUDIO_MUTED"),"\nthat will be overwritten if you follow the below guide.")),(0,l.kt)("p",null,"Jitsi-Meet uses two configuration files for changing default settings within\nthe web interface: ",(0,l.kt)("inlineCode",{parentName:"p"},"config.js")," and ",(0,l.kt)("inlineCode",{parentName:"p"},"interface_config.js"),". The files are\nlocated within the ",(0,l.kt)("inlineCode",{parentName:"p"},"CONFIG")," directory configured within your environment file."),(0,l.kt)("p",null,"These files are re-created on every container restart.\nIf you'd like to provide your own settings, create your own config files:\n",(0,l.kt)("inlineCode",{parentName:"p"},"custom-config.js")," and ",(0,l.kt)("inlineCode",{parentName:"p"},"custom-interface_config.js"),"."),(0,l.kt)("p",null,"It's enough to provide your relevant settings only, the docker scripts will\nappend your custom files to the default ones!"),(0,l.kt)("h3",{id:"authentication"},"Authentication"),(0,l.kt)("p",null,"Authentication can be controlled with the environment variables below. If guest\naccess is enabled, unauthenticated users will need to wait until a user authenticates\nbefore they can join a room. If guest access is not enabled, every user will need\nto authenticate before they can join."),(0,l.kt)("p",null,"If authentication is enabled, once an authenticated user logged in, it is always\nlogged in before the session timeout. You can set ",(0,l.kt)("inlineCode",{parentName:"p"},"ENABLE_AUTO_LOGIN=0")," to disable\nthis default auto login feature or you can set ",(0,l.kt)("inlineCode",{parentName:"p"},"JICOFO_AUTH_LIFETIME")," to limit\nthe session lifetime."),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Example"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_AUTH")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable authentication"),(0,l.kt)("td",{parentName:"tr",align:null},"1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_GUESTS")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable guest access"),(0,l.kt)("td",{parentName:"tr",align:null},"1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"AUTH_TYPE")),(0,l.kt)("td",{parentName:"tr",align:null},"Select authentication type (internal, jwt or ldap)"),(0,l.kt)("td",{parentName:"tr",align:null},"internal")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_AUTO_LOGIN")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable auto login"),(0,l.kt)("td",{parentName:"tr",align:null},"1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JICOFO_AUTH_LIFETIME")),(0,l.kt)("td",{parentName:"tr",align:null},"Select session timeout value for an authenticated user"),(0,l.kt)("td",{parentName:"tr",align:null},"3 hours")))),(0,l.kt)("h4",{id:"internal-authentication"},"Internal authentication"),(0,l.kt)("p",null,"The default authentication mode (",(0,l.kt)("inlineCode",{parentName:"p"},"internal"),") uses XMPP credentials to authenticate users.\nTo enable it you have to enable authentication with ",(0,l.kt)("inlineCode",{parentName:"p"},"ENABLE_AUTH")," and set ",(0,l.kt)("inlineCode",{parentName:"p"},"AUTH_TYPE")," to ",(0,l.kt)("inlineCode",{parentName:"p"},"internal"),",\nthen configure the settings you can see below."),(0,l.kt)("p",null,"Internal users must be created with the ",(0,l.kt)("inlineCode",{parentName:"p"},"prosodyctl")," utility in the ",(0,l.kt)("inlineCode",{parentName:"p"},"prosody")," container.\nIn order to do that, first, execute a shell in the corresponding container:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"docker-compose exec prosody /bin/bash\n")),(0,l.kt)("p",null,"Once in the container, run the following command to create a user:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"prosodyctl --config /config/prosody.cfg.lua register TheDesiredUsername meet.jitsi TheDesiredPassword\n")),(0,l.kt)("p",null,"Note that the command produces no output."),(0,l.kt)("p",null,"To delete a user, run the following command in the container:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"prosodyctl --config /config/prosody.cfg.lua unregister TheDesiredUsername meet.jitsi\n")),(0,l.kt)("p",null,"To list all users, run the following command in the container:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"find /config/data/meet%2ejitsi/accounts -type f -exec basename {} .dat \\;\n")),(0,l.kt)("h4",{id:"authentication-using-ldap"},"Authentication using LDAP"),(0,l.kt)("p",null,"You can use LDAP to authenticate users. To enable it you have to enable authentication with ",(0,l.kt)("inlineCode",{parentName:"p"},"ENABLE_AUTH")," and\nset ",(0,l.kt)("inlineCode",{parentName:"p"},"AUTH_TYPE")," to ",(0,l.kt)("inlineCode",{parentName:"p"},"ldap"),", then configure the settings you can see below."),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Example"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"LDAP_URL")),(0,l.kt)("td",{parentName:"tr",align:null},"URL for ldap connection"),(0,l.kt)("td",{parentName:"tr",align:null},"ldaps://ldap.domain.com/")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"LDAP_BASE")),(0,l.kt)("td",{parentName:"tr",align:null},"LDAP base DN. Can be empty."),(0,l.kt)("td",{parentName:"tr",align:null},"DC=example,DC=domain,DC=com")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"LDAP_BINDDN")),(0,l.kt)("td",{parentName:"tr",align:null},"LDAP user DN. Do not specify this parameter for the anonymous bind."),(0,l.kt)("td",{parentName:"tr",align:null},"CN=binduser,OU=users,DC=example,DC=domain,DC=com")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"LDAP_BINDPW")),(0,l.kt)("td",{parentName:"tr",align:null},"LDAP user password. Do not specify this parameter for the anonymous bind."),(0,l.kt)("td",{parentName:"tr",align:null},"LdapUserPassw0rd")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"LDAP_FILTER")),(0,l.kt)("td",{parentName:"tr",align:null},"LDAP filter."),(0,l.kt)("td",{parentName:"tr",align:null},"(sAMAccountName=%u)")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"LDAP_AUTH_METHOD")),(0,l.kt)("td",{parentName:"tr",align:null},"LDAP authentication method."),(0,l.kt)("td",{parentName:"tr",align:null},"bind")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"LDAP_VERSION")),(0,l.kt)("td",{parentName:"tr",align:null},"LDAP protocol version"),(0,l.kt)("td",{parentName:"tr",align:null},"3")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"LDAP_USE_TLS")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable LDAP TLS"),(0,l.kt)("td",{parentName:"tr",align:null},"1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"LDAP_TLS_CIPHERS")),(0,l.kt)("td",{parentName:"tr",align:null},"Set TLS ciphers list to allow"),(0,l.kt)("td",{parentName:"tr",align:null},"SECURE256:SECURE128")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"LDAP_TLS_CHECK_PEER")),(0,l.kt)("td",{parentName:"tr",align:null},"Require and verify LDAP server certificate"),(0,l.kt)("td",{parentName:"tr",align:null},"1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"LDAP_TLS_CACERT_FILE")),(0,l.kt)("td",{parentName:"tr",align:null},"Path to CA cert file. Used when server certificate verification is enabled"),(0,l.kt)("td",{parentName:"tr",align:null},"/etc/ssl/certs/ca-certificates.crt")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"LDAP_TLS_CACERT_DIR")),(0,l.kt)("td",{parentName:"tr",align:null},"Path to CA certs directory. Used when server certificate verification is enabled."),(0,l.kt)("td",{parentName:"tr",align:null},"/etc/ssl/certs")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"LDAP_START_TLS")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable START_TLS, requires LDAPv3, URL must be ldap:// not ldaps://"),(0,l.kt)("td",{parentName:"tr",align:null},"0")))),(0,l.kt)("h4",{id:"authentication-using-jwt-tokens"},"Authentication using JWT tokens"),(0,l.kt)("p",null,"You can use JWT tokens to authenticate users. To enable it you have to enable authentication with ",(0,l.kt)("inlineCode",{parentName:"p"},"ENABLE_AUTH")," and\nset ",(0,l.kt)("inlineCode",{parentName:"p"},"AUTH_TYPE")," to ",(0,l.kt)("inlineCode",{parentName:"p"},"jwt"),", then configure the settings you can see below."),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Example"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JWT_APP_ID")),(0,l.kt)("td",{parentName:"tr",align:null},"Application identifier"),(0,l.kt)("td",{parentName:"tr",align:null},"my_jitsi_app_id")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JWT_APP_SECRET")),(0,l.kt)("td",{parentName:"tr",align:null},"Application secret known only to your token"),(0,l.kt)("td",{parentName:"tr",align:null},"my_jitsi_app_secret")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JWT_ACCEPTED_ISSUERS")),(0,l.kt)("td",{parentName:"tr",align:null},"(Optional) Set asap_accepted_issuers as a comma separated list"),(0,l.kt)("td",{parentName:"tr",align:null},"my_web_client,my_app_client")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JWT_ACCEPTED_AUDIENCES")),(0,l.kt)("td",{parentName:"tr",align:null},"(Optional) Set asap_accepted_audiences as a comma separated list"),(0,l.kt)("td",{parentName:"tr",align:null},"my_server1,my_server2")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JWT_ASAP_KEYSERVER")),(0,l.kt)("td",{parentName:"tr",align:null},"(Optional) Set asap_keyserver to a url where public keys can be found"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"https://example.com/asap"},"https://example.com/asap"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JWT_ALLOW_EMPTY")),(0,l.kt)("td",{parentName:"tr",align:null},"(Optional) Allow anonymous users with no JWT while validating JWTs when provided"),(0,l.kt)("td",{parentName:"tr",align:null},"0")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JWT_AUTH_TYPE")),(0,l.kt)("td",{parentName:"tr",align:null},"(Optional) Controls which module is used for processing incoming JWTs"),(0,l.kt)("td",{parentName:"tr",align:null},"token")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JWT_TOKEN_AUTH_MODULE")),(0,l.kt)("td",{parentName:"tr",align:null},"(Optional) Controls which module is used for validating JWTs"),(0,l.kt)("td",{parentName:"tr",align:null},"token_verification")))),(0,l.kt)("p",null,"This can be tested using the ",(0,l.kt)("a",{parentName:"p",href:"https://jwt.io/#debugger-io"},"jwt.io")," debugger. Use the following sample payload:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-json"},'{\n "context": {\n "user": {\n "avatar": "https://robohash.org/john-doe",\n "name": "John Doe",\n "email": "jdoe@example.com"\n }\n },\n "aud": "my_jitsi_app_id",\n "iss": "my_jitsi_app_id",\n "sub": "meet.jitsi",\n "room": "*"\n}\n')),(0,l.kt)("h4",{id:"authentication-using-matrix"},"Authentication using Matrix"),(0,l.kt)("p",null,'For more information see the documentation of the "Prosody Auth Matrix User Verification" ',(0,l.kt)("a",{parentName:"p",href:"https://github.com/matrix-org/prosody-mod-auth-matrix-user-verification"},"here"),"."),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Example"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"MATRIX_UVS_URL")),(0,l.kt)("td",{parentName:"tr",align:null},"Base URL to the matrix user verification service (without ending slash)"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"https://uvs.example.com:3000"},"https://uvs.example.com:3000"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"MATRIX_UVS_ISSUER")),(0,l.kt)("td",{parentName:"tr",align:null},"(optional) The issuer of the auth token to be passed through. Must match what is being set as ",(0,l.kt)("inlineCode",{parentName:"td"},"iss")," in the JWT."),(0,l.kt)("td",{parentName:"tr",align:null},"issuer (default)")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"MATRIX_UVS_AUTH_TOKEN")),(0,l.kt)("td",{parentName:"tr",align:null},"(optional) user verification service auth token, if authentication enabled"),(0,l.kt)("td",{parentName:"tr",align:null},"changeme")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"MATRIX_UVS_SYNC_POWER_LEVELS")),(0,l.kt)("td",{parentName:"tr",align:null},"(optional) Make Matrix room moderators owners of the Prosody room."),(0,l.kt)("td",{parentName:"tr",align:null},"1")))),(0,l.kt)("h4",{id:"authentication-using-hybrid-matrix-token"},"Authentication using Hybrid Matrix Token"),(0,l.kt)("p",null,"You can use ",(0,l.kt)("inlineCode",{parentName:"p"},"Hybrid Matrix Token")," to authenticate users. It supports ",(0,l.kt)("inlineCode",{parentName:"p"},"Matrix")," and ",(0,l.kt)("inlineCode",{parentName:"p"},"JWT Token")," authentications\non the same setup. To enable it you have to enable authentication with ",(0,l.kt)("inlineCode",{parentName:"p"},"ENABLE_AUTH")," and set ",(0,l.kt)("inlineCode",{parentName:"p"},"AUTH_TYPE")," to\n",(0,l.kt)("inlineCode",{parentName:"p"},"hybrid_matrix_token"),", then configure the settings you can see below."),(0,l.kt)("p",null,'For more information see the documentation of the "Hybrid Matrix Token"\n',(0,l.kt)("a",{parentName:"p",href:"https://github.com/jitsi-contrib/prosody-plugins/tree/main/auth_hybrid_matrix_token"},"here"),"."),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Example"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"MATRIX_UVS_URL")),(0,l.kt)("td",{parentName:"tr",align:null},"Base URL to the matrix user verification service (without ending slash)"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"https://uvs.example.com:3000"},"https://uvs.example.com:3000"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"MATRIX_UVS_ISSUER")),(0,l.kt)("td",{parentName:"tr",align:null},"(optional) The issuer of the auth token to be passed through. Must match what is being set as ",(0,l.kt)("inlineCode",{parentName:"td"},"iss")," in the JWT. It allows all issuers (",(0,l.kt)("inlineCode",{parentName:"td"},"*"),") by default."),(0,l.kt)("td",{parentName:"tr",align:null},"my_issuer")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"MATRIX_UVS_AUTH_TOKEN")),(0,l.kt)("td",{parentName:"tr",align:null},"(optional) user verification service auth token, if authentication enabled"),(0,l.kt)("td",{parentName:"tr",align:null},"my_matrix_secret")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"MATRIX_UVS_SYNC_POWER_LEVELS")),(0,l.kt)("td",{parentName:"tr",align:null},"(optional) Make Matrix room moderators owners of the Prosody room."),(0,l.kt)("td",{parentName:"tr",align:null},"1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JWT_APP_ID")),(0,l.kt)("td",{parentName:"tr",align:null},"Application identifier"),(0,l.kt)("td",{parentName:"tr",align:null},"my_jitsi_app_id")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JWT_APP_SECRET")),(0,l.kt)("td",{parentName:"tr",align:null},"Application secret known only to your token"),(0,l.kt)("td",{parentName:"tr",align:null},"my_jitsi_app_secret")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JWT_ALLOW_EMPTY")),(0,l.kt)("td",{parentName:"tr",align:null},"(Optional) Allow anonymous users with no JWT while validating JWTs when provided"),(0,l.kt)("td",{parentName:"tr",align:null},"0")))),(0,l.kt)("h4",{id:"external-authentication"},"External authentication"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Example"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"TOKEN_AUTH_URL")),(0,l.kt)("td",{parentName:"tr",align:null},"Authenticate using external service or just focus external auth window if there is one already."),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"https://auth.meet.example.com/%7Broom%7D"},"https://auth.meet.example.com/{room}"))))),(0,l.kt)("h3",{id:"shared-document-editing-using-etherpad"},"Shared document editing using Etherpad"),(0,l.kt)("p",null,"You can collaboratively edit a document via ",(0,l.kt)("a",{parentName:"p",href:"https://github.com/ether/etherpad-lite"},"Etherpad"),". In order to enable it, set the config options below and run\nDocker Compose with the additional config file ",(0,l.kt)("inlineCode",{parentName:"p"},"etherpad.yml"),"."),(0,l.kt)("p",null,"Here are the required options:"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Example"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ETHERPAD_URL_BASE")),(0,l.kt)("td",{parentName:"tr",align:null},"Set etherpad-lite URL"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"http://etherpad.meet.jitsi:9001"},"http://etherpad.meet.jitsi:9001"))))),(0,l.kt)("h3",{id:"transcription-configuration"},"Transcription configuration"),(0,l.kt)("p",null,"If you want to enable the Transcribing function, these options are required:"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Example"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_TRANSCRIPTIONS")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable Jigasi transcription in a conference"),(0,l.kt)("td",{parentName:"tr",align:null},"1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"GC_PROJECT_ID")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"project_id")," from Google Cloud Credentials"),(0,l.kt)("td",{parentName:"tr",align:null})),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"GC_PRIVATE_KEY_ID")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"private_key_id")," from Google Cloud Credentials"),(0,l.kt)("td",{parentName:"tr",align:null})),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"GC_PRIVATE_KEY")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"private_key")," from Google Cloud Credentials"),(0,l.kt)("td",{parentName:"tr",align:null})),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"GC_CLIENT_EMAIL")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"client_email")," from Google Cloud Credentials"),(0,l.kt)("td",{parentName:"tr",align:null})),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"GC_CLIENT_ID")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"client_id")," from Google Cloud Credentials"),(0,l.kt)("td",{parentName:"tr",align:null})),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"GC_CLIENT_CERT_URL")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"client_x509_cert_url")," from Google Cloud Credentials"),(0,l.kt)("td",{parentName:"tr",align:null})),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_TRANSCRIBER_RECORD_AUDIO")),(0,l.kt)("td",{parentName:"tr",align:null},"Jigasi will record audio when transcriber is on"),(0,l.kt)("td",{parentName:"tr",align:null},"true")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_TRANSCRIBER_SEND_TXT")),(0,l.kt)("td",{parentName:"tr",align:null},"Jigasi will send transcribed text to the chat when transcriber is on"),(0,l.kt)("td",{parentName:"tr",align:null},"true")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_TRANSCRIBER_ADVERTISE_URL")),(0,l.kt)("td",{parentName:"tr",align:null},"Jigasi will post an url to the chat with transcription file"),(0,l.kt)("td",{parentName:"tr",align:null},"true")))),(0,l.kt)("p",null,"For setting the Google Cloud Credentials please read ",(0,l.kt)("a",{parentName:"p",href:"https://cloud.google.com/text-to-speech/docs/quickstart-protocol"},"https://cloud.google.com/text-to-speech/docs/quickstart-protocol"),' section "Before you begin" paragraph 1 to 5.'),(0,l.kt)("h3",{id:"sentry-logging-configuration"},"Sentry logging configuration"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Default value"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JVB_SENTRY_DSN")),(0,l.kt)("td",{parentName:"tr",align:null},"Sentry Data Source Name (Endpoint for Sentry project)"),(0,l.kt)("td",{parentName:"tr",align:null},"https://public:private@host:port/1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JICOFO_SENTRY_DSN")),(0,l.kt)("td",{parentName:"tr",align:null},"Sentry Data Source Name (Endpoint for Sentry project)"),(0,l.kt)("td",{parentName:"tr",align:null},"https://public:private@host:port/1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_SENTRY_DSN")),(0,l.kt)("td",{parentName:"tr",align:null},"Sentry Data Source Name (Endpoint for Sentry project)"),(0,l.kt)("td",{parentName:"tr",align:null},"https://public:private@host:port/1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"SENTRY_ENVIRONMENT")),(0,l.kt)("td",{parentName:"tr",align:null},"Optional environment info to filter events"),(0,l.kt)("td",{parentName:"tr",align:null},"production")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"SENTRY_RELEASE")),(0,l.kt)("td",{parentName:"tr",align:null},"Optional release info to filter events"),(0,l.kt)("td",{parentName:"tr",align:null},"1.0.0")))),(0,l.kt)("h3",{id:"turn-server-configuration"},"TURN server configuration"),(0,l.kt)("p",null,"Configure external TURN servers."),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Default value"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"TURN_CREDENTIALS")),(0,l.kt)("td",{parentName:"tr",align:null},"Credentials for TURN servers"),(0,l.kt)("td",{parentName:"tr",align:null})),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"TURN_HOST")),(0,l.kt)("td",{parentName:"tr",align:null},"TURN server hostnames as a comma separated list (UDP or TCP transport)"),(0,l.kt)("td",{parentName:"tr",align:null})),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"TURN_PORT")),(0,l.kt)("td",{parentName:"tr",align:null},"TURN server port (UDP or TCP transport)"),(0,l.kt)("td",{parentName:"tr",align:null},"443")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"TURN_TRANSPORT")),(0,l.kt)("td",{parentName:"tr",align:null},"TURN server protocols as a comma separated list (UDP or TCP or both)"),(0,l.kt)("td",{parentName:"tr",align:null},"tcp")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"TURNS_HOST")),(0,l.kt)("td",{parentName:"tr",align:null},"TURN server hostnames as a comma separated list (TLS transport)"),(0,l.kt)("td",{parentName:"tr",align:null})),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"TURNS_PORT")),(0,l.kt)("td",{parentName:"tr",align:null},"TURN server port (TLS transport)"),(0,l.kt)("td",{parentName:"tr",align:null},"443")))),(0,l.kt)("h3",{id:"advanced-configuration"},"Advanced configuration"),(0,l.kt)("p",null,"These configuration options are already set and generally don't need to be changed."),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Default value"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"XMPP_DOMAIN")),(0,l.kt)("td",{parentName:"tr",align:null},"Internal XMPP domain"),(0,l.kt)("td",{parentName:"tr",align:null},"meet.jitsi")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"XMPP_AUTH_DOMAIN")),(0,l.kt)("td",{parentName:"tr",align:null},"Internal XMPP domain for authenticated services"),(0,l.kt)("td",{parentName:"tr",align:null},"auth.meet.jitsi")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"XMPP_SERVER")),(0,l.kt)("td",{parentName:"tr",align:null},"Internal XMPP server name xmpp.meet.jitsi"),(0,l.kt)("td",{parentName:"tr",align:null},"xmpp.meet.jitsi")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"XMPP_BOSH_URL_BASE")),(0,l.kt)("td",{parentName:"tr",align:null},"Internal XMPP server URL for BOSH module"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"http://xmpp.meet.jitsi:5280"},"http://xmpp.meet.jitsi:5280"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"XMPP_MUC_DOMAIN")),(0,l.kt)("td",{parentName:"tr",align:null},"XMPP domain for the MUC"),(0,l.kt)("td",{parentName:"tr",align:null},"muc.meet.jitsi")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"XMPP_INTERNAL_MUC_DOMAIN")),(0,l.kt)("td",{parentName:"tr",align:null},"XMPP domain for the internal MUC"),(0,l.kt)("td",{parentName:"tr",align:null},"internal-muc.meet.jitsi")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"XMPP_GUEST_DOMAIN")),(0,l.kt)("td",{parentName:"tr",align:null},"XMPP domain for unauthenticated users"),(0,l.kt)("td",{parentName:"tr",align:null},"guest.meet.jitsi")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"XMPP_RECORDER_DOMAIN")),(0,l.kt)("td",{parentName:"tr",align:null},"Domain for the jibri recorder"),(0,l.kt)("td",{parentName:"tr",align:null},"recorder.meet.jitsi")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"XMPP_MODULES")),(0,l.kt)("td",{parentName:"tr",align:null},"Custom Prosody modules for XMPP_DOMAIN (comma separated)"),(0,l.kt)("td",{parentName:"tr",align:null},"info,alert")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"XMPP_MUC_MODULES")),(0,l.kt)("td",{parentName:"tr",align:null},"Custom Prosody modules for MUC component (comma separated)"),(0,l.kt)("td",{parentName:"tr",align:null},"info,alert")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"XMPP_INTERNAL_MUC_MODULES")),(0,l.kt)("td",{parentName:"tr",align:null},"Custom Prosody modules for internal MUC component (comma separated)"),(0,l.kt)("td",{parentName:"tr",align:null},"info,alert")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"GLOBAL_MODULES")),(0,l.kt)("td",{parentName:"tr",align:null},"Custom prosody modules to load in global configuration (comma separated)"),(0,l.kt)("td",{parentName:"tr",align:null},"statistics,alert")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"GLOBAL_CONFIG")),(0,l.kt)("td",{parentName:"tr",align:null},"Custom configuration string with escaped newlines"),(0,l.kt)("td",{parentName:"tr",align:null},"foo = bar;\\nkey = val;")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"RESTART_POLICY")),(0,l.kt)("td",{parentName:"tr",align:null},"Container restart policy"),(0,l.kt)("td",{parentName:"tr",align:null},"defaults to ",(0,l.kt)("inlineCode",{parentName:"td"},"unless-stopped"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"DISABLE_HTTPS")),(0,l.kt)("td",{parentName:"tr",align:null},"Handle TLS connections outside of this setup"),(0,l.kt)("td",{parentName:"tr",align:null},"0")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_HTTP_REDIRECT")),(0,l.kt)("td",{parentName:"tr",align:null},"Redirect HTTP traffic to HTTPS"),(0,l.kt)("td",{parentName:"tr",align:null},"0")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"LOG_LEVEL")),(0,l.kt)("td",{parentName:"tr",align:null},"Controls which logs are output from prosody and associated modules"),(0,l.kt)("td",{parentName:"tr",align:null},"info")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_HSTS")),(0,l.kt)("td",{parentName:"tr",align:null},"Send a ",(0,l.kt)("inlineCode",{parentName:"td"},"strict-transport-security")," header to force browsers to use a secure and trusted connection. Recommended for production use."),(0,l.kt)("td",{parentName:"tr",align:null},"1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_IPV6")),(0,l.kt)("td",{parentName:"tr",align:null},"Provides means to disable IPv6 in environments that don't support it"),(0,l.kt)("td",{parentName:"tr",align:null},"1")))),(0,l.kt)("h4",{id:"advanced-prosody-options"},"Advanced Prosody options"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Default value"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"PROSODY_RESERVATION_ENABLED")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable Prosody's reservation REST API"),(0,l.kt)("td",{parentName:"tr",align:null},"false")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"PROSODY_RESERVATION_REST_BASE_URL")),(0,l.kt)("td",{parentName:"tr",align:null},"Base URL of Prosody's reservation REST API"),(0,l.kt)("td",{parentName:"tr",align:null})),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"PROSODY_AUTH_TYPE")),(0,l.kt)("td",{parentName:"tr",align:null},"Select authentication type for Prosody (internal, jwt or ldap)"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"AUTH_TYPE"))))),(0,l.kt)("h4",{id:"advanced-jicofo-options"},"Advanced Jicofo options"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Default value"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JICOFO_COMPONENT_SECRET")),(0,l.kt)("td",{parentName:"tr",align:null},"XMPP component password for Jicofo"),(0,l.kt)("td",{parentName:"tr",align:null},"s3cr37")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JICOFO_AUTH_USER")),(0,l.kt)("td",{parentName:"tr",align:null},"XMPP user for Jicofo client connections"),(0,l.kt)("td",{parentName:"tr",align:null},"focus")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JICOFO_AUTH_PASSWORD")),(0,l.kt)("td",{parentName:"tr",align:null},"XMPP password for Jicofo client connections"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},""))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JICOFO_ENABLE_AUTH")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable authentication in Jicofo"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_AUTH"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JICOFO_AUTH_TYPE")),(0,l.kt)("td",{parentName:"tr",align:null},"Select authentication type for Jicofo (internal, jwt or ldap)"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"AUTH_TYPE"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JICOFO_AUTH_LIFETIME")),(0,l.kt)("td",{parentName:"tr",align:null},"Select session timeout value for an authenticated user"),(0,l.kt)("td",{parentName:"tr",align:null},"24 hours")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JICOFO_ENABLE_HEALTH_CHECKS")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable health checks inside Jicofo, allowing the use of the REST api to check Jicofo's status"),(0,l.kt)("td",{parentName:"tr",align:null},"false")))),(0,l.kt)("h4",{id:"advanced-jvb-options"},"Advanced JVB options"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Default value"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JVB_AUTH_USER")),(0,l.kt)("td",{parentName:"tr",align:null},"XMPP user for JVB MUC client connections"),(0,l.kt)("td",{parentName:"tr",align:null},"jvb")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JVB_AUTH_PASSWORD")),(0,l.kt)("td",{parentName:"tr",align:null},"XMPP password for JVB MUC client connections"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},""))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JVB_STUN_SERVERS")),(0,l.kt)("td",{parentName:"tr",align:null},"STUN servers used to discover the server's public IP"),(0,l.kt)("td",{parentName:"tr",align:null},"stun.l.google.com:19302, stun1.l.google.com:19302, stun2.l.google.com:19302")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JVB_PORT")),(0,l.kt)("td",{parentName:"tr",align:null},"UDP port for media used by Jitsi Videobridge"),(0,l.kt)("td",{parentName:"tr",align:null},"10000")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JVB_COLIBRI_PORT")),(0,l.kt)("td",{parentName:"tr",align:null},"COLIBRI REST API port of JVB exposed to localhost"),(0,l.kt)("td",{parentName:"tr",align:null},"8080")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JVB_BREWERY_MUC")),(0,l.kt)("td",{parentName:"tr",align:null},"MUC name for the JVB pool"),(0,l.kt)("td",{parentName:"tr",align:null},"jvbbrewery")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"COLIBRI_REST_ENABLED")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable the COLIBRI REST API"),(0,l.kt)("td",{parentName:"tr",align:null},"true")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"SHUTDOWN_REST_ENABLED")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable the shutdown REST API"),(0,l.kt)("td",{parentName:"tr",align:null},"true")))),(0,l.kt)("h4",{id:"advanced-jigasi-options"},"Advanced Jigasi options"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Default value"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_ENABLE_SDES_SRTP")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable SDES srtp"),(0,l.kt)("td",{parentName:"tr",align:null},"0")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_SIP_KEEP_ALIVE_METHOD")),(0,l.kt)("td",{parentName:"tr",align:null},"Keepalive method"),(0,l.kt)("td",{parentName:"tr",align:null},"OPTIONS")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_HEALTH_CHECK_SIP_URI")),(0,l.kt)("td",{parentName:"tr",align:null},"Health-check extension"),(0,l.kt)("td",{parentName:"tr",align:null})),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_HEALTH_CHECK_INTERVAL")),(0,l.kt)("td",{parentName:"tr",align:null},"Health-check interval"),(0,l.kt)("td",{parentName:"tr",align:null},"300000")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_XMPP_USER")),(0,l.kt)("td",{parentName:"tr",align:null},"XMPP user for Jigasi MUC client connections"),(0,l.kt)("td",{parentName:"tr",align:null},"jigasi")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_XMPP_PASSWORD")),(0,l.kt)("td",{parentName:"tr",align:null},"XMPP password for Jigasi MUC client connections"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},""))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_BREWERY_MUC")),(0,l.kt)("td",{parentName:"tr",align:null},"MUC name for the Jigasi pool"),(0,l.kt)("td",{parentName:"tr",align:null},"jigasibrewery")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_PORT_MIN")),(0,l.kt)("td",{parentName:"tr",align:null},"Minimum port for media used by Jigasi"),(0,l.kt)("td",{parentName:"tr",align:null},"20000")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_PORT_MAX")),(0,l.kt)("td",{parentName:"tr",align:null},"Maximum port for media used by Jigasi"),(0,l.kt)("td",{parentName:"tr",align:null},"20050")))),(0,l.kt)("h3",{id:"running-behind-nat-or-on-a-lan-environment"},"Running behind NAT or on a LAN environment"),(0,l.kt)("p",null,"When running running in a LAN environment, or on the public Internet via NAT, the ",(0,l.kt)("inlineCode",{parentName:"p"},"JVB_ADVERTISE_IPS")," env variable should be set.\nThis variable allows to control which IP addresses the JVB will advertise for WebRTC media traffic."),(0,l.kt)("admonition",{type:"note"},(0,l.kt)("p",{parentName:"admonition"},"This variable used to be called ",(0,l.kt)("inlineCode",{parentName:"p"},"DOCKER_HOST_ADDRESS")," but it got renamed for clarity and to support a list of IPs.")),(0,l.kt)("p",null,"If your users are coming in over the Internet (and not over LAN), this will likely be your public IP address. If this is not set up correctly, calls will crash when more than two users join a meeting."),(0,l.kt)("p",null,"The public IP address is attempted to be discovered via ",(0,l.kt)("a",{parentName:"p",href:"https://en.wikipedia.org/wiki/STUN"},"STUN"),".\nSTUN servers can be specified with the ",(0,l.kt)("inlineCode",{parentName:"p"},"JVB_STUN_SERVERS")," option."),(0,l.kt)("admonition",{type:"note"},(0,l.kt)("p",{parentName:"admonition"},"Due to a bug in the docker version currently in the Debian repos (20.10.5), ",(0,l.kt)("a",{parentName:"p",href:"https://forums.docker.com/t/docker-doesnt-open-ipv6-ports/106201/2"},"Docker does not listen on IPv6 ports"),", so for that combination you will have to ",(0,l.kt)("a",{parentName:"p",href:"https://docs.docker.com/engine/install/debian/"},"manually obtain the latest version"),".")),(0,l.kt)("h4",{id:"split-horizon"},"Split horizon"),(0,l.kt)("p",null,"If you are running in a split horizon environemt (LAN internal clients connect to a local IP and other clients connect to a public IP) you can specify\nmultiple advertised IPs by separating them with commas:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"JVB_ADVERTISE_IPS=192.168.1.1,1.2.3.4\n")),(0,l.kt)("h4",{id:"offline--airgapped-installation"},"Offline / airgapped installation"),(0,l.kt)("p",null,"If your setup does not have access to the Internet you'll need to disable STUN on the JVB since discovering its own IP address will fail, but that is not necessary on that type of environment."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"JVB_DISABLE_STUN=true\n")),(0,l.kt)("h2",{id:"accessing-server-logs"},"Accessing server logs"),(0,l.kt)("p",null,"The default bahavior of ",(0,l.kt)("inlineCode",{parentName:"p"},"docker-jitsi-meet")," is to log to ",(0,l.kt)("inlineCode",{parentName:"p"},"stdout"),"."),(0,l.kt)("p",null,"While the logs are sent to ",(0,l.kt)("inlineCode",{parentName:"p"},"stdout"),", they are not lost: unless configured to drop all logs, Docker keeps them available for future retrieval and processing."),(0,l.kt)("p",null,"If you need to access the container's logs you have multiple options. Here are the main ones:"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},"run ",(0,l.kt)("inlineCode",{parentName:"li"},"docker-compose logs -t -f ")," from command line, where ",(0,l.kt)("inlineCode",{parentName:"li"},"")," is one of ",(0,l.kt)("inlineCode",{parentName:"li"},"web"),", ",(0,l.kt)("inlineCode",{parentName:"li"},"prosody"),",",(0,l.kt)("inlineCode",{parentName:"li"},"jvb"),", ",(0,l.kt)("inlineCode",{parentName:"li"},"jicofo"),". This command will output the logs for the selected service to stdout with timestamps."),(0,l.kt)("li",{parentName:"ul"},"use a standard ",(0,l.kt)("a",{parentName:"li",href:"https://docs.docker.com/config/containers/logging/configure/"},"docker logging driver")," to redirect the logs to the desired target (for instance ",(0,l.kt)("inlineCode",{parentName:"li"},"syslog")," or ",(0,l.kt)("inlineCode",{parentName:"li"},"splunk"),")."),(0,l.kt)("li",{parentName:"ul"},"serach ",(0,l.kt)("a",{parentName:"li",href:"https://hub.docker.com/search?q="},"docker hub")," for a third party ",(0,l.kt)("a",{parentName:"li",href:"https://docs.docker.com/config/containers/logging/plugins/"},"docker logging driver plugin")," "),(0,l.kt)("li",{parentName:"ul"},"or ",(0,l.kt)("a",{parentName:"li",href:"https://docs.docker.com/engine/extend/plugins_logging/"},"write your own driver plugin")," if you have a very specific need.")),(0,l.kt)("p",null,"For instance, if you want to have all logs related to a ",(0,l.kt)("inlineCode",{parentName:"p"},"")," written to ",(0,l.kt)("inlineCode",{parentName:"p"},"/var/log/jitsi/")," as ",(0,l.kt)("inlineCode",{parentName:"p"},"json")," output, you could use ",(0,l.kt)("a",{parentName:"p",href:"https://github.com/deep-compute/docker-file-log-driver"},"docker-file-log-driver")," and configure it by adding the following block in your ",(0,l.kt)("inlineCode",{parentName:"p"},"docker-compose.yml")," file, at the same level as the ",(0,l.kt)("inlineCode",{parentName:"p"},"image")," block of the selected ",(0,l.kt)("inlineCode",{parentName:"p"},""),":"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},'services:\n :\n image: ...\n ...\n logging:\n driver: file-log-driver\n options:\n fpath: "/jitsi/.log"\n')),(0,l.kt)("p",null,"If you want to only display the ",(0,l.kt)("inlineCode",{parentName:"p"},"message")," part of the log in ",(0,l.kt)("inlineCode",{parentName:"p"},"json")," format, simply execute the following command (for instance if ",(0,l.kt)("inlineCode",{parentName:"p"},"fpath")," was set to ",(0,l.kt)("inlineCode",{parentName:"p"},"/jitsi/jvb.log"),") which uses ",(0,l.kt)("inlineCode",{parentName:"p"},"jq")," to extract the relevant part of the logs:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"sudo cat /var/log/jitsi/jvb.log | jq -r '.msg' | jq -r '.message'\n")),(0,l.kt)("h2",{id:"build-instructions"},"Build Instructions"),(0,l.kt)("p",null,"Building your images allows you to edit the configuration files of each image individually, providing more customization for your deployment."),(0,l.kt)("p",null,"The docker images can be built by running the ",(0,l.kt)("inlineCode",{parentName:"p"},"make")," command in the main repository folder. If you need to overwrite existing images from the remote source, use ",(0,l.kt)("inlineCode",{parentName:"p"},"FORCE_REBUILD=1 make"),"."),(0,l.kt)("p",null,"If you are on the unstable branch, build the images with ",(0,l.kt)("inlineCode",{parentName:"p"},"FORCE_REBUILD=1 JITSI_RELEASE=unstable make"),"."),(0,l.kt)("p",null,"You are now able to run ",(0,l.kt)("inlineCode",{parentName:"p"},"docker-compose up")," as usual."),(0,l.kt)("h2",{id:"running-behind-a-reverse-proxy"},"Running behind a reverse proxy"),(0,l.kt)("p",null,"By default this setup is using WebSocket connections for 2 core components:"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},"Signalling (XMPP)"),(0,l.kt)("li",{parentName:"ul"},"Bridge channel (colibri)")),(0,l.kt)("p",null,"Due to the hop-by-hop nature of WebSockets the reverse proxy must properly terminate and forward WebSocket connections. There 2 routes require such treatment:"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},"/xmpp-websocket"),(0,l.kt)("li",{parentName:"ul"},"/colibri-ws")),(0,l.kt)("p",null,"With nginx, these routes can be forwarded using the following config snippet:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},'location /xmpp-websocket {\n proxy_pass https://localhost:8443;\n proxy_http_version 1.1;\n proxy_set_header Upgrade $http_upgrade;\n proxy_set_header Connection "upgrade";\n}\nlocation /colibri-ws {\n proxy_pass https://localhost:8443;\n proxy_http_version 1.1;\n proxy_set_header Upgrade $http_upgrade;\n proxy_set_header Connection "upgrade";\n}\n')),(0,l.kt)("p",null,"With apache, ",(0,l.kt)("inlineCode",{parentName:"p"},"mod_proxy")," and ",(0,l.kt)("inlineCode",{parentName:"p"},"mod_proxy_wstunnel")," need to be enabled and these routes can be forwarded using the following config snippet:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},'\n \n ProxyTimeout 900\n \n ProxyPass "wss://localhost:8443/xmpp-websocket"\n \n \n ProxyPass "wss://localhost:8443/colibri-ws/"\n \n \n\n\n')),(0,l.kt)("p",null,"where ",(0,l.kt)("inlineCode",{parentName:"p"},"https://localhost:8443/")," is the url of the web service's ingress."),(0,l.kt)("h3",{id:"disabling-websocket-connections"},"Disabling WebSocket connections"),(0,l.kt)("admonition",{type:"note"},(0,l.kt)("p",{parentName:"admonition"},"This is not the recommended setup.")),(0,l.kt)("p",null,"If using WebSockets is not an option, these environment variables can be set to fallback to HTTP polling and WebRTC datachannels:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"ENABLE_SCTP=1\nENABLE_COLIBRI_WEBSOCKET=0\nENABLE_XMPP_WEBSOCKET=0\n")))}k.isMDXComponent=!0},168:(t,e,n)=>{n.d(e,{Z:()=>a});const a=n.p+"assets/images/docker-jitsi-meet-afafdf87fea30a2fa6412baa4a3f8248.png"}}]); \ No newline at end of file +"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[3634],{3905:(t,e,n)=>{n.d(e,{Zo:()=>m,kt:()=>k});var a=n(7294);function r(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function l(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);e&&(a=a.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,a)}return n}function i(t){for(var e=1;e=0||(r[n]=t[n]);return r}(t,e);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(t);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(t,n)&&(r[n]=t[n])}return r}var p=a.createContext({}),d=function(t){var e=a.useContext(p),n=e;return t&&(n="function"==typeof t?t(e):i(i({},e),t)),n},m=function(t){var e=d(t.components);return a.createElement(p.Provider,{value:e},t.children)},s={inlineCode:"code",wrapper:function(t){var e=t.children;return a.createElement(a.Fragment,{},e)}},u=a.forwardRef((function(t,e){var n=t.components,r=t.mdxType,l=t.originalType,p=t.parentName,m=o(t,["components","mdxType","originalType","parentName"]),u=d(n),k=r,N=u["".concat(p,".").concat(k)]||u[k]||s[k]||l;return n?a.createElement(N,i(i({ref:e},m),{},{components:n})):a.createElement(N,i({ref:e},m))}));function k(t,e){var n=arguments,r=e&&e.mdxType;if("string"==typeof t||r){var l=n.length,i=new Array(l);i[0]=u;var o={};for(var p in e)hasOwnProperty.call(e,p)&&(o[p]=e[p]);o.originalType=t,o.mdxType="string"==typeof t?t:r,i[1]=o;for(var d=2;d{n.r(e),n.d(e,{assets:()=>m,contentTitle:()=>p,default:()=>k,frontMatter:()=>o,metadata:()=>d,toc:()=>s});var a=n(7462),r=n(3366),l=(n(7294),n(3905)),i=["components"],o={id:"devops-guide-docker",title:"Self-Hosting Guide - Docker",sidebar_label:"Docker"},p=void 0,d={unversionedId:"devops-guide/devops-guide-docker",id:"devops-guide/devops-guide-docker",title:"Self-Hosting Guide - Docker",description:"Starting with release stable-7289-1 our images are provided with amd64 and arm64 architecture.",source:"@site/docs/devops-guide/docker.md",sourceDirName:"devops-guide",slug:"/devops-guide/devops-guide-docker",permalink:"/handbook/docs/devops-guide/devops-guide-docker",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/devops-guide/docker.md",tags:[],version:"current",lastUpdatedAt:1695203964,formattedLastUpdatedAt:"Sep 20, 2023",frontMatter:{id:"devops-guide-docker",title:"Self-Hosting Guide - Docker",sidebar_label:"Docker"},sidebar:"docs",previous:{title:"openSUSE",permalink:"/handbook/docs/devops-guide/devops-guide-opensuse"},next:{title:"Manual installation",permalink:"/handbook/docs/devops-guide/devops-guide-manual"}},m={},s=[{value:"Quick start",id:"quick-start",level:2},{value:"Testing development / unstable builds",id:"testing-development--unstable-builds",level:3},{value:"Building your own images",id:"building-your-own-images",level:3},{value:"Security note",id:"security-note",level:3},{value:"Architecture",id:"architecture",level:2},{value:"External Ports",id:"external-ports",level:3},{value:"Images",id:"images",level:3},{value:"Design considerations",id:"design-considerations",level:3},{value:"Configuration",id:"configuration",level:2},{value:"TLS Configuration",id:"tls-configuration",level:3},{value:"Let's Encrypt configuration",id:"lets-encrypt-configuration",level:4},{value:"Using existing TLS certificate and key",id:"using-existing-tls-certificate-and-key",level:4},{value:"Features configuration (config.js)",id:"features-configuration-configjs",level:3},{value:"Jigasi SIP gateway (audio only) configuration",id:"jigasi-sip-gateway-audio-only-configuration",level:3},{value:"Display Dial-In information",id:"display-dial-in-information",level:4},{value:"Recording / live streaming configuration with Jibri",id:"recording--live-streaming-configuration-with-jibri",level:3},{value:"Jitsi Meet configuration",id:"jitsi-meet-configuration",level:3},{value:"Authentication",id:"authentication",level:3},{value:"Internal authentication",id:"internal-authentication",level:4},{value:"Authentication using LDAP",id:"authentication-using-ldap",level:4},{value:"Authentication using JWT tokens",id:"authentication-using-jwt-tokens",level:4},{value:"Authentication using Matrix",id:"authentication-using-matrix",level:4},{value:"Authentication using Hybrid Matrix Token",id:"authentication-using-hybrid-matrix-token",level:4},{value:"External authentication",id:"external-authentication",level:4},{value:"Shared document editing using Etherpad",id:"shared-document-editing-using-etherpad",level:3},{value:"Transcription configuration",id:"transcription-configuration",level:3},{value:"Sentry logging configuration",id:"sentry-logging-configuration",level:3},{value:"TURN server configuration",id:"turn-server-configuration",level:3},{value:"Advanced configuration",id:"advanced-configuration",level:3},{value:"Advanced Prosody options",id:"advanced-prosody-options",level:4},{value:"Advanced Jicofo options",id:"advanced-jicofo-options",level:4},{value:"Advanced JVB options",id:"advanced-jvb-options",level:4},{value:"Advanced Jigasi options",id:"advanced-jigasi-options",level:4},{value:"Running behind NAT or on a LAN environment",id:"running-behind-nat-or-on-a-lan-environment",level:3},{value:"Split horizon",id:"split-horizon",level:4},{value:"Offline / airgapped installation",id:"offline--airgapped-installation",level:4},{value:"Accessing server logs",id:"accessing-server-logs",level:2},{value:"Build Instructions",id:"build-instructions",level:2},{value:"Running behind a reverse proxy",id:"running-behind-a-reverse-proxy",level:2},{value:"Disabling WebSocket connections",id:"disabling-websocket-connections",level:3}],u={toc:s};function k(t){var e=t.components,o=(0,r.Z)(t,i);return(0,l.kt)("wrapper",(0,a.Z)({},u,o,{components:e,mdxType:"MDXLayout"}),(0,l.kt)("admonition",{type:"note"},(0,l.kt)("p",{parentName:"admonition"},"Starting with release ",(0,l.kt)("inlineCode",{parentName:"p"},"stable-7289-1")," our images are provided with ",(0,l.kt)("inlineCode",{parentName:"p"},"amd64")," and ",(0,l.kt)("inlineCode",{parentName:"p"},"arm64")," architecture.")),(0,l.kt)("h2",{id:"quick-start"},"Quick start"),(0,l.kt)("p",null,"In order to quickly run Jitsi Meet on a machine running Docker and Docker Compose,\nfollow these steps:"),(0,l.kt)("ol",null,(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("p",{parentName:"li"},"Download and extract the ",(0,l.kt)("a",{parentName:"p",href:"https://github.com/jitsi/docker-jitsi-meet/releases/latest"},"latest release"),". ",(0,l.kt)("strong",{parentName:"p"},"DO NOT")," clone the git repository. See below if you are interested in running test images.")),(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("p",{parentName:"li"},"Create a ",(0,l.kt)("inlineCode",{parentName:"p"},".env")," file by copying and adjusting ",(0,l.kt)("inlineCode",{parentName:"p"},"env.example"),":"),(0,l.kt)("pre",{parentName:"li"},(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"cp env.example .env\n"))),(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("p",{parentName:"li"},"Set strong passwords in the security section options of ",(0,l.kt)("inlineCode",{parentName:"p"},".env")," file by running the following bash script"),(0,l.kt)("pre",{parentName:"li"},(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"./gen-passwords.sh\n"))),(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("p",{parentName:"li"},"Create required ",(0,l.kt)("inlineCode",{parentName:"p"},"CONFIG")," directories"),(0,l.kt)("ul",{parentName:"li"},(0,l.kt)("li",{parentName:"ul"},"For linux: ")),(0,l.kt)("pre",{parentName:"li"},(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"mkdir -p ~/.jitsi-meet-cfg/{web,transcripts,prosody/config,prosody/prosody-plugins-custom,jicofo,jvb,jigasi,jibri}\n")),(0,l.kt)("ul",{parentName:"li"},(0,l.kt)("li",{parentName:"ul"},"For Windows: ")),(0,l.kt)("pre",{parentName:"li"},(0,l.kt)("code",{parentName:"pre",className:"language-bash"},'echo web,transcripts,prosody/config,prosody/prosody-plugins-custom,jicofo,jvb,jigasi,jibri | % { mkdir "~/.jitsi-meet-cfg/$_" }\n'))),(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("p",{parentName:"li"},"Run ",(0,l.kt)("inlineCode",{parentName:"p"},"docker-compose up -d"))),(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("p",{parentName:"li"},"Access the web UI at ",(0,l.kt)("a",{parentName:"p",href:"https://localhost:8443"},(0,l.kt)("inlineCode",{parentName:"a"},"https://localhost:8443"))," (or a different port, in case you edited the ",(0,l.kt)("inlineCode",{parentName:"p"},".env")," file)."))),(0,l.kt)("admonition",{type:"note"},(0,l.kt)("p",{parentName:"admonition"},"HTTP (not HTTPS) is also available (on port 8000, by default), but that's e.g. for a reverse proxy setup;\ndirect access via HTTP instead HTTPS leads to WebRTC errors such as\n",(0,l.kt)("em",{parentName:"p"},"Failed to access your microphone/camera: Cannot use microphone/camera for an unknown reason. Cannot read property 'getUserMedia' of undefined"),"\nor ",(0,l.kt)("em",{parentName:"p"},"navigator.mediaDevices is undefined"),".")),(0,l.kt)("p",null,"If you want to use jigasi too, first configure your env file with SIP credentials\nand then run Docker Compose as follows: "),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"docker-compose -f docker-compose.yml -f jigasi.yml up\n")),(0,l.kt)("p",null,"If you want to enable document sharing via ",(0,l.kt)("a",{parentName:"p",href:"https://github.com/ether/etherpad-lite"},"Etherpad"),",\nconfigure it and run Docker Compose as follows: "),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"docker-compose -f docker-compose.yml -f etherpad.yml up\n")),(0,l.kt)("p",null,"If you want to use jibri too, first configure a host as described in JItsi BRoadcasting Infrastructure configuration section\nand then run Docker Compose as follows:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"docker-compose -f docker-compose.yml -f jibri.yml up -d\n")),(0,l.kt)("p",null,"or to use jigasi too:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"docker-compose -f docker-compose.yml -f jigasi.yml -f jibri.yml up -d\n")),(0,l.kt)("h3",{id:"testing-development--unstable-builds"},"Testing development / unstable builds"),(0,l.kt)("p",null,"Download the latest code:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"git clone https://github.com/jitsi/docker-jitsi-meet && cd docker-jitsi-meet\n")),(0,l.kt)("admonition",{type:"note"},(0,l.kt)("p",{parentName:"admonition"},"The code in ",(0,l.kt)("inlineCode",{parentName:"p"},"master")," is designed to work with the unstable images. Do not run it with release images.")),(0,l.kt)("p",null,"Run ",(0,l.kt)("inlineCode",{parentName:"p"},"docker-compose up")," as usual."),(0,l.kt)("p",null,'Every day a new "unstable" image build is uploaded.'),(0,l.kt)("h3",{id:"building-your-own-images"},"Building your own images"),(0,l.kt)("p",null,"Download the latest code:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"git clone https://github.com/jitsi/docker-jitsi-meet && cd docker-jitsi-meet\n")),(0,l.kt)("p",null,"The provided ",(0,l.kt)("inlineCode",{parentName:"p"},"Makefile")," provides a comprehensive way of building the whole stack or individual images."),(0,l.kt)("p",null,"To build all images:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"make\n")),(0,l.kt)("p",null,"To build a specific image (the web image for example):"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"make build_web\n")),(0,l.kt)("p",null,"Once your local build is ready make sure to add ",(0,l.kt)("inlineCode",{parentName:"p"},"JITSI_IMAGE_VERSION=latest")," to your ",(0,l.kt)("inlineCode",{parentName:"p"},".env")," file."),(0,l.kt)("h3",{id:"security-note"},"Security note"),(0,l.kt)("p",null,"This setup used to have default passwords for internal accounts used across components.\nIn order to make the default setup secure by default these have been removed and the respective containers won't start without having a password set."),(0,l.kt)("p",null,"Strong passwords may be generated as follows: ",(0,l.kt)("inlineCode",{parentName:"p"},"./gen-passwords.sh"),"\nThis will modify your ",(0,l.kt)("inlineCode",{parentName:"p"},".env")," file (a backup is saved in ",(0,l.kt)("inlineCode",{parentName:"p"},".env.bak"),") and set strong passwords for each of the\nrequired options. Passwords are generated using ",(0,l.kt)("inlineCode",{parentName:"p"},"openssl rand -hex 16")," ."),(0,l.kt)("p",null,"DO NOT reuse any of the passwords."),(0,l.kt)("h2",{id:"architecture"},"Architecture"),(0,l.kt)("p",null,"A Jitsi Meet installation can be broken down into the following components:"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},"A web interface"),(0,l.kt)("li",{parentName:"ul"},"An XMPP server"),(0,l.kt)("li",{parentName:"ul"},"A conference focus component"),(0,l.kt)("li",{parentName:"ul"},"A video router (could be more than one)"),(0,l.kt)("li",{parentName:"ul"},"A SIP gateway for audio calls"),(0,l.kt)("li",{parentName:"ul"},"A Broadcasting Infrastructure for recording or streaming a conference.")),(0,l.kt)("p",null,(0,l.kt)("img",{src:n(168).Z,width:"882",height:"505"})),(0,l.kt)("p",null,"The diagram shows a typical deployment in a host running Docker. This project\nseparates each of the components above into interlinked containers. To this end,\nseveral container images are provided."),(0,l.kt)("h3",{id:"external-ports"},"External Ports"),(0,l.kt)("p",null,"The following external ports must be opened on a firewall:"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"80/tcp")," for Web UI HTTP (really just to redirect, after uncommenting ",(0,l.kt)("inlineCode",{parentName:"li"},"ENABLE_HTTP_REDIRECT=1")," in ",(0,l.kt)("inlineCode",{parentName:"li"},".env"),")"),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"443/tcp")," for Web UI HTTPS"),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"10000/udp")," for RTP media over UDP")),(0,l.kt)("p",null,"Also ",(0,l.kt)("inlineCode",{parentName:"p"},"20000-20050/udp")," for jigasi, in case you choose to deploy that to facilitate SIP access."),(0,l.kt)("p",null,"E.g. on a CentOS/Fedora server this would be done like this (without SIP access):"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"sudo firewall-cmd --permanent --add-port=80/tcp\nsudo firewall-cmd --permanent --add-port=443/tcp\nsudo firewall-cmd --permanent --add-port=10000/udp\nsudo firewall-cmd --reload\n")),(0,l.kt)("p",null,"See ",(0,l.kt)("a",{parentName:"p",href:"https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-quickstart#setup-and-configure-your-firewall"},"the corresponding section in the manual setup guide"),"."),(0,l.kt)("h3",{id:"images"},"Images"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("strong",{parentName:"li"},"base"),": Debian stable base image with the ",(0,l.kt)("a",{parentName:"li",href:"https://github.com/just-containers/s6-overlay"},"S6 Overlay")," for process control and the\n",(0,l.kt)("a",{parentName:"li",href:"https://jitsi.org/downloads/"},"Jitsi repositories")," enabled. All other images are based on this one."),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("strong",{parentName:"li"},"base-java"),": Same as the above, plus Java (OpenJDK)."),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("strong",{parentName:"li"},"web"),": Jitsi Meet web UI, served with nginx."),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("strong",{parentName:"li"},"prosody"),": ",(0,l.kt)("a",{parentName:"li",href:"https://prosody.im/"},"Prosody"),", the XMPP server."),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("strong",{parentName:"li"},"jicofo"),": ",(0,l.kt)("a",{parentName:"li",href:"https://github.com/jitsi/jicofo"},"Jicofo"),", the XMPP focus component."),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("strong",{parentName:"li"},"jvb"),": ",(0,l.kt)("a",{parentName:"li",href:"https://github.com/jitsi/jitsi-videobridge"},"Jitsi Videobridge"),", the video router."),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("strong",{parentName:"li"},"jigasi"),": ",(0,l.kt)("a",{parentName:"li",href:"https://github.com/jitsi/jigasi"},"Jigasi"),", the SIP (audio only) gateway."),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("strong",{parentName:"li"},"jibri"),": ",(0,l.kt)("a",{parentName:"li",href:"https://github.com/jitsi/jibri"},"Jibri"),", the broadcasting infrastructure.")),(0,l.kt)("h3",{id:"design-considerations"},"Design considerations"),(0,l.kt)("p",null,"Jitsi Meet uses XMPP for signaling, thus the need for the XMPP server.\nThe setup provided by these containers does not expose the XMPP server to the outside world.\nInstead, it's kept completely sealed, and routing of XMPP traffic only happens on a user-defined network."),(0,l.kt)("p",null,"The XMPP server can be exposed to the outside world,\nbut that's out of the scope of this project."),(0,l.kt)("h2",{id:"configuration"},"Configuration"),(0,l.kt)("p",null,"The configuration is performed via environment variables contained in a ",(0,l.kt)("inlineCode",{parentName:"p"},".env")," file.\nYou can copy the provided ",(0,l.kt)("inlineCode",{parentName:"p"},"env.example")," file as a reference."),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Example"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"CONFIG")),(0,l.kt)("td",{parentName:"tr",align:null},"Directory where all configuration will be stored"),(0,l.kt)("td",{parentName:"tr",align:null},"/opt/jitsi-meet-cfg")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"TZ")),(0,l.kt)("td",{parentName:"tr",align:null},"System Time Zone"),(0,l.kt)("td",{parentName:"tr",align:null},"Europe/Amsterdam")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"HTTP_PORT")),(0,l.kt)("td",{parentName:"tr",align:null},"Exposed port for HTTP traffic"),(0,l.kt)("td",{parentName:"tr",align:null},"8000")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"HTTPS_PORT")),(0,l.kt)("td",{parentName:"tr",align:null},"Exposed port for HTTPS traffic"),(0,l.kt)("td",{parentName:"tr",align:null},"8443")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JVB_ADVERTISE_IPS")),(0,l.kt)("td",{parentName:"tr",align:null},"IP addresses of the Docker host (comma separated), needed for LAN environments"),(0,l.kt)("td",{parentName:"tr",align:null},"192.168.1.1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"PUBLIC_URL")),(0,l.kt)("td",{parentName:"tr",align:null},"Public URL for the web service"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"https://meet.example.com"},"https://meet.example.com"))))),(0,l.kt)("admonition",{type:"note"},(0,l.kt)("p",{parentName:"admonition"},"The mobile apps won't work with self-signed certificates (the default).\nSee below for instructions on how to obtain a proper certificate with Let's Encrypt.")),(0,l.kt)("h3",{id:"tls-configuration"},"TLS Configuration"),(0,l.kt)("h4",{id:"lets-encrypt-configuration"},"Let's Encrypt configuration"),(0,l.kt)("p",null,"If you want to expose your Jitsi Meet instance to the outside traffic directly, but don't own a proper TLS certificate, you are in luck\nbecause Let's Encrypt support is built right in. Here are the required options:"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Example"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_LETSENCRYPT")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable Let's Encrypt certificate generation"),(0,l.kt)("td",{parentName:"tr",align:null},"1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"LETSENCRYPT_DOMAIN")),(0,l.kt)("td",{parentName:"tr",align:null},"Domain for which to generate the certificate"),(0,l.kt)("td",{parentName:"tr",align:null},"meet.example.com")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"LETSENCRYPT_EMAIL")),(0,l.kt)("td",{parentName:"tr",align:null},"E-Mail for receiving important account notifications (mandatory)"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"mailto:alice@atlanta.net"},"alice@atlanta.net"))))),(0,l.kt)("p",null,"In addition, you will need to set ",(0,l.kt)("inlineCode",{parentName:"p"},"HTTP_PORT")," to 80 and ",(0,l.kt)("inlineCode",{parentName:"p"},"HTTPS_PORT")," to 443 and PUBLIC_URL to your domain.\nYou might also consider to redirect HTTP traffic to HTTPS by setting ",(0,l.kt)("inlineCode",{parentName:"p"},"ENABLE_HTTP_REDIRECT=1"),"."),(0,l.kt)("p",null,(0,l.kt)("strong",{parentName:"p"},"Let's Encrypt rate limit warning"),": Let's Encrypt has a limit to how many times you can submit a request\nfor a new certificate for your domain name. At the time of writing, the current limit is five new (duplicate)\ncertificates for the same domain name every seven days. Because of this, it is recommended that you disable the\nLet's Encrypt enviroment variables from ",(0,l.kt)("inlineCode",{parentName:"p"},".env")," if you plan on deleting the ",(0,l.kt)("inlineCode",{parentName:"p"},".jitsi-meet-cfg")," folder.\nOtherwise, you might want to consider moving the ",(0,l.kt)("inlineCode",{parentName:"p"},".jitsi-meet-cfg")," folder to a different location so you have a safe place to find\nthe certificate that already Let's Encrypt issued. Or do initial testing with Let's Encrypt disabled, then re-enable\nLet's Encrypt once you are done testing."),(0,l.kt)("admonition",{type:"note"},(0,l.kt)("p",{parentName:"admonition"},"When you move away from ",(0,l.kt)("inlineCode",{parentName:"p"},"LETSENCRYPT_USE_STAGING"),",\nyou will have to manually clear the certificates from ",(0,l.kt)("inlineCode",{parentName:"p"},".jitsi-meet-cfg/web"),".")),(0,l.kt)("p",null,"For more information on Let's Encrypt's rate limits, visit:\n",(0,l.kt)("a",{parentName:"p",href:"https://letsencrypt.org/docs/rate-limits/"},"https://letsencrypt.org/docs/rate-limits/")),(0,l.kt)("h4",{id:"using-existing-tls-certificate-and-key"},"Using existing TLS certificate and key"),(0,l.kt)("p",null,"If you own a proper TLS certificate and don't need a Let's Encrypt certificate, you can configure Jitsi Meet container\nto use it. "),(0,l.kt)("p",null,"Unlike Let's Encrypt certificates, this is not configured through the ",(0,l.kt)("inlineCode",{parentName:"p"},".env"),"file, but by telling Jitsi Meet's ",(0,l.kt)("inlineCode",{parentName:"p"},"web")," service\nto mount the following two volumes: "),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},"mount ",(0,l.kt)("inlineCode",{parentName:"li"},"/path/to/your/cert.key")," file to ",(0,l.kt)("inlineCode",{parentName:"li"},"/config/keys/cert.key")," mount point"),(0,l.kt)("li",{parentName:"ul"},"mount ",(0,l.kt)("inlineCode",{parentName:"li"},"/path/to/your/cert.fullchain")," file to the ",(0,l.kt)("inlineCode",{parentName:"li"},"/config/keys/cert.crt")," mount point.")),(0,l.kt)("p",null,"Doing it in ",(0,l.kt)("inlineCode",{parentName:"p"},"docker-compose.yml")," file should look like this:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"services:\n web:\n ...\n volumes:\n ...\n - /path/to/your/cert.fullchain:/config/keys/cert.crt\n - /path/to/your/cert.key:/config/keys/cert.key\n")),(0,l.kt)("h3",{id:"features-configuration-configjs"},"Features configuration (config.js)"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Example"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"TOOLBAR_BUTTONS")),(0,l.kt)("td",{parentName:"tr",align:null},"Configure toolbar buttons. Add the buttons name separated with comma(no spaces between comma)"),(0,l.kt)("td",{parentName:"tr",align:null})),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"HIDE_PREMEETING_BUTTONS")),(0,l.kt)("td",{parentName:"tr",align:null},"Hide the buttons at pre-join screen. Add the buttons name separated with comma"),(0,l.kt)("td",{parentName:"tr",align:null})),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_LOBBY")),(0,l.kt)("td",{parentName:"tr",align:null},"Control whether the lobby feature should be enabled or not"),(0,l.kt)("td",{parentName:"tr",align:null},"1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_AV_MODERATION")),(0,l.kt)("td",{parentName:"tr",align:null},"Control whether the A/V moderation should be enabled or not"),(0,l.kt)("td",{parentName:"tr",align:null},"1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_PREJOIN_PAGE")),(0,l.kt)("td",{parentName:"tr",align:null},"Show a prejoin page before entering a conference"),(0,l.kt)("td",{parentName:"tr",align:null},"1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_WELCOME_PAGE")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable the welcome page"),(0,l.kt)("td",{parentName:"tr",align:null},"1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_CLOSE_PAGE")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable the close page"),(0,l.kt)("td",{parentName:"tr",align:null},"0")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"DISABLE_AUDIO_LEVELS")),(0,l.kt)("td",{parentName:"tr",align:null},"Disable measuring of audio levels"),(0,l.kt)("td",{parentName:"tr",align:null},"0")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_NOISY_MIC_DETECTION")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable noisy mic detection"),(0,l.kt)("td",{parentName:"tr",align:null},"1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_BREAKOUT_ROOMS")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable breakout rooms"),(0,l.kt)("td",{parentName:"tr",align:null},"1")))),(0,l.kt)("h3",{id:"jigasi-sip-gateway-audio-only-configuration"},"Jigasi SIP gateway (audio only) configuration"),(0,l.kt)("p",null,"If you want to enable the SIP gateway, these options are required:"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Example"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_SIP_URI")),(0,l.kt)("td",{parentName:"tr",align:null},"SIP URI for incoming / outgoing calls"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"mailto:test@sip2sip.info"},"test@sip2sip.info"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_SIP_PASSWORD")),(0,l.kt)("td",{parentName:"tr",align:null},"Password for the specified SIP account"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},""))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_SIP_SERVER")),(0,l.kt)("td",{parentName:"tr",align:null},"SIP server (use the SIP account domain if in doubt)"),(0,l.kt)("td",{parentName:"tr",align:null},"sip2sip.info")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_SIP_PORT")),(0,l.kt)("td",{parentName:"tr",align:null},"SIP server port"),(0,l.kt)("td",{parentName:"tr",align:null},"5060")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_SIP_TRANSPORT")),(0,l.kt)("td",{parentName:"tr",align:null},"SIP transport"),(0,l.kt)("td",{parentName:"tr",align:null},"UDP")))),(0,l.kt)("h4",{id:"display-dial-in-information"},"Display Dial-In information"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Example"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"DIALIN_NUMBERS_URL")),(0,l.kt)("td",{parentName:"tr",align:null},"URL to the JSON with all Dial-In numbers"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"https://meet.example.com/dialin.json"},"https://meet.example.com/dialin.json"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"CONFCODE_URL")),(0,l.kt)("td",{parentName:"tr",align:null},"URL to the API for checking/generating Dial-In codes"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"https://jitsi-api.jitsi.net/conferenceMapper"},"https://jitsi-api.jitsi.net/conferenceMapper"))))),(0,l.kt)("p",null,"The JSON with the Dial-In numbers should look like this:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-json"},'{"message":"Dial-In numbers:","numbers":{"DE": ["+49-721-0000-0000"]},"numbersEnabled":true}\n')),(0,l.kt)("h3",{id:"recording--live-streaming-configuration-with-jibri"},"Recording / live streaming configuration with Jibri"),(0,l.kt)("details",null,(0,l.kt)("summary",null,"If you are using a release older than 7439 some extra setup is necessary."),"Before running Jibri **on releases older than 7439**, you need to set up an ALSA loopback device on the host. This **will not** work on a non-Linux host.",(0,l.kt)("p",null,"For CentOS 7, the module is already compiled with the kernel, so just run:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},'# configure 5 capture/playback interfaces\necho "options snd-aloop enable=1,1,1,1,1 index=0,1,2,3,4" > /etc/modprobe.d/alsa-loopback.conf\n# setup autoload the module\necho "snd_aloop" > /etc/modules-load.d/snd_aloop.conf\n# load the module\nmodprobe snd-aloop\n# check that the module is loaded\nlsmod | grep snd_aloop\n')),(0,l.kt)("p",null,"For Ubuntu:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},'# install the module\napt update && apt install linux-image-extra-virtual\n# configure 5 capture/playback interfaces\necho "options snd-aloop enable=1,1,1,1,1 index=0,1,2,3,4" > /etc/modprobe.d/alsa-loopback.conf\n# setup autoload the module\necho "snd-aloop" >> /etc/modules\n# check that the module is loaded\nlsmod | grep snd_aloop\n')),(0,l.kt)("admonition",{type:"note"},(0,l.kt)("p",{parentName:"admonition"},'If you are running on AWS you may need to reboot your machine to use the generic kernel instead\nof the "aws" kernel. If after reboot, your machine is still using the "aws" kernel, you\'ll need to manually update the grub file. So just run:')),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},'# open the grub file in editor\nnano /etc/default/grub\n# Modify the value of GRUB_DEFAULT from "0" to "1>2"\n# Save and exit from file\n\n# Update grub\nupdate-grub\n# Reboot the machine\nreboot now\n')),(0,l.kt)("p",null,"For using multiple Jibri instances, you have to select different loopback interfaces for each instance manually."),(0,l.kt)("p",null," Default the first instance has:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},'...\nslave.pcm "hw:Loopback,0,0"\n...\nslave.pcm "hw:Loopback,0,1"\n...\nslave.pcm "hw:Loopback,1,1"\n...\nslave.pcm "hw:Loopback,1,0"\n...\n')),(0,l.kt)("p",null," To setup the second instance, run container with changed ",(0,l.kt)("inlineCode",{parentName:"p"},"/home/jibri/.asoundrc"),":"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},'...\nslave.pcm "hw:Loopback_1,0,0"\n...\nslave.pcm "hw:Loopback_1,0,1"\n...\nslave.pcm "hw:Loopback_1,1,1"\n...\nslave.pcm "hw:Loopback_1,1,0"\n...\n')),(0,l.kt)("p",null," Also you can use numbering id for set loopback interface. The third instance will have ",(0,l.kt)("inlineCode",{parentName:"p"},".asoundrc")," that looks like:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},'...\nslave.pcm "hw:2,0,0"\n...\nslave.pcm "hw:2,0,1"\n...\nslave.pcm "hw:2,1,1"\n...\nslave.pcm "hw:2,1,0"\n...\n\n'))),(0,l.kt)("p",null,"If you want to enable Jibri these options are required:"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Example"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_RECORDING")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable recording / live streaming"),(0,l.kt)("td",{parentName:"tr",align:null},"1")))),(0,l.kt)("p",null,"Extended Jibri configuration:"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Example"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIBRI_RECORDER_USER")),(0,l.kt)("td",{parentName:"tr",align:null},"Internal recorder user for Jibri client connections"),(0,l.kt)("td",{parentName:"tr",align:null},"recorder")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIBRI_RECORDER_PASSWORD")),(0,l.kt)("td",{parentName:"tr",align:null},"Internal recorder password for Jibri client connections"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},""))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIBRI_RECORDING_DIR")),(0,l.kt)("td",{parentName:"tr",align:null},"Directory for recordings inside Jibri container"),(0,l.kt)("td",{parentName:"tr",align:null},"/config/recordings")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIBRI_FINALIZE_RECORDING_SCRIPT_PATH")),(0,l.kt)("td",{parentName:"tr",align:null},"The finalizing script. Will run after recording is complete"),(0,l.kt)("td",{parentName:"tr",align:null},"/config/finalize.sh")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIBRI_XMPP_USER")),(0,l.kt)("td",{parentName:"tr",align:null},"Internal user for Jibri client connections."),(0,l.kt)("td",{parentName:"tr",align:null},"jibri")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIBRI_STRIP_DOMAIN_JID")),(0,l.kt)("td",{parentName:"tr",align:null},"Prefix domain for strip inside Jibri (please see env.example for details)"),(0,l.kt)("td",{parentName:"tr",align:null},"muc")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIBRI_BREWERY_MUC")),(0,l.kt)("td",{parentName:"tr",align:null},"MUC name for the Jibri pool"),(0,l.kt)("td",{parentName:"tr",align:null},"jibribrewery")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIBRI_PENDING_TIMEOUT")),(0,l.kt)("td",{parentName:"tr",align:null},"MUC connection timeout"),(0,l.kt)("td",{parentName:"tr",align:null},"90")))),(0,l.kt)("h3",{id:"jitsi-meet-configuration"},"Jitsi Meet configuration"),(0,l.kt)("admonition",{title:"This section partly contains duplicate settings",type:"tip"},(0,l.kt)("p",{parentName:"admonition"},"There are settings within your ",(0,l.kt)("inlineCode",{parentName:"p"},"docker-compose.yml")," like ",(0,l.kt)("inlineCode",{parentName:"p"},"START_AUDIO_MUTED"),"\nthat will be overwritten if you follow the below guide.")),(0,l.kt)("p",null,"Jitsi-Meet uses two configuration files for changing default settings within\nthe web interface: ",(0,l.kt)("inlineCode",{parentName:"p"},"config.js")," and ",(0,l.kt)("inlineCode",{parentName:"p"},"interface_config.js"),". The files are\nlocated within the ",(0,l.kt)("inlineCode",{parentName:"p"},"CONFIG")," directory configured within your environment file."),(0,l.kt)("p",null,"These files are re-created on every container restart.\nIf you'd like to provide your own settings, create your own config files:\n",(0,l.kt)("inlineCode",{parentName:"p"},"custom-config.js")," and ",(0,l.kt)("inlineCode",{parentName:"p"},"custom-interface_config.js"),"."),(0,l.kt)("p",null,"It's enough to provide your relevant settings only, the docker scripts will\nappend your custom files to the default ones!"),(0,l.kt)("h3",{id:"authentication"},"Authentication"),(0,l.kt)("p",null,"Authentication can be controlled with the environment variables below. If guest\naccess is enabled, unauthenticated users will need to wait until a user authenticates\nbefore they can join a room. If guest access is not enabled, every user will need\nto authenticate before they can join."),(0,l.kt)("p",null,"If authentication is enabled, once an authenticated user logged in, it is always\nlogged in before the session timeout. You can set ",(0,l.kt)("inlineCode",{parentName:"p"},"ENABLE_AUTO_LOGIN=0")," to disable\nthis default auto login feature or you can set ",(0,l.kt)("inlineCode",{parentName:"p"},"JICOFO_AUTH_LIFETIME")," to limit\nthe session lifetime."),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Example"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_AUTH")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable authentication"),(0,l.kt)("td",{parentName:"tr",align:null},"1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_GUESTS")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable guest access"),(0,l.kt)("td",{parentName:"tr",align:null},"1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"AUTH_TYPE")),(0,l.kt)("td",{parentName:"tr",align:null},"Select authentication type (internal, jwt or ldap)"),(0,l.kt)("td",{parentName:"tr",align:null},"internal")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_AUTO_LOGIN")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable auto login"),(0,l.kt)("td",{parentName:"tr",align:null},"1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JICOFO_AUTH_LIFETIME")),(0,l.kt)("td",{parentName:"tr",align:null},"Select session timeout value for an authenticated user"),(0,l.kt)("td",{parentName:"tr",align:null},"3 hours")))),(0,l.kt)("h4",{id:"internal-authentication"},"Internal authentication"),(0,l.kt)("p",null,"The default authentication mode (",(0,l.kt)("inlineCode",{parentName:"p"},"internal"),") uses XMPP credentials to authenticate users.\nTo enable it you have to enable authentication with ",(0,l.kt)("inlineCode",{parentName:"p"},"ENABLE_AUTH")," and set ",(0,l.kt)("inlineCode",{parentName:"p"},"AUTH_TYPE")," to ",(0,l.kt)("inlineCode",{parentName:"p"},"internal"),",\nthen configure the settings you can see below."),(0,l.kt)("p",null,"Internal users must be created with the ",(0,l.kt)("inlineCode",{parentName:"p"},"prosodyctl")," utility in the ",(0,l.kt)("inlineCode",{parentName:"p"},"prosody")," container.\nIn order to do that, first, execute a shell in the corresponding container:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"docker-compose exec prosody /bin/bash\n")),(0,l.kt)("p",null,"Once in the container, run the following command to create a user:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"prosodyctl --config /config/prosody.cfg.lua register TheDesiredUsername meet.jitsi TheDesiredPassword\n")),(0,l.kt)("p",null,"Note that the command produces no output."),(0,l.kt)("p",null,"To delete a user, run the following command in the container:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"prosodyctl --config /config/prosody.cfg.lua unregister TheDesiredUsername meet.jitsi\n")),(0,l.kt)("p",null,"To list all users, run the following command in the container:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"find /config/data/meet%2ejitsi/accounts -type f -exec basename {} .dat \\;\n")),(0,l.kt)("h4",{id:"authentication-using-ldap"},"Authentication using LDAP"),(0,l.kt)("p",null,"You can use LDAP to authenticate users. To enable it you have to enable authentication with ",(0,l.kt)("inlineCode",{parentName:"p"},"ENABLE_AUTH")," and\nset ",(0,l.kt)("inlineCode",{parentName:"p"},"AUTH_TYPE")," to ",(0,l.kt)("inlineCode",{parentName:"p"},"ldap"),", then configure the settings you can see below."),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Example"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"LDAP_URL")),(0,l.kt)("td",{parentName:"tr",align:null},"URL for ldap connection"),(0,l.kt)("td",{parentName:"tr",align:null},"ldaps://ldap.domain.com/")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"LDAP_BASE")),(0,l.kt)("td",{parentName:"tr",align:null},"LDAP base DN. Can be empty."),(0,l.kt)("td",{parentName:"tr",align:null},"DC=example,DC=domain,DC=com")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"LDAP_BINDDN")),(0,l.kt)("td",{parentName:"tr",align:null},"LDAP user DN. Do not specify this parameter for the anonymous bind."),(0,l.kt)("td",{parentName:"tr",align:null},"CN=binduser,OU=users,DC=example,DC=domain,DC=com")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"LDAP_BINDPW")),(0,l.kt)("td",{parentName:"tr",align:null},"LDAP user password. Do not specify this parameter for the anonymous bind."),(0,l.kt)("td",{parentName:"tr",align:null},"LdapUserPassw0rd")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"LDAP_FILTER")),(0,l.kt)("td",{parentName:"tr",align:null},"LDAP filter."),(0,l.kt)("td",{parentName:"tr",align:null},"(sAMAccountName=%u)")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"LDAP_AUTH_METHOD")),(0,l.kt)("td",{parentName:"tr",align:null},"LDAP authentication method."),(0,l.kt)("td",{parentName:"tr",align:null},"bind")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"LDAP_VERSION")),(0,l.kt)("td",{parentName:"tr",align:null},"LDAP protocol version"),(0,l.kt)("td",{parentName:"tr",align:null},"3")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"LDAP_USE_TLS")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable LDAP TLS"),(0,l.kt)("td",{parentName:"tr",align:null},"1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"LDAP_TLS_CIPHERS")),(0,l.kt)("td",{parentName:"tr",align:null},"Set TLS ciphers list to allow"),(0,l.kt)("td",{parentName:"tr",align:null},"SECURE256:SECURE128")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"LDAP_TLS_CHECK_PEER")),(0,l.kt)("td",{parentName:"tr",align:null},"Require and verify LDAP server certificate"),(0,l.kt)("td",{parentName:"tr",align:null},"1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"LDAP_TLS_CACERT_FILE")),(0,l.kt)("td",{parentName:"tr",align:null},"Path to CA cert file. Used when server certificate verification is enabled"),(0,l.kt)("td",{parentName:"tr",align:null},"/etc/ssl/certs/ca-certificates.crt")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"LDAP_TLS_CACERT_DIR")),(0,l.kt)("td",{parentName:"tr",align:null},"Path to CA certs directory. Used when server certificate verification is enabled."),(0,l.kt)("td",{parentName:"tr",align:null},"/etc/ssl/certs")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"LDAP_START_TLS")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable START_TLS, requires LDAPv3, URL must be ldap:// not ldaps://"),(0,l.kt)("td",{parentName:"tr",align:null},"0")))),(0,l.kt)("h4",{id:"authentication-using-jwt-tokens"},"Authentication using JWT tokens"),(0,l.kt)("p",null,"You can use JWT tokens to authenticate users. To enable it you have to enable authentication with ",(0,l.kt)("inlineCode",{parentName:"p"},"ENABLE_AUTH")," and\nset ",(0,l.kt)("inlineCode",{parentName:"p"},"AUTH_TYPE")," to ",(0,l.kt)("inlineCode",{parentName:"p"},"jwt"),", then configure the settings you can see below."),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Example"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JWT_APP_ID")),(0,l.kt)("td",{parentName:"tr",align:null},"Application identifier"),(0,l.kt)("td",{parentName:"tr",align:null},"my_jitsi_app_id")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JWT_APP_SECRET")),(0,l.kt)("td",{parentName:"tr",align:null},"Application secret known only to your token"),(0,l.kt)("td",{parentName:"tr",align:null},"my_jitsi_app_secret")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JWT_ACCEPTED_ISSUERS")),(0,l.kt)("td",{parentName:"tr",align:null},"(Optional) Set asap_accepted_issuers as a comma separated list"),(0,l.kt)("td",{parentName:"tr",align:null},"my_web_client,my_app_client")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JWT_ACCEPTED_AUDIENCES")),(0,l.kt)("td",{parentName:"tr",align:null},"(Optional) Set asap_accepted_audiences as a comma separated list"),(0,l.kt)("td",{parentName:"tr",align:null},"my_server1,my_server2")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JWT_ASAP_KEYSERVER")),(0,l.kt)("td",{parentName:"tr",align:null},"(Optional) Set asap_keyserver to a url where public keys can be found"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"https://example.com/asap"},"https://example.com/asap"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JWT_ALLOW_EMPTY")),(0,l.kt)("td",{parentName:"tr",align:null},"(Optional) Allow anonymous users with no JWT while validating JWTs when provided"),(0,l.kt)("td",{parentName:"tr",align:null},"0")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JWT_AUTH_TYPE")),(0,l.kt)("td",{parentName:"tr",align:null},"(Optional) Controls which module is used for processing incoming JWTs"),(0,l.kt)("td",{parentName:"tr",align:null},"token")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JWT_TOKEN_AUTH_MODULE")),(0,l.kt)("td",{parentName:"tr",align:null},"(Optional) Controls which module is used for validating JWTs"),(0,l.kt)("td",{parentName:"tr",align:null},"token_verification")))),(0,l.kt)("p",null,"This can be tested using the ",(0,l.kt)("a",{parentName:"p",href:"https://jwt.io/#debugger-io"},"jwt.io")," debugger. Use the following sample payload:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-json"},'{\n "context": {\n "user": {\n "avatar": "https://robohash.org/john-doe",\n "name": "John Doe",\n "email": "jdoe@example.com"\n }\n },\n "aud": "my_jitsi_app_id",\n "iss": "my_jitsi_app_id",\n "sub": "meet.jitsi",\n "room": "*"\n}\n')),(0,l.kt)("h4",{id:"authentication-using-matrix"},"Authentication using Matrix"),(0,l.kt)("p",null,'For more information see the documentation of the "Prosody Auth Matrix User Verification" ',(0,l.kt)("a",{parentName:"p",href:"https://github.com/matrix-org/prosody-mod-auth-matrix-user-verification"},"here"),"."),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Example"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"MATRIX_UVS_URL")),(0,l.kt)("td",{parentName:"tr",align:null},"Base URL to the matrix user verification service (without ending slash)"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"https://uvs.example.com:3000"},"https://uvs.example.com:3000"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"MATRIX_UVS_ISSUER")),(0,l.kt)("td",{parentName:"tr",align:null},"(optional) The issuer of the auth token to be passed through. Must match what is being set as ",(0,l.kt)("inlineCode",{parentName:"td"},"iss")," in the JWT."),(0,l.kt)("td",{parentName:"tr",align:null},"issuer (default)")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"MATRIX_UVS_AUTH_TOKEN")),(0,l.kt)("td",{parentName:"tr",align:null},"(optional) user verification service auth token, if authentication enabled"),(0,l.kt)("td",{parentName:"tr",align:null},"changeme")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"MATRIX_UVS_SYNC_POWER_LEVELS")),(0,l.kt)("td",{parentName:"tr",align:null},"(optional) Make Matrix room moderators owners of the Prosody room."),(0,l.kt)("td",{parentName:"tr",align:null},"1")))),(0,l.kt)("h4",{id:"authentication-using-hybrid-matrix-token"},"Authentication using Hybrid Matrix Token"),(0,l.kt)("p",null,"You can use ",(0,l.kt)("inlineCode",{parentName:"p"},"Hybrid Matrix Token")," to authenticate users. It supports ",(0,l.kt)("inlineCode",{parentName:"p"},"Matrix")," and ",(0,l.kt)("inlineCode",{parentName:"p"},"JWT Token")," authentications\non the same setup. To enable it you have to enable authentication with ",(0,l.kt)("inlineCode",{parentName:"p"},"ENABLE_AUTH")," and set ",(0,l.kt)("inlineCode",{parentName:"p"},"AUTH_TYPE")," to\n",(0,l.kt)("inlineCode",{parentName:"p"},"hybrid_matrix_token"),", then configure the settings you can see below."),(0,l.kt)("p",null,'For more information see the documentation of the "Hybrid Matrix Token"\n',(0,l.kt)("a",{parentName:"p",href:"https://github.com/jitsi-contrib/prosody-plugins/tree/main/auth_hybrid_matrix_token"},"here"),"."),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Example"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"MATRIX_UVS_URL")),(0,l.kt)("td",{parentName:"tr",align:null},"Base URL to the matrix user verification service (without ending slash)"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"https://uvs.example.com:3000"},"https://uvs.example.com:3000"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"MATRIX_UVS_ISSUER")),(0,l.kt)("td",{parentName:"tr",align:null},"(optional) The issuer of the auth token to be passed through. Must match what is being set as ",(0,l.kt)("inlineCode",{parentName:"td"},"iss")," in the JWT. It allows all issuers (",(0,l.kt)("inlineCode",{parentName:"td"},"*"),") by default."),(0,l.kt)("td",{parentName:"tr",align:null},"my_issuer")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"MATRIX_UVS_AUTH_TOKEN")),(0,l.kt)("td",{parentName:"tr",align:null},"(optional) user verification service auth token, if authentication enabled"),(0,l.kt)("td",{parentName:"tr",align:null},"my_matrix_secret")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"MATRIX_UVS_SYNC_POWER_LEVELS")),(0,l.kt)("td",{parentName:"tr",align:null},"(optional) Make Matrix room moderators owners of the Prosody room."),(0,l.kt)("td",{parentName:"tr",align:null},"1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JWT_APP_ID")),(0,l.kt)("td",{parentName:"tr",align:null},"Application identifier"),(0,l.kt)("td",{parentName:"tr",align:null},"my_jitsi_app_id")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JWT_APP_SECRET")),(0,l.kt)("td",{parentName:"tr",align:null},"Application secret known only to your token"),(0,l.kt)("td",{parentName:"tr",align:null},"my_jitsi_app_secret")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JWT_ALLOW_EMPTY")),(0,l.kt)("td",{parentName:"tr",align:null},"(Optional) Allow anonymous users with no JWT while validating JWTs when provided"),(0,l.kt)("td",{parentName:"tr",align:null},"0")))),(0,l.kt)("h4",{id:"external-authentication"},"External authentication"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Example"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"TOKEN_AUTH_URL")),(0,l.kt)("td",{parentName:"tr",align:null},"Authenticate using external service or just focus external auth window if there is one already."),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"https://auth.meet.example.com/%7Broom%7D"},"https://auth.meet.example.com/{room}"))))),(0,l.kt)("h3",{id:"shared-document-editing-using-etherpad"},"Shared document editing using Etherpad"),(0,l.kt)("p",null,"You can collaboratively edit a document via ",(0,l.kt)("a",{parentName:"p",href:"https://github.com/ether/etherpad-lite"},"Etherpad"),". In order to enable it, set the config options below and run\nDocker Compose with the additional config file ",(0,l.kt)("inlineCode",{parentName:"p"},"etherpad.yml"),"."),(0,l.kt)("p",null,"Here are the required options:"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Example"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ETHERPAD_URL_BASE")),(0,l.kt)("td",{parentName:"tr",align:null},"Set etherpad-lite URL"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"http://etherpad.meet.jitsi:9001"},"http://etherpad.meet.jitsi:9001"))))),(0,l.kt)("h3",{id:"transcription-configuration"},"Transcription configuration"),(0,l.kt)("p",null,"If you want to enable the Transcribing function, these options are required:"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Example"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_TRANSCRIPTIONS")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable Jigasi transcription in a conference"),(0,l.kt)("td",{parentName:"tr",align:null},"1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"GC_PROJECT_ID")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"project_id")," from Google Cloud Credentials"),(0,l.kt)("td",{parentName:"tr",align:null})),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"GC_PRIVATE_KEY_ID")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"private_key_id")," from Google Cloud Credentials"),(0,l.kt)("td",{parentName:"tr",align:null})),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"GC_PRIVATE_KEY")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"private_key")," from Google Cloud Credentials"),(0,l.kt)("td",{parentName:"tr",align:null})),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"GC_CLIENT_EMAIL")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"client_email")," from Google Cloud Credentials"),(0,l.kt)("td",{parentName:"tr",align:null})),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"GC_CLIENT_ID")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"client_id")," from Google Cloud Credentials"),(0,l.kt)("td",{parentName:"tr",align:null})),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"GC_CLIENT_CERT_URL")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"client_x509_cert_url")," from Google Cloud Credentials"),(0,l.kt)("td",{parentName:"tr",align:null})),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_TRANSCRIBER_RECORD_AUDIO")),(0,l.kt)("td",{parentName:"tr",align:null},"Jigasi will record audio when transcriber is on"),(0,l.kt)("td",{parentName:"tr",align:null},"true")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_TRANSCRIBER_SEND_TXT")),(0,l.kt)("td",{parentName:"tr",align:null},"Jigasi will send transcribed text to the chat when transcriber is on"),(0,l.kt)("td",{parentName:"tr",align:null},"true")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_TRANSCRIBER_ADVERTISE_URL")),(0,l.kt)("td",{parentName:"tr",align:null},"Jigasi will post an url to the chat with transcription file"),(0,l.kt)("td",{parentName:"tr",align:null},"true")))),(0,l.kt)("p",null,"For setting the Google Cloud Credentials please read ",(0,l.kt)("a",{parentName:"p",href:"https://cloud.google.com/text-to-speech/docs/quickstart-protocol"},"https://cloud.google.com/text-to-speech/docs/quickstart-protocol"),' section "Before you begin" paragraph 1 to 5.'),(0,l.kt)("h3",{id:"sentry-logging-configuration"},"Sentry logging configuration"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Default value"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JVB_SENTRY_DSN")),(0,l.kt)("td",{parentName:"tr",align:null},"Sentry Data Source Name (Endpoint for Sentry project)"),(0,l.kt)("td",{parentName:"tr",align:null},"https://public:private@host:port/1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JICOFO_SENTRY_DSN")),(0,l.kt)("td",{parentName:"tr",align:null},"Sentry Data Source Name (Endpoint for Sentry project)"),(0,l.kt)("td",{parentName:"tr",align:null},"https://public:private@host:port/1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_SENTRY_DSN")),(0,l.kt)("td",{parentName:"tr",align:null},"Sentry Data Source Name (Endpoint for Sentry project)"),(0,l.kt)("td",{parentName:"tr",align:null},"https://public:private@host:port/1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"SENTRY_ENVIRONMENT")),(0,l.kt)("td",{parentName:"tr",align:null},"Optional environment info to filter events"),(0,l.kt)("td",{parentName:"tr",align:null},"production")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"SENTRY_RELEASE")),(0,l.kt)("td",{parentName:"tr",align:null},"Optional release info to filter events"),(0,l.kt)("td",{parentName:"tr",align:null},"1.0.0")))),(0,l.kt)("h3",{id:"turn-server-configuration"},"TURN server configuration"),(0,l.kt)("p",null,"Configure external TURN servers."),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Default value"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"TURN_CREDENTIALS")),(0,l.kt)("td",{parentName:"tr",align:null},"Credentials for TURN servers"),(0,l.kt)("td",{parentName:"tr",align:null})),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"TURN_HOST")),(0,l.kt)("td",{parentName:"tr",align:null},"TURN server hostnames as a comma separated list (UDP or TCP transport)"),(0,l.kt)("td",{parentName:"tr",align:null})),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"TURN_PORT")),(0,l.kt)("td",{parentName:"tr",align:null},"TURN server port (UDP or TCP transport)"),(0,l.kt)("td",{parentName:"tr",align:null},"443")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"TURN_TRANSPORT")),(0,l.kt)("td",{parentName:"tr",align:null},"TURN server protocols as a comma separated list (UDP or TCP or both)"),(0,l.kt)("td",{parentName:"tr",align:null},"tcp")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"TURNS_HOST")),(0,l.kt)("td",{parentName:"tr",align:null},"TURN server hostnames as a comma separated list (TLS transport)"),(0,l.kt)("td",{parentName:"tr",align:null})),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"TURNS_PORT")),(0,l.kt)("td",{parentName:"tr",align:null},"TURN server port (TLS transport)"),(0,l.kt)("td",{parentName:"tr",align:null},"443")))),(0,l.kt)("h3",{id:"advanced-configuration"},"Advanced configuration"),(0,l.kt)("p",null,"These configuration options are already set and generally don't need to be changed."),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Default value"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"XMPP_DOMAIN")),(0,l.kt)("td",{parentName:"tr",align:null},"Internal XMPP domain"),(0,l.kt)("td",{parentName:"tr",align:null},"meet.jitsi")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"XMPP_AUTH_DOMAIN")),(0,l.kt)("td",{parentName:"tr",align:null},"Internal XMPP domain for authenticated services"),(0,l.kt)("td",{parentName:"tr",align:null},"auth.meet.jitsi")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"XMPP_SERVER")),(0,l.kt)("td",{parentName:"tr",align:null},"Internal XMPP server name xmpp.meet.jitsi"),(0,l.kt)("td",{parentName:"tr",align:null},"xmpp.meet.jitsi")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"XMPP_BOSH_URL_BASE")),(0,l.kt)("td",{parentName:"tr",align:null},"Internal XMPP server URL for BOSH module"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"http://xmpp.meet.jitsi:5280"},"http://xmpp.meet.jitsi:5280"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"XMPP_MUC_DOMAIN")),(0,l.kt)("td",{parentName:"tr",align:null},"XMPP domain for the MUC"),(0,l.kt)("td",{parentName:"tr",align:null},"muc.meet.jitsi")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"XMPP_INTERNAL_MUC_DOMAIN")),(0,l.kt)("td",{parentName:"tr",align:null},"XMPP domain for the internal MUC"),(0,l.kt)("td",{parentName:"tr",align:null},"internal-muc.meet.jitsi")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"XMPP_GUEST_DOMAIN")),(0,l.kt)("td",{parentName:"tr",align:null},"XMPP domain for unauthenticated users"),(0,l.kt)("td",{parentName:"tr",align:null},"guest.meet.jitsi")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"XMPP_RECORDER_DOMAIN")),(0,l.kt)("td",{parentName:"tr",align:null},"Domain for the jibri recorder"),(0,l.kt)("td",{parentName:"tr",align:null},"recorder.meet.jitsi")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"XMPP_MODULES")),(0,l.kt)("td",{parentName:"tr",align:null},"Custom Prosody modules for XMPP_DOMAIN (comma separated)"),(0,l.kt)("td",{parentName:"tr",align:null},"info,alert")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"XMPP_MUC_MODULES")),(0,l.kt)("td",{parentName:"tr",align:null},"Custom Prosody modules for MUC component (comma separated)"),(0,l.kt)("td",{parentName:"tr",align:null},"info,alert")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"XMPP_INTERNAL_MUC_MODULES")),(0,l.kt)("td",{parentName:"tr",align:null},"Custom Prosody modules for internal MUC component (comma separated)"),(0,l.kt)("td",{parentName:"tr",align:null},"info,alert")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"GLOBAL_MODULES")),(0,l.kt)("td",{parentName:"tr",align:null},"Custom prosody modules to load in global configuration (comma separated)"),(0,l.kt)("td",{parentName:"tr",align:null},"statistics,alert")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"GLOBAL_CONFIG")),(0,l.kt)("td",{parentName:"tr",align:null},"Custom configuration string with escaped newlines"),(0,l.kt)("td",{parentName:"tr",align:null},"foo = bar;\\nkey = val;")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"RESTART_POLICY")),(0,l.kt)("td",{parentName:"tr",align:null},"Container restart policy"),(0,l.kt)("td",{parentName:"tr",align:null},"defaults to ",(0,l.kt)("inlineCode",{parentName:"td"},"unless-stopped"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"DISABLE_HTTPS")),(0,l.kt)("td",{parentName:"tr",align:null},"Handle TLS connections outside of this setup"),(0,l.kt)("td",{parentName:"tr",align:null},"0")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_HTTP_REDIRECT")),(0,l.kt)("td",{parentName:"tr",align:null},"Redirect HTTP traffic to HTTPS"),(0,l.kt)("td",{parentName:"tr",align:null},"0")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"LOG_LEVEL")),(0,l.kt)("td",{parentName:"tr",align:null},"Controls which logs are output from prosody and associated modules"),(0,l.kt)("td",{parentName:"tr",align:null},"info")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_HSTS")),(0,l.kt)("td",{parentName:"tr",align:null},"Send a ",(0,l.kt)("inlineCode",{parentName:"td"},"strict-transport-security")," header to force browsers to use a secure and trusted connection. Recommended for production use."),(0,l.kt)("td",{parentName:"tr",align:null},"1")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_IPV6")),(0,l.kt)("td",{parentName:"tr",align:null},"Provides means to disable IPv6 in environments that don't support it"),(0,l.kt)("td",{parentName:"tr",align:null},"1")))),(0,l.kt)("h4",{id:"advanced-prosody-options"},"Advanced Prosody options"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Default value"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"PROSODY_RESERVATION_ENABLED")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable Prosody's reservation REST API"),(0,l.kt)("td",{parentName:"tr",align:null},"false")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"PROSODY_RESERVATION_REST_BASE_URL")),(0,l.kt)("td",{parentName:"tr",align:null},"Base URL of Prosody's reservation REST API"),(0,l.kt)("td",{parentName:"tr",align:null})),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"PROSODY_AUTH_TYPE")),(0,l.kt)("td",{parentName:"tr",align:null},"Select authentication type for Prosody (internal, jwt or ldap)"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"AUTH_TYPE"))))),(0,l.kt)("h4",{id:"advanced-jicofo-options"},"Advanced Jicofo options"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Default value"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JICOFO_COMPONENT_SECRET")),(0,l.kt)("td",{parentName:"tr",align:null},"XMPP component password for Jicofo"),(0,l.kt)("td",{parentName:"tr",align:null},"s3cr37")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JICOFO_AUTH_USER")),(0,l.kt)("td",{parentName:"tr",align:null},"XMPP user for Jicofo client connections"),(0,l.kt)("td",{parentName:"tr",align:null},"focus")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JICOFO_AUTH_PASSWORD")),(0,l.kt)("td",{parentName:"tr",align:null},"XMPP password for Jicofo client connections"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},""))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JICOFO_ENABLE_AUTH")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable authentication in Jicofo"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"ENABLE_AUTH"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JICOFO_AUTH_TYPE")),(0,l.kt)("td",{parentName:"tr",align:null},"Select authentication type for Jicofo (internal, jwt or ldap)"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"AUTH_TYPE"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JICOFO_AUTH_LIFETIME")),(0,l.kt)("td",{parentName:"tr",align:null},"Select session timeout value for an authenticated user"),(0,l.kt)("td",{parentName:"tr",align:null},"24 hours")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JICOFO_ENABLE_HEALTH_CHECKS")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable health checks inside Jicofo, allowing the use of the REST api to check Jicofo's status"),(0,l.kt)("td",{parentName:"tr",align:null},"false")))),(0,l.kt)("h4",{id:"advanced-jvb-options"},"Advanced JVB options"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Default value"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JVB_AUTH_USER")),(0,l.kt)("td",{parentName:"tr",align:null},"XMPP user for JVB MUC client connections"),(0,l.kt)("td",{parentName:"tr",align:null},"jvb")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JVB_AUTH_PASSWORD")),(0,l.kt)("td",{parentName:"tr",align:null},"XMPP password for JVB MUC client connections"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},""))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JVB_STUN_SERVERS")),(0,l.kt)("td",{parentName:"tr",align:null},"STUN servers used to discover the server's public IP"),(0,l.kt)("td",{parentName:"tr",align:null},"stun.l.google.com:19302, stun1.l.google.com:19302, stun2.l.google.com:19302")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JVB_PORT")),(0,l.kt)("td",{parentName:"tr",align:null},"UDP port for media used by Jitsi Videobridge"),(0,l.kt)("td",{parentName:"tr",align:null},"10000")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JVB_COLIBRI_PORT")),(0,l.kt)("td",{parentName:"tr",align:null},"COLIBRI REST API port of JVB exposed to localhost"),(0,l.kt)("td",{parentName:"tr",align:null},"8080")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JVB_BREWERY_MUC")),(0,l.kt)("td",{parentName:"tr",align:null},"MUC name for the JVB pool"),(0,l.kt)("td",{parentName:"tr",align:null},"jvbbrewery")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"COLIBRI_REST_ENABLED")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable the COLIBRI REST API"),(0,l.kt)("td",{parentName:"tr",align:null},"true")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"SHUTDOWN_REST_ENABLED")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable the shutdown REST API"),(0,l.kt)("td",{parentName:"tr",align:null},"true")))),(0,l.kt)("h4",{id:"advanced-jigasi-options"},"Advanced Jigasi options"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Variable"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"),(0,l.kt)("th",{parentName:"tr",align:null},"Default value"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_ENABLE_SDES_SRTP")),(0,l.kt)("td",{parentName:"tr",align:null},"Enable SDES srtp"),(0,l.kt)("td",{parentName:"tr",align:null},"0")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_SIP_KEEP_ALIVE_METHOD")),(0,l.kt)("td",{parentName:"tr",align:null},"Keepalive method"),(0,l.kt)("td",{parentName:"tr",align:null},"OPTIONS")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_HEALTH_CHECK_SIP_URI")),(0,l.kt)("td",{parentName:"tr",align:null},"Health-check extension"),(0,l.kt)("td",{parentName:"tr",align:null})),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_HEALTH_CHECK_INTERVAL")),(0,l.kt)("td",{parentName:"tr",align:null},"Health-check interval"),(0,l.kt)("td",{parentName:"tr",align:null},"300000")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_XMPP_USER")),(0,l.kt)("td",{parentName:"tr",align:null},"XMPP user for Jigasi MUC client connections"),(0,l.kt)("td",{parentName:"tr",align:null},"jigasi")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_XMPP_PASSWORD")),(0,l.kt)("td",{parentName:"tr",align:null},"XMPP password for Jigasi MUC client connections"),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},""))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_BREWERY_MUC")),(0,l.kt)("td",{parentName:"tr",align:null},"MUC name for the Jigasi pool"),(0,l.kt)("td",{parentName:"tr",align:null},"jigasibrewery")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_PORT_MIN")),(0,l.kt)("td",{parentName:"tr",align:null},"Minimum port for media used by Jigasi"),(0,l.kt)("td",{parentName:"tr",align:null},"20000")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"JIGASI_PORT_MAX")),(0,l.kt)("td",{parentName:"tr",align:null},"Maximum port for media used by Jigasi"),(0,l.kt)("td",{parentName:"tr",align:null},"20050")))),(0,l.kt)("h3",{id:"running-behind-nat-or-on-a-lan-environment"},"Running behind NAT or on a LAN environment"),(0,l.kt)("p",null,"When running running in a LAN environment, or on the public Internet via NAT, the ",(0,l.kt)("inlineCode",{parentName:"p"},"JVB_ADVERTISE_IPS")," env variable should be set.\nThis variable allows to control which IP addresses the JVB will advertise for WebRTC media traffic."),(0,l.kt)("admonition",{type:"note"},(0,l.kt)("p",{parentName:"admonition"},"This variable used to be called ",(0,l.kt)("inlineCode",{parentName:"p"},"DOCKER_HOST_ADDRESS")," but it got renamed for clarity and to support a list of IPs.")),(0,l.kt)("p",null,"If your users are coming in over the Internet (and not over LAN), this will likely be your public IP address. If this is not set up correctly, calls will crash when more than two users join a meeting."),(0,l.kt)("p",null,"The public IP address is attempted to be discovered via ",(0,l.kt)("a",{parentName:"p",href:"https://en.wikipedia.org/wiki/STUN"},"STUN"),".\nSTUN servers can be specified with the ",(0,l.kt)("inlineCode",{parentName:"p"},"JVB_STUN_SERVERS")," option."),(0,l.kt)("admonition",{type:"note"},(0,l.kt)("p",{parentName:"admonition"},"Due to a bug in the docker version currently in the Debian repos (20.10.5), ",(0,l.kt)("a",{parentName:"p",href:"https://forums.docker.com/t/docker-doesnt-open-ipv6-ports/106201/2"},"Docker does not listen on IPv6 ports"),", so for that combination you will have to ",(0,l.kt)("a",{parentName:"p",href:"https://docs.docker.com/engine/install/debian/"},"manually obtain the latest version"),".")),(0,l.kt)("h4",{id:"split-horizon"},"Split horizon"),(0,l.kt)("p",null,"If you are running in a split horizon environemt (LAN internal clients connect to a local IP and other clients connect to a public IP) you can specify\nmultiple advertised IPs by separating them with commas:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"JVB_ADVERTISE_IPS=192.168.1.1,1.2.3.4\n")),(0,l.kt)("h4",{id:"offline--airgapped-installation"},"Offline / airgapped installation"),(0,l.kt)("p",null,"If your setup does not have access to the Internet you'll need to disable STUN on the JVB since discovering its own IP address will fail, but that is not necessary on that type of environment."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"JVB_DISABLE_STUN=true\n")),(0,l.kt)("h2",{id:"accessing-server-logs"},"Accessing server logs"),(0,l.kt)("p",null,"The default bahavior of ",(0,l.kt)("inlineCode",{parentName:"p"},"docker-jitsi-meet")," is to log to ",(0,l.kt)("inlineCode",{parentName:"p"},"stdout"),"."),(0,l.kt)("p",null,"While the logs are sent to ",(0,l.kt)("inlineCode",{parentName:"p"},"stdout"),", they are not lost: unless configured to drop all logs, Docker keeps them available for future retrieval and processing."),(0,l.kt)("p",null,"If you need to access the container's logs you have multiple options. Here are the main ones:"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},"run ",(0,l.kt)("inlineCode",{parentName:"li"},"docker-compose logs -t -f ")," from command line, where ",(0,l.kt)("inlineCode",{parentName:"li"},"")," is one of ",(0,l.kt)("inlineCode",{parentName:"li"},"web"),", ",(0,l.kt)("inlineCode",{parentName:"li"},"prosody"),",",(0,l.kt)("inlineCode",{parentName:"li"},"jvb"),", ",(0,l.kt)("inlineCode",{parentName:"li"},"jicofo"),". This command will output the logs for the selected service to stdout with timestamps."),(0,l.kt)("li",{parentName:"ul"},"use a standard ",(0,l.kt)("a",{parentName:"li",href:"https://docs.docker.com/config/containers/logging/configure/"},"docker logging driver")," to redirect the logs to the desired target (for instance ",(0,l.kt)("inlineCode",{parentName:"li"},"syslog")," or ",(0,l.kt)("inlineCode",{parentName:"li"},"splunk"),")."),(0,l.kt)("li",{parentName:"ul"},"serach ",(0,l.kt)("a",{parentName:"li",href:"https://hub.docker.com/search?q="},"docker hub")," for a third party ",(0,l.kt)("a",{parentName:"li",href:"https://docs.docker.com/config/containers/logging/plugins/"},"docker logging driver plugin")," "),(0,l.kt)("li",{parentName:"ul"},"or ",(0,l.kt)("a",{parentName:"li",href:"https://docs.docker.com/engine/extend/plugins_logging/"},"write your own driver plugin")," if you have a very specific need.")),(0,l.kt)("p",null,"For instance, if you want to have all logs related to a ",(0,l.kt)("inlineCode",{parentName:"p"},"")," written to ",(0,l.kt)("inlineCode",{parentName:"p"},"/var/log/jitsi/")," as ",(0,l.kt)("inlineCode",{parentName:"p"},"json")," output, you could use ",(0,l.kt)("a",{parentName:"p",href:"https://github.com/deep-compute/docker-file-log-driver"},"docker-file-log-driver")," and configure it by adding the following block in your ",(0,l.kt)("inlineCode",{parentName:"p"},"docker-compose.yml")," file, at the same level as the ",(0,l.kt)("inlineCode",{parentName:"p"},"image")," block of the selected ",(0,l.kt)("inlineCode",{parentName:"p"},""),":"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},'services:\n :\n image: ...\n ...\n logging:\n driver: file-log-driver\n options:\n fpath: "/jitsi/.log"\n')),(0,l.kt)("p",null,"If you want to only display the ",(0,l.kt)("inlineCode",{parentName:"p"},"message")," part of the log in ",(0,l.kt)("inlineCode",{parentName:"p"},"json")," format, simply execute the following command (for instance if ",(0,l.kt)("inlineCode",{parentName:"p"},"fpath")," was set to ",(0,l.kt)("inlineCode",{parentName:"p"},"/jitsi/jvb.log"),") which uses ",(0,l.kt)("inlineCode",{parentName:"p"},"jq")," to extract the relevant part of the logs:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"sudo cat /var/log/jitsi/jvb.log | jq -r '.msg' | jq -r '.message'\n")),(0,l.kt)("h2",{id:"build-instructions"},"Build Instructions"),(0,l.kt)("p",null,"Building your images allows you to edit the configuration files of each image individually, providing more customization for your deployment."),(0,l.kt)("p",null,"The docker images can be built by running the ",(0,l.kt)("inlineCode",{parentName:"p"},"make")," command in the main repository folder. If you need to overwrite existing images from the remote source, use ",(0,l.kt)("inlineCode",{parentName:"p"},"FORCE_REBUILD=1 make"),"."),(0,l.kt)("p",null,"If you are on the unstable branch, build the images with ",(0,l.kt)("inlineCode",{parentName:"p"},"FORCE_REBUILD=1 JITSI_RELEASE=unstable make"),"."),(0,l.kt)("p",null,"You are now able to run ",(0,l.kt)("inlineCode",{parentName:"p"},"docker-compose up")," as usual."),(0,l.kt)("h2",{id:"running-behind-a-reverse-proxy"},"Running behind a reverse proxy"),(0,l.kt)("p",null,"By default this setup is using WebSocket connections for 2 core components:"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},"Signalling (XMPP)"),(0,l.kt)("li",{parentName:"ul"},"Bridge channel (colibri)")),(0,l.kt)("p",null,"Due to the hop-by-hop nature of WebSockets the reverse proxy must properly terminate and forward WebSocket connections. There 2 routes require such treatment:"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},"/xmpp-websocket"),(0,l.kt)("li",{parentName:"ul"},"/colibri-ws")),(0,l.kt)("p",null,"With nginx, these routes can be forwarded using the following config snippet:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},'location /xmpp-websocket {\n proxy_pass https://localhost:8443;\n proxy_http_version 1.1;\n proxy_set_header Upgrade $http_upgrade;\n proxy_set_header Connection "upgrade";\n}\nlocation /colibri-ws {\n proxy_pass https://localhost:8443;\n proxy_http_version 1.1;\n proxy_set_header Upgrade $http_upgrade;\n proxy_set_header Connection "upgrade";\n}\n')),(0,l.kt)("p",null,"With apache, ",(0,l.kt)("inlineCode",{parentName:"p"},"mod_proxy")," and ",(0,l.kt)("inlineCode",{parentName:"p"},"mod_proxy_wstunnel")," need to be enabled and these routes can be forwarded using the following config snippet:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},'\n \n ProxyTimeout 900\n \n ProxyPass "wss://localhost:8443/xmpp-websocket"\n \n \n ProxyPass "wss://localhost:8443/colibri-ws/"\n \n \n\n\n')),(0,l.kt)("p",null,"where ",(0,l.kt)("inlineCode",{parentName:"p"},"https://localhost:8443/")," is the url of the web service's ingress."),(0,l.kt)("h3",{id:"disabling-websocket-connections"},"Disabling WebSocket connections"),(0,l.kt)("admonition",{type:"note"},(0,l.kt)("p",{parentName:"admonition"},"This is not the recommended setup.")),(0,l.kt)("p",null,"If using WebSockets is not an option, these environment variables can be set to fallback to HTTP polling and WebRTC datachannels:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"ENABLE_SCTP=1\nENABLE_COLIBRI_WEBSOCKET=0\nENABLE_XMPP_WEBSOCKET=0\n")))}k.isMDXComponent=!0},168:(t,e,n)=>{n.d(e,{Z:()=>a});const a=n.p+"assets/images/docker-jitsi-meet-afafdf87fea30a2fa6412baa4a3f8248.png"}}]); \ No newline at end of file diff --git a/assets/js/31c6f806.c1a692b6.js b/assets/js/31c6f806.d1016507.js similarity index 98% rename from assets/js/31c6f806.c1a692b6.js rename to assets/js/31c6f806.d1016507.js index 028cfa1f7..61f89b22a 100644 --- a/assets/js/31c6f806.c1a692b6.js +++ b/assets/js/31c6f806.d1016507.js @@ -1 +1 @@ -"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[186],{3905:(e,t,i)=>{i.d(t,{Zo:()=>l,kt:()=>f});var r=i(7294);function n(e,t,i){return t in e?Object.defineProperty(e,t,{value:i,enumerable:!0,configurable:!0,writable:!0}):e[t]=i,e}function o(e,t){var i=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),i.push.apply(i,r)}return i}function s(e){for(var t=1;t=0||(n[i]=e[i]);return n}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,i)&&(n[i]=e[i])}return n}var p=r.createContext({}),d=function(e){var t=r.useContext(p),i=t;return e&&(i="function"==typeof e?e(t):s(s({},t),e)),i},l=function(e){var t=d(e.components);return r.createElement(p.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},u=r.forwardRef((function(e,t){var i=e.components,n=e.mdxType,o=e.originalType,p=e.parentName,l=a(e,["components","mdxType","originalType","parentName"]),u=d(i),f=n,h=u["".concat(p,".").concat(f)]||u[f]||c[f]||o;return i?r.createElement(h,s(s({ref:t},l),{},{components:i})):r.createElement(h,s({ref:t},l))}));function f(e,t){var i=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var o=i.length,s=new Array(o);s[0]=u;var a={};for(var p in t)hasOwnProperty.call(t,p)&&(a[p]=t[p]);a.originalType=e,a.mdxType="string"==typeof e?e:n,s[1]=a;for(var d=2;d{i.r(t),i.d(t,{assets:()=>l,contentTitle:()=>p,default:()=>f,frontMatter:()=>a,metadata:()=>d,toc:()=>c});var r=i(7462),n=i(3366),o=(i(7294),i(3905)),s=["components"],a={id:"devops-guide-bsd",title:"Self-Hosting Guide - FreeBSD/NetBSD/OpenBSD",sidebar_label:"BSD"},p=void 0,d={unversionedId:"devops-guide/devops-guide-bsd",id:"devops-guide/devops-guide-bsd",title:"Self-Hosting Guide - FreeBSD/NetBSD/OpenBSD",description:"This document is a reference point for pointing to the upstream packages provided by the FreeBSD, NetBSD and OpenBSD distributions. Jitsi only officially supports Linux, for any problems with the BSD packages you can contact their respective mailing lists.",source:"@site/docs/devops-guide/bsd.md",sourceDirName:"devops-guide",slug:"/devops-guide/devops-guide-bsd",permalink:"/handbook/docs/devops-guide/devops-guide-bsd",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/devops-guide/bsd.md",tags:[],version:"current",lastUpdatedAt:1694635654,formattedLastUpdatedAt:"Sep 13, 2023",frontMatter:{id:"devops-guide-bsd",title:"Self-Hosting Guide - FreeBSD/NetBSD/OpenBSD",sidebar_label:"BSD"}},l={},c=[{value:"FreeBSD",id:"freebsd",level:2},{value:"NetBSD",id:"netbsd",level:2},{value:"OpenBSD",id:"openbsd",level:2},{value:"Limitations",id:"limitations",level:2}],u={toc:c};function f(e){var t=e.components,i=(0,n.Z)(e,s);return(0,o.kt)("wrapper",(0,r.Z)({},u,i,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("p",null,"This document is a reference point for pointing to the upstream packages provided by the FreeBSD, NetBSD and OpenBSD distributions. Jitsi only officially supports Linux, for any problems with the BSD packages you can contact their respective mailing lists."),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},"Note"),": Many of the installation steps require root access."),(0,o.kt)("h2",{id:"freebsd"},"FreeBSD"),(0,o.kt)("p",null,"FreeBSD provides ports for ",(0,o.kt)("a",{parentName:"p",href:"https://www.freshports.org/net-im/jitsi-meet-full"},"Jitsi")," along with documentation on how to configure it and the current limitations - ",(0,o.kt)("a",{parentName:"p",href:"https://wiki.freebsd.org/Jitsi"},"https://wiki.freebsd.org/Jitsi"),"."),(0,o.kt)("p",null,"Jitsi can be installed using the meta port ",(0,o.kt)("a",{parentName:"p",href:"https://www.freshports.org/net-im/jitsi-meet-full"},"net-im/jitsi-meet-full")," which pulls in Jitsi Videobridge, Jicofo and Jitsi Meet Web UI, along with prosody, the Jitsi prosody plugins, nginx and other required dependencies. Instructions on how to build the port can be read on the FreeBSD Foundation site - ",(0,o.kt)("a",{parentName:"p",href:"https://freebsdfoundation.org/freebsd-project/resourcesold/installing-a-port-on-freebsd/"},"https://freebsdfoundation.org/freebsd-project/resourcesold/installing-a-port-on-freebsd/"),"."),(0,o.kt)("h2",{id:"netbsd"},"NetBSD"),(0,o.kt)("p",null,"NetBSD provides individual ports for ",(0,o.kt)("a",{parentName:"p",href:"https://pkgsrc.se/chat/jitsi-videobridge"},"Jitsi Videobridge"),", ",(0,o.kt)("a",{parentName:"p",href:"https://pkgsrc.se/chat/jicofo"},"Jicofo"),", ",(0,o.kt)("a",{parentName:"p",href:"https://pkgsrc.se/chat/jitsi-meet-prosody"},"Jitsi prosody plugins")," and ",(0,o.kt)("a",{parentName:"p",href:"https://pkgsrc.se/wip/jitsi-meet"},"Jitsi Meet Web UI"),". They can be installed using the command ",(0,o.kt)("inlineCode",{parentName:"p"},"pkg_add "),"."),(0,o.kt)("h2",{id:"openbsd"},"OpenBSD"),(0,o.kt)("p",null,"OpenBSD provides ports for ",(0,o.kt)("a",{parentName:"p",href:"https://cvsweb.openbsd.org/cgi-bin/cvsweb/ports/meta/jitsi/"},"Jitsi"),", along with a pkg-readme which details how to configure Jitsi for a single host install, located at ",(0,o.kt)("a",{parentName:"p",href:"https://cvsweb.openbsd.org/cgi-bin/cvsweb/ports/meta/jitsi/pkg/"},"/usr/local/share/docs/pkg-readme/jitsi"),"."),(0,o.kt)("p",null,"The meta port can be installed by the command ",(0,o.kt)("inlineCode",{parentName:"p"},"pkg_add jitsi"),", which pulls in the individual ports, Jitsi Videobridge, Jicofo and Jitsi Meet Web UI, along with prosody, Jitsi prosody plugins and other required dependencies."),(0,o.kt)("h2",{id:"limitations"},"Limitations"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Jigasi and Jibri have not yet been ported to work with any BSD systems.")))}f.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[186],{3905:(e,t,i)=>{i.d(t,{Zo:()=>l,kt:()=>f});var r=i(7294);function n(e,t,i){return t in e?Object.defineProperty(e,t,{value:i,enumerable:!0,configurable:!0,writable:!0}):e[t]=i,e}function o(e,t){var i=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),i.push.apply(i,r)}return i}function s(e){for(var t=1;t=0||(n[i]=e[i]);return n}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,i)&&(n[i]=e[i])}return n}var p=r.createContext({}),d=function(e){var t=r.useContext(p),i=t;return e&&(i="function"==typeof e?e(t):s(s({},t),e)),i},l=function(e){var t=d(e.components);return r.createElement(p.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},u=r.forwardRef((function(e,t){var i=e.components,n=e.mdxType,o=e.originalType,p=e.parentName,l=a(e,["components","mdxType","originalType","parentName"]),u=d(i),f=n,h=u["".concat(p,".").concat(f)]||u[f]||c[f]||o;return i?r.createElement(h,s(s({ref:t},l),{},{components:i})):r.createElement(h,s({ref:t},l))}));function f(e,t){var i=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var o=i.length,s=new Array(o);s[0]=u;var a={};for(var p in t)hasOwnProperty.call(t,p)&&(a[p]=t[p]);a.originalType=e,a.mdxType="string"==typeof e?e:n,s[1]=a;for(var d=2;d{i.r(t),i.d(t,{assets:()=>l,contentTitle:()=>p,default:()=>f,frontMatter:()=>a,metadata:()=>d,toc:()=>c});var r=i(7462),n=i(3366),o=(i(7294),i(3905)),s=["components"],a={id:"devops-guide-bsd",title:"Self-Hosting Guide - FreeBSD/NetBSD/OpenBSD",sidebar_label:"BSD"},p=void 0,d={unversionedId:"devops-guide/devops-guide-bsd",id:"devops-guide/devops-guide-bsd",title:"Self-Hosting Guide - FreeBSD/NetBSD/OpenBSD",description:"This document is a reference point for pointing to the upstream packages provided by the FreeBSD, NetBSD and OpenBSD distributions. Jitsi only officially supports Linux, for any problems with the BSD packages you can contact their respective mailing lists.",source:"@site/docs/devops-guide/bsd.md",sourceDirName:"devops-guide",slug:"/devops-guide/devops-guide-bsd",permalink:"/handbook/docs/devops-guide/devops-guide-bsd",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/devops-guide/bsd.md",tags:[],version:"current",lastUpdatedAt:1695203964,formattedLastUpdatedAt:"Sep 20, 2023",frontMatter:{id:"devops-guide-bsd",title:"Self-Hosting Guide - FreeBSD/NetBSD/OpenBSD",sidebar_label:"BSD"}},l={},c=[{value:"FreeBSD",id:"freebsd",level:2},{value:"NetBSD",id:"netbsd",level:2},{value:"OpenBSD",id:"openbsd",level:2},{value:"Limitations",id:"limitations",level:2}],u={toc:c};function f(e){var t=e.components,i=(0,n.Z)(e,s);return(0,o.kt)("wrapper",(0,r.Z)({},u,i,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("p",null,"This document is a reference point for pointing to the upstream packages provided by the FreeBSD, NetBSD and OpenBSD distributions. Jitsi only officially supports Linux, for any problems with the BSD packages you can contact their respective mailing lists."),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},"Note"),": Many of the installation steps require root access."),(0,o.kt)("h2",{id:"freebsd"},"FreeBSD"),(0,o.kt)("p",null,"FreeBSD provides ports for ",(0,o.kt)("a",{parentName:"p",href:"https://www.freshports.org/net-im/jitsi-meet-full"},"Jitsi")," along with documentation on how to configure it and the current limitations - ",(0,o.kt)("a",{parentName:"p",href:"https://wiki.freebsd.org/Jitsi"},"https://wiki.freebsd.org/Jitsi"),"."),(0,o.kt)("p",null,"Jitsi can be installed using the meta port ",(0,o.kt)("a",{parentName:"p",href:"https://www.freshports.org/net-im/jitsi-meet-full"},"net-im/jitsi-meet-full")," which pulls in Jitsi Videobridge, Jicofo and Jitsi Meet Web UI, along with prosody, the Jitsi prosody plugins, nginx and other required dependencies. Instructions on how to build the port can be read on the FreeBSD Foundation site - ",(0,o.kt)("a",{parentName:"p",href:"https://freebsdfoundation.org/freebsd-project/resourcesold/installing-a-port-on-freebsd/"},"https://freebsdfoundation.org/freebsd-project/resourcesold/installing-a-port-on-freebsd/"),"."),(0,o.kt)("h2",{id:"netbsd"},"NetBSD"),(0,o.kt)("p",null,"NetBSD provides individual ports for ",(0,o.kt)("a",{parentName:"p",href:"https://pkgsrc.se/chat/jitsi-videobridge"},"Jitsi Videobridge"),", ",(0,o.kt)("a",{parentName:"p",href:"https://pkgsrc.se/chat/jicofo"},"Jicofo"),", ",(0,o.kt)("a",{parentName:"p",href:"https://pkgsrc.se/chat/jitsi-meet-prosody"},"Jitsi prosody plugins")," and ",(0,o.kt)("a",{parentName:"p",href:"https://pkgsrc.se/wip/jitsi-meet"},"Jitsi Meet Web UI"),". They can be installed using the command ",(0,o.kt)("inlineCode",{parentName:"p"},"pkg_add "),"."),(0,o.kt)("h2",{id:"openbsd"},"OpenBSD"),(0,o.kt)("p",null,"OpenBSD provides ports for ",(0,o.kt)("a",{parentName:"p",href:"https://cvsweb.openbsd.org/cgi-bin/cvsweb/ports/meta/jitsi/"},"Jitsi"),", along with a pkg-readme which details how to configure Jitsi for a single host install, located at ",(0,o.kt)("a",{parentName:"p",href:"https://cvsweb.openbsd.org/cgi-bin/cvsweb/ports/meta/jitsi/pkg/"},"/usr/local/share/docs/pkg-readme/jitsi"),"."),(0,o.kt)("p",null,"The meta port can be installed by the command ",(0,o.kt)("inlineCode",{parentName:"p"},"pkg_add jitsi"),", which pulls in the individual ports, Jitsi Videobridge, Jicofo and Jitsi Meet Web UI, along with prosody, Jitsi prosody plugins and other required dependencies."),(0,o.kt)("h2",{id:"limitations"},"Limitations"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Jigasi and Jibri have not yet been ported to work with any BSD systems.")))}f.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/3280bb49.9a579050.js b/assets/js/3280bb49.4d8185f8.js similarity index 99% rename from assets/js/3280bb49.9a579050.js rename to assets/js/3280bb49.4d8185f8.js index a88d0f7dd..0079fb16c 100644 --- a/assets/js/3280bb49.9a579050.js +++ b/assets/js/3280bb49.4d8185f8.js @@ -1 +1 @@ -"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[2556],{3905:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>m});var i=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function r(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=i.createContext({}),p=function(e){var t=i.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},d=function(e){var t=p(e.components);return i.createElement(s.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},c=i.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,s=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),c=p(n),m=a,k=c["".concat(s,".").concat(m)]||c[m]||u[m]||o;return n?i.createElement(k,r(r({ref:t},d),{},{components:n})):i.createElement(k,r({ref:t},d))}));function m(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,r=new Array(o);r[0]=c;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:a,r[1]=l;for(var p=2;p{n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>s,default:()=>m,frontMatter:()=>l,metadata:()=>p,toc:()=>u});var i=n(7462),a=n(3366),o=(n(7294),n(3905)),r=["components"],l={id:"devops-guide-opensuse",title:"Self-Hosting Guide - openSUSE",sidebar_label:"openSUSE"},s=void 0,p={unversionedId:"devops-guide/devops-guide-opensuse",id:"devops-guide/devops-guide-opensuse",title:"Self-Hosting Guide - openSUSE",description:"This document describes the steps for a quick Jitsi-Meet installation, paired",source:"@site/docs/devops-guide/opensuse.md",sourceDirName:"devops-guide",slug:"/devops-guide/devops-guide-opensuse",permalink:"/handbook/docs/devops-guide/devops-guide-opensuse",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/devops-guide/opensuse.md",tags:[],version:"current",lastUpdatedAt:1694635654,formattedLastUpdatedAt:"Sep 13, 2023",frontMatter:{id:"devops-guide-opensuse",title:"Self-Hosting Guide - openSUSE",sidebar_label:"openSUSE"},sidebar:"docs",previous:{title:"Debian/Ubuntu server",permalink:"/handbook/docs/devops-guide/devops-guide-quickstart"},next:{title:"Docker",permalink:"/handbook/docs/devops-guide/devops-guide-docker"}},d={},u=[{value:"Installation",id:"installation",level:2},{value:"optional Add-Ons",id:"optional-add-ons",level:3},{value:"Configuration",id:"configuration",level:2},{value:"Prosody",id:"prosody",level:3},{value:"Nginx",id:"nginx",level:3},{value:"Jitsi-Meet",id:"jitsi-meet",level:3},{value:"Jitsi-Videobridge",id:"jitsi-videobridge",level:3},{value:"Jitsi-Jicofo",id:"jitsi-jicofo",level:3},{value:"Add-On: Jitsi-Jibri",id:"add-on-jitsi-jibri",level:2},{value:"Add-On: Jitsi-Jigasi",id:"add-on-jitsi-jigasi",level:2},{value:"Services",id:"services",level:2},{value:"Final notes",id:"final-notes",level:2}],c={toc:u};function m(e){var t=e.components,n=(0,a.Z)(e,r);return(0,o.kt)("wrapper",(0,i.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("p",null,"This document describes the steps for a quick Jitsi-Meet installation, paired\nwith a single Videobridge and a single Jicofo on openSUSE Leap 15.2."),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},"Note"),": Many of the installation steps require root access."),(0,o.kt)("h2",{id:"installation"},"Installation"),(0,o.kt)("ol",null,(0,o.kt)("li",{parentName:"ol"},"Add the OBS repository:",(0,o.kt)("br",{parentName:"li"}),(0,o.kt)("strong",{parentName:"li"},"Note:")," When Jitsi-Meet is merged into openSUSE Factory, this will be obsolete.")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-shell"},"zypper addrepo https://download.opensuse.org/repositories/home:/SchoolGuy:/jitsi/openSUSE_Leap_15.2/home:SchoolGuy:jitsi.repo\n")),(0,o.kt)("ol",{start:2},(0,o.kt)("li",{parentName:"ol"},"Refresh the repositories:")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-shell"},"zypper refresh\n")),(0,o.kt)("ol",{start:3},(0,o.kt)("li",{parentName:"ol"},"Install Jitsi-Meet and its dependencies:")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-shell"},"zypper install nginx prosody lua51-zlib jitsi-meet jitsi-videobridge jitsi-jicofo\n")),(0,o.kt)("h3",{id:"optional-add-ons"},"optional Add-Ons"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Install the Jibri Add-On: ",(0,o.kt)("inlineCode",{parentName:"li"},"zypper install jitsi-jibri")),(0,o.kt)("li",{parentName:"ul"},"Install the Jigasi Add-On: ",(0,o.kt)("inlineCode",{parentName:"li"},"zypper install jitsi-jigasi"))),(0,o.kt)("h2",{id:"configuration"},"Configuration"),(0,o.kt)("p",null,"The following sections describe how to configure the different packages.",(0,o.kt)("br",{parentName:"p"}),"\n","Replace ",(0,o.kt)("inlineCode",{parentName:"p"},"")," with your domain name and ",(0,o.kt)("inlineCode",{parentName:"p"},"YOURSECRET3")," with a strong password."),(0,o.kt)("h3",{id:"prosody"},"Prosody"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Open and adjust the Prosody configuration file under ",(0,o.kt)("inlineCode",{parentName:"li"},"/etc/prosody/prosody.cfg.lua"),":")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-lua"},'---------- Server-wide settings ----------\nadmins = { "focus@auth." }\ncross_domain_bosh = true;\nmodules_enabled = {\n -- HTTP modules\n "bosh"; -- Enable BOSH clients, aka "Jabber over HTTP"\n -- jitsi\n "smacks";\n "mam";\n "lastactivity";\n "offline";\n "pubsub";\n "adhoc";\n "websocket";\n "http_altconnect";\n "compression";\n}\n')),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Create a new configuration file named ",(0,o.kt)("inlineCode",{parentName:"li"},".cfg.lua")," in ",(0,o.kt)("inlineCode",{parentName:"li"},"/etc/prosody/conf.avail/"),"\nwith the following content:")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-lua",metastring:'title="/etc/prosody/conf.avail/meet.example.org.cfg.lua"',title:'"/etc/prosody/conf.avail/meet.example.org.cfg.lua"'},'plugin_paths = { "/usr/share/jitsi-meet/prosody-plugins/" }\n\n-- As per https://prosody.im/doc/setting_up_bosh#proxying_requests\nconsider_bosh_secure = true\n\n-- domain mapper options, must at least have domain base set to use the mapper\nmuc_mapper_domain_base = "";\n\nturncredentials_secret = "YOURSECRET3";\n\nturncredentials = {\n { type = "stun", host = "", port = "3478" },\n { type = "turn", host = "", port = "3478", transport = "udp" },\n -- { type = "turns", host = "", port = "443", transport = "tcp" }\n};\n\nVirtualHost ""\n authentication = "anonymous"\n ssl = {\n key = "/var/lib/prosody/.key";\n certificate = "/var/lib/prosody/.crt";\n }\n speakerstats_component = "speakerstats."\n conference_duration_component = "conferenceduration."\n modules_enabled = {\n "bosh";\n "pubsub";\n "speakerstats";\n "turncredentials";\n "conference_duration";\n }\n c2s_require_encryption = false\n\nComponent "conference." "muc"\n modules_enabled = {\n "muc_meeting_id";\n "muc_domain_mapper";\n }\n admins = { "focus@auth." }\n muc_room_locking = false\n muc_room_default_public_jids = true\n\n-- internal muc component\nComponent "internal.auth." "muc"\n modules_enabled = {\n "ping";\n }\n admins = { "focus@auth." }\n muc_room_locking = false\n muc_room_default_public_jids = true\n muc_room_cache_size = 1000\n\nComponent "jitsi-videobridge."\n component_secret = "YOURSECRET3"\n\nVirtualHost "auth."\n ssl = {\n key = "/var/lib/prosody/auth..key";\n certificate = "/var/lib/prosody/auth..crt";\n }\n authentication = "internal_plain"\n\nComponent "focus."\n component_secret = "YOURSECRET3"\n\nComponent "speakerstats." "speakerstats_component"\n muc_component = "conference."\n\nComponent "conferenceduration." "conference_duration_component"\n muc_component = "conference."\n')),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("p",{parentName:"li"},"Create a symlink for the configuration:",(0,o.kt)("br",{parentName:"p"}),"\n",(0,o.kt)("inlineCode",{parentName:"p"},"ln --symbolic /etc/prosody/conf.avail/.cfg.lua /etc/prosody/conf.d/.cfg.lua"))),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("p",{parentName:"li"},"Create the certificates via ",(0,o.kt)("inlineCode",{parentName:"p"},"prosodyctl cert generate "),".",(0,o.kt)("br",{parentName:"p"}),"\n","The value ",(0,o.kt)("inlineCode",{parentName:"p"},"")," represents the following URLs."),(0,o.kt)("pre",{parentName:"li"},(0,o.kt)("code",{parentName:"pre"},"* `auth.`\n* `conference.`\n* `conferenceduration.`\n* `internal.auth.`\n* `FQDN`\n* `focus.`\n* `jitsi-videobridge.`\n* `callcontrol.` __Note:__ This is only needed if you deploy Jigasi\n* `recorder.` __Note:__ This is only needed if you deploy Jibri\n"))),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("p",{parentName:"li"},(0,o.kt)("inlineCode",{parentName:"p"},"/var/lib/prosody/"),": Symlink all generated ",(0,o.kt)("inlineCode",{parentName:"p"},"*.crt")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"*.key")," files to ",(0,o.kt)("inlineCode",{parentName:"p"},"/etc/prosody/certs/"),". "))),(0,o.kt)("admonition",{type:"note"},(0,o.kt)("p",{parentName:"admonition"},"Please do not link other certificates.")),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Add the certificates to the system keystore:",(0,o.kt)("ul",{parentName:"li"},(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"ln --symbolic --force /var/lib/prosody/auth..crt /usr/local/share/ca-certificates/auth..crt")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"update-ca-certificates --fresh")))),(0,o.kt)("li",{parentName:"ul"},"Create conference focus user: ",(0,o.kt)("inlineCode",{parentName:"li"},"prosodyctl register focus auth. YOURSECRET3"))),(0,o.kt)("h3",{id:"nginx"},"Nginx"),(0,o.kt)("p",null,"Edit the file ",(0,o.kt)("inlineCode",{parentName:"p"},"jitsi-meet.conf")," in ",(0,o.kt)("inlineCode",{parentName:"p"},"/etc/nginx/vhosts.d/")," (which was installed\nalong with ",(0,o.kt)("inlineCode",{parentName:"p"},"jitsi-meet"),") and do the following:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Check the ",(0,o.kt)("inlineCode",{parentName:"li"},"server_name")," value."),(0,o.kt)("li",{parentName:"ul"},"Check the TLS certificates (Let's Encrypt for production use, Prosody for testing, for example).")),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},"Note:")," If you are using an existing server, please make sure to adjust the websocket and bosh part, too."),(0,o.kt)("h3",{id:"jitsi-meet"},"Jitsi-Meet"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Go to ",(0,o.kt)("inlineCode",{parentName:"li"},"/srv/jitsi-meet")," and edit ",(0,o.kt)("inlineCode",{parentName:"li"},"config.js"),":")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js",metastring:'title="/srv/jitsi-meet/config.js"',title:'"/srv/jitsi-meet/config.js"'},"var config = {\n hosts: {\n domain: '',\n muc: 'conference.',\n bridge: 'jitsi-videobridge.',\n focus: 'focus.'\n },\n useNicks: false,\n bosh: '///http-bind',\n};\n")),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},"Note:")," Please be aware that this is the minimal configuration."),(0,o.kt)("h3",{id:"jitsi-videobridge"},"Jitsi-Videobridge"),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},"Note:")," We use a combination of the ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-videobridge/blob/master/doc/muc.md#videobridge-configuration"},"new Videobridge configuration"),"\nand the legacy one with the ",(0,o.kt)("inlineCode",{parentName:"p"},"sip-communicator.properties")," file. We have\nto do this because of the ",(0,o.kt)("inlineCode",{parentName:"p"},"STATISTICS_TRANSPORT")," property."),(0,o.kt)("p",null,"If we remove ",(0,o.kt)("inlineCode",{parentName:"p"},"org.jitsi.videobridge.STATISTICS_TRANSPORT=muc,colibri"),"\nfrom ",(0,o.kt)("inlineCode",{parentName:"p"},"sip-communicator.properties"),", the videobridge will not work!"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Go to the directory ",(0,o.kt)("inlineCode",{parentName:"li"},"/etc/jitsi/videobridge")),(0,o.kt)("li",{parentName:"ul"},"Edit the file ",(0,o.kt)("inlineCode",{parentName:"li"},"jitsi-videobridge.conf"),(0,o.kt)("ul",{parentName:"li"},(0,o.kt)("li",{parentName:"ul"},"Set ",(0,o.kt)("inlineCode",{parentName:"li"},"JVB_HOSTNAME")," to your ",(0,o.kt)("inlineCode",{parentName:"li"},""),"."),(0,o.kt)("li",{parentName:"ul"},"Set ",(0,o.kt)("inlineCode",{parentName:"li"},"JVB_SECRET")," to your own secret."))),(0,o.kt)("li",{parentName:"ul"},"Edit the file ",(0,o.kt)("inlineCode",{parentName:"li"},"application.conf")," and adjust the values under ",(0,o.kt)("inlineCode",{parentName:"li"},"apis"),"\nand ",(0,o.kt)("inlineCode",{parentName:"li"},"websockets"),", especially set a unique ID as ",(0,o.kt)("inlineCode",{parentName:"li"},"muc_nickname"),"\nwith ",(0,o.kt)("inlineCode",{parentName:"li"},"uuidgen")," for example.")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-HUCON"},'apis {\n xmpp-client {\n configs {\n xmpp-server-1 {\n hostname="localhost"\n domain = "auth.${FQDN}"\n username = "focus"\n password = "YOURSECRET3"\n muc_jids = "JvbBrewery@internal.auth.${FQDN}"\n # The muc_nickname must be unique across all jitsi-videobridge instances\n muc_nickname = "unique-id"\n disable_certificate_verification = true\n }\n }\n }\n}\nwebsockets {\n enabled=true\n server-id="default-id"\n domain="${FQDN}"\n}\n')),(0,o.kt)("h3",{id:"jitsi-jicofo"},"Jitsi-Jicofo"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Go to the directory ",(0,o.kt)("inlineCode",{parentName:"li"},"/etc/jitsi/jicofo")),(0,o.kt)("li",{parentName:"ul"},"Edit the file ",(0,o.kt)("inlineCode",{parentName:"li"},"jitsi-jicofo.conf"),(0,o.kt)("ul",{parentName:"li"},(0,o.kt)("li",{parentName:"ul"},"Set the property ",(0,o.kt)("inlineCode",{parentName:"li"},"JICOFO_HOSTNAME")," to ",(0,o.kt)("inlineCode",{parentName:"li"},""),"."),(0,o.kt)("li",{parentName:"ul"},"Set the property ",(0,o.kt)("inlineCode",{parentName:"li"},"JICOFO_SECRET")," to the password the Prosody user got in above setup."),(0,o.kt)("li",{parentName:"ul"},"Set the property ",(0,o.kt)("inlineCode",{parentName:"li"},"JICOFO_AUTH_DOMAIN")," to ",(0,o.kt)("inlineCode",{parentName:"li"},"auth."),"."),(0,o.kt)("li",{parentName:"ul"},"Set the property ",(0,o.kt)("inlineCode",{parentName:"li"},"JICOFO_AUTH_USER")," to the Prosody user from above setup."))),(0,o.kt)("li",{parentName:"ul"},"Edit the file ",(0,o.kt)("inlineCode",{parentName:"li"},"sip-cmmunicator.properties"),(0,o.kt)("ul",{parentName:"li"},(0,o.kt)("li",{parentName:"ul"},"Set the property ",(0,o.kt)("inlineCode",{parentName:"li"},"org.jitsi.jicofo.BRIDGE_MUC")," to ",(0,o.kt)("inlineCode",{parentName:"li"},"JvbBrewery@internal.auth."),"."),(0,o.kt)("li",{parentName:"ul"},"Set the property ",(0,o.kt)("inlineCode",{parentName:"li"},"org.jitsi.jicofo.jibri.BREWERY")," to ",(0,o.kt)("inlineCode",{parentName:"li"},"JibriBrewery@internal.auth."),"."),(0,o.kt)("li",{parentName:"ul"},"Depending on your cert setup set ",(0,o.kt)("inlineCode",{parentName:"li"},"org.jitsi.jicofo.ALWAYS_TRUST_MODE_ENABLED")," to ",(0,o.kt)("inlineCode",{parentName:"li"},"true")," or ",(0,o.kt)("inlineCode",{parentName:"li"},"false"),".")))),(0,o.kt)("h2",{id:"add-on-jitsi-jibri"},"Add-On: Jitsi-Jibri"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Add to the file ",(0,o.kt)("inlineCode",{parentName:"li"},"/etc/prosody/conf.avail/.cfg.lua")," the following snippet at the end of the file.")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-lua"},'VirtualHost "recorder."\n modules_enabled = {\n "ping";\n }\n authentication = "internal_plain"\n')),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Run ",(0,o.kt)("inlineCode",{parentName:"li"},"prosodyctl register jibri auth. YOURSECRET3")," and replace ",(0,o.kt)("inlineCode",{parentName:"li"},"YOURSECRET3")," with an appropiate one."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"prosodyctl register recorder recorder. YOURSECRET3")," and replace ",(0,o.kt)("inlineCode",{parentName:"li"},"YOURSECRET3")," with an appropiate one."),(0,o.kt)("li",{parentName:"ul"},"Go to the directory ",(0,o.kt)("inlineCode",{parentName:"li"},"/etc/jitsi/jibri")," and edit the following properties you see listed below. The rest can be left as is.")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-HUCON"},'jibri{\n api{\n environments = [\n {\n xmpp-domain = ""\n control-muc {\n domain = "internal."\n }\n control-login {\n domain = "recorder."\n username = "recorder"\n password = "YOURSECRET3"\n } \n call-login {\n domain = "recorder."\n username = "recorder"\n password = "YOURSECRET3"\n }\n }\n ]\n }\n}\n')),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Edit the file ",(0,o.kt)("inlineCode",{parentName:"li"},"/etc/jitsi/jicofo/sip-communicator.properties")," and add the\nfollowing properties:")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-HUCON"},"org.jitsi.jicofo.jibri.BREWERY=JibriBrewery@internal.auth.\norg.jitsi.jicofo.jibri.PENDING_TIMEOUT=90\n")),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Edit the file ",(0,o.kt)("inlineCode",{parentName:"li"},"/srv/jitsi-meet/config.js")," and set the\nfollowing properties:")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},"fileRecordingsEnabled: true, // If you want to enable file recording\nliveStreamingEnabled: true, // If you want to enable live streaming\nhiddenDomain: 'recorder.',\n")),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Edit ",(0,o.kt)("inlineCode",{parentName:"li"},"/srv/jitsi-meet/interface_config.js")," and make sure the\n",(0,o.kt)("inlineCode",{parentName:"li"},"TOOLBAR_BUTTONS")," array contains the ",(0,o.kt)("inlineCode",{parentName:"li"},"recording")," and\nthe ",(0,o.kt)("inlineCode",{parentName:"li"},"livestreaming")," value if you want those features.")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},"TOOLBAR_BUTTONS: [\n 'microphone', 'camera', 'closedcaptions', 'desktop', 'embedmeeting', 'fullscreen',\n 'fodeviceselection', 'hangup', 'profile', 'chat', 'recording',\n 'livestreaming', 'etherpad', 'sharedvideo', 'settings', 'raisehand',\n 'videoquality', 'filmstrip', 'invite', 'feedback', 'stats', 'shortcuts',\n 'tileview', 'videobackgroundblur', 'download', 'help', 'mute-everyone', 'security'\n],\n")),(0,o.kt)("h2",{id:"add-on-jitsi-jigasi"},"Add-On: Jitsi-Jigasi"),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},"Note from openSUSE packagers:")," We've packaged it but we don't have the infrastructure to set up this component. Hence we can't provide a guide for this so far."),(0,o.kt)("h2",{id:"services"},"Services"),(0,o.kt)("p",null,"Now everything should be working. That means you are ready to start everything up:"),(0,o.kt)("ol",null,(0,o.kt)("li",{parentName:"ol"},(0,o.kt)("inlineCode",{parentName:"li"},"systemctl start prosody")),(0,o.kt)("li",{parentName:"ol"},(0,o.kt)("inlineCode",{parentName:"li"},"systemctl start jitsi-videbridge")),(0,o.kt)("li",{parentName:"ol"},(0,o.kt)("inlineCode",{parentName:"li"},"systemctl start jitsi-jicofo")),(0,o.kt)("li",{parentName:"ol"},(0,o.kt)("inlineCode",{parentName:"li"},"systemctl start jitsi-jibri")," (if configured and installed beforehand)"),(0,o.kt)("li",{parentName:"ol"},(0,o.kt)("inlineCode",{parentName:"li"},"systemctl start jitsi-jigasi")," (if configured and installed beforehand)"),(0,o.kt)("li",{parentName:"ol"},(0,o.kt)("inlineCode",{parentName:"li"},"systemctl start nginx"))),(0,o.kt)("h2",{id:"final-notes"},"Final notes"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"The Jitsi Software has a lot of dependencies and thus we recommend to run\nthis on a dedicated host for Jitsi."),(0,o.kt)("li",{parentName:"ul"},"Updating Jitsi is crucial to get rid of bugs and updated dependencies with\npossible security fixes."),(0,o.kt)("li",{parentName:"ul"},"Although tempted through Chrome: Don't install a full X11 stack like KDE or\nGnome for this."),(0,o.kt)("li",{parentName:"ul"},"Don't mix the ",(0,o.kt)("inlineCode",{parentName:"li"},"rpms")," or ",(0,o.kt)("inlineCode",{parentName:"li"},"debs")," with a source installation of the same component."),(0,o.kt)("li",{parentName:"ul"},"Securely backup your configuration, preferably in a VCS. This saves time and\npain when doing rollbacks or dealing with other problems.")))}m.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[2556],{3905:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>m});var i=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function r(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=i.createContext({}),p=function(e){var t=i.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},d=function(e){var t=p(e.components);return i.createElement(s.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},c=i.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,s=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),c=p(n),m=a,k=c["".concat(s,".").concat(m)]||c[m]||u[m]||o;return n?i.createElement(k,r(r({ref:t},d),{},{components:n})):i.createElement(k,r({ref:t},d))}));function m(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,r=new Array(o);r[0]=c;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:a,r[1]=l;for(var p=2;p{n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>s,default:()=>m,frontMatter:()=>l,metadata:()=>p,toc:()=>u});var i=n(7462),a=n(3366),o=(n(7294),n(3905)),r=["components"],l={id:"devops-guide-opensuse",title:"Self-Hosting Guide - openSUSE",sidebar_label:"openSUSE"},s=void 0,p={unversionedId:"devops-guide/devops-guide-opensuse",id:"devops-guide/devops-guide-opensuse",title:"Self-Hosting Guide - openSUSE",description:"This document describes the steps for a quick Jitsi-Meet installation, paired",source:"@site/docs/devops-guide/opensuse.md",sourceDirName:"devops-guide",slug:"/devops-guide/devops-guide-opensuse",permalink:"/handbook/docs/devops-guide/devops-guide-opensuse",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/devops-guide/opensuse.md",tags:[],version:"current",lastUpdatedAt:1695203964,formattedLastUpdatedAt:"Sep 20, 2023",frontMatter:{id:"devops-guide-opensuse",title:"Self-Hosting Guide - openSUSE",sidebar_label:"openSUSE"},sidebar:"docs",previous:{title:"Debian/Ubuntu server",permalink:"/handbook/docs/devops-guide/devops-guide-quickstart"},next:{title:"Docker",permalink:"/handbook/docs/devops-guide/devops-guide-docker"}},d={},u=[{value:"Installation",id:"installation",level:2},{value:"optional Add-Ons",id:"optional-add-ons",level:3},{value:"Configuration",id:"configuration",level:2},{value:"Prosody",id:"prosody",level:3},{value:"Nginx",id:"nginx",level:3},{value:"Jitsi-Meet",id:"jitsi-meet",level:3},{value:"Jitsi-Videobridge",id:"jitsi-videobridge",level:3},{value:"Jitsi-Jicofo",id:"jitsi-jicofo",level:3},{value:"Add-On: Jitsi-Jibri",id:"add-on-jitsi-jibri",level:2},{value:"Add-On: Jitsi-Jigasi",id:"add-on-jitsi-jigasi",level:2},{value:"Services",id:"services",level:2},{value:"Final notes",id:"final-notes",level:2}],c={toc:u};function m(e){var t=e.components,n=(0,a.Z)(e,r);return(0,o.kt)("wrapper",(0,i.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("p",null,"This document describes the steps for a quick Jitsi-Meet installation, paired\nwith a single Videobridge and a single Jicofo on openSUSE Leap 15.2."),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},"Note"),": Many of the installation steps require root access."),(0,o.kt)("h2",{id:"installation"},"Installation"),(0,o.kt)("ol",null,(0,o.kt)("li",{parentName:"ol"},"Add the OBS repository:",(0,o.kt)("br",{parentName:"li"}),(0,o.kt)("strong",{parentName:"li"},"Note:")," When Jitsi-Meet is merged into openSUSE Factory, this will be obsolete.")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-shell"},"zypper addrepo https://download.opensuse.org/repositories/home:/SchoolGuy:/jitsi/openSUSE_Leap_15.2/home:SchoolGuy:jitsi.repo\n")),(0,o.kt)("ol",{start:2},(0,o.kt)("li",{parentName:"ol"},"Refresh the repositories:")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-shell"},"zypper refresh\n")),(0,o.kt)("ol",{start:3},(0,o.kt)("li",{parentName:"ol"},"Install Jitsi-Meet and its dependencies:")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-shell"},"zypper install nginx prosody lua51-zlib jitsi-meet jitsi-videobridge jitsi-jicofo\n")),(0,o.kt)("h3",{id:"optional-add-ons"},"optional Add-Ons"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Install the Jibri Add-On: ",(0,o.kt)("inlineCode",{parentName:"li"},"zypper install jitsi-jibri")),(0,o.kt)("li",{parentName:"ul"},"Install the Jigasi Add-On: ",(0,o.kt)("inlineCode",{parentName:"li"},"zypper install jitsi-jigasi"))),(0,o.kt)("h2",{id:"configuration"},"Configuration"),(0,o.kt)("p",null,"The following sections describe how to configure the different packages.",(0,o.kt)("br",{parentName:"p"}),"\n","Replace ",(0,o.kt)("inlineCode",{parentName:"p"},"")," with your domain name and ",(0,o.kt)("inlineCode",{parentName:"p"},"YOURSECRET3")," with a strong password."),(0,o.kt)("h3",{id:"prosody"},"Prosody"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Open and adjust the Prosody configuration file under ",(0,o.kt)("inlineCode",{parentName:"li"},"/etc/prosody/prosody.cfg.lua"),":")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-lua"},'---------- Server-wide settings ----------\nadmins = { "focus@auth." }\ncross_domain_bosh = true;\nmodules_enabled = {\n -- HTTP modules\n "bosh"; -- Enable BOSH clients, aka "Jabber over HTTP"\n -- jitsi\n "smacks";\n "mam";\n "lastactivity";\n "offline";\n "pubsub";\n "adhoc";\n "websocket";\n "http_altconnect";\n "compression";\n}\n')),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Create a new configuration file named ",(0,o.kt)("inlineCode",{parentName:"li"},".cfg.lua")," in ",(0,o.kt)("inlineCode",{parentName:"li"},"/etc/prosody/conf.avail/"),"\nwith the following content:")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-lua",metastring:'title="/etc/prosody/conf.avail/meet.example.org.cfg.lua"',title:'"/etc/prosody/conf.avail/meet.example.org.cfg.lua"'},'plugin_paths = { "/usr/share/jitsi-meet/prosody-plugins/" }\n\n-- As per https://prosody.im/doc/setting_up_bosh#proxying_requests\nconsider_bosh_secure = true\n\n-- domain mapper options, must at least have domain base set to use the mapper\nmuc_mapper_domain_base = "";\n\nturncredentials_secret = "YOURSECRET3";\n\nturncredentials = {\n { type = "stun", host = "", port = "3478" },\n { type = "turn", host = "", port = "3478", transport = "udp" },\n -- { type = "turns", host = "", port = "443", transport = "tcp" }\n};\n\nVirtualHost ""\n authentication = "anonymous"\n ssl = {\n key = "/var/lib/prosody/.key";\n certificate = "/var/lib/prosody/.crt";\n }\n speakerstats_component = "speakerstats."\n conference_duration_component = "conferenceduration."\n modules_enabled = {\n "bosh";\n "pubsub";\n "speakerstats";\n "turncredentials";\n "conference_duration";\n }\n c2s_require_encryption = false\n\nComponent "conference." "muc"\n modules_enabled = {\n "muc_meeting_id";\n "muc_domain_mapper";\n }\n admins = { "focus@auth." }\n muc_room_locking = false\n muc_room_default_public_jids = true\n\n-- internal muc component\nComponent "internal.auth." "muc"\n modules_enabled = {\n "ping";\n }\n admins = { "focus@auth." }\n muc_room_locking = false\n muc_room_default_public_jids = true\n muc_room_cache_size = 1000\n\nComponent "jitsi-videobridge."\n component_secret = "YOURSECRET3"\n\nVirtualHost "auth."\n ssl = {\n key = "/var/lib/prosody/auth..key";\n certificate = "/var/lib/prosody/auth..crt";\n }\n authentication = "internal_plain"\n\nComponent "focus."\n component_secret = "YOURSECRET3"\n\nComponent "speakerstats." "speakerstats_component"\n muc_component = "conference."\n\nComponent "conferenceduration." "conference_duration_component"\n muc_component = "conference."\n')),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("p",{parentName:"li"},"Create a symlink for the configuration:",(0,o.kt)("br",{parentName:"p"}),"\n",(0,o.kt)("inlineCode",{parentName:"p"},"ln --symbolic /etc/prosody/conf.avail/.cfg.lua /etc/prosody/conf.d/.cfg.lua"))),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("p",{parentName:"li"},"Create the certificates via ",(0,o.kt)("inlineCode",{parentName:"p"},"prosodyctl cert generate "),".",(0,o.kt)("br",{parentName:"p"}),"\n","The value ",(0,o.kt)("inlineCode",{parentName:"p"},"")," represents the following URLs."),(0,o.kt)("pre",{parentName:"li"},(0,o.kt)("code",{parentName:"pre"},"* `auth.`\n* `conference.`\n* `conferenceduration.`\n* `internal.auth.`\n* `FQDN`\n* `focus.`\n* `jitsi-videobridge.`\n* `callcontrol.` __Note:__ This is only needed if you deploy Jigasi\n* `recorder.` __Note:__ This is only needed if you deploy Jibri\n"))),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("p",{parentName:"li"},(0,o.kt)("inlineCode",{parentName:"p"},"/var/lib/prosody/"),": Symlink all generated ",(0,o.kt)("inlineCode",{parentName:"p"},"*.crt")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"*.key")," files to ",(0,o.kt)("inlineCode",{parentName:"p"},"/etc/prosody/certs/"),". "))),(0,o.kt)("admonition",{type:"note"},(0,o.kt)("p",{parentName:"admonition"},"Please do not link other certificates.")),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Add the certificates to the system keystore:",(0,o.kt)("ul",{parentName:"li"},(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"ln --symbolic --force /var/lib/prosody/auth..crt /usr/local/share/ca-certificates/auth..crt")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"update-ca-certificates --fresh")))),(0,o.kt)("li",{parentName:"ul"},"Create conference focus user: ",(0,o.kt)("inlineCode",{parentName:"li"},"prosodyctl register focus auth. YOURSECRET3"))),(0,o.kt)("h3",{id:"nginx"},"Nginx"),(0,o.kt)("p",null,"Edit the file ",(0,o.kt)("inlineCode",{parentName:"p"},"jitsi-meet.conf")," in ",(0,o.kt)("inlineCode",{parentName:"p"},"/etc/nginx/vhosts.d/")," (which was installed\nalong with ",(0,o.kt)("inlineCode",{parentName:"p"},"jitsi-meet"),") and do the following:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Check the ",(0,o.kt)("inlineCode",{parentName:"li"},"server_name")," value."),(0,o.kt)("li",{parentName:"ul"},"Check the TLS certificates (Let's Encrypt for production use, Prosody for testing, for example).")),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},"Note:")," If you are using an existing server, please make sure to adjust the websocket and bosh part, too."),(0,o.kt)("h3",{id:"jitsi-meet"},"Jitsi-Meet"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Go to ",(0,o.kt)("inlineCode",{parentName:"li"},"/srv/jitsi-meet")," and edit ",(0,o.kt)("inlineCode",{parentName:"li"},"config.js"),":")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js",metastring:'title="/srv/jitsi-meet/config.js"',title:'"/srv/jitsi-meet/config.js"'},"var config = {\n hosts: {\n domain: '',\n muc: 'conference.',\n bridge: 'jitsi-videobridge.',\n focus: 'focus.'\n },\n useNicks: false,\n bosh: '///http-bind',\n};\n")),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},"Note:")," Please be aware that this is the minimal configuration."),(0,o.kt)("h3",{id:"jitsi-videobridge"},"Jitsi-Videobridge"),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},"Note:")," We use a combination of the ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-videobridge/blob/master/doc/muc.md#videobridge-configuration"},"new Videobridge configuration"),"\nand the legacy one with the ",(0,o.kt)("inlineCode",{parentName:"p"},"sip-communicator.properties")," file. We have\nto do this because of the ",(0,o.kt)("inlineCode",{parentName:"p"},"STATISTICS_TRANSPORT")," property."),(0,o.kt)("p",null,"If we remove ",(0,o.kt)("inlineCode",{parentName:"p"},"org.jitsi.videobridge.STATISTICS_TRANSPORT=muc,colibri"),"\nfrom ",(0,o.kt)("inlineCode",{parentName:"p"},"sip-communicator.properties"),", the videobridge will not work!"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Go to the directory ",(0,o.kt)("inlineCode",{parentName:"li"},"/etc/jitsi/videobridge")),(0,o.kt)("li",{parentName:"ul"},"Edit the file ",(0,o.kt)("inlineCode",{parentName:"li"},"jitsi-videobridge.conf"),(0,o.kt)("ul",{parentName:"li"},(0,o.kt)("li",{parentName:"ul"},"Set ",(0,o.kt)("inlineCode",{parentName:"li"},"JVB_HOSTNAME")," to your ",(0,o.kt)("inlineCode",{parentName:"li"},""),"."),(0,o.kt)("li",{parentName:"ul"},"Set ",(0,o.kt)("inlineCode",{parentName:"li"},"JVB_SECRET")," to your own secret."))),(0,o.kt)("li",{parentName:"ul"},"Edit the file ",(0,o.kt)("inlineCode",{parentName:"li"},"application.conf")," and adjust the values under ",(0,o.kt)("inlineCode",{parentName:"li"},"apis"),"\nand ",(0,o.kt)("inlineCode",{parentName:"li"},"websockets"),", especially set a unique ID as ",(0,o.kt)("inlineCode",{parentName:"li"},"muc_nickname"),"\nwith ",(0,o.kt)("inlineCode",{parentName:"li"},"uuidgen")," for example.")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-HUCON"},'apis {\n xmpp-client {\n configs {\n xmpp-server-1 {\n hostname="localhost"\n domain = "auth.${FQDN}"\n username = "focus"\n password = "YOURSECRET3"\n muc_jids = "JvbBrewery@internal.auth.${FQDN}"\n # The muc_nickname must be unique across all jitsi-videobridge instances\n muc_nickname = "unique-id"\n disable_certificate_verification = true\n }\n }\n }\n}\nwebsockets {\n enabled=true\n server-id="default-id"\n domain="${FQDN}"\n}\n')),(0,o.kt)("h3",{id:"jitsi-jicofo"},"Jitsi-Jicofo"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Go to the directory ",(0,o.kt)("inlineCode",{parentName:"li"},"/etc/jitsi/jicofo")),(0,o.kt)("li",{parentName:"ul"},"Edit the file ",(0,o.kt)("inlineCode",{parentName:"li"},"jitsi-jicofo.conf"),(0,o.kt)("ul",{parentName:"li"},(0,o.kt)("li",{parentName:"ul"},"Set the property ",(0,o.kt)("inlineCode",{parentName:"li"},"JICOFO_HOSTNAME")," to ",(0,o.kt)("inlineCode",{parentName:"li"},""),"."),(0,o.kt)("li",{parentName:"ul"},"Set the property ",(0,o.kt)("inlineCode",{parentName:"li"},"JICOFO_SECRET")," to the password the Prosody user got in above setup."),(0,o.kt)("li",{parentName:"ul"},"Set the property ",(0,o.kt)("inlineCode",{parentName:"li"},"JICOFO_AUTH_DOMAIN")," to ",(0,o.kt)("inlineCode",{parentName:"li"},"auth."),"."),(0,o.kt)("li",{parentName:"ul"},"Set the property ",(0,o.kt)("inlineCode",{parentName:"li"},"JICOFO_AUTH_USER")," to the Prosody user from above setup."))),(0,o.kt)("li",{parentName:"ul"},"Edit the file ",(0,o.kt)("inlineCode",{parentName:"li"},"sip-cmmunicator.properties"),(0,o.kt)("ul",{parentName:"li"},(0,o.kt)("li",{parentName:"ul"},"Set the property ",(0,o.kt)("inlineCode",{parentName:"li"},"org.jitsi.jicofo.BRIDGE_MUC")," to ",(0,o.kt)("inlineCode",{parentName:"li"},"JvbBrewery@internal.auth."),"."),(0,o.kt)("li",{parentName:"ul"},"Set the property ",(0,o.kt)("inlineCode",{parentName:"li"},"org.jitsi.jicofo.jibri.BREWERY")," to ",(0,o.kt)("inlineCode",{parentName:"li"},"JibriBrewery@internal.auth."),"."),(0,o.kt)("li",{parentName:"ul"},"Depending on your cert setup set ",(0,o.kt)("inlineCode",{parentName:"li"},"org.jitsi.jicofo.ALWAYS_TRUST_MODE_ENABLED")," to ",(0,o.kt)("inlineCode",{parentName:"li"},"true")," or ",(0,o.kt)("inlineCode",{parentName:"li"},"false"),".")))),(0,o.kt)("h2",{id:"add-on-jitsi-jibri"},"Add-On: Jitsi-Jibri"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Add to the file ",(0,o.kt)("inlineCode",{parentName:"li"},"/etc/prosody/conf.avail/.cfg.lua")," the following snippet at the end of the file.")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-lua"},'VirtualHost "recorder."\n modules_enabled = {\n "ping";\n }\n authentication = "internal_plain"\n')),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Run ",(0,o.kt)("inlineCode",{parentName:"li"},"prosodyctl register jibri auth. YOURSECRET3")," and replace ",(0,o.kt)("inlineCode",{parentName:"li"},"YOURSECRET3")," with an appropiate one."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"prosodyctl register recorder recorder. YOURSECRET3")," and replace ",(0,o.kt)("inlineCode",{parentName:"li"},"YOURSECRET3")," with an appropiate one."),(0,o.kt)("li",{parentName:"ul"},"Go to the directory ",(0,o.kt)("inlineCode",{parentName:"li"},"/etc/jitsi/jibri")," and edit the following properties you see listed below. The rest can be left as is.")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-HUCON"},'jibri{\n api{\n environments = [\n {\n xmpp-domain = ""\n control-muc {\n domain = "internal."\n }\n control-login {\n domain = "recorder."\n username = "recorder"\n password = "YOURSECRET3"\n } \n call-login {\n domain = "recorder."\n username = "recorder"\n password = "YOURSECRET3"\n }\n }\n ]\n }\n}\n')),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Edit the file ",(0,o.kt)("inlineCode",{parentName:"li"},"/etc/jitsi/jicofo/sip-communicator.properties")," and add the\nfollowing properties:")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-HUCON"},"org.jitsi.jicofo.jibri.BREWERY=JibriBrewery@internal.auth.\norg.jitsi.jicofo.jibri.PENDING_TIMEOUT=90\n")),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Edit the file ",(0,o.kt)("inlineCode",{parentName:"li"},"/srv/jitsi-meet/config.js")," and set the\nfollowing properties:")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},"fileRecordingsEnabled: true, // If you want to enable file recording\nliveStreamingEnabled: true, // If you want to enable live streaming\nhiddenDomain: 'recorder.',\n")),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Edit ",(0,o.kt)("inlineCode",{parentName:"li"},"/srv/jitsi-meet/interface_config.js")," and make sure the\n",(0,o.kt)("inlineCode",{parentName:"li"},"TOOLBAR_BUTTONS")," array contains the ",(0,o.kt)("inlineCode",{parentName:"li"},"recording")," and\nthe ",(0,o.kt)("inlineCode",{parentName:"li"},"livestreaming")," value if you want those features.")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-js"},"TOOLBAR_BUTTONS: [\n 'microphone', 'camera', 'closedcaptions', 'desktop', 'embedmeeting', 'fullscreen',\n 'fodeviceselection', 'hangup', 'profile', 'chat', 'recording',\n 'livestreaming', 'etherpad', 'sharedvideo', 'settings', 'raisehand',\n 'videoquality', 'filmstrip', 'invite', 'feedback', 'stats', 'shortcuts',\n 'tileview', 'videobackgroundblur', 'download', 'help', 'mute-everyone', 'security'\n],\n")),(0,o.kt)("h2",{id:"add-on-jitsi-jigasi"},"Add-On: Jitsi-Jigasi"),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},"Note from openSUSE packagers:")," We've packaged it but we don't have the infrastructure to set up this component. Hence we can't provide a guide for this so far."),(0,o.kt)("h2",{id:"services"},"Services"),(0,o.kt)("p",null,"Now everything should be working. That means you are ready to start everything up:"),(0,o.kt)("ol",null,(0,o.kt)("li",{parentName:"ol"},(0,o.kt)("inlineCode",{parentName:"li"},"systemctl start prosody")),(0,o.kt)("li",{parentName:"ol"},(0,o.kt)("inlineCode",{parentName:"li"},"systemctl start jitsi-videbridge")),(0,o.kt)("li",{parentName:"ol"},(0,o.kt)("inlineCode",{parentName:"li"},"systemctl start jitsi-jicofo")),(0,o.kt)("li",{parentName:"ol"},(0,o.kt)("inlineCode",{parentName:"li"},"systemctl start jitsi-jibri")," (if configured and installed beforehand)"),(0,o.kt)("li",{parentName:"ol"},(0,o.kt)("inlineCode",{parentName:"li"},"systemctl start jitsi-jigasi")," (if configured and installed beforehand)"),(0,o.kt)("li",{parentName:"ol"},(0,o.kt)("inlineCode",{parentName:"li"},"systemctl start nginx"))),(0,o.kt)("h2",{id:"final-notes"},"Final notes"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"The Jitsi Software has a lot of dependencies and thus we recommend to run\nthis on a dedicated host for Jitsi."),(0,o.kt)("li",{parentName:"ul"},"Updating Jitsi is crucial to get rid of bugs and updated dependencies with\npossible security fixes."),(0,o.kt)("li",{parentName:"ul"},"Although tempted through Chrome: Don't install a full X11 stack like KDE or\nGnome for this."),(0,o.kt)("li",{parentName:"ul"},"Don't mix the ",(0,o.kt)("inlineCode",{parentName:"li"},"rpms")," or ",(0,o.kt)("inlineCode",{parentName:"li"},"debs")," with a source installation of the same component."),(0,o.kt)("li",{parentName:"ul"},"Securely backup your configuration, preferably in a VCS. This saves time and\npain when doing rollbacks or dealing with other problems.")))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/39715811.afa92fd5.js b/assets/js/39715811.337e9aff.js similarity index 99% rename from assets/js/39715811.afa92fd5.js rename to assets/js/39715811.337e9aff.js index 492420b46..5a8eb61ef 100644 --- a/assets/js/39715811.afa92fd5.js +++ b/assets/js/39715811.337e9aff.js @@ -1 +1 @@ -"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[2746],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>h});var a=n(7294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function s(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var l=a.createContext({}),d=function(e){var t=a.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):s(s({},t),e)),n},u=function(e){var t=d(e.components);return a.createElement(l.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},c=a.forwardRef((function(e,t){var n=e.components,o=e.mdxType,i=e.originalType,l=e.parentName,u=r(e,["components","mdxType","originalType","parentName"]),c=d(n),h=o,m=c["".concat(l,".").concat(h)]||c[h]||p[h]||i;return n?a.createElement(m,s(s({ref:t},u),{},{components:n})):a.createElement(m,s({ref:t},u))}));function h(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=n.length,s=new Array(i);s[0]=c;var r={};for(var l in t)hasOwnProperty.call(t,l)&&(r[l]=t[l]);r.originalType=e,r.mdxType="string"==typeof e?e:o,s[1]=r;for(var d=2;d{n.r(t),n.d(t,{assets:()=>u,contentTitle:()=>l,default:()=>h,frontMatter:()=>r,metadata:()=>d,toc:()=>p});var a=n(7462),o=n(3366),i=(n(7294),n(3905)),s=["components"],r={id:"ldap-authentication",title:"LDAP authentication",sidebar_label:"LDAP Authentication"},l=void 0,d={unversionedId:"devops-guide/ldap-authentication",id:"devops-guide/ldap-authentication",title:"LDAP authentication",description:"This is a first draft and might not work on your system. It has been tested on a Debian 11 installation with prosody 0.11 and authenticates against an OpenLDAP directory.",source:"@site/docs/devops-guide/ldap-authentication.md",sourceDirName:"devops-guide",slug:"/devops-guide/ldap-authentication",permalink:"/handbook/docs/devops-guide/ldap-authentication",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/devops-guide/ldap-authentication.md",tags:[],version:"current",lastUpdatedAt:1694635654,formattedLastUpdatedAt:"Sep 13, 2023",frontMatter:{id:"ldap-authentication",title:"LDAP authentication",sidebar_label:"LDAP Authentication"},sidebar:"docs",previous:{title:"Authentication (Secure Domain)",permalink:"/handbook/docs/devops-guide/secure-domain"},next:{title:"Scalable setup",permalink:"/handbook/docs/devops-guide/devops-guide-scalable"}},u={},p=[{value:"Prerequisites",id:"prerequisites",level:2},{value:"Required packages",id:"required-packages",level:3},{value:"Install and set up Cyrus SASL",id:"install-and-set-up-cyrus-sasl",level:2},{value:"Test LDAP authentication",id:"test-ldap-authentication",level:3},{value:"Enable the saslauthd service",id:"enable-the-saslauthd-service",level:3},{value:"Cyrus SASL Configuration file",id:"cyrus-sasl-configuration-file",level:3},{value:"Set up Prosody",id:"set-up-prosody",level:2},{value:"Set Permissions",id:"set-permissions",level:3}],c={toc:p};function h(e){var t=e.components,n=(0,o.Z)(e,s);return(0,i.kt)("wrapper",(0,a.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("admonition",{type:"note"},(0,i.kt)("p",{parentName:"admonition"},"This is a first draft and might not work on your system. It has been tested on a Debian 11 installation with prosody 0.11 and authenticates against an OpenLDAP directory.")),(0,i.kt)("p",null,"If you want to authenticate your users against an LDAP directory instead\nof the local Prosody user database, you can use the Cyrus SASL package.\nUsing this package you might be able to validate user-supplied credentials\nagainst other sources, such as PAM, SQL and more - but this is beyond\nthis article."),(0,i.kt)("h2",{id:"prerequisites"},"Prerequisites"),(0,i.kt)("p",null,"Before following this article, make sure you have set up Prosody as\ndescribed in ",(0,i.kt)("a",{parentName:"p",href:"/handbook/docs/devops-guide/secure-domain"},"Authentication (Secure Domain)")," first."),(0,i.kt)("h3",{id:"required-packages"},"Required packages"),(0,i.kt)("p",null,"On Debian systems you need to install some required packages:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"sudo apt-get install sasl2-bin libsasl2-modules-ldap lua-cyrussasl\nsudo prosodyctl install --server=https://modules.prosody.im/rocks/ mod_auth_cyrus\n")),(0,i.kt)("p",null,"The first two packages are necessary for Cyrus' ",(0,i.kt)("inlineCode",{parentName:"p"},"saslauthd")," and allow it\nto connect to an LDAP directory. The ",(0,i.kt)("inlineCode",{parentName:"p"},"lua-cyrussasl"),"-package allows\nProsody to access Cyrus SASL."),(0,i.kt)("p",null,"Installing the ",(0,i.kt)("a",{parentName:"p",href:"https://modules.prosody.im/mod_auth_cyrus"},"mod_auth_cyrus")," module is neccessary because support for Cyrus SASL has been ",(0,i.kt)("a",{parentName:"p",href:"https://prosody.im/doc/cyrus_sasl"},"removed")," from mainline Prosody and placed in the community module repository."),(0,i.kt)("h2",{id:"install-and-set-up-cyrus-sasl"},"Install and set up Cyrus SASL"),(0,i.kt)("p",null,"The following options define a basic LDAP configuration. A full set of\npossible options can be found in ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/winlibs/cyrus-sasl/blob/master/saslauthd/LDAP_SASLAUTHD"},"LDAP_SASLAUTHD"),"."),(0,i.kt)("p",null,"By default Cyrus' ",(0,i.kt)("inlineCode",{parentName:"p"},"saslauthd")," searches for its LDAP configuration in\n",(0,i.kt)("inlineCode",{parentName:"p"},"/etc/saslauthd.conf"),". So create this file and enter something similar\nto define your LDAP environment:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"ldap_servers: ldaps://ldap.example.com\nldap_bind_dn: admin@example.com\nldap_bind_pw: topsecret\nldap_auth_method: bind\nldap_search_base: ou=people,dc=example,dc=com\n")),(0,i.kt)("admonition",{type:"note"},(0,i.kt)("p",{parentName:"admonition"},"One omitted option you might want to look into is ",(0,i.kt)("inlineCode",{parentName:"p"},"ldap_filter")," which defaults to ",(0,i.kt)("inlineCode",{parentName:"p"},"uid=%u")," and should work for a lot of systems. If you are using a Samba or Microsoft AD instance as your LDAP server you may need to change this to ",(0,i.kt)("inlineCode",{parentName:"p"},"ldap_filter: (sAMAccountName=%U)")," as ",(0,i.kt)("inlineCode",{parentName:"p"},"uid")," is NULL by default many configurations. You can also use the ",(0,i.kt)("inlineCode",{parentName:"p"},"ldap_filter")," to allow only specific users access. For more details on this and other options see the ",(0,i.kt)("inlineCode",{parentName:"p"},"LDAP_SASLAUTHD")," document linked above."),(0,i.kt)("p",{parentName:"admonition"},'Please note that Prosody may experience issues with usernames containing the "@"-symbol. You can work around this issue by changing ',(0,i.kt)("inlineCode",{parentName:"p"},"uid=%u")," to ",(0,i.kt)("inlineCode",{parentName:"p"},"uid=%U"),", which is ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/winlibs/cyrus-sasl/blob/d933c030ce12ec0668469d79ab8378e347a1b3ba/saslauthd/LDAP_SASLAUTHD#L126"},"defined"),' as the "user portion of %u (%U = test when %u = ',(0,i.kt)("a",{parentName:"p",href:"mailto:test@domain.tld"},"test@domain.tld"),')"')),(0,i.kt)("h3",{id:"test-ldap-authentication"},"Test LDAP authentication"),(0,i.kt)("p",null,"To test if the LDAP configuration is working, you can start ",(0,i.kt)("inlineCode",{parentName:"p"},"saslauthd")," in\ndebug mode while specifying the mandatory LDAP authentication mechanism:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"sudo saslauthd -d -a ldap\n")),(0,i.kt)("p",null,"The test utility for the SASL authentication server can then be used in a\nsecondary terminal. Replace ",(0,i.kt)("inlineCode",{parentName:"p"},"user")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"password")," with credentials stored\nin LDAP."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'sudo testsaslauthd -u user -p password\n0: OK "Success."\n\nsudo testsaslauthd -u user -p wrongpassword\n0: NO "authentication failed"\n')),(0,i.kt)("p",null,"After testing, you can stop ",(0,i.kt)("inlineCode",{parentName:"p"},"saslauthd")," using ",(0,i.kt)("inlineCode",{parentName:"p"},"ctrl-c"),"."),(0,i.kt)("h3",{id:"enable-the-saslauthd-service"},"Enable the ",(0,i.kt)("inlineCode",{parentName:"h3"},"saslauthd")," service"),(0,i.kt)("p",null,"You will need to edit the ",(0,i.kt)("inlineCode",{parentName:"p"},"/etc/default/saslauthd")," to enable the ",(0,i.kt)("inlineCode",{parentName:"p"},"saslauthd")," service to run at boot and have it use LDAP for authentication. You can use sed to do this quickly."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'sudo sed -i -e "s/START=.*/START=yes/" -e "s/MECHANISMS=.*/MECHANISMS=\\"ldap\\"/" /etc/default/saslauthd\n')),(0,i.kt)("p",null,"This will make the following changes to ",(0,i.kt)("inlineCode",{parentName:"p"},"/etc/default/saslauthd"),"."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'[...]\n# Should saslauthd run automatically on startup? (default: no)\nSTART=yes\n[...]\n# Example: MECHANISMS="pam"\nMECHANISMS="ldap"\n[...]\n')),(0,i.kt)("p",null,"It is not necessary to point ",(0,i.kt)("inlineCode",{parentName:"p"},"MECH_OPTIONS")," to the LDAP configuration file\nsince this is the default for this mechanism."),(0,i.kt)("p",null,"Now you can start, restart and stop ",(0,i.kt)("inlineCode",{parentName:"p"},"saslauthd")," using the ",(0,i.kt)("inlineCode",{parentName:"p"},"service")," scripts:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"sudo service saslauthd restart\n")),(0,i.kt)("p",null,"If you experience issues, check ",(0,i.kt)("inlineCode",{parentName:"p"},"/var/log/auth.log")," for ",(0,i.kt)("inlineCode",{parentName:"p"},"saslauthd")," entries."),(0,i.kt)("h3",{id:"cyrus-sasl-configuration-file"},"Cyrus SASL Configuration file"),(0,i.kt)("p",null,"Cyrus SASL requires a configuration file in order to know how to check user\ncredentials. For Prosody, the file is named ",(0,i.kt)("inlineCode",{parentName:"p"},"prosody.conf")," by default.\nIts location varies by OS and distribution as shown in the following table:"),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:null},"Platform"),(0,i.kt)("th",{parentName:"tr",align:null},"Location"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"Debian and Ubuntu"),(0,i.kt)("td",{parentName:"tr",align:null},"/etc/sasl")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"Arch, RHEL/CentOS"),(0,i.kt)("td",{parentName:"tr",align:null},"/etc/sasl2")))),(0,i.kt)("p",null,"So for Debian systems, create the file ",(0,i.kt)("inlineCode",{parentName:"p"},"/etc/sasl/prosody.conf"),".\nThe directory ",(0,i.kt)("inlineCode",{parentName:"p"},"/etc/sasl")," might not yet exist."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"sudo mkdir /etc/sasl/\n\ncat << 'EOF' |sudo tee /etc/sasl/prosody.conf > /dev/null\npwcheck_method: saslauthd\nmech_list: PLAIN\nEOF\n")),(0,i.kt)("admonition",{type:"note"},(0,i.kt)("p",{parentName:"admonition"},"The filename ",(0,i.kt)("inlineCode",{parentName:"p"},"prosody.conf")," corresponds to a value for ",(0,i.kt)("inlineCode",{parentName:"p"},"cyrus_application_name"),"\nin the Prosody config. Since we have not changed the default this has a value of ",(0,i.kt)("inlineCode",{parentName:"p"},"prosody"),".")),(0,i.kt)("p",null,"The Prosody documentation has more details on a\n",(0,i.kt)("a",{parentName:"p",href:"https://prosody.im/doc/cyrus_sasl"},"Cyrus SASL-related setup"),"."),(0,i.kt)("h2",{id:"set-up-prosody"},"Set up Prosody"),(0,i.kt)("p",null,"If you have tested the LDAP authentication successfully and enabled the ",(0,i.kt)("inlineCode",{parentName:"p"},"saslauthd")," service, you can change Prosody's authentication to the Cyrus backend by changing the ",(0,i.kt)("inlineCode",{parentName:"p"},"authentication")," setting in ",(0,i.kt)("inlineCode",{parentName:"p"},"/etc/prosody/conf.avail/$(hostname -f).cfg.lua")," via the command:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'sed -i -E -e "/^ *VirtualHost \\"$(hostname -f)\\"/,/^ *VirtualHost/ {s/authentication ?=.*$/authentication = \\"cyrus\\"/}" /etc/prosody/conf.avail/$(hostname -f).cfg.lua\n')),(0,i.kt)("p",null,"You might also have to add the ",(0,i.kt)("inlineCode",{parentName:"p"},"allow_unencrypted_plain_auth")," option to allow\nplain-text passwords to be sent over the network. ",(0,i.kt)("em",{parentName:"p"},"This is not recommended")," as it\nmakes the setup less secure. So please try without this line first and only add\nit if you have problems authenticating."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},' authentication = "cyrus"\n allow_unencrypted_plain_auth = true\n')),(0,i.kt)("h3",{id:"set-permissions"},"Set Permissions"),(0,i.kt)("p",null,"Prosody will now try to access the saslauthd socket in\n",(0,i.kt)("inlineCode",{parentName:"p"},"/var/run/saslauthd/")," to communicate with the authentication daemon.\nThis folder only allows access to user ",(0,i.kt)("inlineCode",{parentName:"p"},"root")," and group ",(0,i.kt)("inlineCode",{parentName:"p"},"sasl")," while prosody\nruns as the system user/group ",(0,i.kt)("inlineCode",{parentName:"p"},"prosody"),". "),(0,i.kt)("p",null,"The easiest solution is to add the ",(0,i.kt)("inlineCode",{parentName:"p"},"sasl")," group to the ",(0,i.kt)("inlineCode",{parentName:"p"},"prosody")," user and\nrestart the service."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"sudo adduser prosody sasl\nsudo service prosody restart\n")))}h.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[2746],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>h});var a=n(7294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function s(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var l=a.createContext({}),d=function(e){var t=a.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):s(s({},t),e)),n},u=function(e){var t=d(e.components);return a.createElement(l.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},c=a.forwardRef((function(e,t){var n=e.components,o=e.mdxType,i=e.originalType,l=e.parentName,u=r(e,["components","mdxType","originalType","parentName"]),c=d(n),h=o,m=c["".concat(l,".").concat(h)]||c[h]||p[h]||i;return n?a.createElement(m,s(s({ref:t},u),{},{components:n})):a.createElement(m,s({ref:t},u))}));function h(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=n.length,s=new Array(i);s[0]=c;var r={};for(var l in t)hasOwnProperty.call(t,l)&&(r[l]=t[l]);r.originalType=e,r.mdxType="string"==typeof e?e:o,s[1]=r;for(var d=2;d{n.r(t),n.d(t,{assets:()=>u,contentTitle:()=>l,default:()=>h,frontMatter:()=>r,metadata:()=>d,toc:()=>p});var a=n(7462),o=n(3366),i=(n(7294),n(3905)),s=["components"],r={id:"ldap-authentication",title:"LDAP authentication",sidebar_label:"LDAP Authentication"},l=void 0,d={unversionedId:"devops-guide/ldap-authentication",id:"devops-guide/ldap-authentication",title:"LDAP authentication",description:"This is a first draft and might not work on your system. It has been tested on a Debian 11 installation with prosody 0.11 and authenticates against an OpenLDAP directory.",source:"@site/docs/devops-guide/ldap-authentication.md",sourceDirName:"devops-guide",slug:"/devops-guide/ldap-authentication",permalink:"/handbook/docs/devops-guide/ldap-authentication",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/devops-guide/ldap-authentication.md",tags:[],version:"current",lastUpdatedAt:1695203964,formattedLastUpdatedAt:"Sep 20, 2023",frontMatter:{id:"ldap-authentication",title:"LDAP authentication",sidebar_label:"LDAP Authentication"},sidebar:"docs",previous:{title:"Authentication (Secure Domain)",permalink:"/handbook/docs/devops-guide/secure-domain"},next:{title:"Scalable setup",permalink:"/handbook/docs/devops-guide/devops-guide-scalable"}},u={},p=[{value:"Prerequisites",id:"prerequisites",level:2},{value:"Required packages",id:"required-packages",level:3},{value:"Install and set up Cyrus SASL",id:"install-and-set-up-cyrus-sasl",level:2},{value:"Test LDAP authentication",id:"test-ldap-authentication",level:3},{value:"Enable the saslauthd service",id:"enable-the-saslauthd-service",level:3},{value:"Cyrus SASL Configuration file",id:"cyrus-sasl-configuration-file",level:3},{value:"Set up Prosody",id:"set-up-prosody",level:2},{value:"Set Permissions",id:"set-permissions",level:3}],c={toc:p};function h(e){var t=e.components,n=(0,o.Z)(e,s);return(0,i.kt)("wrapper",(0,a.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("admonition",{type:"note"},(0,i.kt)("p",{parentName:"admonition"},"This is a first draft and might not work on your system. It has been tested on a Debian 11 installation with prosody 0.11 and authenticates against an OpenLDAP directory.")),(0,i.kt)("p",null,"If you want to authenticate your users against an LDAP directory instead\nof the local Prosody user database, you can use the Cyrus SASL package.\nUsing this package you might be able to validate user-supplied credentials\nagainst other sources, such as PAM, SQL and more - but this is beyond\nthis article."),(0,i.kt)("h2",{id:"prerequisites"},"Prerequisites"),(0,i.kt)("p",null,"Before following this article, make sure you have set up Prosody as\ndescribed in ",(0,i.kt)("a",{parentName:"p",href:"/handbook/docs/devops-guide/secure-domain"},"Authentication (Secure Domain)")," first."),(0,i.kt)("h3",{id:"required-packages"},"Required packages"),(0,i.kt)("p",null,"On Debian systems you need to install some required packages:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"sudo apt-get install sasl2-bin libsasl2-modules-ldap lua-cyrussasl\nsudo prosodyctl install --server=https://modules.prosody.im/rocks/ mod_auth_cyrus\n")),(0,i.kt)("p",null,"The first two packages are necessary for Cyrus' ",(0,i.kt)("inlineCode",{parentName:"p"},"saslauthd")," and allow it\nto connect to an LDAP directory. The ",(0,i.kt)("inlineCode",{parentName:"p"},"lua-cyrussasl"),"-package allows\nProsody to access Cyrus SASL."),(0,i.kt)("p",null,"Installing the ",(0,i.kt)("a",{parentName:"p",href:"https://modules.prosody.im/mod_auth_cyrus"},"mod_auth_cyrus")," module is neccessary because support for Cyrus SASL has been ",(0,i.kt)("a",{parentName:"p",href:"https://prosody.im/doc/cyrus_sasl"},"removed")," from mainline Prosody and placed in the community module repository."),(0,i.kt)("h2",{id:"install-and-set-up-cyrus-sasl"},"Install and set up Cyrus SASL"),(0,i.kt)("p",null,"The following options define a basic LDAP configuration. A full set of\npossible options can be found in ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/winlibs/cyrus-sasl/blob/master/saslauthd/LDAP_SASLAUTHD"},"LDAP_SASLAUTHD"),"."),(0,i.kt)("p",null,"By default Cyrus' ",(0,i.kt)("inlineCode",{parentName:"p"},"saslauthd")," searches for its LDAP configuration in\n",(0,i.kt)("inlineCode",{parentName:"p"},"/etc/saslauthd.conf"),". So create this file and enter something similar\nto define your LDAP environment:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"ldap_servers: ldaps://ldap.example.com\nldap_bind_dn: admin@example.com\nldap_bind_pw: topsecret\nldap_auth_method: bind\nldap_search_base: ou=people,dc=example,dc=com\n")),(0,i.kt)("admonition",{type:"note"},(0,i.kt)("p",{parentName:"admonition"},"One omitted option you might want to look into is ",(0,i.kt)("inlineCode",{parentName:"p"},"ldap_filter")," which defaults to ",(0,i.kt)("inlineCode",{parentName:"p"},"uid=%u")," and should work for a lot of systems. If you are using a Samba or Microsoft AD instance as your LDAP server you may need to change this to ",(0,i.kt)("inlineCode",{parentName:"p"},"ldap_filter: (sAMAccountName=%U)")," as ",(0,i.kt)("inlineCode",{parentName:"p"},"uid")," is NULL by default many configurations. You can also use the ",(0,i.kt)("inlineCode",{parentName:"p"},"ldap_filter")," to allow only specific users access. For more details on this and other options see the ",(0,i.kt)("inlineCode",{parentName:"p"},"LDAP_SASLAUTHD")," document linked above."),(0,i.kt)("p",{parentName:"admonition"},'Please note that Prosody may experience issues with usernames containing the "@"-symbol. You can work around this issue by changing ',(0,i.kt)("inlineCode",{parentName:"p"},"uid=%u")," to ",(0,i.kt)("inlineCode",{parentName:"p"},"uid=%U"),", which is ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/winlibs/cyrus-sasl/blob/d933c030ce12ec0668469d79ab8378e347a1b3ba/saslauthd/LDAP_SASLAUTHD#L126"},"defined"),' as the "user portion of %u (%U = test when %u = ',(0,i.kt)("a",{parentName:"p",href:"mailto:test@domain.tld"},"test@domain.tld"),')"')),(0,i.kt)("h3",{id:"test-ldap-authentication"},"Test LDAP authentication"),(0,i.kt)("p",null,"To test if the LDAP configuration is working, you can start ",(0,i.kt)("inlineCode",{parentName:"p"},"saslauthd")," in\ndebug mode while specifying the mandatory LDAP authentication mechanism:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"sudo saslauthd -d -a ldap\n")),(0,i.kt)("p",null,"The test utility for the SASL authentication server can then be used in a\nsecondary terminal. Replace ",(0,i.kt)("inlineCode",{parentName:"p"},"user")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"password")," with credentials stored\nin LDAP."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'sudo testsaslauthd -u user -p password\n0: OK "Success."\n\nsudo testsaslauthd -u user -p wrongpassword\n0: NO "authentication failed"\n')),(0,i.kt)("p",null,"After testing, you can stop ",(0,i.kt)("inlineCode",{parentName:"p"},"saslauthd")," using ",(0,i.kt)("inlineCode",{parentName:"p"},"ctrl-c"),"."),(0,i.kt)("h3",{id:"enable-the-saslauthd-service"},"Enable the ",(0,i.kt)("inlineCode",{parentName:"h3"},"saslauthd")," service"),(0,i.kt)("p",null,"You will need to edit the ",(0,i.kt)("inlineCode",{parentName:"p"},"/etc/default/saslauthd")," to enable the ",(0,i.kt)("inlineCode",{parentName:"p"},"saslauthd")," service to run at boot and have it use LDAP for authentication. You can use sed to do this quickly."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'sudo sed -i -e "s/START=.*/START=yes/" -e "s/MECHANISMS=.*/MECHANISMS=\\"ldap\\"/" /etc/default/saslauthd\n')),(0,i.kt)("p",null,"This will make the following changes to ",(0,i.kt)("inlineCode",{parentName:"p"},"/etc/default/saslauthd"),"."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'[...]\n# Should saslauthd run automatically on startup? (default: no)\nSTART=yes\n[...]\n# Example: MECHANISMS="pam"\nMECHANISMS="ldap"\n[...]\n')),(0,i.kt)("p",null,"It is not necessary to point ",(0,i.kt)("inlineCode",{parentName:"p"},"MECH_OPTIONS")," to the LDAP configuration file\nsince this is the default for this mechanism."),(0,i.kt)("p",null,"Now you can start, restart and stop ",(0,i.kt)("inlineCode",{parentName:"p"},"saslauthd")," using the ",(0,i.kt)("inlineCode",{parentName:"p"},"service")," scripts:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"sudo service saslauthd restart\n")),(0,i.kt)("p",null,"If you experience issues, check ",(0,i.kt)("inlineCode",{parentName:"p"},"/var/log/auth.log")," for ",(0,i.kt)("inlineCode",{parentName:"p"},"saslauthd")," entries."),(0,i.kt)("h3",{id:"cyrus-sasl-configuration-file"},"Cyrus SASL Configuration file"),(0,i.kt)("p",null,"Cyrus SASL requires a configuration file in order to know how to check user\ncredentials. For Prosody, the file is named ",(0,i.kt)("inlineCode",{parentName:"p"},"prosody.conf")," by default.\nIts location varies by OS and distribution as shown in the following table:"),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:null},"Platform"),(0,i.kt)("th",{parentName:"tr",align:null},"Location"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"Debian and Ubuntu"),(0,i.kt)("td",{parentName:"tr",align:null},"/etc/sasl")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"Arch, RHEL/CentOS"),(0,i.kt)("td",{parentName:"tr",align:null},"/etc/sasl2")))),(0,i.kt)("p",null,"So for Debian systems, create the file ",(0,i.kt)("inlineCode",{parentName:"p"},"/etc/sasl/prosody.conf"),".\nThe directory ",(0,i.kt)("inlineCode",{parentName:"p"},"/etc/sasl")," might not yet exist."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"sudo mkdir /etc/sasl/\n\ncat << 'EOF' |sudo tee /etc/sasl/prosody.conf > /dev/null\npwcheck_method: saslauthd\nmech_list: PLAIN\nEOF\n")),(0,i.kt)("admonition",{type:"note"},(0,i.kt)("p",{parentName:"admonition"},"The filename ",(0,i.kt)("inlineCode",{parentName:"p"},"prosody.conf")," corresponds to a value for ",(0,i.kt)("inlineCode",{parentName:"p"},"cyrus_application_name"),"\nin the Prosody config. Since we have not changed the default this has a value of ",(0,i.kt)("inlineCode",{parentName:"p"},"prosody"),".")),(0,i.kt)("p",null,"The Prosody documentation has more details on a\n",(0,i.kt)("a",{parentName:"p",href:"https://prosody.im/doc/cyrus_sasl"},"Cyrus SASL-related setup"),"."),(0,i.kt)("h2",{id:"set-up-prosody"},"Set up Prosody"),(0,i.kt)("p",null,"If you have tested the LDAP authentication successfully and enabled the ",(0,i.kt)("inlineCode",{parentName:"p"},"saslauthd")," service, you can change Prosody's authentication to the Cyrus backend by changing the ",(0,i.kt)("inlineCode",{parentName:"p"},"authentication")," setting in ",(0,i.kt)("inlineCode",{parentName:"p"},"/etc/prosody/conf.avail/$(hostname -f).cfg.lua")," via the command:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'sed -i -E -e "/^ *VirtualHost \\"$(hostname -f)\\"/,/^ *VirtualHost/ {s/authentication ?=.*$/authentication = \\"cyrus\\"/}" /etc/prosody/conf.avail/$(hostname -f).cfg.lua\n')),(0,i.kt)("p",null,"You might also have to add the ",(0,i.kt)("inlineCode",{parentName:"p"},"allow_unencrypted_plain_auth")," option to allow\nplain-text passwords to be sent over the network. ",(0,i.kt)("em",{parentName:"p"},"This is not recommended")," as it\nmakes the setup less secure. So please try without this line first and only add\nit if you have problems authenticating."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},' authentication = "cyrus"\n allow_unencrypted_plain_auth = true\n')),(0,i.kt)("h3",{id:"set-permissions"},"Set Permissions"),(0,i.kt)("p",null,"Prosody will now try to access the saslauthd socket in\n",(0,i.kt)("inlineCode",{parentName:"p"},"/var/run/saslauthd/")," to communicate with the authentication daemon.\nThis folder only allows access to user ",(0,i.kt)("inlineCode",{parentName:"p"},"root")," and group ",(0,i.kt)("inlineCode",{parentName:"p"},"sasl")," while prosody\nruns as the system user/group ",(0,i.kt)("inlineCode",{parentName:"p"},"prosody"),". "),(0,i.kt)("p",null,"The easiest solution is to add the ",(0,i.kt)("inlineCode",{parentName:"p"},"sasl")," group to the ",(0,i.kt)("inlineCode",{parentName:"p"},"prosody")," user and\nrestart the service."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"sudo adduser prosody sasl\nsudo service prosody restart\n")))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/3dddb2f7.6606a012.js b/assets/js/3dddb2f7.a1bd2389.js similarity index 98% rename from assets/js/3dddb2f7.6606a012.js rename to assets/js/3dddb2f7.a1bd2389.js index 1f3695e92..c9a4c00c9 100644 --- a/assets/js/3dddb2f7.6606a012.js +++ b/assets/js/3dddb2f7.a1bd2389.js @@ -1 +1 @@ -"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[8264],{3905:(e,t,a)=>{a.d(t,{Zo:()=>u,kt:()=>k});var n=a(7294);function r(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function i(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function l(e){for(var t=1;t=0||(r[a]=e[a]);return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(r[a]=e[a])}return r}var o=n.createContext({}),p=function(e){var t=n.useContext(o),a=t;return e&&(a="function"==typeof e?e(t):l(l({},t),e)),a},u=function(e){var t=p(e.components);return n.createElement(o.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},s=n.forwardRef((function(e,t){var a=e.components,r=e.mdxType,i=e.originalType,o=e.parentName,u=d(e,["components","mdxType","originalType","parentName"]),s=p(a),k=r,c=s["".concat(o,".").concat(k)]||s[k]||m[k]||i;return a?n.createElement(c,l(l({ref:t},u),{},{components:a})):n.createElement(c,l({ref:t},u))}));function k(e,t){var a=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var i=a.length,l=new Array(i);l[0]=s;var d={};for(var o in t)hasOwnProperty.call(t,o)&&(d[o]=t[o]);d.originalType=e,d.mdxType="string"==typeof e?e:r,l[1]=d;for(var p=2;p{a.r(t),a.d(t,{assets:()=>u,contentTitle:()=>o,default:()=>k,frontMatter:()=>d,metadata:()=>p,toc:()=>m});var n=a(7462),r=a(3366),i=(a(7294),a(3905)),l=["components"],d={id:"user-guide-advanced",title:"User Guide (advanced)",sidebar_label:"Advanced options"},o=void 0,p={unversionedId:"user-guide/user-guide-advanced",id:"user-guide/user-guide-advanced",title:"User Guide (advanced)",description:"There are some options to tweak the invitation link to unlock more features in",source:"@site/docs/user-guide/advanced.md",sourceDirName:"user-guide",slug:"/user-guide/user-guide-advanced",permalink:"/handbook/docs/user-guide/user-guide-advanced",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/user-guide/advanced.md",tags:[],version:"current",lastUpdatedAt:1694635654,formattedLastUpdatedAt:"Sep 13, 2023",frontMatter:{id:"user-guide-advanced",title:"User Guide (advanced)",sidebar_label:"Advanced options"},sidebar:"docs",previous:{title:"Basic options",permalink:"/handbook/docs/user-guide/user-guide-basic"},next:{title:"Developer Guide",permalink:"/handbook/docs/category/developer-guide"}},u={},m=[{value:"Invitations",id:"invitations",level:2},{value:"UI",id:"ui",level:2},{value:"Video",id:"video",level:2},{value:"Audio",id:"audio",level:2}],s={toc:m};function k(e){var t=e.components,a=(0,r.Z)(e,l);return(0,i.kt)("wrapper",(0,n.Z)({},s,a,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"There are some options to tweak the invitation link to unlock more features in\nJitsi. The following parameters apply to the web, iframe and mobile version."),(0,i.kt)("p",null,"All keys listed here are prefixed with ",(0,i.kt)("inlineCode",{parentName:"p"},"config."),".\nYou pick a key, combine it with its value using ",(0,i.kt)("inlineCode",{parentName:"p"},"=")," and link parameters\nwith ",(0,i.kt)("inlineCode",{parentName:"p"},"&"),", e.g. ",(0,i.kt)("inlineCode",{parentName:"p"},"#config.defaultLanguage=en&config.minParticipants=3"),"."),(0,i.kt)("h2",{id:"invitations"},"Invitations"),(0,i.kt)("p",null,"These parameters affect how you can invite people either before or within a session."),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:null},"Key"),(0,i.kt)("th",{parentName:"tr",align:null},"Value"),(0,i.kt)("th",{parentName:"tr",align:null},"Effect"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"disableInviteFunctions")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"true")),(0,i.kt)("td",{parentName:"tr",align:null},"disable invite function of the app")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"minParticipants")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"2")),(0,i.kt)("td",{parentName:"tr",align:null},"override the minimum number of participants before starting a call")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"prejoinConfig.enabled")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"true")),(0,i.kt)("td",{parentName:"tr",align:null},"show an intermediate page before joining to allow for adjustment of devices")))),(0,i.kt)("h2",{id:"ui"},"UI"),(0,i.kt)("p",null,"These parameters have an effect on the user interface."),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:null},"Key"),(0,i.kt)("th",{parentName:"tr",align:null},"Value"),(0,i.kt)("th",{parentName:"tr",align:null},"Effect"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"defaultLanguage")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"en")),(0,i.kt)("td",{parentName:"tr",align:null},"change the UI default language")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"disableThirdPartyRequests")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"true")),(0,i.kt)("td",{parentName:"tr",align:null},"generate avatars locally and disable callstats integration")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"enableDisplayNameInStats")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"true")),(0,i.kt)("td",{parentName:"tr",align:null},"send display names of participants to callstats")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"enableEmailInStats")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"true")),(0,i.kt)("td",{parentName:"tr",align:null},"send email (if available) to callstats and other analytics")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"enableInsecureRoomNameWarning")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"true")),(0,i.kt)("td",{parentName:"tr",align:null},"show a warning label if the room name is deemed insecure")))),(0,i.kt)("h2",{id:"video"},"Video"),(0,i.kt)("p",null,"Use these parameters to influence the video of a session."),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:null},"Key"),(0,i.kt)("th",{parentName:"tr",align:null},"Value"),(0,i.kt)("th",{parentName:"tr",align:null},"Effect"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"desktopSharingFrameRate.min")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"5")),(0,i.kt)("td",{parentName:"tr",align:null},"override the minimum framerate for desktop sharing")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"desktopSharingFrameRate.max")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"5")),(0,i.kt)("td",{parentName:"tr",align:null},"override the maximum framerate for desktop sharing")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"startWithVideoMuted")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"true")),(0,i.kt)("td",{parentName:"tr",align:null},"disable video when joining")))),(0,i.kt)("h2",{id:"audio"},"Audio"),(0,i.kt)("p",null,"Use these parameters to influence the audio of a session."),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:null},"Key"),(0,i.kt)("th",{parentName:"tr",align:null},"Value"),(0,i.kt)("th",{parentName:"tr",align:null},"Effect"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"disableAudioLevels")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"true")),(0,i.kt)("td",{parentName:"tr",align:null},"disable audio statistics polling (thereby perhaps improving performance)")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"disableRemoteMute")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"true")),(0,i.kt)("td",{parentName:"tr",align:null},"disable all muting operations of remote participants")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"startWithAudioMuted")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"true")),(0,i.kt)("td",{parentName:"tr",align:null},"turn off audio input when joining")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"startSilent")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"true")),(0,i.kt)("td",{parentName:"tr",align:null},"mute audio input and output")))))}k.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[8264],{3905:(e,t,a)=>{a.d(t,{Zo:()=>u,kt:()=>k});var n=a(7294);function r(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function i(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function l(e){for(var t=1;t=0||(r[a]=e[a]);return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(r[a]=e[a])}return r}var o=n.createContext({}),p=function(e){var t=n.useContext(o),a=t;return e&&(a="function"==typeof e?e(t):l(l({},t),e)),a},u=function(e){var t=p(e.components);return n.createElement(o.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},s=n.forwardRef((function(e,t){var a=e.components,r=e.mdxType,i=e.originalType,o=e.parentName,u=d(e,["components","mdxType","originalType","parentName"]),s=p(a),k=r,c=s["".concat(o,".").concat(k)]||s[k]||m[k]||i;return a?n.createElement(c,l(l({ref:t},u),{},{components:a})):n.createElement(c,l({ref:t},u))}));function k(e,t){var a=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var i=a.length,l=new Array(i);l[0]=s;var d={};for(var o in t)hasOwnProperty.call(t,o)&&(d[o]=t[o]);d.originalType=e,d.mdxType="string"==typeof e?e:r,l[1]=d;for(var p=2;p{a.r(t),a.d(t,{assets:()=>u,contentTitle:()=>o,default:()=>k,frontMatter:()=>d,metadata:()=>p,toc:()=>m});var n=a(7462),r=a(3366),i=(a(7294),a(3905)),l=["components"],d={id:"user-guide-advanced",title:"User Guide (advanced)",sidebar_label:"Advanced options"},o=void 0,p={unversionedId:"user-guide/user-guide-advanced",id:"user-guide/user-guide-advanced",title:"User Guide (advanced)",description:"There are some options to tweak the invitation link to unlock more features in",source:"@site/docs/user-guide/advanced.md",sourceDirName:"user-guide",slug:"/user-guide/user-guide-advanced",permalink:"/handbook/docs/user-guide/user-guide-advanced",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/user-guide/advanced.md",tags:[],version:"current",lastUpdatedAt:1695203964,formattedLastUpdatedAt:"Sep 20, 2023",frontMatter:{id:"user-guide-advanced",title:"User Guide (advanced)",sidebar_label:"Advanced options"},sidebar:"docs",previous:{title:"Basic options",permalink:"/handbook/docs/user-guide/user-guide-basic"},next:{title:"Developer Guide",permalink:"/handbook/docs/category/developer-guide"}},u={},m=[{value:"Invitations",id:"invitations",level:2},{value:"UI",id:"ui",level:2},{value:"Video",id:"video",level:2},{value:"Audio",id:"audio",level:2}],s={toc:m};function k(e){var t=e.components,a=(0,r.Z)(e,l);return(0,i.kt)("wrapper",(0,n.Z)({},s,a,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"There are some options to tweak the invitation link to unlock more features in\nJitsi. The following parameters apply to the web, iframe and mobile version."),(0,i.kt)("p",null,"All keys listed here are prefixed with ",(0,i.kt)("inlineCode",{parentName:"p"},"config."),".\nYou pick a key, combine it with its value using ",(0,i.kt)("inlineCode",{parentName:"p"},"=")," and link parameters\nwith ",(0,i.kt)("inlineCode",{parentName:"p"},"&"),", e.g. ",(0,i.kt)("inlineCode",{parentName:"p"},"#config.defaultLanguage=en&config.minParticipants=3"),"."),(0,i.kt)("h2",{id:"invitations"},"Invitations"),(0,i.kt)("p",null,"These parameters affect how you can invite people either before or within a session."),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:null},"Key"),(0,i.kt)("th",{parentName:"tr",align:null},"Value"),(0,i.kt)("th",{parentName:"tr",align:null},"Effect"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"disableInviteFunctions")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"true")),(0,i.kt)("td",{parentName:"tr",align:null},"disable invite function of the app")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"minParticipants")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"2")),(0,i.kt)("td",{parentName:"tr",align:null},"override the minimum number of participants before starting a call")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"prejoinConfig.enabled")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"true")),(0,i.kt)("td",{parentName:"tr",align:null},"show an intermediate page before joining to allow for adjustment of devices")))),(0,i.kt)("h2",{id:"ui"},"UI"),(0,i.kt)("p",null,"These parameters have an effect on the user interface."),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:null},"Key"),(0,i.kt)("th",{parentName:"tr",align:null},"Value"),(0,i.kt)("th",{parentName:"tr",align:null},"Effect"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"defaultLanguage")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"en")),(0,i.kt)("td",{parentName:"tr",align:null},"change the UI default language")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"disableThirdPartyRequests")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"true")),(0,i.kt)("td",{parentName:"tr",align:null},"generate avatars locally and disable callstats integration")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"enableDisplayNameInStats")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"true")),(0,i.kt)("td",{parentName:"tr",align:null},"send display names of participants to callstats")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"enableEmailInStats")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"true")),(0,i.kt)("td",{parentName:"tr",align:null},"send email (if available) to callstats and other analytics")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"enableInsecureRoomNameWarning")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"true")),(0,i.kt)("td",{parentName:"tr",align:null},"show a warning label if the room name is deemed insecure")))),(0,i.kt)("h2",{id:"video"},"Video"),(0,i.kt)("p",null,"Use these parameters to influence the video of a session."),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:null},"Key"),(0,i.kt)("th",{parentName:"tr",align:null},"Value"),(0,i.kt)("th",{parentName:"tr",align:null},"Effect"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"desktopSharingFrameRate.min")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"5")),(0,i.kt)("td",{parentName:"tr",align:null},"override the minimum framerate for desktop sharing")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"desktopSharingFrameRate.max")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"5")),(0,i.kt)("td",{parentName:"tr",align:null},"override the maximum framerate for desktop sharing")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"startWithVideoMuted")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"true")),(0,i.kt)("td",{parentName:"tr",align:null},"disable video when joining")))),(0,i.kt)("h2",{id:"audio"},"Audio"),(0,i.kt)("p",null,"Use these parameters to influence the audio of a session."),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:null},"Key"),(0,i.kt)("th",{parentName:"tr",align:null},"Value"),(0,i.kt)("th",{parentName:"tr",align:null},"Effect"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"disableAudioLevels")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"true")),(0,i.kt)("td",{parentName:"tr",align:null},"disable audio statistics polling (thereby perhaps improving performance)")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"disableRemoteMute")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"true")),(0,i.kt)("td",{parentName:"tr",align:null},"disable all muting operations of remote participants")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"startWithAudioMuted")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"true")),(0,i.kt)("td",{parentName:"tr",align:null},"turn off audio input when joining")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"startSilent")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"true")),(0,i.kt)("td",{parentName:"tr",align:null},"mute audio input and output")))))}k.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/4835d996.3480d8e4.js b/assets/js/4835d996.6f27efad.js similarity index 98% rename from assets/js/4835d996.3480d8e4.js rename to assets/js/4835d996.6f27efad.js index cd4d84678..2af07a584 100644 --- a/assets/js/4835d996.3480d8e4.js +++ b/assets/js/4835d996.6f27efad.js @@ -1 +1 @@ -"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[133],{3905:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>u});var i=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function o(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var l=i.createContext({}),s=function(e){var t=i.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},d=function(e){var t=s(e.components);return i.createElement(l.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},c=i.forwardRef((function(e,t){var n=e.components,a=e.mdxType,r=e.originalType,l=e.parentName,d=p(e,["components","mdxType","originalType","parentName"]),c=s(n),u=a,h=c["".concat(l,".").concat(u)]||c[u]||m[u]||r;return n?i.createElement(h,o(o({ref:t},d),{},{components:n})):i.createElement(h,o({ref:t},d))}));function u(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var r=n.length,o=new Array(r);o[0]=c;var p={};for(var l in t)hasOwnProperty.call(t,l)&&(p[l]=t[l]);p.originalType=e,p.mdxType="string"==typeof e?e:a,o[1]=p;for(var s=2;s{n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>l,default:()=>u,frontMatter:()=>p,metadata:()=>s,toc:()=>m});var i=n(7462),a=n(3366),r=(n(7294),n(3905)),o=["components"],p={id:"dev-guide-react-sdk",title:"React SDK"},l=void 0,s={unversionedId:"dev-guide/dev-guide-react-sdk",id:"dev-guide/dev-guide-react-sdk",title:"React SDK",description:"The Jitsi Meet React SDK provides the same user experience as the Jitsi Meet app, in a customizable way which you can embed in your apps.",source:"@site/docs/dev-guide/react-sdk.md",sourceDirName:"dev-guide",slug:"/dev-guide/dev-guide-react-sdk",permalink:"/handbook/docs/dev-guide/dev-guide-react-sdk",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/dev-guide/react-sdk.md",tags:[],version:"current",lastUpdatedAt:1694635654,formattedLastUpdatedAt:"Sep 13, 2023",frontMatter:{id:"dev-guide-react-sdk",title:"React SDK"},sidebar:"docs",previous:{title:"lib-jitsi-meet API (low level)",permalink:"/handbook/docs/dev-guide/dev-guide-ljm-api"},next:{title:"Android SDK",permalink:"/handbook/docs/dev-guide/dev-guide-android-sdk"}},d={},m=[{value:"Sample application using the SDK",id:"sample-application-using-the-sdk",level:2},{value:"Installation",id:"installation",level:2},{value:"Modules",id:"modules",level:2},{value:"JitsiMeeting",id:"jitsimeeting",level:3},{value:"Properties specific to the JitsiMeeting component",id:"properties-specific-to-the-jitsimeeting-component",level:4},{value:"JaaSMeeting",id:"jaasmeeting",level:3},{value:"Properties specific to the JaaSMeeting component",id:"properties-specific-to-the-jaasmeeting-component",level:4},{value:"Common properties",id:"common-properties",level:2}],c={toc:m};function u(e){var t=e.components,n=(0,a.Z)(e,o);return(0,r.kt)("wrapper",(0,i.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("p",null,"The Jitsi Meet React SDK provides the same user experience as the Jitsi Meet app, in a customizable way which you can embed in your apps."),(0,r.kt)("admonition",{type:"important"},(0,r.kt)("p",{parentName:"admonition"},"React 16 or higher is required.")),(0,r.kt)("h2",{id:"sample-application-using-the-sdk"},"Sample application using the SDK"),(0,r.kt)("p",null,"If you want to see how easy integrating the Jitsi Meet React SDK into a React application is, take a look at our ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet-react-sdk/tree/main/example"},"example"),"."),(0,r.kt)("h2",{id:"installation"},"Installation"),(0,r.kt)("p",null,"To access the React SDK modules in your application you need to install it as a dependency:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"npm install @jitsi/react-sdk\n")),(0,r.kt)("h2",{id:"modules"},"Modules"),(0,r.kt)("p",null,"The SDK exposes two components with similar properties, intended for different use-cases."),(0,r.kt)("h3",{id:"jitsimeeting"},"JitsiMeeting"),(0,r.kt)("p",null,"To be used with custom domains as-it-is in React projects:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-jsx"}," {\n // here you can attach custom event listeners to the Jitsi Meet External API\n // you can also store it locally to execute commands\n } }\n getIFrameRef = { (iframeRef) => { iframeRef.style.height = '400px'; } }\n/>\n")),(0,r.kt)("h4",{id:"properties-specific-to-the-jitsimeeting-component"},"Properties specific to the ",(0,r.kt)("inlineCode",{parentName:"h4"},"JitsiMeeting")," component"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"domain"),": Optional. Field used to retrieve the external_api.js file that initializes the IFrame. If omitted, defaults to ",(0,r.kt)("inlineCode",{parentName:"li"},"meet.jit.si"),".")),(0,r.kt)("h3",{id:"jaasmeeting"},"JaaSMeeting"),(0,r.kt)("p",null,"To be used with the ",(0,r.kt)("inlineCode",{parentName:"p"},"8x8.vc")," domain as-it-is in React projects:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-jsx"}," { ... } }\n/>\n")),(0,r.kt)("p",null,"...or with the ",(0,r.kt)("inlineCode",{parentName:"p"},"stage.8x8.vc")," domain:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'\n')),(0,r.kt)("h4",{id:"properties-specific-to-the-jaasmeeting-component"},"Properties specific to the ",(0,r.kt)("inlineCode",{parentName:"h4"},"JaaSMeeting")," component"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"appId"),": Required. Provides an isolated context and prefixes the room name."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"useStaging"),": Optional. Tells whether to use the staging environment or not.")),(0,r.kt)("h2",{id:"common-properties"},"Common properties"),(0,r.kt)("p",null,"The component modules support a similar kind of customization to the Jitsi Meet IFrame. The following properties can be passed down to your instances of ",(0,r.kt)("inlineCode",{parentName:"p"},"JitsiMeeting")," or ",(0,r.kt)("inlineCode",{parentName:"p"},"JaaSMeeting"),"."),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},(0,r.kt)("inlineCode",{parentName:"p"},"roomName"),": Required. The name of the room to join.")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},(0,r.kt)("inlineCode",{parentName:"p"},"configOverwrite"),": Optional. The JS object with overrides for options defined in the ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet/blob/master/config.js"},"config.js")," file.")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},(0,r.kt)("inlineCode",{parentName:"p"},"interfaceConfigOverwrite"),": Optional. The JS object with overrides for options defined in the ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet/blob/master/interface_config.js"},"interface_config.js")," file.")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},(0,r.kt)("inlineCode",{parentName:"p"},"jwt"),": Optional. The ",(0,r.kt)("a",{parentName:"p",href:"https://jwt.io/"},"JWT")," token.")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},(0,r.kt)("inlineCode",{parentName:"p"},"invitees"),": Optional. Object arrays that contain information about participants invited to a call.")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},(0,r.kt)("inlineCode",{parentName:"p"},"devices"),": Optional. Information map about the devices used in a call.")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},(0,r.kt)("inlineCode",{parentName:"p"},"userInfo"),": Optional. The JS object that contains information about the participant starting or joining the meeting (e.g., email).")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},(0,r.kt)("inlineCode",{parentName:"p"},"release"),": Optional. Information regarding the ",(0,r.kt)("inlineCode",{parentName:"p"},"stage.8x8.vc")," or ",(0,r.kt)("inlineCode",{parentName:"p"},"8x8.vc")," release version. Expects the following format: ",(0,r.kt)("inlineCode",{parentName:"p"},"release-1234"),".")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},(0,r.kt)("inlineCode",{parentName:"p"},"spinner"),": Optional. The custom spinner to be displayed while the IFrame is loading.")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},(0,r.kt)("inlineCode",{parentName:"p"},"onApiReady"),": Optional. The external API reference for events and commands.")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},(0,r.kt)("inlineCode",{parentName:"p"},"onReadyToClose"),": Optional. The callback for when the meeting is ready to be closed.")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},(0,r.kt)("inlineCode",{parentName:"p"},"getIFrameRef"),": Optional. The parent node used by the IFrame."))))}u.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[133],{3905:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>u});var i=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function o(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var l=i.createContext({}),s=function(e){var t=i.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},d=function(e){var t=s(e.components);return i.createElement(l.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},c=i.forwardRef((function(e,t){var n=e.components,a=e.mdxType,r=e.originalType,l=e.parentName,d=p(e,["components","mdxType","originalType","parentName"]),c=s(n),u=a,h=c["".concat(l,".").concat(u)]||c[u]||m[u]||r;return n?i.createElement(h,o(o({ref:t},d),{},{components:n})):i.createElement(h,o({ref:t},d))}));function u(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var r=n.length,o=new Array(r);o[0]=c;var p={};for(var l in t)hasOwnProperty.call(t,l)&&(p[l]=t[l]);p.originalType=e,p.mdxType="string"==typeof e?e:a,o[1]=p;for(var s=2;s{n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>l,default:()=>u,frontMatter:()=>p,metadata:()=>s,toc:()=>m});var i=n(7462),a=n(3366),r=(n(7294),n(3905)),o=["components"],p={id:"dev-guide-react-sdk",title:"React SDK"},l=void 0,s={unversionedId:"dev-guide/dev-guide-react-sdk",id:"dev-guide/dev-guide-react-sdk",title:"React SDK",description:"The Jitsi Meet React SDK provides the same user experience as the Jitsi Meet app, in a customizable way which you can embed in your apps.",source:"@site/docs/dev-guide/react-sdk.md",sourceDirName:"dev-guide",slug:"/dev-guide/dev-guide-react-sdk",permalink:"/handbook/docs/dev-guide/dev-guide-react-sdk",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/dev-guide/react-sdk.md",tags:[],version:"current",lastUpdatedAt:1695203964,formattedLastUpdatedAt:"Sep 20, 2023",frontMatter:{id:"dev-guide-react-sdk",title:"React SDK"},sidebar:"docs",previous:{title:"lib-jitsi-meet API (low level)",permalink:"/handbook/docs/dev-guide/dev-guide-ljm-api"},next:{title:"Android SDK",permalink:"/handbook/docs/dev-guide/dev-guide-android-sdk"}},d={},m=[{value:"Sample application using the SDK",id:"sample-application-using-the-sdk",level:2},{value:"Installation",id:"installation",level:2},{value:"Modules",id:"modules",level:2},{value:"JitsiMeeting",id:"jitsimeeting",level:3},{value:"Properties specific to the JitsiMeeting component",id:"properties-specific-to-the-jitsimeeting-component",level:4},{value:"JaaSMeeting",id:"jaasmeeting",level:3},{value:"Properties specific to the JaaSMeeting component",id:"properties-specific-to-the-jaasmeeting-component",level:4},{value:"Common properties",id:"common-properties",level:2}],c={toc:m};function u(e){var t=e.components,n=(0,a.Z)(e,o);return(0,r.kt)("wrapper",(0,i.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("p",null,"The Jitsi Meet React SDK provides the same user experience as the Jitsi Meet app, in a customizable way which you can embed in your apps."),(0,r.kt)("admonition",{type:"important"},(0,r.kt)("p",{parentName:"admonition"},"React 16 or higher is required.")),(0,r.kt)("h2",{id:"sample-application-using-the-sdk"},"Sample application using the SDK"),(0,r.kt)("p",null,"If you want to see how easy integrating the Jitsi Meet React SDK into a React application is, take a look at our ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet-react-sdk/tree/main/example"},"example"),"."),(0,r.kt)("h2",{id:"installation"},"Installation"),(0,r.kt)("p",null,"To access the React SDK modules in your application you need to install it as a dependency:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"npm install @jitsi/react-sdk\n")),(0,r.kt)("h2",{id:"modules"},"Modules"),(0,r.kt)("p",null,"The SDK exposes two components with similar properties, intended for different use-cases."),(0,r.kt)("h3",{id:"jitsimeeting"},"JitsiMeeting"),(0,r.kt)("p",null,"To be used with custom domains as-it-is in React projects:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-jsx"}," {\n // here you can attach custom event listeners to the Jitsi Meet External API\n // you can also store it locally to execute commands\n } }\n getIFrameRef = { (iframeRef) => { iframeRef.style.height = '400px'; } }\n/>\n")),(0,r.kt)("h4",{id:"properties-specific-to-the-jitsimeeting-component"},"Properties specific to the ",(0,r.kt)("inlineCode",{parentName:"h4"},"JitsiMeeting")," component"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"domain"),": Optional. Field used to retrieve the external_api.js file that initializes the IFrame. If omitted, defaults to ",(0,r.kt)("inlineCode",{parentName:"li"},"meet.jit.si"),".")),(0,r.kt)("h3",{id:"jaasmeeting"},"JaaSMeeting"),(0,r.kt)("p",null,"To be used with the ",(0,r.kt)("inlineCode",{parentName:"p"},"8x8.vc")," domain as-it-is in React projects:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-jsx"}," { ... } }\n/>\n")),(0,r.kt)("p",null,"...or with the ",(0,r.kt)("inlineCode",{parentName:"p"},"stage.8x8.vc")," domain:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'\n')),(0,r.kt)("h4",{id:"properties-specific-to-the-jaasmeeting-component"},"Properties specific to the ",(0,r.kt)("inlineCode",{parentName:"h4"},"JaaSMeeting")," component"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"appId"),": Required. Provides an isolated context and prefixes the room name."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"useStaging"),": Optional. Tells whether to use the staging environment or not.")),(0,r.kt)("h2",{id:"common-properties"},"Common properties"),(0,r.kt)("p",null,"The component modules support a similar kind of customization to the Jitsi Meet IFrame. The following properties can be passed down to your instances of ",(0,r.kt)("inlineCode",{parentName:"p"},"JitsiMeeting")," or ",(0,r.kt)("inlineCode",{parentName:"p"},"JaaSMeeting"),"."),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},(0,r.kt)("inlineCode",{parentName:"p"},"roomName"),": Required. The name of the room to join.")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},(0,r.kt)("inlineCode",{parentName:"p"},"configOverwrite"),": Optional. The JS object with overrides for options defined in the ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet/blob/master/config.js"},"config.js")," file.")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},(0,r.kt)("inlineCode",{parentName:"p"},"interfaceConfigOverwrite"),": Optional. The JS object with overrides for options defined in the ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet/blob/master/interface_config.js"},"interface_config.js")," file.")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},(0,r.kt)("inlineCode",{parentName:"p"},"jwt"),": Optional. The ",(0,r.kt)("a",{parentName:"p",href:"https://jwt.io/"},"JWT")," token.")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},(0,r.kt)("inlineCode",{parentName:"p"},"invitees"),": Optional. Object arrays that contain information about participants invited to a call.")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},(0,r.kt)("inlineCode",{parentName:"p"},"devices"),": Optional. Information map about the devices used in a call.")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},(0,r.kt)("inlineCode",{parentName:"p"},"userInfo"),": Optional. The JS object that contains information about the participant starting or joining the meeting (e.g., email).")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},(0,r.kt)("inlineCode",{parentName:"p"},"release"),": Optional. Information regarding the ",(0,r.kt)("inlineCode",{parentName:"p"},"stage.8x8.vc")," or ",(0,r.kt)("inlineCode",{parentName:"p"},"8x8.vc")," release version. Expects the following format: ",(0,r.kt)("inlineCode",{parentName:"p"},"release-1234"),".")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},(0,r.kt)("inlineCode",{parentName:"p"},"spinner"),": Optional. The custom spinner to be displayed while the IFrame is loading.")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},(0,r.kt)("inlineCode",{parentName:"p"},"onApiReady"),": Optional. The external API reference for events and commands.")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},(0,r.kt)("inlineCode",{parentName:"p"},"onReadyToClose"),": Optional. The callback for when the meeting is ready to be closed.")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},(0,r.kt)("inlineCode",{parentName:"p"},"getIFrameRef"),": Optional. The parent node used by the IFrame."))))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/4a0a1b35.2f89a216.js b/assets/js/4a0a1b35.6673b410.js similarity index 98% rename from assets/js/4a0a1b35.2f89a216.js rename to assets/js/4a0a1b35.6673b410.js index 518a0da4d..594d1851a 100644 --- a/assets/js/4a0a1b35.2f89a216.js +++ b/assets/js/4a0a1b35.6673b410.js @@ -1 +1 @@ -"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[7711],{3905:(e,t,r)=>{r.d(t,{Zo:()=>c,kt:()=>p});var n=r(7294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var s=n.createContext({}),u=function(e){var t=n.useContext(s),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},c=function(e){var t=u(e.components);return n.createElement(s.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},f=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,s=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),f=u(r),p=o,m=f["".concat(s,".").concat(p)]||f[p]||d[p]||a;return r?n.createElement(m,i(i({ref:t},c),{},{components:r})):n.createElement(m,i({ref:t},c))}));function p(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,i=new Array(a);i[0]=f;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:o,i[1]=l;for(var u=2;u{r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>s,default:()=>p,frontMatter:()=>l,metadata:()=>u,toc:()=>d});var n=r(7462),o=r(3366),a=(r(7294),r(3905)),i=["components"],l={id:"mobile-feature-flags",title:"Feature flags"},s=void 0,u={unversionedId:"dev-guide/mobile-feature-flags",id:"dev-guide/mobile-feature-flags",title:"Feature flags",description:"The mobile SDK supports a number of feature flags which allow for customizing certain",source:"@site/docs/dev-guide/mobile-feature-flags.md",sourceDirName:"dev-guide",slug:"/dev-guide/mobile-feature-flags",permalink:"/handbook/docs/dev-guide/mobile-feature-flags",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/dev-guide/mobile-feature-flags.md",tags:[],version:"current",lastUpdatedAt:1694635654,formattedLastUpdatedAt:"Sep 13, 2023",frontMatter:{id:"mobile-feature-flags",title:"Feature flags"},sidebar:"docs",previous:{title:"Jitsi Meet development",permalink:"/handbook/docs/dev-guide/dev-guide-mobile-jitsi-meet"},next:{title:"Android SDK",permalink:"/handbook/docs/dev-guide/dev-guide-android-sdk"}},c={},d=[],f={toc:d};function p(e){var t=e.components,r=(0,o.Z)(e,i);return(0,a.kt)("wrapper",(0,n.Z)({},f,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("p",null,"The mobile SDK supports a number of feature flags which allow for customizing certain\nUI aspects and behavior."),(0,a.kt)("p",null,"All flags are defined ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet/blob/master/react/features/base/flags/constants.ts"},"here"),"."))}p.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[7711],{3905:(e,t,r)=>{r.d(t,{Zo:()=>c,kt:()=>p});var n=r(7294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var s=n.createContext({}),u=function(e){var t=n.useContext(s),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},c=function(e){var t=u(e.components);return n.createElement(s.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},f=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,s=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),f=u(r),p=o,m=f["".concat(s,".").concat(p)]||f[p]||d[p]||a;return r?n.createElement(m,i(i({ref:t},c),{},{components:r})):n.createElement(m,i({ref:t},c))}));function p(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,i=new Array(a);i[0]=f;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:o,i[1]=l;for(var u=2;u{r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>s,default:()=>p,frontMatter:()=>l,metadata:()=>u,toc:()=>d});var n=r(7462),o=r(3366),a=(r(7294),r(3905)),i=["components"],l={id:"mobile-feature-flags",title:"Feature flags"},s=void 0,u={unversionedId:"dev-guide/mobile-feature-flags",id:"dev-guide/mobile-feature-flags",title:"Feature flags",description:"The mobile SDK supports a number of feature flags which allow for customizing certain",source:"@site/docs/dev-guide/mobile-feature-flags.md",sourceDirName:"dev-guide",slug:"/dev-guide/mobile-feature-flags",permalink:"/handbook/docs/dev-guide/mobile-feature-flags",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/dev-guide/mobile-feature-flags.md",tags:[],version:"current",lastUpdatedAt:1695203964,formattedLastUpdatedAt:"Sep 20, 2023",frontMatter:{id:"mobile-feature-flags",title:"Feature flags"},sidebar:"docs",previous:{title:"Jitsi Meet development",permalink:"/handbook/docs/dev-guide/dev-guide-mobile-jitsi-meet"},next:{title:"Android SDK",permalink:"/handbook/docs/dev-guide/dev-guide-android-sdk"}},c={},d=[],f={toc:d};function p(e){var t=e.components,r=(0,o.Z)(e,i);return(0,a.kt)("wrapper",(0,n.Z)({},f,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("p",null,"The mobile SDK supports a number of feature flags which allow for customizing certain\nUI aspects and behavior."),(0,a.kt)("p",null,"All flags are defined ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet/blob/master/react/features/base/flags/constants.ts"},"here"),"."))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/4d66996a.d0d62256.js b/assets/js/4d66996a.9c489270.js similarity index 99% rename from assets/js/4d66996a.d0d62256.js rename to assets/js/4d66996a.9c489270.js index bec31a74f..e59f09ebf 100644 --- a/assets/js/4d66996a.d0d62256.js +++ b/assets/js/4d66996a.9c489270.js @@ -1 +1 @@ -"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[7805],{3905:(e,t,a)=>{a.d(t,{Zo:()=>d,kt:()=>u});var r=a(7294);function n(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function i(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,r)}return a}function s(e){for(var t=1;t=0||(n[a]=e[a]);return n}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(n[a]=e[a])}return n}var l=r.createContext({}),p=function(e){var t=r.useContext(l),a=t;return e&&(a="function"==typeof e?e(t):s(s({},t),e)),a},d=function(e){var t=p(e.components);return r.createElement(l.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},c=r.forwardRef((function(e,t){var a=e.components,n=e.mdxType,i=e.originalType,l=e.parentName,d=o(e,["components","mdxType","originalType","parentName"]),c=p(a),u=n,k=c["".concat(l,".").concat(u)]||c[u]||m[u]||i;return a?r.createElement(k,s(s({ref:t},d),{},{components:a})):r.createElement(k,s({ref:t},d))}));function u(e,t){var a=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var i=a.length,s=new Array(i);s[0]=c;var o={};for(var l in t)hasOwnProperty.call(t,l)&&(o[l]=t[l]);o.originalType=e,o.mdxType="string"==typeof e?e:n,s[1]=o;for(var p=2;p{a.r(t),a.d(t,{assets:()=>d,contentTitle:()=>l,default:()=>u,frontMatter:()=>o,metadata:()=>p,toc:()=>m});var r=a(7462),n=a(3366),i=(a(7294),a(3905)),s=["components"],o={id:"releases",title:"Releases"},l=void 0,p={unversionedId:"releases",id:"releases",title:"Releases",description:"Release notes for Jitsi Meet are kept here.",source:"@site/docs/releases.md",sourceDirName:".",slug:"/releases",permalink:"/handbook/docs/releases",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/releases.md",tags:[],version:"current",lastUpdatedAt:1694635654,formattedLastUpdatedAt:"Sep 13, 2023",frontMatter:{id:"releases",title:"Releases"},sidebar:"releases-sidebar"},d={},m=[{value:"Mobile apps",id:"mobile-apps",level:2},{value:"Beta versions",id:"beta-versions",level:3},{value:"Desktop apps",id:"desktop-apps",level:2},{value:"Mobile SDKs",id:"mobile-sdks",level:2},{value:"Docker images",id:"docker-images",level:2},{value:"Debian/Ubuntu packages",id:"debianubuntu-packages",level:2},{value:"Web frontend",id:"web-frontend",level:2}],c={toc:m};function u(e){var t=e.components,a=(0,n.Z)(e,s);return(0,i.kt)("wrapper",(0,r.Z)({},c,a,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("admonition",{type:"tip"},(0,i.kt)("p",{parentName:"admonition"},"Release notes for Jitsi Meet are kept ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet-release-notes"},"here"),".")),(0,i.kt)("h2",{id:"mobile-apps"},"Mobile apps"),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:"center"},"Android"),(0,i.kt)("th",{parentName:"tr",align:"center"},"Android (F-Droid)"),(0,i.kt)("th",{parentName:"tr",align:"center"},"iOS"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:"center"},(0,i.kt)("a",{parentName:"td",href:"https://play.google.com/store/apps/details?id=org.jitsi.meet"},(0,i.kt)("img",{src:"https://raw.githubusercontent.com/jitsi/handbook/master/docs/assets/google-play-badge.png",height:"50"}))),(0,i.kt)("td",{parentName:"tr",align:"center"},(0,i.kt)("a",{parentName:"td",href:"https://f-droid.org/en/packages/org.jitsi.meet/"},(0,i.kt)("img",{src:"https://raw.githubusercontent.com/jitsi/handbook/master/docs/assets/f-droid-badge.png",height:"50"}))),(0,i.kt)("td",{parentName:"tr",align:"center"},(0,i.kt)("a",{parentName:"td",href:"https://itunes.apple.com/us/app/jitsi-meet/id1165103905"},(0,i.kt)("img",{src:"https://raw.githubusercontent.com/jitsi/handbook/master/docs/assets/appstore-badge.png",height:"50"})))))),(0,i.kt)("h3",{id:"beta-versions"},"Beta versions"),(0,i.kt)("p",null,"If you are feeling adventurous and want to get an early scoop of the features as they are being\ndeveloped you can also sign up for our open beta testing here:"),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:"center"},"Android"),(0,i.kt)("th",{parentName:"tr",align:"center"},"iOS"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:"center"},(0,i.kt)("a",{parentName:"td",href:"https://play.google.com/apps/testing/org.jitsi.meet"},"Play Store Beta")),(0,i.kt)("td",{parentName:"tr",align:"center"},(0,i.kt)("a",{parentName:"td",href:"https://testflight.apple.com/join/isy6ja7S"},"TestFlight"))))),(0,i.kt)("h2",{id:"desktop-apps"},"Desktop apps"),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:"center"},"Windows"),(0,i.kt)("th",{parentName:"tr",align:"center"},"macOS"),(0,i.kt)("th",{parentName:"tr",align:"center"},"GNU/Linux (AppImage)"),(0,i.kt)("th",{parentName:"tr",align:"center"},"GNU/Linux (Deb)"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:"center"},(0,i.kt)("a",{parentName:"td",href:"https://github.com/jitsi/jitsi-meet-electron/releases/latest/download/jitsi-meet.exe"},"Download")),(0,i.kt)("td",{parentName:"tr",align:"center"},(0,i.kt)("a",{parentName:"td",href:"https://github.com/jitsi/jitsi-meet-electron/releases/latest/download/jitsi-meet.dmg"},"Download")),(0,i.kt)("td",{parentName:"tr",align:"center"},(0,i.kt)("a",{parentName:"td",href:"https://github.com/jitsi/jitsi-meet-electron/releases/latest/download/jitsi-meet-x86_64.AppImage"},"Download")),(0,i.kt)("td",{parentName:"tr",align:"center"},(0,i.kt)("a",{parentName:"td",href:"https://github.com/jitsi/jitsi-meet-electron/releases/latest/download/jitsi-meet-amd64.deb"},"Download"))))),(0,i.kt)("p",null,"The desktop applications are based on Electron. For macOS it is also available as a ",(0,i.kt)("inlineCode",{parentName:"p"},"brew")," cask which can be installed using ",(0,i.kt)("inlineCode",{parentName:"p"},"brew install jitsi-meet"),"."),(0,i.kt)("h2",{id:"mobile-sdks"},"Mobile SDKs"),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:"center"},"Android"),(0,i.kt)("th",{parentName:"tr",align:"center"},"iOS"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:"center"},(0,i.kt)("a",{parentName:"td",href:"https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-android-sdk#use-pre-build-sdk-artifactsbinaries"},"Maven repository")),(0,i.kt)("td",{parentName:"tr",align:"center"},(0,i.kt)("a",{parentName:"td",href:"https://cocoapods.org/pods/JitsiMeetSDK"},"CocoaPods"))))),(0,i.kt)("h2",{id:"docker-images"},"Docker images"),(0,i.kt)("p",null,"See the Docker image releases ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/jitsi/docker-jitsi-meet/releases"},"here"),"."),(0,i.kt)("h2",{id:"debianubuntu-packages"},"Debian/Ubuntu packages"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://download.jitsi.org/stable/"},(0,i.kt)("inlineCode",{parentName:"a"},"stable"))," (",(0,i.kt)("a",{parentName:"li",href:"https://jitsi.org/downloads/ubuntu-debian-installations-instructions/"},"instructions"),")"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://download.jitsi.org/testing/"},(0,i.kt)("inlineCode",{parentName:"a"},"testing"))," (",(0,i.kt)("a",{parentName:"li",href:"https://jitsi.org/downloads/ubuntu-debian-installations-instructions-for-testing/"},"instructions"),")"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://download.jitsi.org/unstable/"},(0,i.kt)("inlineCode",{parentName:"a"},"nightly"))," (",(0,i.kt)("a",{parentName:"li",href:"https://jitsi.org/downloads/ubuntu-debian-installations-instructions-nightly/"},"instructions"),")")),(0,i.kt)("h2",{id:"web-frontend"},"Web frontend"),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:null},"Latest stable release"),(0,i.kt)("th",{parentName:"tr",align:null},(0,i.kt)("a",{parentName:"th",href:"https://github.com/jitsi/jitsi-meet/releases/latest"},(0,i.kt)("img",{parentName:"a",src:"https://img.shields.io/badge/release-latest-green.svg",alt:"release"})))))),(0,i.kt)("p",null,"Prebuilt ",(0,i.kt)("a",{parentName:"p",href:"https://download.jitsi.org/jitsi-meet/src/"},"source builds")," are also available."),(0,i.kt)("admonition",{type:"note"},(0,i.kt)("p",{parentName:"admonition"},"Generally, you won't need to download this, as it's part of the Debian packages and Docker images already.")))}u.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[7805],{3905:(e,t,a)=>{a.d(t,{Zo:()=>d,kt:()=>u});var r=a(7294);function n(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function i(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,r)}return a}function s(e){for(var t=1;t=0||(n[a]=e[a]);return n}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(n[a]=e[a])}return n}var l=r.createContext({}),p=function(e){var t=r.useContext(l),a=t;return e&&(a="function"==typeof e?e(t):s(s({},t),e)),a},d=function(e){var t=p(e.components);return r.createElement(l.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},c=r.forwardRef((function(e,t){var a=e.components,n=e.mdxType,i=e.originalType,l=e.parentName,d=o(e,["components","mdxType","originalType","parentName"]),c=p(a),u=n,k=c["".concat(l,".").concat(u)]||c[u]||m[u]||i;return a?r.createElement(k,s(s({ref:t},d),{},{components:a})):r.createElement(k,s({ref:t},d))}));function u(e,t){var a=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var i=a.length,s=new Array(i);s[0]=c;var o={};for(var l in t)hasOwnProperty.call(t,l)&&(o[l]=t[l]);o.originalType=e,o.mdxType="string"==typeof e?e:n,s[1]=o;for(var p=2;p{a.r(t),a.d(t,{assets:()=>d,contentTitle:()=>l,default:()=>u,frontMatter:()=>o,metadata:()=>p,toc:()=>m});var r=a(7462),n=a(3366),i=(a(7294),a(3905)),s=["components"],o={id:"releases",title:"Releases"},l=void 0,p={unversionedId:"releases",id:"releases",title:"Releases",description:"Release notes for Jitsi Meet are kept here.",source:"@site/docs/releases.md",sourceDirName:".",slug:"/releases",permalink:"/handbook/docs/releases",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/releases.md",tags:[],version:"current",lastUpdatedAt:1695203964,formattedLastUpdatedAt:"Sep 20, 2023",frontMatter:{id:"releases",title:"Releases"},sidebar:"releases-sidebar"},d={},m=[{value:"Mobile apps",id:"mobile-apps",level:2},{value:"Beta versions",id:"beta-versions",level:3},{value:"Desktop apps",id:"desktop-apps",level:2},{value:"Mobile SDKs",id:"mobile-sdks",level:2},{value:"Docker images",id:"docker-images",level:2},{value:"Debian/Ubuntu packages",id:"debianubuntu-packages",level:2},{value:"Web frontend",id:"web-frontend",level:2}],c={toc:m};function u(e){var t=e.components,a=(0,n.Z)(e,s);return(0,i.kt)("wrapper",(0,r.Z)({},c,a,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("admonition",{type:"tip"},(0,i.kt)("p",{parentName:"admonition"},"Release notes for Jitsi Meet are kept ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet-release-notes"},"here"),".")),(0,i.kt)("h2",{id:"mobile-apps"},"Mobile apps"),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:"center"},"Android"),(0,i.kt)("th",{parentName:"tr",align:"center"},"Android (F-Droid)"),(0,i.kt)("th",{parentName:"tr",align:"center"},"iOS"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:"center"},(0,i.kt)("a",{parentName:"td",href:"https://play.google.com/store/apps/details?id=org.jitsi.meet"},(0,i.kt)("img",{src:"https://raw.githubusercontent.com/jitsi/handbook/master/docs/assets/google-play-badge.png",height:"50"}))),(0,i.kt)("td",{parentName:"tr",align:"center"},(0,i.kt)("a",{parentName:"td",href:"https://f-droid.org/en/packages/org.jitsi.meet/"},(0,i.kt)("img",{src:"https://raw.githubusercontent.com/jitsi/handbook/master/docs/assets/f-droid-badge.png",height:"50"}))),(0,i.kt)("td",{parentName:"tr",align:"center"},(0,i.kt)("a",{parentName:"td",href:"https://itunes.apple.com/us/app/jitsi-meet/id1165103905"},(0,i.kt)("img",{src:"https://raw.githubusercontent.com/jitsi/handbook/master/docs/assets/appstore-badge.png",height:"50"})))))),(0,i.kt)("h3",{id:"beta-versions"},"Beta versions"),(0,i.kt)("p",null,"If you are feeling adventurous and want to get an early scoop of the features as they are being\ndeveloped you can also sign up for our open beta testing here:"),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:"center"},"Android"),(0,i.kt)("th",{parentName:"tr",align:"center"},"iOS"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:"center"},(0,i.kt)("a",{parentName:"td",href:"https://play.google.com/apps/testing/org.jitsi.meet"},"Play Store Beta")),(0,i.kt)("td",{parentName:"tr",align:"center"},(0,i.kt)("a",{parentName:"td",href:"https://testflight.apple.com/join/isy6ja7S"},"TestFlight"))))),(0,i.kt)("h2",{id:"desktop-apps"},"Desktop apps"),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:"center"},"Windows"),(0,i.kt)("th",{parentName:"tr",align:"center"},"macOS"),(0,i.kt)("th",{parentName:"tr",align:"center"},"GNU/Linux (AppImage)"),(0,i.kt)("th",{parentName:"tr",align:"center"},"GNU/Linux (Deb)"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:"center"},(0,i.kt)("a",{parentName:"td",href:"https://github.com/jitsi/jitsi-meet-electron/releases/latest/download/jitsi-meet.exe"},"Download")),(0,i.kt)("td",{parentName:"tr",align:"center"},(0,i.kt)("a",{parentName:"td",href:"https://github.com/jitsi/jitsi-meet-electron/releases/latest/download/jitsi-meet.dmg"},"Download")),(0,i.kt)("td",{parentName:"tr",align:"center"},(0,i.kt)("a",{parentName:"td",href:"https://github.com/jitsi/jitsi-meet-electron/releases/latest/download/jitsi-meet-x86_64.AppImage"},"Download")),(0,i.kt)("td",{parentName:"tr",align:"center"},(0,i.kt)("a",{parentName:"td",href:"https://github.com/jitsi/jitsi-meet-electron/releases/latest/download/jitsi-meet-amd64.deb"},"Download"))))),(0,i.kt)("p",null,"The desktop applications are based on Electron. For macOS it is also available as a ",(0,i.kt)("inlineCode",{parentName:"p"},"brew")," cask which can be installed using ",(0,i.kt)("inlineCode",{parentName:"p"},"brew install jitsi-meet"),"."),(0,i.kt)("h2",{id:"mobile-sdks"},"Mobile SDKs"),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:"center"},"Android"),(0,i.kt)("th",{parentName:"tr",align:"center"},"iOS"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:"center"},(0,i.kt)("a",{parentName:"td",href:"https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-android-sdk#use-pre-build-sdk-artifactsbinaries"},"Maven repository")),(0,i.kt)("td",{parentName:"tr",align:"center"},(0,i.kt)("a",{parentName:"td",href:"https://cocoapods.org/pods/JitsiMeetSDK"},"CocoaPods"))))),(0,i.kt)("h2",{id:"docker-images"},"Docker images"),(0,i.kt)("p",null,"See the Docker image releases ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/jitsi/docker-jitsi-meet/releases"},"here"),"."),(0,i.kt)("h2",{id:"debianubuntu-packages"},"Debian/Ubuntu packages"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://download.jitsi.org/stable/"},(0,i.kt)("inlineCode",{parentName:"a"},"stable"))," (",(0,i.kt)("a",{parentName:"li",href:"https://jitsi.org/downloads/ubuntu-debian-installations-instructions/"},"instructions"),")"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://download.jitsi.org/testing/"},(0,i.kt)("inlineCode",{parentName:"a"},"testing"))," (",(0,i.kt)("a",{parentName:"li",href:"https://jitsi.org/downloads/ubuntu-debian-installations-instructions-for-testing/"},"instructions"),")"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://download.jitsi.org/unstable/"},(0,i.kt)("inlineCode",{parentName:"a"},"nightly"))," (",(0,i.kt)("a",{parentName:"li",href:"https://jitsi.org/downloads/ubuntu-debian-installations-instructions-nightly/"},"instructions"),")")),(0,i.kt)("h2",{id:"web-frontend"},"Web frontend"),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:null},"Latest stable release"),(0,i.kt)("th",{parentName:"tr",align:null},(0,i.kt)("a",{parentName:"th",href:"https://github.com/jitsi/jitsi-meet/releases/latest"},(0,i.kt)("img",{parentName:"a",src:"https://img.shields.io/badge/release-latest-green.svg",alt:"release"})))))),(0,i.kt)("p",null,"Prebuilt ",(0,i.kt)("a",{parentName:"p",href:"https://download.jitsi.org/jitsi-meet/src/"},"source builds")," are also available."),(0,i.kt)("admonition",{type:"note"},(0,i.kt)("p",{parentName:"admonition"},"Generally, you won't need to download this, as it's part of the Debian packages and Docker images already.")))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/4e572726.216278e7.js b/assets/js/4e572726.fce206f0.js similarity index 98% rename from assets/js/4e572726.216278e7.js rename to assets/js/4e572726.fce206f0.js index eca64e30b..86ed7d3c7 100644 --- a/assets/js/4e572726.216278e7.js +++ b/assets/js/4e572726.fce206f0.js @@ -1 +1 @@ -"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[9719],{3905:(e,t,r)=>{r.d(t,{Zo:()=>d,kt:()=>m});var n=r(7294);function i(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function o(e){for(var t=1;t=0||(i[r]=e[r]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(i[r]=e[r])}return i}var c=n.createContext({}),l=function(e){var t=n.useContext(c),r=t;return e&&(r="function"==typeof e?e(t):o(o({},t),e)),r},d=function(e){var t=l(e.components);return n.createElement(c.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},u=n.forwardRef((function(e,t){var r=e.components,i=e.mdxType,a=e.originalType,c=e.parentName,d=s(e,["components","mdxType","originalType","parentName"]),u=l(r),m=i,f=u["".concat(c,".").concat(m)]||u[m]||p[m]||a;return r?n.createElement(f,o(o({ref:t},d),{},{components:r})):n.createElement(f,o({ref:t},d))}));function m(e,t){var r=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=r.length,o=new Array(a);o[0]=u;var s={};for(var c in t)hasOwnProperty.call(t,c)&&(s[c]=t[c]);s.originalType=e,s.mdxType="string"==typeof e?e:i,o[1]=s;for(var l=2;l{r.d(t,{Z:()=>y});var n=r(7294),i=r(6010),a=r(8425),o=r(9960),s=r(3919),c=r(5999);const l="cardContainer_fWXF",d="cardTitle_rnsV",p="cardDescription_PWke";function u(e){var t=e.href,r=e.children;return n.createElement(o.Z,{href:t,className:(0,i.Z)("card padding--lg",l)},r)}function m(e){var t=e.href,r=e.icon,a=e.title,o=e.description;return n.createElement(u,{href:t},n.createElement("h2",{className:(0,i.Z)("text--truncate",d),title:a},r," ",a),o&&n.createElement("p",{className:(0,i.Z)("text--truncate",p),title:o},o))}function f(e){var t,r=e.item,i=(0,a.Wl)(r);return i?n.createElement(m,{href:i,icon:"\ud83d\uddc3\ufe0f",title:r.label,description:null!=(t=r.description)?t:(0,c.I)({message:"{count} items",id:"theme.docs.DocCard.categoryDescription",description:"The default description for a category card in the generated index about how many items this category includes"},{count:r.items.length})}):null}function g(e){var t,r,i=e.item,o=(0,s.Z)(i.href)?"\ud83d\udcc4\ufe0f":"\ud83d\udd17",c=(0,a.xz)(null!=(t=i.docId)?t:void 0);return n.createElement(m,{href:i.href,icon:o,title:i.label,description:null!=(r=i.description)?r:null==c?void 0:c.description})}function v(e){var t=e.item;switch(t.type){case"link":return n.createElement(g,{item:t});case"category":return n.createElement(f,{item:t});default:throw new Error("unknown item type "+JSON.stringify(t))}}function h(e){var t=e.className,r=(0,a.jA)();return n.createElement(y,{items:r.items,className:t})}function y(e){var t=e.items,r=e.className;if(!t)return n.createElement(h,e);var o=(0,a.MN)(t);return n.createElement("section",{className:(0,i.Z)("row",r)},o.map((function(e,t){return n.createElement("article",{key:t,className:"col col--6 margin-bottom--lg"},n.createElement(v,{item:e}))})))}},565:(e,t,r)=>{r.r(t),r.d(t,{assets:()=>p,contentTitle:()=>l,default:()=>f,frontMatter:()=>c,metadata:()=>d,toc:()=>u});var n=r(7462),i=r(3366),a=(r(7294),r(3905)),o=r(2991),s=["components"],c={id:"devops-guide-start",title:"Self-Hosting Guide - Overview",sidebar_label:"Overview"},l=void 0,d={unversionedId:"devops-guide/devops-guide-start",id:"devops-guide/devops-guide-start",title:"Self-Hosting Guide - Overview",description:"These guides help you to host your own Jitsi-Meet server.",source:"@site/docs/devops-guide/devops-guide.md",sourceDirName:"devops-guide",slug:"/devops-guide/",permalink:"/handbook/docs/devops-guide/",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/devops-guide/devops-guide.md",tags:[],version:"current",lastUpdatedAt:1694635654,formattedLastUpdatedAt:"Sep 13, 2023",frontMatter:{id:"devops-guide-start",title:"Self-Hosting Guide - Overview",sidebar_label:"Overview"},sidebar:"docs",previous:{title:"Configuration",permalink:"/handbook/docs/dev-guide/dev-guide-configuration"},next:{title:"Deployment",permalink:"/handbook/docs/category/deployment"}},p={},u=[{value:"First, a bit of general advice",id:"first-a-bit-of-general-advice",level:2}],m={toc:u};function f(e){var t=e.components,r=(0,i.Z)(e,s);return(0,a.kt)("wrapper",(0,n.Z)({},m,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("admonition",{type:"note"},(0,a.kt)("p",{parentName:"admonition"},"These guides help you to ",(0,a.kt)("strong",{parentName:"p"},(0,a.kt)("em",{parentName:"strong"},"host your own Jitsi-Meet server")),".",(0,a.kt)("br",{parentName:"p"}),"\n","If you want to have a video conference without setting up any infrastructure, use ",(0,a.kt)("a",{parentName:"p",href:"https://meet.jit.si"},"https://meet.jit.si")," instead.")),(0,a.kt)("h2",{id:"first-a-bit-of-general-advice"},"First, a bit of general advice"),(0,a.kt)("p",null,"Jitsi Meet being based on ",(0,a.kt)("a",{parentName:"p",href:"https://en.wikipedia.org/wiki/WebRTC"},"WebRTC"),", an encrypted communication link (https) is ",(0,a.kt)("strong",{parentName:"p"},(0,a.kt)("em",{parentName:"strong"},"necessary"))," to get working multimedia, and the setup is not always trivial."),(0,a.kt)("p",null,"The best option is an Internet server with a certificate for a domain registered in the ",(0,a.kt)("a",{parentName:"p",href:"https://en.wikipedia.org/wiki/Domain_Name_System#Domain_name_registration"},"DNS"),"."),(0,a.kt)("p",null,"While it's possible to setup a server on a private network and/or a self-signed certificate, it can be less straightforward and you can expect difficulties, first if you want access both from the private network and the public internet, and second when using phones as these clients often don't accept self-signed certificates."),(0,a.kt)("p",null,"In case of trouble with clients using phones, ",(0,a.kt)("a",{parentName:"p",href:"https://whatsmychaincert.com"},"check your certificate"),"."),(0,a.kt)("hr",null),(0,a.kt)(o.Z,{mdxType:"DocCardList"}))}f.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[9719],{3905:(e,t,r)=>{r.d(t,{Zo:()=>d,kt:()=>m});var n=r(7294);function i(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function o(e){for(var t=1;t=0||(i[r]=e[r]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(i[r]=e[r])}return i}var c=n.createContext({}),l=function(e){var t=n.useContext(c),r=t;return e&&(r="function"==typeof e?e(t):o(o({},t),e)),r},d=function(e){var t=l(e.components);return n.createElement(c.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},u=n.forwardRef((function(e,t){var r=e.components,i=e.mdxType,a=e.originalType,c=e.parentName,d=s(e,["components","mdxType","originalType","parentName"]),u=l(r),m=i,f=u["".concat(c,".").concat(m)]||u[m]||p[m]||a;return r?n.createElement(f,o(o({ref:t},d),{},{components:r})):n.createElement(f,o({ref:t},d))}));function m(e,t){var r=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=r.length,o=new Array(a);o[0]=u;var s={};for(var c in t)hasOwnProperty.call(t,c)&&(s[c]=t[c]);s.originalType=e,s.mdxType="string"==typeof e?e:i,o[1]=s;for(var l=2;l{r.d(t,{Z:()=>y});var n=r(7294),i=r(6010),a=r(8425),o=r(9960),s=r(3919),c=r(5999);const l="cardContainer_fWXF",d="cardTitle_rnsV",p="cardDescription_PWke";function u(e){var t=e.href,r=e.children;return n.createElement(o.Z,{href:t,className:(0,i.Z)("card padding--lg",l)},r)}function m(e){var t=e.href,r=e.icon,a=e.title,o=e.description;return n.createElement(u,{href:t},n.createElement("h2",{className:(0,i.Z)("text--truncate",d),title:a},r," ",a),o&&n.createElement("p",{className:(0,i.Z)("text--truncate",p),title:o},o))}function f(e){var t,r=e.item,i=(0,a.Wl)(r);return i?n.createElement(m,{href:i,icon:"\ud83d\uddc3\ufe0f",title:r.label,description:null!=(t=r.description)?t:(0,c.I)({message:"{count} items",id:"theme.docs.DocCard.categoryDescription",description:"The default description for a category card in the generated index about how many items this category includes"},{count:r.items.length})}):null}function g(e){var t,r,i=e.item,o=(0,s.Z)(i.href)?"\ud83d\udcc4\ufe0f":"\ud83d\udd17",c=(0,a.xz)(null!=(t=i.docId)?t:void 0);return n.createElement(m,{href:i.href,icon:o,title:i.label,description:null!=(r=i.description)?r:null==c?void 0:c.description})}function v(e){var t=e.item;switch(t.type){case"link":return n.createElement(g,{item:t});case"category":return n.createElement(f,{item:t});default:throw new Error("unknown item type "+JSON.stringify(t))}}function h(e){var t=e.className,r=(0,a.jA)();return n.createElement(y,{items:r.items,className:t})}function y(e){var t=e.items,r=e.className;if(!t)return n.createElement(h,e);var o=(0,a.MN)(t);return n.createElement("section",{className:(0,i.Z)("row",r)},o.map((function(e,t){return n.createElement("article",{key:t,className:"col col--6 margin-bottom--lg"},n.createElement(v,{item:e}))})))}},565:(e,t,r)=>{r.r(t),r.d(t,{assets:()=>p,contentTitle:()=>l,default:()=>f,frontMatter:()=>c,metadata:()=>d,toc:()=>u});var n=r(7462),i=r(3366),a=(r(7294),r(3905)),o=r(2991),s=["components"],c={id:"devops-guide-start",title:"Self-Hosting Guide - Overview",sidebar_label:"Overview"},l=void 0,d={unversionedId:"devops-guide/devops-guide-start",id:"devops-guide/devops-guide-start",title:"Self-Hosting Guide - Overview",description:"These guides help you to host your own Jitsi-Meet server.",source:"@site/docs/devops-guide/devops-guide.md",sourceDirName:"devops-guide",slug:"/devops-guide/",permalink:"/handbook/docs/devops-guide/",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/devops-guide/devops-guide.md",tags:[],version:"current",lastUpdatedAt:1695203964,formattedLastUpdatedAt:"Sep 20, 2023",frontMatter:{id:"devops-guide-start",title:"Self-Hosting Guide - Overview",sidebar_label:"Overview"},sidebar:"docs",previous:{title:"Configuration",permalink:"/handbook/docs/dev-guide/dev-guide-configuration"},next:{title:"Deployment",permalink:"/handbook/docs/category/deployment"}},p={},u=[{value:"First, a bit of general advice",id:"first-a-bit-of-general-advice",level:2}],m={toc:u};function f(e){var t=e.components,r=(0,i.Z)(e,s);return(0,a.kt)("wrapper",(0,n.Z)({},m,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("admonition",{type:"note"},(0,a.kt)("p",{parentName:"admonition"},"These guides help you to ",(0,a.kt)("strong",{parentName:"p"},(0,a.kt)("em",{parentName:"strong"},"host your own Jitsi-Meet server")),".",(0,a.kt)("br",{parentName:"p"}),"\n","If you want to have a video conference without setting up any infrastructure, use ",(0,a.kt)("a",{parentName:"p",href:"https://meet.jit.si"},"https://meet.jit.si")," instead.")),(0,a.kt)("h2",{id:"first-a-bit-of-general-advice"},"First, a bit of general advice"),(0,a.kt)("p",null,"Jitsi Meet being based on ",(0,a.kt)("a",{parentName:"p",href:"https://en.wikipedia.org/wiki/WebRTC"},"WebRTC"),", an encrypted communication link (https) is ",(0,a.kt)("strong",{parentName:"p"},(0,a.kt)("em",{parentName:"strong"},"necessary"))," to get working multimedia, and the setup is not always trivial."),(0,a.kt)("p",null,"The best option is an Internet server with a certificate for a domain registered in the ",(0,a.kt)("a",{parentName:"p",href:"https://en.wikipedia.org/wiki/Domain_Name_System#Domain_name_registration"},"DNS"),"."),(0,a.kt)("p",null,"While it's possible to setup a server on a private network and/or a self-signed certificate, it can be less straightforward and you can expect difficulties, first if you want access both from the private network and the public internet, and second when using phones as these clients often don't accept self-signed certificates."),(0,a.kt)("p",null,"In case of trouble with clients using phones, ",(0,a.kt)("a",{parentName:"p",href:"https://whatsmychaincert.com"},"check your certificate"),"."),(0,a.kt)("hr",null),(0,a.kt)(o.Z,{mdxType:"DocCardList"}))}f.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/4e8cc489.5cc5a366.js b/assets/js/4e8cc489.f033f90e.js similarity index 98% rename from assets/js/4e8cc489.5cc5a366.js rename to assets/js/4e8cc489.f033f90e.js index bde622b0b..cd861f863 100644 --- a/assets/js/4e8cc489.5cc5a366.js +++ b/assets/js/4e8cc489.f033f90e.js @@ -1 +1 @@ -"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[1837],{3905:(e,t,r)=>{r.d(t,{Zo:()=>d,kt:()=>b});var n=r(7294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function a(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var c=n.createContext({}),u=function(e){var t=n.useContext(c),r=t;return e&&(r="function"==typeof e?e(t):a(a({},t),e)),r},d=function(e){var t=u(e.components);return n.createElement(c.Provider,{value:t},e.children)},l={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},p=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,i=e.originalType,c=e.parentName,d=s(e,["components","mdxType","originalType","parentName"]),p=u(r),b=o,f=p["".concat(c,".").concat(b)]||p[b]||l[b]||i;return r?n.createElement(f,a(a({ref:t},d),{},{components:r})):n.createElement(f,a({ref:t},d))}));function b(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=r.length,a=new Array(i);a[0]=p;var s={};for(var c in t)hasOwnProperty.call(t,c)&&(s[c]=t[c]);s.originalType=e,s.mdxType="string"==typeof e?e:o,a[1]=s;for(var u=2;u{r.r(t),r.d(t,{assets:()=>d,contentTitle:()=>c,default:()=>b,frontMatter:()=>s,metadata:()=>u,toc:()=>l});var n=r(7462),o=r(3366),i=(r(7294),r(3905)),a=["components"],s={id:"user-guide-basic",title:"User Guide (basic)",sidebar_label:"Basic options"},c=void 0,u={unversionedId:"user-guide/user-guide-basic",id:"user-guide/user-guide-basic",title:"User Guide (basic)",description:"Welcome to the user guide!",source:"@site/docs/user-guide/basic.md",sourceDirName:"user-guide",slug:"/user-guide/user-guide-basic",permalink:"/handbook/docs/user-guide/user-guide-basic",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/user-guide/basic.md",tags:[],version:"current",lastUpdatedAt:1694635654,formattedLastUpdatedAt:"Sep 13, 2023",frontMatter:{id:"user-guide-basic",title:"User Guide (basic)",sidebar_label:"Basic options"},sidebar:"docs",previous:{title:"Keyboard shortcuts",permalink:"/handbook/docs/user-guide/keyboard-shortcuts"},next:{title:"Advanced options",permalink:"/handbook/docs/user-guide/user-guide-advanced"}},d={},l=[],p={toc:l};function b(e){var t=e.components,r=(0,o.Z)(e,a);return(0,i.kt)("wrapper",(0,n.Z)({},p,r,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"Welcome to the user guide!"),(0,i.kt)("p",null,"Check back soon!"))}b.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[1837],{3905:(e,t,r)=>{r.d(t,{Zo:()=>d,kt:()=>b});var n=r(7294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function a(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var c=n.createContext({}),u=function(e){var t=n.useContext(c),r=t;return e&&(r="function"==typeof e?e(t):a(a({},t),e)),r},d=function(e){var t=u(e.components);return n.createElement(c.Provider,{value:t},e.children)},l={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},p=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,i=e.originalType,c=e.parentName,d=s(e,["components","mdxType","originalType","parentName"]),p=u(r),b=o,f=p["".concat(c,".").concat(b)]||p[b]||l[b]||i;return r?n.createElement(f,a(a({ref:t},d),{},{components:r})):n.createElement(f,a({ref:t},d))}));function b(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=r.length,a=new Array(i);a[0]=p;var s={};for(var c in t)hasOwnProperty.call(t,c)&&(s[c]=t[c]);s.originalType=e,s.mdxType="string"==typeof e?e:o,a[1]=s;for(var u=2;u{r.r(t),r.d(t,{assets:()=>d,contentTitle:()=>c,default:()=>b,frontMatter:()=>s,metadata:()=>u,toc:()=>l});var n=r(7462),o=r(3366),i=(r(7294),r(3905)),a=["components"],s={id:"user-guide-basic",title:"User Guide (basic)",sidebar_label:"Basic options"},c=void 0,u={unversionedId:"user-guide/user-guide-basic",id:"user-guide/user-guide-basic",title:"User Guide (basic)",description:"Welcome to the user guide!",source:"@site/docs/user-guide/basic.md",sourceDirName:"user-guide",slug:"/user-guide/user-guide-basic",permalink:"/handbook/docs/user-guide/user-guide-basic",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/user-guide/basic.md",tags:[],version:"current",lastUpdatedAt:1695203964,formattedLastUpdatedAt:"Sep 20, 2023",frontMatter:{id:"user-guide-basic",title:"User Guide (basic)",sidebar_label:"Basic options"},sidebar:"docs",previous:{title:"Keyboard shortcuts",permalink:"/handbook/docs/user-guide/keyboard-shortcuts"},next:{title:"Advanced options",permalink:"/handbook/docs/user-guide/user-guide-advanced"}},d={},l=[],p={toc:l};function b(e){var t=e.components,r=(0,o.Z)(e,a);return(0,i.kt)("wrapper",(0,n.Z)({},p,r,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"Welcome to the user guide!"),(0,i.kt)("p",null,"Check back soon!"))}b.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/5055ed84.a0f78bd3.js b/assets/js/5055ed84.471169ad.js similarity index 99% rename from assets/js/5055ed84.a0f78bd3.js rename to assets/js/5055ed84.471169ad.js index 23fc10646..5406eba55 100644 --- a/assets/js/5055ed84.a0f78bd3.js +++ b/assets/js/5055ed84.471169ad.js @@ -1 +1 @@ -"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[2074],{3905:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>m});var o=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function i(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var l=o.createContext({}),p=function(e){var t=o.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},c=function(e){var t=p(e.components);return o.createElement(l.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},u=o.forwardRef((function(e,t){var n=e.components,r=e.mdxType,a=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),u=p(n),m=r,f=u["".concat(l,".").concat(m)]||u[m]||d[m]||a;return n?o.createElement(f,i(i({ref:t},c),{},{components:n})):o.createElement(f,i({ref:t},c))}));function m(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var a=n.length,i=new Array(a);i[0]=u;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:r,i[1]=s;for(var p=2;p{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>l,default:()=>m,frontMatter:()=>s,metadata:()=>p,toc:()=>d});var o=n(7462),r=n(3366),a=(n(7294),n(3905)),i=["components"],s={id:"reservation",title:"Reservation System setup",sidebar_label:"Reservation System"},l=void 0,p={unversionedId:"devops-guide/reservation",id:"devops-guide/reservation",title:"Reservation System setup",description:"Support for a reservation system over REST API",source:"@site/docs/devops-guide/reservation.md",sourceDirName:"devops-guide",slug:"/devops-guide/reservation",permalink:"/handbook/docs/devops-guide/reservation",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/devops-guide/reservation.md",tags:[],version:"current",lastUpdatedAt:1694635654,formattedLastUpdatedAt:"Sep 13, 2023",frontMatter:{id:"reservation",title:"Reservation System setup",sidebar_label:"Reservation System"},sidebar:"docs",previous:{title:"Scalable setup",permalink:"/handbook/docs/devops-guide/devops-guide-scalable"},next:{title:"TURN setup",permalink:"/handbook/docs/devops-guide/turn"}},c={},d=[{value:"Support for a reservation system over REST API",id:"support-for-a-reservation-system-over-rest-api",level:3},{value:"Enable reservation system",id:"enable-reservation-system",level:4},{value:"Call flow",id:"call-flow",level:4},{value:"Notes",id:"notes",level:5},{value:"Conference allocation",id:"conference-allocation",level:5},{value:"HTTP 200 or 201 Conference created successfully",id:"http-200-or-201-conference-created-successfully",level:6},{value:"HTTP 409 - Conference already exists",id:"http-409---conference-already-exists",level:6},{value:"HTTP 4xx",id:"http-4xx",level:6},{value:"Reading conference info",id:"reading-conference-info",level:5},{value:"Deleting conference",id:"deleting-conference",level:5},{value:"Implementation diagram",id:"implementation-diagram",level:4}],u={toc:d};function m(e){var t=e.components,n=(0,r.Z)(e,i);return(0,a.kt)("wrapper",(0,o.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h3",{id:"support-for-a-reservation-system-over-rest-api"},"Support for a reservation system over REST API"),(0,a.kt)("p",null,"It is possible to connect to an external conference reservation system using a\nREST API. Before a new Jitsi Meet conference is created, the reservation system will be\nqueried for room availability. The system is supposed to return a positive or\nnegative response code, which also contains conference duration. Prosody will enforce\nconference duration and if the time limit is exceeded the conference will be\nterminated."),(0,a.kt)("h4",{id:"enable-reservation-system"},"Enable reservation system"),(0,a.kt)("p",null,'In order to enable the reservation system, the URL base for the REST API endpoint must be\nconfigured. Under the main virtual host in prosody, enable module "reservations" and\nadd the config ',(0,a.kt)("inlineCode",{parentName:"p"},"reservations_api_prefix"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},'VirtualHost "jitmeet.example.com"\n -- ....\n modules_enabled = {\n -- ....\n "reservations";\n }\n reservations_api_prefix = "http://reservation.example.com"\n')),(0,a.kt)("p",null,"The URL base is used to construct the request URL. Currently, only ",(0,a.kt)("inlineCode",{parentName:"p"},"'/conference'"),"\nendpoint is supported, so all request will go to:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"http://reservation.example.com/conference\n")),(0,a.kt)("p",null,"Additional configuration options are available:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},'"reservations_api_timeout" to change API call timeouts (defaults to 20 seconds)'),(0,a.kt)("li",{parentName:"ul"},'"reservations_api_headers" to specify custom HTTP headers included in\nall API calls e.g. to provide auth tokens.'),(0,a.kt)("li",{parentName:"ul"},'"reservations_api_retry_count" to specify the number of times API call failures are retried (defaults to 3)'),(0,a.kt)("li",{parentName:"ul"},'"reservations_api_retry_delay" seconds to wait between retries (defaults to 3s)'),(0,a.kt)("li",{parentName:"ul"},'"reservations_api_should_retry_for_code" as a function that takes an HTTP response code and\nreturns true if the API call should be retried. By default, retries are done for 5XX\nresponses. Timeouts are never retried, and HTTP call failures are always retried.'),(0,a.kt)("li",{parentName:"ul"},'"reservations_enable_max_occupants" to enable support for setting max occupants. If this is set to ',(0,a.kt)("inlineCode",{parentName:"li"},"true"),', and if\nthe API response payload includes a "max_occupants" value, then that value will be set as the max occupancy limit\nfor that specific room.',(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},'"muc_max_occupants" module must also be enabled for this to work.'))),(0,a.kt)("li",{parentName:"ul"},'"reservations_enable_lobby_support" to enable support for lobby. If this is set to ',(0,a.kt)("inlineCode",{parentName:"li"},"true"),', and if\nthe API response payload includes a "lobby" field set to ',(0,a.kt)("inlineCode",{parentName:"li"},"true")," , then the lobby will be enabled for the room.",(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},'"muc_lobby_rooms" and "persistent_lobby" modules must also be enabled for this to work.'))),(0,a.kt)("li",{parentName:"ul"},'"reservations_enable_password_support" to enable support for room password. If this is set to ',(0,a.kt)("inlineCode",{parentName:"li"},"true"),', and if\nthe API response payload includes a "password" value, then that value will be set as room password. Users will then\nbe required to know that password to be able to join the room, or in the case where lobby is enabled, can use the\npassword to bypass the lobby.')),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},' --- The following are all optional\n reservations_api_headers = {\n ["Authorization"] = "Bearer TOKEN-237958623045";\n }\n reservations_api_timeout = 10 -- timeout if API does not respond within 10s\n reservations_api_retry_count = 5 -- retry up to 5 times\n reservations_api_retry_delay = 1 -- wait 1s between retries\n reservations_api_should_retry_for_code = function (code)\n return code >= 500 or code == 408\n end\n reservations_enable_max_occupants = true -- enable integration with muc_max_occupants\n reservations_enable_lobby_support = true -- enable integration with muc_lobby_rooms\n reservations_enable_password_support = true -- enable support for setting room passwords\n')),(0,a.kt)("h4",{id:"call-flow"},"Call flow"),(0,a.kt)("h5",{id:"notes"},"Notes"),(0,a.kt)("p",null,"All API calls use the following datetime format:"),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"yyyy-MM-dd'T'HH:mm:ss.SSSX")," - more info can be found in\n",(0,a.kt)("inlineCode",{parentName:"p"},"SimpleDateFormat")," ",(0,a.kt)("a",{parentName:"p",href:"https://docs.oracle.com/javase/6/docs/api/java/text/SimpleDateFormat.html"},"JavaDoc")),(0,a.kt)("h5",{id:"conference-allocation"},"Conference allocation"),(0,a.kt)("p",null,"When the first user joins a MUC room (i.e. Jitsi Meet URL is opened), an ",(0,a.kt)("inlineCode",{parentName:"p"},"HTTP POST"),"\nrequest is sent to ",(0,a.kt)("inlineCode",{parentName:"p"},"'/conference'")," endpoint with the following parameters\nincluded:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"name (string)")," - short name of the conference room (not full MUC address). If tenant is used, the name will be ",(0,a.kt)("inlineCode",{parentName:"li"},"[tenant]roomname"),"."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"start_time (string)")," - conference start date and time"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"mail_owner (string)")," - if authentication system is enabled this field will\ncontain user's identity. It that case it will not be possible to create a new\nconference room without authenticating.")),(0,a.kt)("p",null,"The payload sent to the endpoint will be encoded as ",(0,a.kt)("inlineCode",{parentName:"p"},"application/x-www-form-urlencoded"),"."),(0,a.kt)("p",null,"The reservation system is expected to respond with one of the following\nresponses:"),(0,a.kt)("h6",{id:"http-200-or-201-conference-created-successfully"},"HTTP 200 or 201 Conference created successfully"),(0,a.kt)("p",null,"In the HTTP response, a JSON object is expected. It should contain conference ",(0,a.kt)("inlineCode",{parentName:"p"},"id"),"\nassigned by the system and ",(0,a.kt)("inlineCode",{parentName:"p"},"duration")," measured in seconds. Sample response body:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},'{\n "id": 364758328,\n "name": "conference1234",\n "mail_owner": "user@server.com",\n "start_time": "2048-04-20T17:55:12.000Z",\n "duration": 900000\n}\n')),(0,a.kt)("p",null,"The object can optionally include a ",(0,a.kt)("inlineCode",{parentName:"p"},"max_occupants")," key with an integer value. When provided, and if\n",(0,a.kt)("inlineCode",{parentName:"p"},"reservations_enable_max_occupants")," is enabled, then the value will be passed to muc_mod_max_occupants to enforce\nper-room occupancy limits."),(0,a.kt)("h6",{id:"http-409---conference-already-exists"},"HTTP 409 - Conference already exists"),(0,a.kt)("p",null,"This is to recover from previous failures. If for some reason the conference was\nrestarted and the user tries to create the room again, this response informs Prosody\nthat the conference room already exists. It is expected to contain\n",(0,a.kt)("inlineCode",{parentName:"p"},"conflict_id")," in the JSON response body:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},'{\n "conflict_id": 364758328\n}\n')),(0,a.kt)("p",null,"Prosody will use ",(0,a.kt)("inlineCode",{parentName:"p"},"HTTP GET")," to fetch information about the conflicting conference for the\ngiven ",(0,a.kt)("inlineCode",{parentName:"p"},"conflict_id"),'. More info about this request can be found in the "Reading conference info"\nsection.'),(0,a.kt)("h6",{id:"http-4xx"},"HTTP 4xx"),(0,a.kt)("p",null,"Other response codes will cause conference creation failure. The JSON response\ncan contain a ",(0,a.kt)("inlineCode",{parentName:"p"},"message")," object which will be sent back to the client."),(0,a.kt)("p",null,"For example ",(0,a.kt)("inlineCode",{parentName:"p"},"user1")," tries to start a new conference by sending\n",(0,a.kt)("inlineCode",{parentName:"p"},"conference")," IQ to Jicofo. The system will reject the request."),(0,a.kt)("p",null,"Client -> Jicofo:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"\n \n\n")),(0,a.kt)("p",null,"Prosody -> Reservation system:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"POST /conference HTTP/1.1\ncontent-type:application/x-www-form-urlencoded;charset=utf-8\nhost: http://reservation.example.com\ncontent-length: length\n\nname=testroom1&start_time=2048-04-20T17%3A55%3A12.000Z&mail_owner=client1%40xmpp.com\n")),(0,a.kt)("p",null,"Reservation system -> Prosody:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},'HTTP/1.1 403 Forbidden\nContent-Type: application/json; charset=utf-8\nContent-Length: length\n\n{\n "message": "client1 is not allowed to create the room at this time"\n}\n')),(0,a.kt)("p",null,"Prosody -> Client:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"\n \n \n \n client1 is not allowed to create the room at this time\n \n \n \n\n")),(0,a.kt)("p",null,"The application can use ",(0,a.kt)("inlineCode",{parentName:"p"},"text")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"reservation-error")," elements to\nprovide meaningful information to the user."),(0,a.kt)("h5",{id:"reading-conference-info"},"Reading conference info"),(0,a.kt)("p",null,"In case of a ",(0,a.kt)("inlineCode",{parentName:"p"},"409")," response to the ",(0,a.kt)("inlineCode",{parentName:"p"},"HTTP POST")," request, Prosody will try\nto read information about the conflicting conference using an ",(0,a.kt)("inlineCode",{parentName:"p"},"HTTP GET"),"\n'/conference/{conflict_id}' endpoint. The response should provide all\ninformation about the conference stored in the reservation system:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},'"id"'),": conference identifier assigned by the reservation system"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},'"name"'),": conference room name"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},'"mail_owner"'),": identity of the user who has created the conference"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},'"start_time"'),": conference start date and time"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},'"duration"'),": scheduled conference duration in seconds")),(0,a.kt)("p",null,"The optional ",(0,a.kt)("inlineCode",{parentName:"p"},"max_occupants")," value should also be provided if applicable."),(0,a.kt)("p",null,"Sample response JSON body (contains the same info as ",(0,a.kt)("inlineCode",{parentName:"p"},"200 OK")," to\n",(0,a.kt)("inlineCode",{parentName:"p"},"HTTP POST"),"):"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},'{\n "id": 364758328,\n "name": "conference1234",\n "mail_owner": "user@server.com",\n "start_time": "2048-04-20T17:55:12.000Z",\n "duration": 900000\n}\n')),(0,a.kt)("h5",{id:"deleting-conference"},"Deleting conference"),(0,a.kt)("p",null,"Prosody deletes conferences in the reservation system in two cases. First when\nall users leave XMPP Multi User Chat room. Secondly when the conference duration limit\nis exceeded. In the latter case Prosody will destroy the XMPP MUC room.\nAfter the MUC room is destroyed, Prosody sends an ",(0,a.kt)("inlineCode",{parentName:"p"},"HTTP DELETE")," request to\n",(0,a.kt)("inlineCode",{parentName:"p"},"'/conference/{id}'")," endpoint where ",(0,a.kt)("inlineCode",{parentName:"p"},"{id}")," is replaced with\nconference identifier assigned by the reservation system."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"DELETE /conference/364758328 HTTP/1.1\nhost: http://reservation.example.com\n...\n")),(0,a.kt)("h4",{id:"implementation-diagram"},"Implementation diagram"),(0,a.kt)("p",null,(0,a.kt)("img",{parentName:"p",src:"https://raw.githubusercontent.com/jitsi/handbook/master/docs/assets/reservation-api.png",alt:null})))}m.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[2074],{3905:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>m});var o=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function i(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var l=o.createContext({}),p=function(e){var t=o.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},c=function(e){var t=p(e.components);return o.createElement(l.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},u=o.forwardRef((function(e,t){var n=e.components,r=e.mdxType,a=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),u=p(n),m=r,f=u["".concat(l,".").concat(m)]||u[m]||d[m]||a;return n?o.createElement(f,i(i({ref:t},c),{},{components:n})):o.createElement(f,i({ref:t},c))}));function m(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var a=n.length,i=new Array(a);i[0]=u;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:r,i[1]=s;for(var p=2;p{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>l,default:()=>m,frontMatter:()=>s,metadata:()=>p,toc:()=>d});var o=n(7462),r=n(3366),a=(n(7294),n(3905)),i=["components"],s={id:"reservation",title:"Reservation System setup",sidebar_label:"Reservation System"},l=void 0,p={unversionedId:"devops-guide/reservation",id:"devops-guide/reservation",title:"Reservation System setup",description:"Support for a reservation system over REST API",source:"@site/docs/devops-guide/reservation.md",sourceDirName:"devops-guide",slug:"/devops-guide/reservation",permalink:"/handbook/docs/devops-guide/reservation",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/devops-guide/reservation.md",tags:[],version:"current",lastUpdatedAt:1695203964,formattedLastUpdatedAt:"Sep 20, 2023",frontMatter:{id:"reservation",title:"Reservation System setup",sidebar_label:"Reservation System"},sidebar:"docs",previous:{title:"Scalable setup",permalink:"/handbook/docs/devops-guide/devops-guide-scalable"},next:{title:"TURN setup",permalink:"/handbook/docs/devops-guide/turn"}},c={},d=[{value:"Support for a reservation system over REST API",id:"support-for-a-reservation-system-over-rest-api",level:3},{value:"Enable reservation system",id:"enable-reservation-system",level:4},{value:"Call flow",id:"call-flow",level:4},{value:"Notes",id:"notes",level:5},{value:"Conference allocation",id:"conference-allocation",level:5},{value:"HTTP 200 or 201 Conference created successfully",id:"http-200-or-201-conference-created-successfully",level:6},{value:"HTTP 409 - Conference already exists",id:"http-409---conference-already-exists",level:6},{value:"HTTP 4xx",id:"http-4xx",level:6},{value:"Reading conference info",id:"reading-conference-info",level:5},{value:"Deleting conference",id:"deleting-conference",level:5},{value:"Implementation diagram",id:"implementation-diagram",level:4}],u={toc:d};function m(e){var t=e.components,n=(0,r.Z)(e,i);return(0,a.kt)("wrapper",(0,o.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h3",{id:"support-for-a-reservation-system-over-rest-api"},"Support for a reservation system over REST API"),(0,a.kt)("p",null,"It is possible to connect to an external conference reservation system using a\nREST API. Before a new Jitsi Meet conference is created, the reservation system will be\nqueried for room availability. The system is supposed to return a positive or\nnegative response code, which also contains conference duration. Prosody will enforce\nconference duration and if the time limit is exceeded the conference will be\nterminated."),(0,a.kt)("h4",{id:"enable-reservation-system"},"Enable reservation system"),(0,a.kt)("p",null,'In order to enable the reservation system, the URL base for the REST API endpoint must be\nconfigured. Under the main virtual host in prosody, enable module "reservations" and\nadd the config ',(0,a.kt)("inlineCode",{parentName:"p"},"reservations_api_prefix"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},'VirtualHost "jitmeet.example.com"\n -- ....\n modules_enabled = {\n -- ....\n "reservations";\n }\n reservations_api_prefix = "http://reservation.example.com"\n')),(0,a.kt)("p",null,"The URL base is used to construct the request URL. Currently, only ",(0,a.kt)("inlineCode",{parentName:"p"},"'/conference'"),"\nendpoint is supported, so all request will go to:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"http://reservation.example.com/conference\n")),(0,a.kt)("p",null,"Additional configuration options are available:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},'"reservations_api_timeout" to change API call timeouts (defaults to 20 seconds)'),(0,a.kt)("li",{parentName:"ul"},'"reservations_api_headers" to specify custom HTTP headers included in\nall API calls e.g. to provide auth tokens.'),(0,a.kt)("li",{parentName:"ul"},'"reservations_api_retry_count" to specify the number of times API call failures are retried (defaults to 3)'),(0,a.kt)("li",{parentName:"ul"},'"reservations_api_retry_delay" seconds to wait between retries (defaults to 3s)'),(0,a.kt)("li",{parentName:"ul"},'"reservations_api_should_retry_for_code" as a function that takes an HTTP response code and\nreturns true if the API call should be retried. By default, retries are done for 5XX\nresponses. Timeouts are never retried, and HTTP call failures are always retried.'),(0,a.kt)("li",{parentName:"ul"},'"reservations_enable_max_occupants" to enable support for setting max occupants. If this is set to ',(0,a.kt)("inlineCode",{parentName:"li"},"true"),', and if\nthe API response payload includes a "max_occupants" value, then that value will be set as the max occupancy limit\nfor that specific room.',(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},'"muc_max_occupants" module must also be enabled for this to work.'))),(0,a.kt)("li",{parentName:"ul"},'"reservations_enable_lobby_support" to enable support for lobby. If this is set to ',(0,a.kt)("inlineCode",{parentName:"li"},"true"),', and if\nthe API response payload includes a "lobby" field set to ',(0,a.kt)("inlineCode",{parentName:"li"},"true")," , then the lobby will be enabled for the room.",(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},'"muc_lobby_rooms" and "persistent_lobby" modules must also be enabled for this to work.'))),(0,a.kt)("li",{parentName:"ul"},'"reservations_enable_password_support" to enable support for room password. If this is set to ',(0,a.kt)("inlineCode",{parentName:"li"},"true"),', and if\nthe API response payload includes a "password" value, then that value will be set as room password. Users will then\nbe required to know that password to be able to join the room, or in the case where lobby is enabled, can use the\npassword to bypass the lobby.')),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},' --- The following are all optional\n reservations_api_headers = {\n ["Authorization"] = "Bearer TOKEN-237958623045";\n }\n reservations_api_timeout = 10 -- timeout if API does not respond within 10s\n reservations_api_retry_count = 5 -- retry up to 5 times\n reservations_api_retry_delay = 1 -- wait 1s between retries\n reservations_api_should_retry_for_code = function (code)\n return code >= 500 or code == 408\n end\n reservations_enable_max_occupants = true -- enable integration with muc_max_occupants\n reservations_enable_lobby_support = true -- enable integration with muc_lobby_rooms\n reservations_enable_password_support = true -- enable support for setting room passwords\n')),(0,a.kt)("h4",{id:"call-flow"},"Call flow"),(0,a.kt)("h5",{id:"notes"},"Notes"),(0,a.kt)("p",null,"All API calls use the following datetime format:"),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"yyyy-MM-dd'T'HH:mm:ss.SSSX")," - more info can be found in\n",(0,a.kt)("inlineCode",{parentName:"p"},"SimpleDateFormat")," ",(0,a.kt)("a",{parentName:"p",href:"https://docs.oracle.com/javase/6/docs/api/java/text/SimpleDateFormat.html"},"JavaDoc")),(0,a.kt)("h5",{id:"conference-allocation"},"Conference allocation"),(0,a.kt)("p",null,"When the first user joins a MUC room (i.e. Jitsi Meet URL is opened), an ",(0,a.kt)("inlineCode",{parentName:"p"},"HTTP POST"),"\nrequest is sent to ",(0,a.kt)("inlineCode",{parentName:"p"},"'/conference'")," endpoint with the following parameters\nincluded:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"name (string)")," - short name of the conference room (not full MUC address). If tenant is used, the name will be ",(0,a.kt)("inlineCode",{parentName:"li"},"[tenant]roomname"),"."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"start_time (string)")," - conference start date and time"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"mail_owner (string)")," - if authentication system is enabled this field will\ncontain user's identity. It that case it will not be possible to create a new\nconference room without authenticating.")),(0,a.kt)("p",null,"The payload sent to the endpoint will be encoded as ",(0,a.kt)("inlineCode",{parentName:"p"},"application/x-www-form-urlencoded"),"."),(0,a.kt)("p",null,"The reservation system is expected to respond with one of the following\nresponses:"),(0,a.kt)("h6",{id:"http-200-or-201-conference-created-successfully"},"HTTP 200 or 201 Conference created successfully"),(0,a.kt)("p",null,"In the HTTP response, a JSON object is expected. It should contain conference ",(0,a.kt)("inlineCode",{parentName:"p"},"id"),"\nassigned by the system and ",(0,a.kt)("inlineCode",{parentName:"p"},"duration")," measured in seconds. Sample response body:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},'{\n "id": 364758328,\n "name": "conference1234",\n "mail_owner": "user@server.com",\n "start_time": "2048-04-20T17:55:12.000Z",\n "duration": 900000\n}\n')),(0,a.kt)("p",null,"The object can optionally include a ",(0,a.kt)("inlineCode",{parentName:"p"},"max_occupants")," key with an integer value. When provided, and if\n",(0,a.kt)("inlineCode",{parentName:"p"},"reservations_enable_max_occupants")," is enabled, then the value will be passed to muc_mod_max_occupants to enforce\nper-room occupancy limits."),(0,a.kt)("h6",{id:"http-409---conference-already-exists"},"HTTP 409 - Conference already exists"),(0,a.kt)("p",null,"This is to recover from previous failures. If for some reason the conference was\nrestarted and the user tries to create the room again, this response informs Prosody\nthat the conference room already exists. It is expected to contain\n",(0,a.kt)("inlineCode",{parentName:"p"},"conflict_id")," in the JSON response body:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},'{\n "conflict_id": 364758328\n}\n')),(0,a.kt)("p",null,"Prosody will use ",(0,a.kt)("inlineCode",{parentName:"p"},"HTTP GET")," to fetch information about the conflicting conference for the\ngiven ",(0,a.kt)("inlineCode",{parentName:"p"},"conflict_id"),'. More info about this request can be found in the "Reading conference info"\nsection.'),(0,a.kt)("h6",{id:"http-4xx"},"HTTP 4xx"),(0,a.kt)("p",null,"Other response codes will cause conference creation failure. The JSON response\ncan contain a ",(0,a.kt)("inlineCode",{parentName:"p"},"message")," object which will be sent back to the client."),(0,a.kt)("p",null,"For example ",(0,a.kt)("inlineCode",{parentName:"p"},"user1")," tries to start a new conference by sending\n",(0,a.kt)("inlineCode",{parentName:"p"},"conference")," IQ to Jicofo. The system will reject the request."),(0,a.kt)("p",null,"Client -> Jicofo:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"\n \n\n")),(0,a.kt)("p",null,"Prosody -> Reservation system:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"POST /conference HTTP/1.1\ncontent-type:application/x-www-form-urlencoded;charset=utf-8\nhost: http://reservation.example.com\ncontent-length: length\n\nname=testroom1&start_time=2048-04-20T17%3A55%3A12.000Z&mail_owner=client1%40xmpp.com\n")),(0,a.kt)("p",null,"Reservation system -> Prosody:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},'HTTP/1.1 403 Forbidden\nContent-Type: application/json; charset=utf-8\nContent-Length: length\n\n{\n "message": "client1 is not allowed to create the room at this time"\n}\n')),(0,a.kt)("p",null,"Prosody -> Client:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"\n \n \n \n client1 is not allowed to create the room at this time\n \n \n \n\n")),(0,a.kt)("p",null,"The application can use ",(0,a.kt)("inlineCode",{parentName:"p"},"text")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"reservation-error")," elements to\nprovide meaningful information to the user."),(0,a.kt)("h5",{id:"reading-conference-info"},"Reading conference info"),(0,a.kt)("p",null,"In case of a ",(0,a.kt)("inlineCode",{parentName:"p"},"409")," response to the ",(0,a.kt)("inlineCode",{parentName:"p"},"HTTP POST")," request, Prosody will try\nto read information about the conflicting conference using an ",(0,a.kt)("inlineCode",{parentName:"p"},"HTTP GET"),"\n'/conference/{conflict_id}' endpoint. The response should provide all\ninformation about the conference stored in the reservation system:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},'"id"'),": conference identifier assigned by the reservation system"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},'"name"'),": conference room name"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},'"mail_owner"'),": identity of the user who has created the conference"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},'"start_time"'),": conference start date and time"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},'"duration"'),": scheduled conference duration in seconds")),(0,a.kt)("p",null,"The optional ",(0,a.kt)("inlineCode",{parentName:"p"},"max_occupants")," value should also be provided if applicable."),(0,a.kt)("p",null,"Sample response JSON body (contains the same info as ",(0,a.kt)("inlineCode",{parentName:"p"},"200 OK")," to\n",(0,a.kt)("inlineCode",{parentName:"p"},"HTTP POST"),"):"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},'{\n "id": 364758328,\n "name": "conference1234",\n "mail_owner": "user@server.com",\n "start_time": "2048-04-20T17:55:12.000Z",\n "duration": 900000\n}\n')),(0,a.kt)("h5",{id:"deleting-conference"},"Deleting conference"),(0,a.kt)("p",null,"Prosody deletes conferences in the reservation system in two cases. First when\nall users leave XMPP Multi User Chat room. Secondly when the conference duration limit\nis exceeded. In the latter case Prosody will destroy the XMPP MUC room.\nAfter the MUC room is destroyed, Prosody sends an ",(0,a.kt)("inlineCode",{parentName:"p"},"HTTP DELETE")," request to\n",(0,a.kt)("inlineCode",{parentName:"p"},"'/conference/{id}'")," endpoint where ",(0,a.kt)("inlineCode",{parentName:"p"},"{id}")," is replaced with\nconference identifier assigned by the reservation system."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"DELETE /conference/364758328 HTTP/1.1\nhost: http://reservation.example.com\n...\n")),(0,a.kt)("h4",{id:"implementation-diagram"},"Implementation diagram"),(0,a.kt)("p",null,(0,a.kt)("img",{parentName:"p",src:"https://raw.githubusercontent.com/jitsi/handbook/master/docs/assets/reservation-api.png",alt:null})))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/5281b7a2.ef709a76.js b/assets/js/5281b7a2.7f9578eb.js similarity index 98% rename from assets/js/5281b7a2.ef709a76.js rename to assets/js/5281b7a2.7f9578eb.js index 30022bdee..703b2fcc0 100644 --- a/assets/js/5281b7a2.ef709a76.js +++ b/assets/js/5281b7a2.7f9578eb.js @@ -1 +1 @@ -"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[5927],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>d});var a=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var c=a.createContext({}),l=function(e){var t=a.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},p=function(e){var t=l(e.components);return a.createElement(c.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},h=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,i=e.originalType,c=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),h=l(n),d=r,f=h["".concat(c,".").concat(d)]||h[d]||u[d]||i;return n?a.createElement(f,o(o({ref:t},p),{},{components:n})):a.createElement(f,o({ref:t},p))}));function d(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var i=n.length,o=new Array(i);o[0]=h;var s={};for(var c in t)hasOwnProperty.call(t,c)&&(s[c]=t[c]);s.originalType=e,s.mdxType="string"==typeof e?e:r,o[1]=s;for(var l=2;l{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>c,default:()=>d,frontMatter:()=>s,metadata:()=>l,toc:()=>u});var a=n(7462),r=n(3366),i=(n(7294),n(3905)),o=["components"],s={id:"architecture",title:"Architecture"},c=void 0,l={unversionedId:"architecture",id:"architecture",title:"Architecture",description:"In this section a global overview of the Jitsi infrastructure is provided. If you just started contributing to the project, we highly recommend reading this section thoroughly.",source:"@site/docs/architecture.md",sourceDirName:".",slug:"/architecture",permalink:"/handbook/docs/architecture",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/architecture.md",tags:[],version:"current",lastUpdatedAt:1694635654,formattedLastUpdatedAt:"Sep 13, 2023",frontMatter:{id:"architecture",title:"Architecture"},sidebar:"docs",previous:{title:"Introduction",permalink:"/handbook/docs/intro"},next:{title:"Security",permalink:"/handbook/docs/security"}},p={},u=[{value:"Components",id:"components",level:2},{value:"Architecture Diagram",id:"architecture-diagram",level:2},{value:"Code Map",id:"code-map",level:2},{value:"Testing",id:"testing",level:2}],h={toc:u};function d(e){var t=e.components,n=(0,r.Z)(e,o);return(0,i.kt)("wrapper",(0,a.Z)({},h,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"In this section a global overview of the Jitsi infrastructure is provided. If you just started contributing to the project, we highly recommend reading this section thoroughly."),(0,i.kt)("h2",{id:"components"},"Components"),(0,i.kt)("p",null,"Jitsi comprises a ",(0,i.kt)("a",{parentName:"p",href:"https://jitsi.org/projects/"},"collection of projects"),":"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://jitsi.org/jitsi-meet"},"Jitsi Meet")," - WebRTC compatible JavaScript application that uses Jitsi Videobridge to provide high quality, scalable video conferences. Build upon React and React Native."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://jitsi.org/jitsi-videobridge"},"Jitsi Videobridge (JVB)")," - WebRTC compatible server designed to route video streams amongst participants in a conference."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://github.com/jitsi/jicofo"},"Jitsi Conference Focus (jicofo)")," - server-side focus component used in Jitsi Meet conferences that manages media sessions and acts as load balancer between each of the participants and the videobridge."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://github.com/jitsi/jigasi"},"Jitsi Gateway to SIP (jigasi)")," - server-side application that allows regular SIP clients to join Jitsi Meet conferences"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://github.com/jitsi/jibri"},"Jitsi Broadcasting Infrastructure (jibri)")," - set of tools for recording and/or streaming a Jitsi Meet conference that works by launching a Chrome instance rendered in a virtual framebuffer and capturing and encoding the output with ffmpeg.")),(0,i.kt)("p",null,"External Software used by Jitsi:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://prosody.im/"},"Prosody")," - XMPP server used for signalling")),(0,i.kt)("h2",{id:"architecture-diagram"},"Architecture Diagram"),(0,i.kt)("p",null,"The individual connections between the previously described components, as well as their external integrations are described in the figure below."),(0,i.kt)("p",null,(0,i.kt)("img",{parentName:"p",src:"https://raw.githubusercontent.com/jitsi/handbook/master/docs/assets/ArchitectureDiagram.png",alt:null})),(0,i.kt)("p",null,"The external connections can be categorized into two main groups. Firstly, the connections between clients that request a video or audio connection performed through remote requests and data streams. The second category of external connections is those to external services that help store recordings, stream recordings, stream videos or help with creating meetings."),(0,i.kt)("h2",{id:"code-map"},"Code Map"),(0,i.kt)("p",null,"In this section we will look at the main parts of the codebase and see what they can be used for."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"./react/features"),"\nThis folder is where it is best to start writing your code, as it contains most of the app components that are used in the apps on Android and iOS, as well as on the web version. This source folder is split up into all the different features that Jitsi has to offer, such as authentication, chat interaction, keyboard shortcuts, screenshot capture, remote control and virtual background. Each of these features has its own folder in this map, which is then again split up to keep a hierarchy and consistency throughout the code."),(0,i.kt)("p",null,"Each feature folder consists of a subfolder called components, in this folder all of the React, or React Native for mobile, components are expressed. Usually in this folder there will be a separation between native and web components, however in some cases the same component could be used for both Android, iOS and web browser, in which case there is no separation made."),(0,i.kt)("p",null,"As stated before, the codebase mostly consists of React and React Native, which is the React version for mobile applications. Most of the features make use of the so-called class component by React ",(0,i.kt)("sup",{parentName:"p",id:"fnref-class-comp"},(0,i.kt)("a",{parentName:"sup",href:"#fn-class-comp",className:"footnote-ref"},"class-comp")),", however some new features start to use the new way to write functional components by using hooks",(0,i.kt)("sup",{parentName:"p",id:"fnref-func-comp"},(0,i.kt)("a",{parentName:"sup",href:"#fn-func-comp",className:"footnote-ref"},"func-comp")),"."),(0,i.kt)("p",null,"The application makes use of React Redux as well, this is used as a general state store to keep track of important parameters that are used throughout the application. More on React Redux can be found here ",(0,i.kt)("sup",{parentName:"p",id:"fnref-react-redux"},(0,i.kt)("a",{parentName:"sup",href:"#fn-react-redux",className:"footnote-ref"},"react-redux")),"."),(0,i.kt)("p",null,"Most features also contain a file called ",(0,i.kt)("inlineCode",{parentName:"p"},"middleware.js"),". This file acts as a bridge between the component and the functionality of the rest of the application."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"./modules/external-api"),"\nIn this folder, the external API can be found. This API can be used in various events like participants joining/leaving the meeting, changes in avatars or chat, as well as errors in using the microphone or camera."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"./android and ./ios"),"\nBoth of these folders contain the basics of the Android and iOS app respectively. However, the code for the application itself and its components can be found in the ",(0,i.kt)("strong",{parentName:"p"},"react/features")," folder, which is explained earlier in this section."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"./conference.js"),"\nThis file can be found at the root of the project, and contains the foundation of any interaction between a user and a conference room. This consists of setting up a connection to it, joining the meeting room, muting and unmuting, but also functions to gather information about the participants that are in the room."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"./lang"),"\nThis folder contains all the different translations that are present in Jitsi Meet. The translations can be found in the code with each of the keys in the translation maps that can be found in ",(0,i.kt)("inlineCode",{parentName:"p"},"main-[language].json")," files."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"./css"),"\nThis folder contains all the css that is used in the project. The files (mostly .scss files",(0,i.kt)("sup",{parentName:"p",id:"fnref-scss"},(0,i.kt)("a",{parentName:"sup",href:"#fn-scss",className:"footnote-ref"},"scss")),") are split up into features like the React features that they are used in."),(0,i.kt)("h2",{id:"testing"},"Testing"),(0,i.kt)("p",null,"The main form of testing code changes is done through torture tests, next to this the code is tested manually."),(0,i.kt)("p",null,"The torture tests are located in a separate repository, ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet-torture"},"Jitsi Meet Torture"),". The project contains end to end tests for several key functions such as peer to peer and invites. The testing can be done for iOS, Android and web, which are all the platforms that Jitsi Meet can be used on. The testing is done automatically for pull requests by project members, where it is used in combination with the continuous integration by a Jenkins instance running the tests, testing on the ",(0,i.kt)("a",{parentName:"p",href:"https://meet.jit.si"},"meet.jit.si")," instance. Other members can run the tests locally. The test results can be viewed on an automatically generated web page."),(0,i.kt)("p",null,"Manual testing is performed while doing code reviews, however there are also testing releases that can be freely downloaded and deployed, or can be used on the ",(0,i.kt)("a",{parentName:"p",href:"https://beta.meet.jit.si/"},"beta test server"),"."))}d.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[5927],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>d});var a=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var c=a.createContext({}),l=function(e){var t=a.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},p=function(e){var t=l(e.components);return a.createElement(c.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},h=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,i=e.originalType,c=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),h=l(n),d=r,f=h["".concat(c,".").concat(d)]||h[d]||u[d]||i;return n?a.createElement(f,o(o({ref:t},p),{},{components:n})):a.createElement(f,o({ref:t},p))}));function d(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var i=n.length,o=new Array(i);o[0]=h;var s={};for(var c in t)hasOwnProperty.call(t,c)&&(s[c]=t[c]);s.originalType=e,s.mdxType="string"==typeof e?e:r,o[1]=s;for(var l=2;l{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>c,default:()=>d,frontMatter:()=>s,metadata:()=>l,toc:()=>u});var a=n(7462),r=n(3366),i=(n(7294),n(3905)),o=["components"],s={id:"architecture",title:"Architecture"},c=void 0,l={unversionedId:"architecture",id:"architecture",title:"Architecture",description:"In this section a global overview of the Jitsi infrastructure is provided. If you just started contributing to the project, we highly recommend reading this section thoroughly.",source:"@site/docs/architecture.md",sourceDirName:".",slug:"/architecture",permalink:"/handbook/docs/architecture",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/architecture.md",tags:[],version:"current",lastUpdatedAt:1695203964,formattedLastUpdatedAt:"Sep 20, 2023",frontMatter:{id:"architecture",title:"Architecture"},sidebar:"docs",previous:{title:"Introduction",permalink:"/handbook/docs/intro"},next:{title:"Security",permalink:"/handbook/docs/security"}},p={},u=[{value:"Components",id:"components",level:2},{value:"Architecture Diagram",id:"architecture-diagram",level:2},{value:"Code Map",id:"code-map",level:2},{value:"Testing",id:"testing",level:2}],h={toc:u};function d(e){var t=e.components,n=(0,r.Z)(e,o);return(0,i.kt)("wrapper",(0,a.Z)({},h,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"In this section a global overview of the Jitsi infrastructure is provided. If you just started contributing to the project, we highly recommend reading this section thoroughly."),(0,i.kt)("h2",{id:"components"},"Components"),(0,i.kt)("p",null,"Jitsi comprises a ",(0,i.kt)("a",{parentName:"p",href:"https://jitsi.org/projects/"},"collection of projects"),":"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://jitsi.org/jitsi-meet"},"Jitsi Meet")," - WebRTC compatible JavaScript application that uses Jitsi Videobridge to provide high quality, scalable video conferences. Build upon React and React Native."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://jitsi.org/jitsi-videobridge"},"Jitsi Videobridge (JVB)")," - WebRTC compatible server designed to route video streams amongst participants in a conference."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://github.com/jitsi/jicofo"},"Jitsi Conference Focus (jicofo)")," - server-side focus component used in Jitsi Meet conferences that manages media sessions and acts as load balancer between each of the participants and the videobridge."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://github.com/jitsi/jigasi"},"Jitsi Gateway to SIP (jigasi)")," - server-side application that allows regular SIP clients to join Jitsi Meet conferences"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://github.com/jitsi/jibri"},"Jitsi Broadcasting Infrastructure (jibri)")," - set of tools for recording and/or streaming a Jitsi Meet conference that works by launching a Chrome instance rendered in a virtual framebuffer and capturing and encoding the output with ffmpeg.")),(0,i.kt)("p",null,"External Software used by Jitsi:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://prosody.im/"},"Prosody")," - XMPP server used for signalling")),(0,i.kt)("h2",{id:"architecture-diagram"},"Architecture Diagram"),(0,i.kt)("p",null,"The individual connections between the previously described components, as well as their external integrations are described in the figure below."),(0,i.kt)("p",null,(0,i.kt)("img",{parentName:"p",src:"https://raw.githubusercontent.com/jitsi/handbook/master/docs/assets/ArchitectureDiagram.png",alt:null})),(0,i.kt)("p",null,"The external connections can be categorized into two main groups. Firstly, the connections between clients that request a video or audio connection performed through remote requests and data streams. The second category of external connections is those to external services that help store recordings, stream recordings, stream videos or help with creating meetings."),(0,i.kt)("h2",{id:"code-map"},"Code Map"),(0,i.kt)("p",null,"In this section we will look at the main parts of the codebase and see what they can be used for."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"./react/features"),"\nThis folder is where it is best to start writing your code, as it contains most of the app components that are used in the apps on Android and iOS, as well as on the web version. This source folder is split up into all the different features that Jitsi has to offer, such as authentication, chat interaction, keyboard shortcuts, screenshot capture, remote control and virtual background. Each of these features has its own folder in this map, which is then again split up to keep a hierarchy and consistency throughout the code."),(0,i.kt)("p",null,"Each feature folder consists of a subfolder called components, in this folder all of the React, or React Native for mobile, components are expressed. Usually in this folder there will be a separation between native and web components, however in some cases the same component could be used for both Android, iOS and web browser, in which case there is no separation made."),(0,i.kt)("p",null,"As stated before, the codebase mostly consists of React and React Native, which is the React version for mobile applications. Most of the features make use of the so-called class component by React ",(0,i.kt)("sup",{parentName:"p",id:"fnref-class-comp"},(0,i.kt)("a",{parentName:"sup",href:"#fn-class-comp",className:"footnote-ref"},"class-comp")),", however some new features start to use the new way to write functional components by using hooks",(0,i.kt)("sup",{parentName:"p",id:"fnref-func-comp"},(0,i.kt)("a",{parentName:"sup",href:"#fn-func-comp",className:"footnote-ref"},"func-comp")),"."),(0,i.kt)("p",null,"The application makes use of React Redux as well, this is used as a general state store to keep track of important parameters that are used throughout the application. More on React Redux can be found here ",(0,i.kt)("sup",{parentName:"p",id:"fnref-react-redux"},(0,i.kt)("a",{parentName:"sup",href:"#fn-react-redux",className:"footnote-ref"},"react-redux")),"."),(0,i.kt)("p",null,"Most features also contain a file called ",(0,i.kt)("inlineCode",{parentName:"p"},"middleware.js"),". This file acts as a bridge between the component and the functionality of the rest of the application."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"./modules/external-api"),"\nIn this folder, the external API can be found. This API can be used in various events like participants joining/leaving the meeting, changes in avatars or chat, as well as errors in using the microphone or camera."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"./android and ./ios"),"\nBoth of these folders contain the basics of the Android and iOS app respectively. However, the code for the application itself and its components can be found in the ",(0,i.kt)("strong",{parentName:"p"},"react/features")," folder, which is explained earlier in this section."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"./conference.js"),"\nThis file can be found at the root of the project, and contains the foundation of any interaction between a user and a conference room. This consists of setting up a connection to it, joining the meeting room, muting and unmuting, but also functions to gather information about the participants that are in the room."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"./lang"),"\nThis folder contains all the different translations that are present in Jitsi Meet. The translations can be found in the code with each of the keys in the translation maps that can be found in ",(0,i.kt)("inlineCode",{parentName:"p"},"main-[language].json")," files."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"./css"),"\nThis folder contains all the css that is used in the project. The files (mostly .scss files",(0,i.kt)("sup",{parentName:"p",id:"fnref-scss"},(0,i.kt)("a",{parentName:"sup",href:"#fn-scss",className:"footnote-ref"},"scss")),") are split up into features like the React features that they are used in."),(0,i.kt)("h2",{id:"testing"},"Testing"),(0,i.kt)("p",null,"The main form of testing code changes is done through torture tests, next to this the code is tested manually."),(0,i.kt)("p",null,"The torture tests are located in a separate repository, ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet-torture"},"Jitsi Meet Torture"),". The project contains end to end tests for several key functions such as peer to peer and invites. The testing can be done for iOS, Android and web, which are all the platforms that Jitsi Meet can be used on. The testing is done automatically for pull requests by project members, where it is used in combination with the continuous integration by a Jenkins instance running the tests, testing on the ",(0,i.kt)("a",{parentName:"p",href:"https://meet.jit.si"},"meet.jit.si")," instance. Other members can run the tests locally. The test results can be viewed on an automatically generated web page."),(0,i.kt)("p",null,"Manual testing is performed while doing code reviews, however there are also testing releases that can be freely downloaded and deployed, or can be used on the ",(0,i.kt)("a",{parentName:"p",href:"https://beta.meet.jit.si/"},"beta test server"),"."))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/53784768.c421067d.js b/assets/js/53784768.d211b978.js similarity index 98% rename from assets/js/53784768.c421067d.js rename to assets/js/53784768.d211b978.js index b34efe823..07a6fe7e1 100644 --- a/assets/js/53784768.c421067d.js +++ b/assets/js/53784768.d211b978.js @@ -1 +1 @@ -"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[9188],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>m});var r=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function a(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var d=r.createContext({}),l=function(e){var t=r.useContext(d),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},u=function(e){var t=l(e.components);return r.createElement(d.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},p=r.forwardRef((function(e,t){var n=e.components,i=e.mdxType,o=e.originalType,d=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),p=l(n),m=i,g=p["".concat(d,".").concat(m)]||p[m]||c[m]||o;return n?r.createElement(g,a(a({ref:t},u),{},{components:n})):r.createElement(g,a({ref:t},u))}));function m(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=n.length,a=new Array(o);a[0]=p;var s={};for(var d in t)hasOwnProperty.call(t,d)&&(s[d]=t[d]);s.originalType=e,s.mdxType="string"==typeof e?e:i,a[1]=s;for(var l=2;l{n.r(t),n.d(t,{assets:()=>u,contentTitle:()=>d,default:()=>m,frontMatter:()=>s,metadata:()=>l,toc:()=>c});var r=n(7462),i=n(3366),o=(n(7294),n(3905)),a=["components"],s={id:"devops-guide-requirements",title:"Requirements"},d=void 0,l={unversionedId:"devops-guide/devops-guide-requirements",id:"devops-guide/devops-guide-requirements",title:"Requirements",description:"Jitsi Meet is a real-time system.",source:"@site/docs/devops-guide/requirements.md",sourceDirName:"devops-guide",slug:"/devops-guide/devops-guide-requirements",permalink:"/handbook/docs/devops-guide/devops-guide-requirements",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/devops-guide/requirements.md",tags:[],version:"current",lastUpdatedAt:1694635654,formattedLastUpdatedAt:"Sep 13, 2023",frontMatter:{id:"devops-guide-requirements",title:"Requirements"},sidebar:"docs",previous:{title:"Deployment",permalink:"/handbook/docs/category/deployment"},next:{title:"Debian/Ubuntu server",permalink:"/handbook/docs/devops-guide/devops-guide-quickstart"}},u={},c=[],p={toc:c};function m(e){var t=e.components,n=(0,i.Z)(e,a);return(0,o.kt)("wrapper",(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("admonition",{type:"note"},(0,o.kt)("p",{parentName:"admonition"},"Jitsi Meet is a real-time system.\nRequirements are very different from a web server and depend on many factors.\nMiscalculations can very easily destroy basic functionality rather than cause slow performance.\nAvoid adding other functions to your Jitsi Meet setup as it can harm performance and complicate optimizations."),(0,o.kt)("p",{parentName:"admonition"},"Note that Jitsi Meet design priorizes scalability by adding servers on using a huge server. Check Jitsi-videobridge documentation on adding several bridges to a Jitsi Meet server, and OCTO to go even beyond that (federation of Jitsi Meet servers). If you feel that you are a network and server administration newbie, don't even think of going there.")),(0,o.kt)("h1",{id:"jitsi-meet-needs-by-order-of-importance"},"Jitsi Meet needs, by order of importance"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Network link: basic speed and reliability are essential. Check speed against the provider claims using any download tool (or ftp), and\nverify latency using a tool such as iperf3.\nExact calculation is very complex and depend on many optimisations and tricks, but you should at least remember these numbers on resolution:\n180 = 200 kbits/s\n360 = 500 kbits/s\n720 (HD) = 2500 kbits/s\n4k = 10 Mbits/s\nSo don't expect to have 20 users using 4K on a server with 100Mbits/s upload and download.\nFor a friends/small organization server, 1 Gbits/s will often be enough but for a serious server 10 Gbits/s\nis advisable. Several (or many...) bridges having each a 10 Gbits/s link are used by big deployments.")),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},"These requirements concern the videobridge. If there are only external videobridges (as can be the case on high end Jitsi Meet servers), network performance matters much less.")),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("strong",{parentName:"li"},"RAM:")," it's usually suggested to get 8 GB.\nFor small meetings you can get away with 4 GB, for test servers or very small meetings you can try to use 2 GB.\nFor big meetings it's suggested to go the scalable way over getting huge amounts of memory.")),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("p",{parentName:"li"},(0,o.kt)("strong",{parentName:"p"},"CPU:")," very low processor performance can seriously harm a real time system, especially when using a shared server (where your CPU performance can be stolen by other customers of your hoster, check on 'dedicated CPU' if you are getting a VPS, rather than a physical server). However, a consideration is that a Jitsi Meet component, Prosody, can only use ONE (1) core. So getting a lot of cores, let's say more than 32, is not always useful. For a basic server, 4 dedicated cores can be enough.")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("p",{parentName:"li"},(0,o.kt)("strong",{parentName:"p"},"Disk:")," unless you are doing heavy logging or have very specific needs, you can get away with 20 Gbytes of standard hard disk.\nSSD are more a nice to have than a necessity."))),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},"If you want additional services, requirements can go up.")),(0,o.kt)("h1",{id:"recording"},"Recording"),(0,o.kt)("p",null,"Jibri needs ONE system per recording.\nOne Jibri instance = one meeting. For 5 meetings recorded simultaneously, you need 5 Jibris.\nThere is no workaround to that.\nIf you are knowledgeable, you can setup Jibris in containers and use a big server to save a bit on resources but that's about it."),(0,o.kt)("p",null,"Jibri RAM, CPU and hard disk needs are far higher than Jitsi Meet itself, as it does video encoding.\nFor ",(0,o.kt)("inlineCode",{parentName:"p"},"1080x720")," you currently need at least 8 GB RAM, for ",(0,o.kt)("inlineCode",{parentName:"p"},"1280x1024")," 12 GB (this is for recording a ",(0,o.kt)("strong",{parentName:"p"},"single")," meeting).\nFor cloud storage you will need at least SSD drives.\nIf memory is not sufficient, CPU can't encode fast enough or hard disk is not fast enough, recordings will fail."),(0,o.kt)("p",null,"While Jibri and Jitsi Meet can technically be hosted in a single server, it's not recommended because Jibri is a resource drain and it can harm Jitsi Meet performance, and can exhaust disk space and stop Jitsi Meet function altogether."))}m.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[9188],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>m});var r=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function a(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var d=r.createContext({}),l=function(e){var t=r.useContext(d),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},u=function(e){var t=l(e.components);return r.createElement(d.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},p=r.forwardRef((function(e,t){var n=e.components,i=e.mdxType,o=e.originalType,d=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),p=l(n),m=i,g=p["".concat(d,".").concat(m)]||p[m]||c[m]||o;return n?r.createElement(g,a(a({ref:t},u),{},{components:n})):r.createElement(g,a({ref:t},u))}));function m(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=n.length,a=new Array(o);a[0]=p;var s={};for(var d in t)hasOwnProperty.call(t,d)&&(s[d]=t[d]);s.originalType=e,s.mdxType="string"==typeof e?e:i,a[1]=s;for(var l=2;l{n.r(t),n.d(t,{assets:()=>u,contentTitle:()=>d,default:()=>m,frontMatter:()=>s,metadata:()=>l,toc:()=>c});var r=n(7462),i=n(3366),o=(n(7294),n(3905)),a=["components"],s={id:"devops-guide-requirements",title:"Requirements"},d=void 0,l={unversionedId:"devops-guide/devops-guide-requirements",id:"devops-guide/devops-guide-requirements",title:"Requirements",description:"Jitsi Meet is a real-time system.",source:"@site/docs/devops-guide/requirements.md",sourceDirName:"devops-guide",slug:"/devops-guide/devops-guide-requirements",permalink:"/handbook/docs/devops-guide/devops-guide-requirements",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/devops-guide/requirements.md",tags:[],version:"current",lastUpdatedAt:1695203964,formattedLastUpdatedAt:"Sep 20, 2023",frontMatter:{id:"devops-guide-requirements",title:"Requirements"},sidebar:"docs",previous:{title:"Deployment",permalink:"/handbook/docs/category/deployment"},next:{title:"Debian/Ubuntu server",permalink:"/handbook/docs/devops-guide/devops-guide-quickstart"}},u={},c=[],p={toc:c};function m(e){var t=e.components,n=(0,i.Z)(e,a);return(0,o.kt)("wrapper",(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("admonition",{type:"note"},(0,o.kt)("p",{parentName:"admonition"},"Jitsi Meet is a real-time system.\nRequirements are very different from a web server and depend on many factors.\nMiscalculations can very easily destroy basic functionality rather than cause slow performance.\nAvoid adding other functions to your Jitsi Meet setup as it can harm performance and complicate optimizations."),(0,o.kt)("p",{parentName:"admonition"},"Note that Jitsi Meet design priorizes scalability by adding servers on using a huge server. Check Jitsi-videobridge documentation on adding several bridges to a Jitsi Meet server, and OCTO to go even beyond that (federation of Jitsi Meet servers). If you feel that you are a network and server administration newbie, don't even think of going there.")),(0,o.kt)("h1",{id:"jitsi-meet-needs-by-order-of-importance"},"Jitsi Meet needs, by order of importance"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Network link: basic speed and reliability are essential. Check speed against the provider claims using any download tool (or ftp), and\nverify latency using a tool such as iperf3.\nExact calculation is very complex and depend on many optimisations and tricks, but you should at least remember these numbers on resolution:\n180 = 200 kbits/s\n360 = 500 kbits/s\n720 (HD) = 2500 kbits/s\n4k = 10 Mbits/s\nSo don't expect to have 20 users using 4K on a server with 100Mbits/s upload and download.\nFor a friends/small organization server, 1 Gbits/s will often be enough but for a serious server 10 Gbits/s\nis advisable. Several (or many...) bridges having each a 10 Gbits/s link are used by big deployments.")),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},"These requirements concern the videobridge. If there are only external videobridges (as can be the case on high end Jitsi Meet servers), network performance matters much less.")),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("strong",{parentName:"li"},"RAM:")," it's usually suggested to get 8 GB.\nFor small meetings you can get away with 4 GB, for test servers or very small meetings you can try to use 2 GB.\nFor big meetings it's suggested to go the scalable way over getting huge amounts of memory.")),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("p",{parentName:"li"},(0,o.kt)("strong",{parentName:"p"},"CPU:")," very low processor performance can seriously harm a real time system, especially when using a shared server (where your CPU performance can be stolen by other customers of your hoster, check on 'dedicated CPU' if you are getting a VPS, rather than a physical server). However, a consideration is that a Jitsi Meet component, Prosody, can only use ONE (1) core. So getting a lot of cores, let's say more than 32, is not always useful. For a basic server, 4 dedicated cores can be enough.")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("p",{parentName:"li"},(0,o.kt)("strong",{parentName:"p"},"Disk:")," unless you are doing heavy logging or have very specific needs, you can get away with 20 Gbytes of standard hard disk.\nSSD are more a nice to have than a necessity."))),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},"If you want additional services, requirements can go up.")),(0,o.kt)("h1",{id:"recording"},"Recording"),(0,o.kt)("p",null,"Jibri needs ONE system per recording.\nOne Jibri instance = one meeting. For 5 meetings recorded simultaneously, you need 5 Jibris.\nThere is no workaround to that.\nIf you are knowledgeable, you can setup Jibris in containers and use a big server to save a bit on resources but that's about it."),(0,o.kt)("p",null,"Jibri RAM, CPU and hard disk needs are far higher than Jitsi Meet itself, as it does video encoding.\nFor ",(0,o.kt)("inlineCode",{parentName:"p"},"1080x720")," you currently need at least 8 GB RAM, for ",(0,o.kt)("inlineCode",{parentName:"p"},"1280x1024")," 12 GB (this is for recording a ",(0,o.kt)("strong",{parentName:"p"},"single")," meeting).\nFor cloud storage you will need at least SSD drives.\nIf memory is not sufficient, CPU can't encode fast enough or hard disk is not fast enough, recordings will fail."),(0,o.kt)("p",null,"While Jibri and Jitsi Meet can technically be hosted in a single server, it's not recommended because Jibri is a resource drain and it can harm Jitsi Meet performance, and can exhaust disk space and stop Jitsi Meet function altogether."))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/53c36cd9.c29f67b3.js b/assets/js/53c36cd9.bea40b18.js similarity index 96% rename from assets/js/53c36cd9.c29f67b3.js rename to assets/js/53c36cd9.bea40b18.js index 764d0021b..90eff1137 100644 --- a/assets/js/53c36cd9.c29f67b3.js +++ b/assets/js/53c36cd9.bea40b18.js @@ -1 +1 @@ -"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[1893],{3905:(e,t,r)=>{r.d(t,{Zo:()=>c,kt:()=>f});var o=r(7294);function n(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,o)}return r}function a(e){for(var t=1;t=0||(n[r]=e[r]);return n}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(n[r]=e[r])}return n}var s=o.createContext({}),u=function(e){var t=o.useContext(s),r=t;return e&&(r="function"==typeof e?e(t):a(a({},t),e)),r},c=function(e){var t=u(e.components);return o.createElement(s.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},p=o.forwardRef((function(e,t){var r=e.components,n=e.mdxType,i=e.originalType,s=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),p=u(r),f=n,g=p["".concat(s,".").concat(f)]||p[f]||d[f]||i;return r?o.createElement(g,a(a({ref:t},c),{},{components:r})):o.createElement(g,a({ref:t},c))}));function f(e,t){var r=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var i=r.length,a=new Array(i);a[0]=p;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:n,a[1]=l;for(var u=2;u{r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>s,default:()=>f,frontMatter:()=>l,metadata:()=>u,toc:()=>d});var o=r(7462),n=r(3366),i=(r(7294),r(3905)),a=["components"],l={id:"user-guide-jitsi-meet-for-google-calendar",title:"Jitsi Meet for Google Calendar",sidebar_label:"Jitsi Meet for Google Calendar"},s=void 0,u={unversionedId:"user-guide/user-guide-jitsi-meet-for-google-calendar",id:"user-guide/user-guide-jitsi-meet-for-google-calendar",title:"Jitsi Meet for Google Calendar",description:"Welcome to the user guide!",source:"@site/docs/user-guide/jitsi-meet-for-google-calendar.md",sourceDirName:"user-guide",slug:"/user-guide/user-guide-jitsi-meet-for-google-calendar",permalink:"/handbook/docs/user-guide/user-guide-jitsi-meet-for-google-calendar",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/user-guide/jitsi-meet-for-google-calendar.md",tags:[],version:"current",lastUpdatedAt:1694635654,formattedLastUpdatedAt:"Sep 13, 2023",frontMatter:{id:"user-guide-jitsi-meet-for-google-calendar",title:"Jitsi Meet for Google Calendar",sidebar_label:"Jitsi Meet for Google Calendar"},sidebar:"docs",previous:{title:"Use Jitsi Meet on Mobile",permalink:"/handbook/docs/user-guide/user-guide-jitsi-meet-on-mobile"},next:{title:"Keyboard shortcuts",permalink:"/handbook/docs/user-guide/keyboard-shortcuts"}},c={},d=[],p={toc:d};function f(e){var t=e.components,r=(0,n.Z)(e,a);return(0,i.kt)("wrapper",(0,o.Z)({},p,r,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"Welcome to the user guide!"),(0,i.kt)("p",null,"Check back soon!"))}f.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[1893],{3905:(e,t,r)=>{r.d(t,{Zo:()=>c,kt:()=>f});var o=r(7294);function n(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,o)}return r}function a(e){for(var t=1;t=0||(n[r]=e[r]);return n}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(n[r]=e[r])}return n}var s=o.createContext({}),u=function(e){var t=o.useContext(s),r=t;return e&&(r="function"==typeof e?e(t):a(a({},t),e)),r},c=function(e){var t=u(e.components);return o.createElement(s.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},p=o.forwardRef((function(e,t){var r=e.components,n=e.mdxType,i=e.originalType,s=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),p=u(r),f=n,g=p["".concat(s,".").concat(f)]||p[f]||d[f]||i;return r?o.createElement(g,a(a({ref:t},c),{},{components:r})):o.createElement(g,a({ref:t},c))}));function f(e,t){var r=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var i=r.length,a=new Array(i);a[0]=p;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:n,a[1]=l;for(var u=2;u{r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>s,default:()=>f,frontMatter:()=>l,metadata:()=>u,toc:()=>d});var o=r(7462),n=r(3366),i=(r(7294),r(3905)),a=["components"],l={id:"user-guide-jitsi-meet-for-google-calendar",title:"Jitsi Meet for Google Calendar",sidebar_label:"Jitsi Meet for Google Calendar"},s=void 0,u={unversionedId:"user-guide/user-guide-jitsi-meet-for-google-calendar",id:"user-guide/user-guide-jitsi-meet-for-google-calendar",title:"Jitsi Meet for Google Calendar",description:"Welcome to the user guide!",source:"@site/docs/user-guide/jitsi-meet-for-google-calendar.md",sourceDirName:"user-guide",slug:"/user-guide/user-guide-jitsi-meet-for-google-calendar",permalink:"/handbook/docs/user-guide/user-guide-jitsi-meet-for-google-calendar",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/user-guide/jitsi-meet-for-google-calendar.md",tags:[],version:"current",lastUpdatedAt:1695203964,formattedLastUpdatedAt:"Sep 20, 2023",frontMatter:{id:"user-guide-jitsi-meet-for-google-calendar",title:"Jitsi Meet for Google Calendar",sidebar_label:"Jitsi Meet for Google Calendar"},sidebar:"docs",previous:{title:"Use Jitsi Meet on Mobile",permalink:"/handbook/docs/user-guide/user-guide-jitsi-meet-on-mobile"},next:{title:"Keyboard shortcuts",permalink:"/handbook/docs/user-guide/keyboard-shortcuts"}},c={},d=[],p={toc:d};function f(e){var t=e.components,r=(0,n.Z)(e,a);return(0,i.kt)("wrapper",(0,o.Z)({},p,r,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"Welcome to the user guide!"),(0,i.kt)("p",null,"Check back soon!"))}f.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/53d3d234.08c5286f.js b/assets/js/53d3d234.26af9d4d.js similarity index 99% rename from assets/js/53d3d234.08c5286f.js rename to assets/js/53d3d234.26af9d4d.js index 1554134bf..d79781320 100644 --- a/assets/js/53d3d234.08c5286f.js +++ b/assets/js/53d3d234.26af9d4d.js @@ -1 +1 @@ -"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[9192],{3905:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>h});var a=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var s=a.createContext({}),c=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},d=function(e){var t=c(e.components);return a.createElement(s.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},u=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,s=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),u=c(n),h=i,g=u["".concat(s,".").concat(h)]||u[h]||p[h]||r;return n?a.createElement(g,o(o({ref:t},d),{},{components:n})):a.createElement(g,o({ref:t},d))}));function h(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,o=new Array(r);o[0]=u;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:i,o[1]=l;for(var c=2;c{n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>s,default:()=>h,frontMatter:()=>l,metadata:()=>c,toc:()=>p});var a=n(7462),i=n(3366),r=(n(7294),n(3905)),o=["components"],l={id:"dev-guide-iframe-events",title:"Events"},s=void 0,c={unversionedId:"dev-guide/dev-guide-iframe-events",id:"dev-guide/dev-guide-iframe-events",title:"Events",description:"The JitsiMeetExternalAPI object implements the [EventEmitter] API for emitting and listening for events.",source:"@site/docs/dev-guide/iframe-events.md",sourceDirName:"dev-guide",slug:"/dev-guide/dev-guide-iframe-events",permalink:"/handbook/docs/dev-guide/dev-guide-iframe-events",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/dev-guide/iframe-events.md",tags:[],version:"current",lastUpdatedAt:1694635654,formattedLastUpdatedAt:"Sep 13, 2023",frontMatter:{id:"dev-guide-iframe-events",title:"Events"},sidebar:"docs",previous:{title:"Commands",permalink:"/handbook/docs/dev-guide/dev-guide-iframe-commands"},next:{title:"React SDK",permalink:"/handbook/docs/dev-guide/dev-guide-react-sdk"}},d={},p=[{value:"cameraError",id:"cameraerror",level:3},{value:"avatarChanged",id:"avatarchanged",level:3},{value:"audioAvailabilityChanged",id:"audioavailabilitychanged",level:3},{value:"audioMuteStatusChanged",id:"audiomutestatuschanged",level:3},{value:"breakoutRoomsUpdated",id:"breakoutroomsupdated",level:3},{value:"browserSupport",id:"browsersupport",level:3},{value:"contentSharingParticipantsChanged",id:"contentsharingparticipantschanged",level:3},{value:"dataChannelOpened",id:"datachannelopened",level:3},{value:"endpointTextMessageReceived",id:"endpointtextmessagereceived",level:3},{value:"nonParticipantMessageReceived",id:"nonparticipantmessagereceived",level:3},{value:"faceLandmarkDetected",id:"facelandmarkdetected",level:3},{value:"errorOccurred",id:"erroroccurred",level:3},{value:"knockingParticipant",id:"knockingparticipant",level:3},{value:"largeVideoChanged",id:"largevideochanged",level:3},{value:"log",id:"log",level:3},{value:"micError",id:"micerror",level:3},{value:"screenSharingStatusChanged",id:"screensharingstatuschanged",level:3},{value:"dominantSpeakerChanged",id:"dominantspeakerchanged",level:3},{value:"raiseHandUpdated",id:"raisehandupdated",level:3},{value:"tileViewChanged",id:"tileviewchanged",level:3},{value:"chatUpdated",id:"chatupdated",level:3},{value:"incomingMessage",id:"incomingmessage",level:3},{value:"mouseEnter",id:"mouseenter",level:3},{value:"mouseLeave",id:"mouseleave",level:3},{value:"mouseMove",id:"mousemove",level:3},{value:"participantMenuButtonClick",id:"participantmenubuttonclick",level:3},{value:"toolbarButtonClicked",id:"toolbarbuttonclicked",level:3},{value:"outgoingMessage",id:"outgoingmessage",level:3},{value:"displayNameChange",id:"displaynamechange",level:3},{value:"deviceListChanged",id:"devicelistchanged",level:3},{value:"emailChange",id:"emailchange",level:3},{value:"feedbackSubmitted",id:"feedbacksubmitted",level:3},{value:"filmstripDisplayChanged",id:"filmstripdisplaychanged",level:3},{value:"moderationStatusChanged",id:"moderationstatuschanged",level:3},{value:"moderationParticipantApproved",id:"moderationparticipantapproved",level:3},{value:"moderationParticipantRejected",id:"moderationparticipantrejected",level:3},{value:"notificationTriggered",id:"notificationtriggered",level:3},{value:"participantJoined",id:"participantjoined",level:3},{value:"participantKickedOut",id:"participantkickedout",level:3},{value:"participantLeft",id:"participantleft",level:3},{value:"participantRoleChanged",id:"participantrolechanged",level:3},{value:"participantsPaneToggled",id:"participantspanetoggled",level:3},{value:"passwordRequired",id:"passwordrequired",level:3},{value:"videoConferenceJoined",id:"videoconferencejoined",level:3},{value:"videoConferenceLeft",id:"videoconferenceleft",level:3},{value:"videoAvailabilityChanged",id:"videoavailabilitychanged",level:3},{value:"videoMuteStatusChanged",id:"videomutestatuschanged",level:3},{value:"videoQualityChanged",id:"videoqualitychanged",level:3},{value:"readyToClose",id:"readytoclose",level:3},{value:"recordingLinkAvailable",id:"recordinglinkavailable",level:3},{value:"recordingStatusChanged",id:"recordingstatuschanged",level:3},{value:"subjectChange",id:"subjectchange",level:3},{value:"suspendDetected",id:"suspenddetected",level:3},{value:"peerConnectionFailure",id:"peerconnectionfailure",level:3},{value:"whiteboardStatusChanged",id:"whiteboardstatuschanged",level:3},{value:"p2pStatusChanged",id:"p2pstatuschanged",level:3}],u={toc:p};function h(e){var t=e.components,n=(0,i.Z)(e,o);return(0,r.kt)("wrapper",(0,a.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"JitsiMeetExternalAPI")," object implements the ",(0,r.kt)("a",{parentName:"p",href:"https://nodejs.org/api/events.html"},"EventEmitter")," API for emitting and listening for events."),(0,r.kt)("p",null,"You can add event listeners to the embedded Jitsi Meet using the ",(0,r.kt)("strong",{parentName:"p"},(0,r.kt)("inlineCode",{parentName:"strong"},"addListener"))," method:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"api.addListener(event, listener);\n")),(0,r.kt)("p",null,"If you want to remove a listener you can use the ",(0,r.kt)("strong",{parentName:"p"},(0,r.kt)("inlineCode",{parentName:"strong"},"removeListener"))," method:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"api.removeListener(event, listener);\n")),(0,r.kt)("p",null,"The ",(0,r.kt)("strong",{parentName:"p"},(0,r.kt)("inlineCode",{parentName:"strong"},"event"))," parameter is a string object with the name of the event."),(0,r.kt)("p",null,"The ",(0,r.kt)("strong",{parentName:"p"},(0,r.kt)("inlineCode",{parentName:"strong"},"listener"))," parameter is a function object with one argument that creates a notification when the event occurs along with related event data."),(0,r.kt)("p",null,"The following events are currently supported:"),(0,r.kt)("h3",{id:"cameraerror"},"cameraError"),(0,r.kt)("p",null,"Provides event notifications about Jitsi Meet having failed to access the meeting camera."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n type: string, // A constant representing the overall type of the error.\n message: string // Additional information about the error.\n}\n")),(0,r.kt)("h3",{id:"avatarchanged"},"avatarChanged"),(0,r.kt)("p",null,"Provides event notifications about changes to a participant's avatar."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n id: string, // the id of the participant that changed his avatar.\n avatarURL: string // the new avatar URL.\n}\n")),(0,r.kt)("h3",{id:"audioavailabilitychanged"},"audioAvailabilityChanged"),(0,r.kt)("p",null,"Provides event notifications about changes to audio availability status."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n available: boolean // new available status - boolean\n}\n")),(0,r.kt)("h3",{id:"audiomutestatuschanged"},"audioMuteStatusChanged"),(0,r.kt)("p",null,"Provides event notifications about changes to audio mute status."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n muted: boolean // new muted status - boolean\n}\n")),(0,r.kt)("h3",{id:"breakoutroomsupdated"},"breakoutRoomsUpdated"),(0,r.kt)("p",null,"Provides notifications about breakout rooms changes."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n [roomId]: {\n id: string,\n jid: string,\n name: string,\n isMainRoom: true | undefined,\n participants: {\n [participantJid]: {\n displayName: string,\n jid: string,\n role: string\n }\n }\n },\n ...\n}\n")),(0,r.kt)("h3",{id:"browsersupport"},"browserSupport"),(0,r.kt)("p",null,"Provides event notifications about the current browser support."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n supported: boolean\n}\n")),(0,r.kt)("h3",{id:"contentsharingparticipantschanged"},"contentSharingParticipantsChanged"),(0,r.kt)("p",null,"Provides real-time list of currently screen sharing participant ID's."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},'{\n data: ["particId1", "particId2", ...]\n}\n')),(0,r.kt)("h3",{id:"datachannelopened"},"dataChannelOpened"),(0,r.kt)("p",null,"Indicates the data channel is open and thus messages can be sent over it."),(0,r.kt)("h3",{id:"endpointtextmessagereceived"},"endpointTextMessageReceived"),(0,r.kt)("p",null,"Provides event notifications about a text messages received through data channels."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n senderInfo: {\n jid: string, // the jid of the sender\n id: string // the participant id of the sender\n },\n eventData: {\n name: string, // the name of the datachannel event: `endpoint-text-message`\n text: string // the received text from the sender\n }\n}\n")),(0,r.kt)("h3",{id:"nonparticipantmessagereceived"},"nonParticipantMessageReceived"),(0,r.kt)("p",null,"Provides event notifications about a messages sent by a non-participant, e.g. a custom prosody message."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n id: string, // the id of the message, may be null\n message: string // the message received\n}\n")),(0,r.kt)("h3",{id:"facelandmarkdetected"},"faceLandmarkDetected"),(0,r.kt)("p",null,"Provides event notifications when a face landmark is detected"),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n faceBox: {\n left: number, // face bounding box distance as percentage from the left video edge\n right: number // face bounding box distance as percentage from the right video edge\n width: number // face bounding box width as percentage of the total video width\n }, // this might be undefined if config.faceLandmarks.faceCenteringThreshold is not passed\n faceExpression: string // check https://github.com/jitsi/jitsi-meet/blob/master/react/features/face-landmarks/constants.js#L3 for available values\n}\n")),(0,r.kt)("h3",{id:"erroroccurred"},"errorOccurred"),(0,r.kt)("p",null,"Provides event notifications about an error which has occurred."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n details: Object?, // additional error details\n message: string?, // the error message\n name: string, // the coded name of the error\n type: string, // error type/source, one of : 'CONFIG', 'CONNECTION', 'CONFERENCE'\n isFatal: boolean // whether this is a fatal error which triggered a reconnect overlay or not\n}\n")),(0,r.kt)("h3",{id:"knockingparticipant"},"knockingParticipant"),(0,r.kt)("p",null,"Provides event notifications about a knocking participant in the lobby."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n participant: {\n // the id and name of the participant that is currently knocking in the lobby\n id: string,\n name: string\n }\n}\n")),(0,r.kt)("h3",{id:"largevideochanged"},"largeVideoChanged"),(0,r.kt)("p",null,"Provides event notifications about changes in the large video display."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n id: string // id of the participant that is now on large video in the stage view.\n}\n")),(0,r.kt)("h3",{id:"log"},"log"),(0,r.kt)("p",null,"Provides log event notifications with the log level being one of the values specified in the ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet/blob/master/config.js"},"config.js")," file in the ",(0,r.kt)("strong",{parentName:"p"},(0,r.kt)("inlineCode",{parentName:"strong"},"apiLogLevels"))," property (if not specified the event does not fire)."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n logLevel: string, // A constant representing the log type (info, error, debug, warn).\n args: string // Additional log information.\n}\n")),(0,r.kt)("h3",{id:"micerror"},"micError"),(0,r.kt)("p",null,"Provides event notifications about Jitsi Meet issues with mic access."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n type: string, // A constant representing the overall type of the error.\n message: string // Additional information about the error.\n}\n")),(0,r.kt)("h3",{id:"screensharingstatuschanged"},"screenSharingStatusChanged"),(0,r.kt)("p",null,"Provides event notifications about either turning on or off local user screen sharing."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n on: boolean, //whether screen sharing is on\n details: {\n\n // From where the screen sharing is capturing, if known. Values which are\n // passed include 'window', 'screen', 'proxy', 'device'. The value undefined\n // will be passed if the source type is unknown or screen share is off.\n sourceType: string|undefined\n }\n}\n")),(0,r.kt)("h3",{id:"dominantspeakerchanged"},"dominantSpeakerChanged"),(0,r.kt)("p",null,"Provides event notifications about dominant speaker changes."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n id: string //participantId of the new dominant speaker\n}\n")),(0,r.kt)("h3",{id:"raisehandupdated"},"raiseHandUpdated"),(0,r.kt)("p",null,"Provides event notifications about the participant raising/lowering the hand."),(0,r.kt)("p",null,"The listener will receive an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n id: string, // participantId of the user who raises/lowers the hand\n handRaised: number // 0 when hand is lowered and the hand raised timestamp when raised.\n}\n")),(0,r.kt)("h3",{id:"tileviewchanged"},"tileViewChanged"),(0,r.kt)("p",null,"Provides event notifications about entrance or exit from the tile view layout mode."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n enabled: boolean, // whether tile view is not displayed or not\n}\n")),(0,r.kt)("h3",{id:"chatupdated"},"chatUpdated"),(0,r.kt)("p",null,"Provides event notifications about chat state being updated."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n isOpen: boolean, // Whether the chat panel is open or not\n unreadCount: number // The unread messages counter\n}\n")),(0,r.kt)("h3",{id:"incomingmessage"},"incomingMessage"),(0,r.kt)("p",null,"Provides event notifications about incoming chat messages."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n from: string, // The id of the user that sent the message\n nick: string, // the nickname of the user that sent the message\n privateMessage: boolean, // whether this is a private or group message\n message: string // the text of the message\n}\n")),(0,r.kt)("h3",{id:"mouseenter"},"mouseEnter"),(0,r.kt)("p",null,"Provides event notifications when mouse enters the iframe.\nThe listener receives an object with the following structure based on ",(0,r.kt)("a",{parentName:"p",href:"https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent"},"MouseEvent"),":"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n event: {\n clientX,\n clientY,\n movementX,\n movementY,\n offsetX,\n offsetY,\n pageX,\n pageY,\n x,\n y,\n screenX,\n screenY\n }\n}\n")),(0,r.kt)("h3",{id:"mouseleave"},"mouseLeave"),(0,r.kt)("p",null,"Provides event notifications when mouse leaves the iframe.\nThe listener receives an object with the following structure based on ",(0,r.kt)("a",{parentName:"p",href:"https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent"},"MouseEvent"),":"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n event: {\n clientX,\n clientY,\n movementX,\n movementY,\n offsetX,\n offsetY,\n pageX,\n pageY,\n x,\n y,\n screenX,\n screenY\n }\n}\n")),(0,r.kt)("h3",{id:"mousemove"},"mouseMove"),(0,r.kt)("p",null,"Provides event notifications when mouse moves inside the iframe.\nTis event is triggered on an interval which can be configured by overriding the config.js mouseMoveCallbackInterval property."),(0,r.kt)("p",null,"The listener receives an object with the following structure based on ",(0,r.kt)("a",{parentName:"p",href:"https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent"},"MouseEvent"),":"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n event: {\n clientX,\n clientY,\n movementX,\n movementY,\n offsetX,\n offsetY,\n pageX,\n pageY,\n x,\n y,\n screenX,\n screenY\n }\n}\n")),(0,r.kt)("h3",{id:"participantmenubuttonclick"},"participantMenuButtonClick"),(0,r.kt)("p",null,"Provides event notifications about a participant context menu button being clicked."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n key: string, // the pressed button's key. The key is as defined in `toolbarButtons` config,\n participantId: string, // the id of the participant for which the button was clicked,\n preventExecution: boolean // whether the execution of the button click was prevented or not\n}\n")),(0,r.kt)("h3",{id:"toolbarbuttonclicked"},"toolbarButtonClicked"),(0,r.kt)("p",null,"Provides event notifications about a toolbar button being clicked and whether the click routine was executed or not.\nFor overriding a button's click, please use the following config overwrite:\n",(0,r.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet/blob/042a2cb447bd9ff39ab3904e493952787bd30924/config.js#L547"},"https://github.com/jitsi/jitsi-meet/blob/042a2cb447bd9ff39ab3904e493952787bd30924/config.js#L547")),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n key: string, // the pressed button's key. The key is as defined in `toolbarButtons` config,\n preventExecution: boolean // whether the click routine execution was prevented or not.\n}\n")),(0,r.kt)("h3",{id:"outgoingmessage"},"outgoingMessage"),(0,r.kt)("p",null,"Provides event notifications about outgoing chat messages."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n message: string, // the text of the message\n privateMessage: boolean // whether this is a private or group message\n}\n")),(0,r.kt)("h3",{id:"displaynamechange"},"displayNameChange"),(0,r.kt)("p",null,"Provides event notifications about display name changes."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n id: string, // the id of the participant that changed their display name\n displayname: string // the new display name\n}\n")),(0,r.kt)("h3",{id:"devicelistchanged"},"deviceListChanged"),(0,r.kt)("p",null,"Provides event notifications about device list changes."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n devices: Object // the new list of available devices.\n}\n")),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"NOTE:")," The ",(0,r.kt)("strong",{parentName:"p"},(0,r.kt)("inlineCode",{parentName:"strong"},"device"))," object has the same format as the ",(0,r.kt)("strong",{parentName:"p"},(0,r.kt)("inlineCode",{parentName:"strong"},"getAvailableDevices"))," result format."),(0,r.kt)("h3",{id:"emailchange"},"emailChange"),(0,r.kt)("p",null,"Provides event notifications about email changes."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n id: string, // the id of the participant that changed his email\n email: string // the new email\n}\n")),(0,r.kt)("h3",{id:"feedbacksubmitted"},"feedbackSubmitted"),(0,r.kt)("p",null,"Provides event notifications about conference feedback submissions:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n error: string // The error which occurred during submission, if any.\n}\n")),(0,r.kt)("h3",{id:"filmstripdisplaychanged"},"filmstripDisplayChanged"),(0,r.kt)("p",null,"Provides event visibility notifications for the filmstrip that is being updated:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n visible: boolean // Whether or not the filmstrip is displayed or hidden.\n}\n")),(0,r.kt)("h3",{id:"moderationstatuschanged"},"moderationStatusChanged"),(0,r.kt)("p",null,"Provides event notifications about changes to moderation status."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n mediaType: string, // The media type for which moderation changed.\n enabled: boolean // Whether or not moderation changed to enabled.\n}\n")),(0,r.kt)("h3",{id:"moderationparticipantapproved"},"moderationParticipantApproved"),(0,r.kt)("p",null,"Provides event notifications about participants approvals for moderation."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n id: string, // The ID of the participant that got approved.\n mediaType: string // The media type for which the participant was approved.\n}\n")),(0,r.kt)("h3",{id:"moderationparticipantrejected"},"moderationParticipantRejected"),(0,r.kt)("p",null,"Provides event notifications about participants rejections for moderation."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n id: string, // The ID of the participant that got rejected.\n mediaType: string // The media type for which the participant was rejected.\n}\n")),(0,r.kt)("h3",{id:"notificationtriggered"},"notificationTriggered"),(0,r.kt)("p",null,"Provides event notifications when an application notification occurs."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n title: string, // The notification title.\n description: string // The notification description.\n}\n")),(0,r.kt)("h3",{id:"participantjoined"},"participantJoined"),(0,r.kt)("p",null,"Provides event notifications about new participants who join the room."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n id: string, // the id of the participant\n displayName: string // the display name of the participant\n}\n")),(0,r.kt)("h3",{id:"participantkickedout"},"participantKickedOut"),(0,r.kt)("p",null,"Provides event notifications about participants being removed from the room."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n kicked: {\n id: string, // the id of the participant removed from the room\n local: boolean // whether or not the participant is the local particiapnt\n },\n kicker: {\n id: string // the id of the participant who kicked out the other participant\n }\n}\n")),(0,r.kt)("h3",{id:"participantleft"},"participantLeft"),(0,r.kt)("p",null,"Provides event notifications about participants that leave the meeting room."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n id: string // the id of the participant\n}\n")),(0,r.kt)("h3",{id:"participantrolechanged"},"participantRoleChanged"),(0,r.kt)("p",null,"Provides event notifications that fire when the local user role has changed (e.g., none, moderator, participant)."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n id: string // the id of the participant\n role: string // the new role of the participant\n}\n")),(0,r.kt)("h3",{id:"participantspanetoggled"},"participantsPaneToggled"),(0,r.kt)("p",null,"Provides event notifications that fire when the participants pane status changes."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n open: boolean // whether the pane is open or not\n}\n")),(0,r.kt)("h3",{id:"passwordrequired"},"passwordRequired"),(0,r.kt)("p",null,"Provides event notifications that fire when participants fail to join a password protected room."),(0,r.kt)("h3",{id:"videoconferencejoined"},"videoConferenceJoined"),(0,r.kt)("p",null,"Provides event notifications that fire when the local user has joined the video conference."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n roomName: string, // the room name of the conference\n id: string, // the id of the local participant\n displayName: string, // the display name of the local participant\n avatarURL: string, // the avatar URL of the local participant\n breakoutRoom: boolean // whether the current room is a breakout room\n}\n")),(0,r.kt)("h3",{id:"videoconferenceleft"},"videoConferenceLeft"),(0,r.kt)("p",null,"Provides event notifications that fire when the local user has left the video conference."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n roomName: string // the room name of the conference\n}\n")),(0,r.kt)("h3",{id:"videoavailabilitychanged"},"videoAvailabilityChanged"),(0,r.kt)("p",null,"Provides event notifications about video availability status changes."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n available: boolean // new available status - boolean\n}\n")),(0,r.kt)("h3",{id:"videomutestatuschanged"},"videoMuteStatusChanged"),(0,r.kt)("p",null,"Provides event notifications about video mute status changes."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n muted: boolean // new muted status - boolean\n}\n")),(0,r.kt)("h3",{id:"videoqualitychanged"},"videoQualityChanged"),(0,r.kt)("p",null,"Provides event notifications about changes to video quality settings."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n videoQuality: number // the height of the resolution related to the new video quality setting.\n}\n")),(0,r.kt)("h3",{id:"readytoclose"},"readyToClose"),(0,r.kt)("p",null,"Provides event notifications that fire when Jitsi Meet is ready to be closed (i.e., hangup operations are completed)."),(0,r.kt)("h3",{id:"recordinglinkavailable"},"recordingLinkAvailable"),(0,r.kt)("p",null,"Provides event notifications about recording link becoming available."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n link: string, // the recording link\n ttl: number // the time to live of the recording link\n}\n")),(0,r.kt)("h3",{id:"recordingstatuschanged"},"recordingStatusChanged"),(0,r.kt)("p",null,"Provides event notifications about recording status changes."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n on: boolean // new recording status - boolean,\n mode: string // recording mode, `local`, `stream` or `file`,\n error: string | undefined // error type if recording fails, undefined otherwise\n}\n")),(0,r.kt)("h3",{id:"subjectchange"},"subjectChange"),(0,r.kt)("p",null,"Provides event notifications regarding the change of subject for a conference."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n subject: string // the new subject\n}\n")),(0,r.kt)("h3",{id:"suspenddetected"},"suspendDetected"),(0,r.kt)("p",null,"Provides notifications about detecting suspended events in the host computer."),(0,r.kt)("h3",{id:"peerconnectionfailure"},"peerConnectionFailure"),(0,r.kt)("p",null,"Notify the external application that a PeerConnection lost connectivity. This event is fired only if\na PC ",(0,r.kt)("inlineCode",{parentName:"p"},"failed")," but connectivity to the rtcstats server is still maintained signaling that there is a\nproblem establishing a link between the app and the JVB server or the remote peer in case of P2P.\nWill only fire if rtcstats is enabled."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n // Type of PC, Peer2Peer or JVB connection.\n isP2P: boolean,\n\n // Was this connection previously connected. If it was it could mean\n // that connectivity was disrupted, if not it most likely means that the app could not reach\n // the JVB server, or the other peer in case of P2P.\n wasConnected: boolean\n}\n")),(0,r.kt)("h3",{id:"whiteboardstatuschanged"},"whiteboardStatusChanged"),(0,r.kt)("p",null,"Provides event notifications about changes to the whiteboard."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n status: string // new whiteboard status\n}\n")),(0,r.kt)("h3",{id:"p2pstatuschanged"},"p2pStatusChanged"),(0,r.kt)("p",null,"Provides event notifications about changes to the connection type."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n isP2p: boolean|null // whether the new connection type is P2P\n}\n")))}h.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[9192],{3905:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>h});var a=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var s=a.createContext({}),c=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},d=function(e){var t=c(e.components);return a.createElement(s.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},u=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,s=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),u=c(n),h=i,g=u["".concat(s,".").concat(h)]||u[h]||p[h]||r;return n?a.createElement(g,o(o({ref:t},d),{},{components:n})):a.createElement(g,o({ref:t},d))}));function h(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,o=new Array(r);o[0]=u;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:i,o[1]=l;for(var c=2;c{n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>s,default:()=>h,frontMatter:()=>l,metadata:()=>c,toc:()=>p});var a=n(7462),i=n(3366),r=(n(7294),n(3905)),o=["components"],l={id:"dev-guide-iframe-events",title:"Events"},s=void 0,c={unversionedId:"dev-guide/dev-guide-iframe-events",id:"dev-guide/dev-guide-iframe-events",title:"Events",description:"The JitsiMeetExternalAPI object implements the [EventEmitter] API for emitting and listening for events.",source:"@site/docs/dev-guide/iframe-events.md",sourceDirName:"dev-guide",slug:"/dev-guide/dev-guide-iframe-events",permalink:"/handbook/docs/dev-guide/dev-guide-iframe-events",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/dev-guide/iframe-events.md",tags:[],version:"current",lastUpdatedAt:1695203964,formattedLastUpdatedAt:"Sep 20, 2023",frontMatter:{id:"dev-guide-iframe-events",title:"Events"},sidebar:"docs",previous:{title:"Commands",permalink:"/handbook/docs/dev-guide/dev-guide-iframe-commands"},next:{title:"React SDK",permalink:"/handbook/docs/dev-guide/dev-guide-react-sdk"}},d={},p=[{value:"cameraError",id:"cameraerror",level:3},{value:"avatarChanged",id:"avatarchanged",level:3},{value:"audioAvailabilityChanged",id:"audioavailabilitychanged",level:3},{value:"audioMuteStatusChanged",id:"audiomutestatuschanged",level:3},{value:"breakoutRoomsUpdated",id:"breakoutroomsupdated",level:3},{value:"browserSupport",id:"browsersupport",level:3},{value:"contentSharingParticipantsChanged",id:"contentsharingparticipantschanged",level:3},{value:"dataChannelOpened",id:"datachannelopened",level:3},{value:"endpointTextMessageReceived",id:"endpointtextmessagereceived",level:3},{value:"nonParticipantMessageReceived",id:"nonparticipantmessagereceived",level:3},{value:"faceLandmarkDetected",id:"facelandmarkdetected",level:3},{value:"errorOccurred",id:"erroroccurred",level:3},{value:"knockingParticipant",id:"knockingparticipant",level:3},{value:"largeVideoChanged",id:"largevideochanged",level:3},{value:"log",id:"log",level:3},{value:"micError",id:"micerror",level:3},{value:"screenSharingStatusChanged",id:"screensharingstatuschanged",level:3},{value:"dominantSpeakerChanged",id:"dominantspeakerchanged",level:3},{value:"raiseHandUpdated",id:"raisehandupdated",level:3},{value:"tileViewChanged",id:"tileviewchanged",level:3},{value:"chatUpdated",id:"chatupdated",level:3},{value:"incomingMessage",id:"incomingmessage",level:3},{value:"mouseEnter",id:"mouseenter",level:3},{value:"mouseLeave",id:"mouseleave",level:3},{value:"mouseMove",id:"mousemove",level:3},{value:"participantMenuButtonClick",id:"participantmenubuttonclick",level:3},{value:"toolbarButtonClicked",id:"toolbarbuttonclicked",level:3},{value:"outgoingMessage",id:"outgoingmessage",level:3},{value:"displayNameChange",id:"displaynamechange",level:3},{value:"deviceListChanged",id:"devicelistchanged",level:3},{value:"emailChange",id:"emailchange",level:3},{value:"feedbackSubmitted",id:"feedbacksubmitted",level:3},{value:"filmstripDisplayChanged",id:"filmstripdisplaychanged",level:3},{value:"moderationStatusChanged",id:"moderationstatuschanged",level:3},{value:"moderationParticipantApproved",id:"moderationparticipantapproved",level:3},{value:"moderationParticipantRejected",id:"moderationparticipantrejected",level:3},{value:"notificationTriggered",id:"notificationtriggered",level:3},{value:"participantJoined",id:"participantjoined",level:3},{value:"participantKickedOut",id:"participantkickedout",level:3},{value:"participantLeft",id:"participantleft",level:3},{value:"participantRoleChanged",id:"participantrolechanged",level:3},{value:"participantsPaneToggled",id:"participantspanetoggled",level:3},{value:"passwordRequired",id:"passwordrequired",level:3},{value:"videoConferenceJoined",id:"videoconferencejoined",level:3},{value:"videoConferenceLeft",id:"videoconferenceleft",level:3},{value:"videoAvailabilityChanged",id:"videoavailabilitychanged",level:3},{value:"videoMuteStatusChanged",id:"videomutestatuschanged",level:3},{value:"videoQualityChanged",id:"videoqualitychanged",level:3},{value:"readyToClose",id:"readytoclose",level:3},{value:"recordingLinkAvailable",id:"recordinglinkavailable",level:3},{value:"recordingStatusChanged",id:"recordingstatuschanged",level:3},{value:"subjectChange",id:"subjectchange",level:3},{value:"suspendDetected",id:"suspenddetected",level:3},{value:"peerConnectionFailure",id:"peerconnectionfailure",level:3},{value:"whiteboardStatusChanged",id:"whiteboardstatuschanged",level:3},{value:"p2pStatusChanged",id:"p2pstatuschanged",level:3}],u={toc:p};function h(e){var t=e.components,n=(0,i.Z)(e,o);return(0,r.kt)("wrapper",(0,a.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"JitsiMeetExternalAPI")," object implements the ",(0,r.kt)("a",{parentName:"p",href:"https://nodejs.org/api/events.html"},"EventEmitter")," API for emitting and listening for events."),(0,r.kt)("p",null,"You can add event listeners to the embedded Jitsi Meet using the ",(0,r.kt)("strong",{parentName:"p"},(0,r.kt)("inlineCode",{parentName:"strong"},"addListener"))," method:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"api.addListener(event, listener);\n")),(0,r.kt)("p",null,"If you want to remove a listener you can use the ",(0,r.kt)("strong",{parentName:"p"},(0,r.kt)("inlineCode",{parentName:"strong"},"removeListener"))," method:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"api.removeListener(event, listener);\n")),(0,r.kt)("p",null,"The ",(0,r.kt)("strong",{parentName:"p"},(0,r.kt)("inlineCode",{parentName:"strong"},"event"))," parameter is a string object with the name of the event."),(0,r.kt)("p",null,"The ",(0,r.kt)("strong",{parentName:"p"},(0,r.kt)("inlineCode",{parentName:"strong"},"listener"))," parameter is a function object with one argument that creates a notification when the event occurs along with related event data."),(0,r.kt)("p",null,"The following events are currently supported:"),(0,r.kt)("h3",{id:"cameraerror"},"cameraError"),(0,r.kt)("p",null,"Provides event notifications about Jitsi Meet having failed to access the meeting camera."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n type: string, // A constant representing the overall type of the error.\n message: string // Additional information about the error.\n}\n")),(0,r.kt)("h3",{id:"avatarchanged"},"avatarChanged"),(0,r.kt)("p",null,"Provides event notifications about changes to a participant's avatar."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n id: string, // the id of the participant that changed his avatar.\n avatarURL: string // the new avatar URL.\n}\n")),(0,r.kt)("h3",{id:"audioavailabilitychanged"},"audioAvailabilityChanged"),(0,r.kt)("p",null,"Provides event notifications about changes to audio availability status."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n available: boolean // new available status - boolean\n}\n")),(0,r.kt)("h3",{id:"audiomutestatuschanged"},"audioMuteStatusChanged"),(0,r.kt)("p",null,"Provides event notifications about changes to audio mute status."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n muted: boolean // new muted status - boolean\n}\n")),(0,r.kt)("h3",{id:"breakoutroomsupdated"},"breakoutRoomsUpdated"),(0,r.kt)("p",null,"Provides notifications about breakout rooms changes."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n [roomId]: {\n id: string,\n jid: string,\n name: string,\n isMainRoom: true | undefined,\n participants: {\n [participantJid]: {\n displayName: string,\n jid: string,\n role: string\n }\n }\n },\n ...\n}\n")),(0,r.kt)("h3",{id:"browsersupport"},"browserSupport"),(0,r.kt)("p",null,"Provides event notifications about the current browser support."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n supported: boolean\n}\n")),(0,r.kt)("h3",{id:"contentsharingparticipantschanged"},"contentSharingParticipantsChanged"),(0,r.kt)("p",null,"Provides real-time list of currently screen sharing participant ID's."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},'{\n data: ["particId1", "particId2", ...]\n}\n')),(0,r.kt)("h3",{id:"datachannelopened"},"dataChannelOpened"),(0,r.kt)("p",null,"Indicates the data channel is open and thus messages can be sent over it."),(0,r.kt)("h3",{id:"endpointtextmessagereceived"},"endpointTextMessageReceived"),(0,r.kt)("p",null,"Provides event notifications about a text messages received through data channels."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n senderInfo: {\n jid: string, // the jid of the sender\n id: string // the participant id of the sender\n },\n eventData: {\n name: string, // the name of the datachannel event: `endpoint-text-message`\n text: string // the received text from the sender\n }\n}\n")),(0,r.kt)("h3",{id:"nonparticipantmessagereceived"},"nonParticipantMessageReceived"),(0,r.kt)("p",null,"Provides event notifications about a messages sent by a non-participant, e.g. a custom prosody message."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n id: string, // the id of the message, may be null\n message: string // the message received\n}\n")),(0,r.kt)("h3",{id:"facelandmarkdetected"},"faceLandmarkDetected"),(0,r.kt)("p",null,"Provides event notifications when a face landmark is detected"),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n faceBox: {\n left: number, // face bounding box distance as percentage from the left video edge\n right: number // face bounding box distance as percentage from the right video edge\n width: number // face bounding box width as percentage of the total video width\n }, // this might be undefined if config.faceLandmarks.faceCenteringThreshold is not passed\n faceExpression: string // check https://github.com/jitsi/jitsi-meet/blob/master/react/features/face-landmarks/constants.js#L3 for available values\n}\n")),(0,r.kt)("h3",{id:"erroroccurred"},"errorOccurred"),(0,r.kt)("p",null,"Provides event notifications about an error which has occurred."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n details: Object?, // additional error details\n message: string?, // the error message\n name: string, // the coded name of the error\n type: string, // error type/source, one of : 'CONFIG', 'CONNECTION', 'CONFERENCE'\n isFatal: boolean // whether this is a fatal error which triggered a reconnect overlay or not\n}\n")),(0,r.kt)("h3",{id:"knockingparticipant"},"knockingParticipant"),(0,r.kt)("p",null,"Provides event notifications about a knocking participant in the lobby."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n participant: {\n // the id and name of the participant that is currently knocking in the lobby\n id: string,\n name: string\n }\n}\n")),(0,r.kt)("h3",{id:"largevideochanged"},"largeVideoChanged"),(0,r.kt)("p",null,"Provides event notifications about changes in the large video display."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n id: string // id of the participant that is now on large video in the stage view.\n}\n")),(0,r.kt)("h3",{id:"log"},"log"),(0,r.kt)("p",null,"Provides log event notifications with the log level being one of the values specified in the ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet/blob/master/config.js"},"config.js")," file in the ",(0,r.kt)("strong",{parentName:"p"},(0,r.kt)("inlineCode",{parentName:"strong"},"apiLogLevels"))," property (if not specified the event does not fire)."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n logLevel: string, // A constant representing the log type (info, error, debug, warn).\n args: string // Additional log information.\n}\n")),(0,r.kt)("h3",{id:"micerror"},"micError"),(0,r.kt)("p",null,"Provides event notifications about Jitsi Meet issues with mic access."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n type: string, // A constant representing the overall type of the error.\n message: string // Additional information about the error.\n}\n")),(0,r.kt)("h3",{id:"screensharingstatuschanged"},"screenSharingStatusChanged"),(0,r.kt)("p",null,"Provides event notifications about either turning on or off local user screen sharing."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n on: boolean, //whether screen sharing is on\n details: {\n\n // From where the screen sharing is capturing, if known. Values which are\n // passed include 'window', 'screen', 'proxy', 'device'. The value undefined\n // will be passed if the source type is unknown or screen share is off.\n sourceType: string|undefined\n }\n}\n")),(0,r.kt)("h3",{id:"dominantspeakerchanged"},"dominantSpeakerChanged"),(0,r.kt)("p",null,"Provides event notifications about dominant speaker changes."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n id: string //participantId of the new dominant speaker\n}\n")),(0,r.kt)("h3",{id:"raisehandupdated"},"raiseHandUpdated"),(0,r.kt)("p",null,"Provides event notifications about the participant raising/lowering the hand."),(0,r.kt)("p",null,"The listener will receive an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n id: string, // participantId of the user who raises/lowers the hand\n handRaised: number // 0 when hand is lowered and the hand raised timestamp when raised.\n}\n")),(0,r.kt)("h3",{id:"tileviewchanged"},"tileViewChanged"),(0,r.kt)("p",null,"Provides event notifications about entrance or exit from the tile view layout mode."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n enabled: boolean, // whether tile view is not displayed or not\n}\n")),(0,r.kt)("h3",{id:"chatupdated"},"chatUpdated"),(0,r.kt)("p",null,"Provides event notifications about chat state being updated."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n isOpen: boolean, // Whether the chat panel is open or not\n unreadCount: number // The unread messages counter\n}\n")),(0,r.kt)("h3",{id:"incomingmessage"},"incomingMessage"),(0,r.kt)("p",null,"Provides event notifications about incoming chat messages."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n from: string, // The id of the user that sent the message\n nick: string, // the nickname of the user that sent the message\n privateMessage: boolean, // whether this is a private or group message\n message: string // the text of the message\n}\n")),(0,r.kt)("h3",{id:"mouseenter"},"mouseEnter"),(0,r.kt)("p",null,"Provides event notifications when mouse enters the iframe.\nThe listener receives an object with the following structure based on ",(0,r.kt)("a",{parentName:"p",href:"https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent"},"MouseEvent"),":"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n event: {\n clientX,\n clientY,\n movementX,\n movementY,\n offsetX,\n offsetY,\n pageX,\n pageY,\n x,\n y,\n screenX,\n screenY\n }\n}\n")),(0,r.kt)("h3",{id:"mouseleave"},"mouseLeave"),(0,r.kt)("p",null,"Provides event notifications when mouse leaves the iframe.\nThe listener receives an object with the following structure based on ",(0,r.kt)("a",{parentName:"p",href:"https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent"},"MouseEvent"),":"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n event: {\n clientX,\n clientY,\n movementX,\n movementY,\n offsetX,\n offsetY,\n pageX,\n pageY,\n x,\n y,\n screenX,\n screenY\n }\n}\n")),(0,r.kt)("h3",{id:"mousemove"},"mouseMove"),(0,r.kt)("p",null,"Provides event notifications when mouse moves inside the iframe.\nTis event is triggered on an interval which can be configured by overriding the config.js mouseMoveCallbackInterval property."),(0,r.kt)("p",null,"The listener receives an object with the following structure based on ",(0,r.kt)("a",{parentName:"p",href:"https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent"},"MouseEvent"),":"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n event: {\n clientX,\n clientY,\n movementX,\n movementY,\n offsetX,\n offsetY,\n pageX,\n pageY,\n x,\n y,\n screenX,\n screenY\n }\n}\n")),(0,r.kt)("h3",{id:"participantmenubuttonclick"},"participantMenuButtonClick"),(0,r.kt)("p",null,"Provides event notifications about a participant context menu button being clicked."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n key: string, // the pressed button's key. The key is as defined in `toolbarButtons` config,\n participantId: string, // the id of the participant for which the button was clicked,\n preventExecution: boolean // whether the execution of the button click was prevented or not\n}\n")),(0,r.kt)("h3",{id:"toolbarbuttonclicked"},"toolbarButtonClicked"),(0,r.kt)("p",null,"Provides event notifications about a toolbar button being clicked and whether the click routine was executed or not.\nFor overriding a button's click, please use the following config overwrite:\n",(0,r.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet/blob/042a2cb447bd9ff39ab3904e493952787bd30924/config.js#L547"},"https://github.com/jitsi/jitsi-meet/blob/042a2cb447bd9ff39ab3904e493952787bd30924/config.js#L547")),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n key: string, // the pressed button's key. The key is as defined in `toolbarButtons` config,\n preventExecution: boolean // whether the click routine execution was prevented or not.\n}\n")),(0,r.kt)("h3",{id:"outgoingmessage"},"outgoingMessage"),(0,r.kt)("p",null,"Provides event notifications about outgoing chat messages."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n message: string, // the text of the message\n privateMessage: boolean // whether this is a private or group message\n}\n")),(0,r.kt)("h3",{id:"displaynamechange"},"displayNameChange"),(0,r.kt)("p",null,"Provides event notifications about display name changes."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n id: string, // the id of the participant that changed their display name\n displayname: string // the new display name\n}\n")),(0,r.kt)("h3",{id:"devicelistchanged"},"deviceListChanged"),(0,r.kt)("p",null,"Provides event notifications about device list changes."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n devices: Object // the new list of available devices.\n}\n")),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"NOTE:")," The ",(0,r.kt)("strong",{parentName:"p"},(0,r.kt)("inlineCode",{parentName:"strong"},"device"))," object has the same format as the ",(0,r.kt)("strong",{parentName:"p"},(0,r.kt)("inlineCode",{parentName:"strong"},"getAvailableDevices"))," result format."),(0,r.kt)("h3",{id:"emailchange"},"emailChange"),(0,r.kt)("p",null,"Provides event notifications about email changes."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n id: string, // the id of the participant that changed his email\n email: string // the new email\n}\n")),(0,r.kt)("h3",{id:"feedbacksubmitted"},"feedbackSubmitted"),(0,r.kt)("p",null,"Provides event notifications about conference feedback submissions:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n error: string // The error which occurred during submission, if any.\n}\n")),(0,r.kt)("h3",{id:"filmstripdisplaychanged"},"filmstripDisplayChanged"),(0,r.kt)("p",null,"Provides event visibility notifications for the filmstrip that is being updated:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n visible: boolean // Whether or not the filmstrip is displayed or hidden.\n}\n")),(0,r.kt)("h3",{id:"moderationstatuschanged"},"moderationStatusChanged"),(0,r.kt)("p",null,"Provides event notifications about changes to moderation status."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n mediaType: string, // The media type for which moderation changed.\n enabled: boolean // Whether or not moderation changed to enabled.\n}\n")),(0,r.kt)("h3",{id:"moderationparticipantapproved"},"moderationParticipantApproved"),(0,r.kt)("p",null,"Provides event notifications about participants approvals for moderation."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n id: string, // The ID of the participant that got approved.\n mediaType: string // The media type for which the participant was approved.\n}\n")),(0,r.kt)("h3",{id:"moderationparticipantrejected"},"moderationParticipantRejected"),(0,r.kt)("p",null,"Provides event notifications about participants rejections for moderation."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n id: string, // The ID of the participant that got rejected.\n mediaType: string // The media type for which the participant was rejected.\n}\n")),(0,r.kt)("h3",{id:"notificationtriggered"},"notificationTriggered"),(0,r.kt)("p",null,"Provides event notifications when an application notification occurs."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n title: string, // The notification title.\n description: string // The notification description.\n}\n")),(0,r.kt)("h3",{id:"participantjoined"},"participantJoined"),(0,r.kt)("p",null,"Provides event notifications about new participants who join the room."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n id: string, // the id of the participant\n displayName: string // the display name of the participant\n}\n")),(0,r.kt)("h3",{id:"participantkickedout"},"participantKickedOut"),(0,r.kt)("p",null,"Provides event notifications about participants being removed from the room."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n kicked: {\n id: string, // the id of the participant removed from the room\n local: boolean // whether or not the participant is the local particiapnt\n },\n kicker: {\n id: string // the id of the participant who kicked out the other participant\n }\n}\n")),(0,r.kt)("h3",{id:"participantleft"},"participantLeft"),(0,r.kt)("p",null,"Provides event notifications about participants that leave the meeting room."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n id: string // the id of the participant\n}\n")),(0,r.kt)("h3",{id:"participantrolechanged"},"participantRoleChanged"),(0,r.kt)("p",null,"Provides event notifications that fire when the local user role has changed (e.g., none, moderator, participant)."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n id: string // the id of the participant\n role: string // the new role of the participant\n}\n")),(0,r.kt)("h3",{id:"participantspanetoggled"},"participantsPaneToggled"),(0,r.kt)("p",null,"Provides event notifications that fire when the participants pane status changes."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n open: boolean // whether the pane is open or not\n}\n")),(0,r.kt)("h3",{id:"passwordrequired"},"passwordRequired"),(0,r.kt)("p",null,"Provides event notifications that fire when participants fail to join a password protected room."),(0,r.kt)("h3",{id:"videoconferencejoined"},"videoConferenceJoined"),(0,r.kt)("p",null,"Provides event notifications that fire when the local user has joined the video conference."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n roomName: string, // the room name of the conference\n id: string, // the id of the local participant\n displayName: string, // the display name of the local participant\n avatarURL: string, // the avatar URL of the local participant\n breakoutRoom: boolean // whether the current room is a breakout room\n}\n")),(0,r.kt)("h3",{id:"videoconferenceleft"},"videoConferenceLeft"),(0,r.kt)("p",null,"Provides event notifications that fire when the local user has left the video conference."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n roomName: string // the room name of the conference\n}\n")),(0,r.kt)("h3",{id:"videoavailabilitychanged"},"videoAvailabilityChanged"),(0,r.kt)("p",null,"Provides event notifications about video availability status changes."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n available: boolean // new available status - boolean\n}\n")),(0,r.kt)("h3",{id:"videomutestatuschanged"},"videoMuteStatusChanged"),(0,r.kt)("p",null,"Provides event notifications about video mute status changes."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n muted: boolean // new muted status - boolean\n}\n")),(0,r.kt)("h3",{id:"videoqualitychanged"},"videoQualityChanged"),(0,r.kt)("p",null,"Provides event notifications about changes to video quality settings."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n videoQuality: number // the height of the resolution related to the new video quality setting.\n}\n")),(0,r.kt)("h3",{id:"readytoclose"},"readyToClose"),(0,r.kt)("p",null,"Provides event notifications that fire when Jitsi Meet is ready to be closed (i.e., hangup operations are completed)."),(0,r.kt)("h3",{id:"recordinglinkavailable"},"recordingLinkAvailable"),(0,r.kt)("p",null,"Provides event notifications about recording link becoming available."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n link: string, // the recording link\n ttl: number // the time to live of the recording link\n}\n")),(0,r.kt)("h3",{id:"recordingstatuschanged"},"recordingStatusChanged"),(0,r.kt)("p",null,"Provides event notifications about recording status changes."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n on: boolean // new recording status - boolean,\n mode: string // recording mode, `local`, `stream` or `file`,\n error: string | undefined // error type if recording fails, undefined otherwise\n}\n")),(0,r.kt)("h3",{id:"subjectchange"},"subjectChange"),(0,r.kt)("p",null,"Provides event notifications regarding the change of subject for a conference."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n subject: string // the new subject\n}\n")),(0,r.kt)("h3",{id:"suspenddetected"},"suspendDetected"),(0,r.kt)("p",null,"Provides notifications about detecting suspended events in the host computer."),(0,r.kt)("h3",{id:"peerconnectionfailure"},"peerConnectionFailure"),(0,r.kt)("p",null,"Notify the external application that a PeerConnection lost connectivity. This event is fired only if\na PC ",(0,r.kt)("inlineCode",{parentName:"p"},"failed")," but connectivity to the rtcstats server is still maintained signaling that there is a\nproblem establishing a link between the app and the JVB server or the remote peer in case of P2P.\nWill only fire if rtcstats is enabled."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n // Type of PC, Peer2Peer or JVB connection.\n isP2P: boolean,\n\n // Was this connection previously connected. If it was it could mean\n // that connectivity was disrupted, if not it most likely means that the app could not reach\n // the JVB server, or the other peer in case of P2P.\n wasConnected: boolean\n}\n")),(0,r.kt)("h3",{id:"whiteboardstatuschanged"},"whiteboardStatusChanged"),(0,r.kt)("p",null,"Provides event notifications about changes to the whiteboard."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n status: string // new whiteboard status\n}\n")),(0,r.kt)("h3",{id:"p2pstatuschanged"},"p2pStatusChanged"),(0,r.kt)("p",null,"Provides event notifications about changes to the connection type."),(0,r.kt)("p",null,"The listener receives an object with the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-javascript"},"{\n isP2p: boolean|null // whether the new connection type is P2P\n}\n")))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/58508fec.e0633219.js b/assets/js/58508fec.a5fd06d5.js similarity index 98% rename from assets/js/58508fec.e0633219.js rename to assets/js/58508fec.a5fd06d5.js index afa116939..e374ea0e2 100644 --- a/assets/js/58508fec.e0633219.js +++ b/assets/js/58508fec.a5fd06d5.js @@ -1 +1 @@ -"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[4962],{3905:(e,t,r)=>{r.d(t,{Zo:()=>d,kt:()=>c});var a=r(7294);function n(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function o(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,a)}return r}function i(e){for(var t=1;t=0||(n[r]=e[r]);return n}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(n[r]=e[r])}return n}var p=a.createContext({}),s=function(e){var t=a.useContext(p),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},d=function(e){var t=s(e.components);return a.createElement(p.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},u=a.forwardRef((function(e,t){var r=e.components,n=e.mdxType,o=e.originalType,p=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),u=s(r),c=n,k=u["".concat(p,".").concat(c)]||u[c]||m[c]||o;return r?a.createElement(k,i(i({ref:t},d),{},{components:r})):a.createElement(k,i({ref:t},d))}));function c(e,t){var r=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var o=r.length,i=new Array(o);i[0]=u;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l.mdxType="string"==typeof e?e:n,i[1]=l;for(var s=2;s{r.r(t),r.d(t,{assets:()=>d,contentTitle:()=>p,default:()=>c,frontMatter:()=>l,metadata:()=>s,toc:()=>m});var a=r(7462),n=r(3366),o=(r(7294),r(3905)),i=["components"],l={id:"supported-browsers",title:"Supported Browsers"},p=void 0,s={unversionedId:"user-guide/supported-browsers",id:"user-guide/supported-browsers",title:"Supported Browsers",description:"Desktop browsers",source:"@site/docs/user-guide/browsers.md",sourceDirName:"user-guide",slug:"/user-guide/supported-browsers",permalink:"/handbook/docs/user-guide/supported-browsers",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/user-guide/browsers.md",tags:[],version:"current",lastUpdatedAt:1694635654,formattedLastUpdatedAt:"Sep 13, 2023",frontMatter:{id:"supported-browsers",title:"Supported Browsers"},sidebar:"docs",previous:{title:"User Guide",permalink:"/handbook/docs/category/user-guide"},next:{title:"Join a Jitsi Meeting",permalink:"/handbook/docs/user-guide/user-guide-join-jitsi-meeting"}},d={},m=[{value:"Desktop browsers",id:"desktop-browsers",level:2},{value:"Mobile browsers",id:"mobile-browsers",level:2},{value:"Android",id:"android",level:3},{value:"iOS",id:"ios",level:3}],u={toc:m};function c(e){var t=e.components,r=(0,n.Z)(e,i);return(0,o.kt)("wrapper",(0,a.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h2",{id:"desktop-browsers"},"Desktop browsers"),(0,o.kt)("table",null,(0,o.kt)("thead",{parentName:"table"},(0,o.kt)("tr",{parentName:"thead"},(0,o.kt)("th",{parentName:"tr",align:null},"Browser"),(0,o.kt)("th",{parentName:"tr",align:"center"},"Support"),(0,o.kt)("th",{parentName:"tr",align:"center"},"Versions"),(0,o.kt)("th",{parentName:"tr",align:null},"Notes"))),(0,o.kt)("tbody",{parentName:"table"},(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"Chrome ",(0,o.kt)("sup",{parentName:"td",id:"fnref-1"},(0,o.kt)("a",{parentName:"sup",href:"#fn-1",className:"footnote-ref"},"1"))),(0,o.kt)("td",{parentName:"tr",align:"center"},"\u2705"),(0,o.kt)("td",{parentName:"tr",align:"center"},">= 72"),(0,o.kt)("td",{parentName:"tr",align:null},"Best results with >= 96")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"Firefox"),(0,o.kt)("td",{parentName:"tr",align:"center"},"\u2705"),(0,o.kt)("td",{parentName:"tr",align:"center"},">= 68"),(0,o.kt)("td",{parentName:"tr",align:null},"Best results with >= 101")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"Safari"),(0,o.kt)("td",{parentName:"tr",align:"center"},"\u2705"),(0,o.kt)("td",{parentName:"tr",align:"center"},">= 14"),(0,o.kt)("td",{parentName:"tr",align:null},"Best results with >= 15, output device selection unsupported")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"Edge"),(0,o.kt)("td",{parentName:"tr",align:"center"},"\u2705"),(0,o.kt)("td",{parentName:"tr",align:"center"},">= 79"),(0,o.kt)("td",{parentName:"tr",align:null},"Edge Legacy is unsupported")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"Internet Explorer"),(0,o.kt)("td",{parentName:"tr",align:"center"},"\u274c"),(0,o.kt)("td",{parentName:"tr",align:"center"}),(0,o.kt)("td",{parentName:"tr",align:null})))),(0,o.kt)("h2",{id:"mobile-browsers"},"Mobile browsers"),(0,o.kt)("h3",{id:"android"},"Android"),(0,o.kt)("table",null,(0,o.kt)("thead",{parentName:"table"},(0,o.kt)("tr",{parentName:"thead"},(0,o.kt)("th",{parentName:"tr",align:null},"Browser"),(0,o.kt)("th",{parentName:"tr",align:"center"},"Support"),(0,o.kt)("th",{parentName:"tr",align:"center"},"Versions"),(0,o.kt)("th",{parentName:"tr",align:null},"Notes"))),(0,o.kt)("tbody",{parentName:"table"},(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"Chrome ",(0,o.kt)("sup",{parentName:"td",id:"fnref-1"},(0,o.kt)("a",{parentName:"sup",href:"#fn-1",className:"footnote-ref"},"1"))),(0,o.kt)("td",{parentName:"tr",align:"center"},"\u2705"),(0,o.kt)("td",{parentName:"tr",align:"center"}),(0,o.kt)("td",{parentName:"tr",align:null},"Same support as the desktop version")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"Firefox"),(0,o.kt)("td",{parentName:"tr",align:"center"},"\u2705"),(0,o.kt)("td",{parentName:"tr",align:"center"}),(0,o.kt)("td",{parentName:"tr",align:null},"Same support as the desktop version")))),(0,o.kt)("admonition",{type:"note"},(0,o.kt)("p",{parentName:"admonition"},"For a better mobile experience (background support, Bluetooth support, etc.) we recommend using a\nnative app instead. We provide a ",(0,o.kt)("a",{parentName:"p",href:"/handbook/docs/dev-guide/dev-guide-android-sdk"},"native Android SDK"),".")),(0,o.kt)("h3",{id:"ios"},"iOS"),(0,o.kt)("table",null,(0,o.kt)("thead",{parentName:"table"},(0,o.kt)("tr",{parentName:"thead"},(0,o.kt)("th",{parentName:"tr",align:null},"Browser"),(0,o.kt)("th",{parentName:"tr",align:"center"},"Support"),(0,o.kt)("th",{parentName:"tr",align:"center"},"Versions"),(0,o.kt)("th",{parentName:"tr",align:null},"Notes"))),(0,o.kt)("tbody",{parentName:"table"},(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"Chrome"),(0,o.kt)("td",{parentName:"tr",align:"center"},"\u2705"),(0,o.kt)("td",{parentName:"tr",align:"center"}),(0,o.kt)("td",{parentName:"tr",align:null},"Same support as Safari as they share the engine")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"Firefox"),(0,o.kt)("td",{parentName:"tr",align:"center"},"\u2705"),(0,o.kt)("td",{parentName:"tr",align:"center"}),(0,o.kt)("td",{parentName:"tr",align:null},"Same support as Safari as they share the engine")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"Safari"),(0,o.kt)("td",{parentName:"tr",align:"center"},"\u2705"),(0,o.kt)("td",{parentName:"tr",align:"center"},">= 14.3"),(0,o.kt)("td",{parentName:"tr",align:null},"Best results with 15.4")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"Edge"),(0,o.kt)("td",{parentName:"tr",align:"center"},"\u2705"),(0,o.kt)("td",{parentName:"tr",align:"center"}),(0,o.kt)("td",{parentName:"tr",align:null},"Same support as Safari as they share the engine")))),(0,o.kt)("admonition",{type:"note"},(0,o.kt)("p",{parentName:"admonition"},"On iOS all browsers share the same engine, Safari. As such all features and limitations on all iOS\nbrowsers are those of Safari."),(0,o.kt)("p",{parentName:"admonition"},"For a better mobile experience (background support, CallKit integration, etc.) we recommend using a\nnative app instead. We provide a ",(0,o.kt)("a",{parentName:"p",href:"/handbook/docs/dev-guide/dev-guide-ios-sdk"},"native iOS SDK"),".")),(0,o.kt)("div",{className:"footnotes"},(0,o.kt)("hr",{parentName:"div"}),(0,o.kt)("ol",{parentName:"div"},(0,o.kt)("li",{parentName:"ol",id:"fn-1"},"This also applies to all Chromium based browsers such as Brave, (current) Edge, Opera, Vivaldi and others.",(0,o.kt)("a",{parentName:"li",href:"#fnref-1",className:"footnote-backref"},"\u21a9")))))}c.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[4962],{3905:(e,t,r)=>{r.d(t,{Zo:()=>d,kt:()=>c});var a=r(7294);function n(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function o(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,a)}return r}function i(e){for(var t=1;t=0||(n[r]=e[r]);return n}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(n[r]=e[r])}return n}var p=a.createContext({}),s=function(e){var t=a.useContext(p),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},d=function(e){var t=s(e.components);return a.createElement(p.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},u=a.forwardRef((function(e,t){var r=e.components,n=e.mdxType,o=e.originalType,p=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),u=s(r),c=n,k=u["".concat(p,".").concat(c)]||u[c]||m[c]||o;return r?a.createElement(k,i(i({ref:t},d),{},{components:r})):a.createElement(k,i({ref:t},d))}));function c(e,t){var r=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var o=r.length,i=new Array(o);i[0]=u;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l.mdxType="string"==typeof e?e:n,i[1]=l;for(var s=2;s{r.r(t),r.d(t,{assets:()=>d,contentTitle:()=>p,default:()=>c,frontMatter:()=>l,metadata:()=>s,toc:()=>m});var a=r(7462),n=r(3366),o=(r(7294),r(3905)),i=["components"],l={id:"supported-browsers",title:"Supported Browsers"},p=void 0,s={unversionedId:"user-guide/supported-browsers",id:"user-guide/supported-browsers",title:"Supported Browsers",description:"Desktop browsers",source:"@site/docs/user-guide/browsers.md",sourceDirName:"user-guide",slug:"/user-guide/supported-browsers",permalink:"/handbook/docs/user-guide/supported-browsers",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/user-guide/browsers.md",tags:[],version:"current",lastUpdatedAt:1695203964,formattedLastUpdatedAt:"Sep 20, 2023",frontMatter:{id:"supported-browsers",title:"Supported Browsers"},sidebar:"docs",previous:{title:"User Guide",permalink:"/handbook/docs/category/user-guide"},next:{title:"Join a Jitsi Meeting",permalink:"/handbook/docs/user-guide/user-guide-join-jitsi-meeting"}},d={},m=[{value:"Desktop browsers",id:"desktop-browsers",level:2},{value:"Mobile browsers",id:"mobile-browsers",level:2},{value:"Android",id:"android",level:3},{value:"iOS",id:"ios",level:3}],u={toc:m};function c(e){var t=e.components,r=(0,n.Z)(e,i);return(0,o.kt)("wrapper",(0,a.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h2",{id:"desktop-browsers"},"Desktop browsers"),(0,o.kt)("table",null,(0,o.kt)("thead",{parentName:"table"},(0,o.kt)("tr",{parentName:"thead"},(0,o.kt)("th",{parentName:"tr",align:null},"Browser"),(0,o.kt)("th",{parentName:"tr",align:"center"},"Support"),(0,o.kt)("th",{parentName:"tr",align:"center"},"Versions"),(0,o.kt)("th",{parentName:"tr",align:null},"Notes"))),(0,o.kt)("tbody",{parentName:"table"},(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"Chrome ",(0,o.kt)("sup",{parentName:"td",id:"fnref-1"},(0,o.kt)("a",{parentName:"sup",href:"#fn-1",className:"footnote-ref"},"1"))),(0,o.kt)("td",{parentName:"tr",align:"center"},"\u2705"),(0,o.kt)("td",{parentName:"tr",align:"center"},">= 72"),(0,o.kt)("td",{parentName:"tr",align:null},"Best results with >= 96")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"Firefox"),(0,o.kt)("td",{parentName:"tr",align:"center"},"\u2705"),(0,o.kt)("td",{parentName:"tr",align:"center"},">= 68"),(0,o.kt)("td",{parentName:"tr",align:null},"Best results with >= 101")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"Safari"),(0,o.kt)("td",{parentName:"tr",align:"center"},"\u2705"),(0,o.kt)("td",{parentName:"tr",align:"center"},">= 14"),(0,o.kt)("td",{parentName:"tr",align:null},"Best results with >= 15, output device selection unsupported")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"Edge"),(0,o.kt)("td",{parentName:"tr",align:"center"},"\u2705"),(0,o.kt)("td",{parentName:"tr",align:"center"},">= 79"),(0,o.kt)("td",{parentName:"tr",align:null},"Edge Legacy is unsupported")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"Internet Explorer"),(0,o.kt)("td",{parentName:"tr",align:"center"},"\u274c"),(0,o.kt)("td",{parentName:"tr",align:"center"}),(0,o.kt)("td",{parentName:"tr",align:null})))),(0,o.kt)("h2",{id:"mobile-browsers"},"Mobile browsers"),(0,o.kt)("h3",{id:"android"},"Android"),(0,o.kt)("table",null,(0,o.kt)("thead",{parentName:"table"},(0,o.kt)("tr",{parentName:"thead"},(0,o.kt)("th",{parentName:"tr",align:null},"Browser"),(0,o.kt)("th",{parentName:"tr",align:"center"},"Support"),(0,o.kt)("th",{parentName:"tr",align:"center"},"Versions"),(0,o.kt)("th",{parentName:"tr",align:null},"Notes"))),(0,o.kt)("tbody",{parentName:"table"},(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"Chrome ",(0,o.kt)("sup",{parentName:"td",id:"fnref-1"},(0,o.kt)("a",{parentName:"sup",href:"#fn-1",className:"footnote-ref"},"1"))),(0,o.kt)("td",{parentName:"tr",align:"center"},"\u2705"),(0,o.kt)("td",{parentName:"tr",align:"center"}),(0,o.kt)("td",{parentName:"tr",align:null},"Same support as the desktop version")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"Firefox"),(0,o.kt)("td",{parentName:"tr",align:"center"},"\u2705"),(0,o.kt)("td",{parentName:"tr",align:"center"}),(0,o.kt)("td",{parentName:"tr",align:null},"Same support as the desktop version")))),(0,o.kt)("admonition",{type:"note"},(0,o.kt)("p",{parentName:"admonition"},"For a better mobile experience (background support, Bluetooth support, etc.) we recommend using a\nnative app instead. We provide a ",(0,o.kt)("a",{parentName:"p",href:"/handbook/docs/dev-guide/dev-guide-android-sdk"},"native Android SDK"),".")),(0,o.kt)("h3",{id:"ios"},"iOS"),(0,o.kt)("table",null,(0,o.kt)("thead",{parentName:"table"},(0,o.kt)("tr",{parentName:"thead"},(0,o.kt)("th",{parentName:"tr",align:null},"Browser"),(0,o.kt)("th",{parentName:"tr",align:"center"},"Support"),(0,o.kt)("th",{parentName:"tr",align:"center"},"Versions"),(0,o.kt)("th",{parentName:"tr",align:null},"Notes"))),(0,o.kt)("tbody",{parentName:"table"},(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"Chrome"),(0,o.kt)("td",{parentName:"tr",align:"center"},"\u2705"),(0,o.kt)("td",{parentName:"tr",align:"center"}),(0,o.kt)("td",{parentName:"tr",align:null},"Same support as Safari as they share the engine")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"Firefox"),(0,o.kt)("td",{parentName:"tr",align:"center"},"\u2705"),(0,o.kt)("td",{parentName:"tr",align:"center"}),(0,o.kt)("td",{parentName:"tr",align:null},"Same support as Safari as they share the engine")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"Safari"),(0,o.kt)("td",{parentName:"tr",align:"center"},"\u2705"),(0,o.kt)("td",{parentName:"tr",align:"center"},">= 14.3"),(0,o.kt)("td",{parentName:"tr",align:null},"Best results with 15.4")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"Edge"),(0,o.kt)("td",{parentName:"tr",align:"center"},"\u2705"),(0,o.kt)("td",{parentName:"tr",align:"center"}),(0,o.kt)("td",{parentName:"tr",align:null},"Same support as Safari as they share the engine")))),(0,o.kt)("admonition",{type:"note"},(0,o.kt)("p",{parentName:"admonition"},"On iOS all browsers share the same engine, Safari. As such all features and limitations on all iOS\nbrowsers are those of Safari."),(0,o.kt)("p",{parentName:"admonition"},"For a better mobile experience (background support, CallKit integration, etc.) we recommend using a\nnative app instead. We provide a ",(0,o.kt)("a",{parentName:"p",href:"/handbook/docs/dev-guide/dev-guide-ios-sdk"},"native iOS SDK"),".")),(0,o.kt)("div",{className:"footnotes"},(0,o.kt)("hr",{parentName:"div"}),(0,o.kt)("ol",{parentName:"div"},(0,o.kt)("li",{parentName:"ol",id:"fn-1"},"This also applies to all Chromium based browsers such as Brave, (current) Edge, Opera, Vivaldi and others.",(0,o.kt)("a",{parentName:"li",href:"#fnref-1",className:"footnote-backref"},"\u21a9")))))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/5ca9a3fa.b5cdd5f0.js b/assets/js/5ca9a3fa.4b454894.js similarity index 99% rename from assets/js/5ca9a3fa.b5cdd5f0.js rename to assets/js/5ca9a3fa.4b454894.js index 2b988b5dc..639a04b9d 100644 --- a/assets/js/5ca9a3fa.b5cdd5f0.js +++ b/assets/js/5ca9a3fa.4b454894.js @@ -1 +1 @@ -"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[5817],{3905:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>h});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var l=r.createContext({}),d=function(e){var t=r.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},c=function(e){var t=d(e.components);return r.createElement(l.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},p=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,i=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),p=d(n),h=a,m=p["".concat(l,".").concat(h)]||p[h]||u[h]||i;return n?r.createElement(m,o(o({ref:t},c),{},{components:n})):r.createElement(m,o({ref:t},c))}));function h(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=n.length,o=new Array(i);o[0]=p;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:a,o[1]=s;for(var d=2;d{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>l,default:()=>h,frontMatter:()=>s,metadata:()=>d,toc:()=>u});var r=n(7462),a=n(3366),i=(n(7294),n(3905)),o=["components"],s={id:"client connection status indicators",title:"Client Connection Status Indicators"},l=void 0,d={unversionedId:"user-guide/client connection status indicators",id:"user-guide/client connection status indicators",title:"Client Connection Status Indicators",description:"This document explains what the different connection quality indicators on the video thumbnails actually mean.",source:"@site/docs/user-guide/client-connection-status-indicators.md",sourceDirName:"user-guide",slug:"/user-guide/client connection status indicators",permalink:"/handbook/docs/user-guide/client connection status indicators",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/user-guide/client-connection-status-indicators.md",tags:[],version:"current",lastUpdatedAt:1694635654,formattedLastUpdatedAt:"Sep 13, 2023",frontMatter:{id:"client connection status indicators",title:"Client Connection Status Indicators"}},c={},u=[{value:"GOOD",id:"good",level:2},{value:"NON-OPTIMAL",id:"non-optimal",level:2},{value:"POOR",id:"poor",level:2},{value:"LOST",id:"lost",level:2},{value:"GHOST/NINJA",id:"ghostninja",level:2},{value:"Target bitrates expected for the video streams",id:"target-bitrates-expected-for-the-video-streams",level:2}],p={toc:u};function h(e){var t=e.components,n=(0,a.Z)(e,o);return(0,i.kt)("wrapper",(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"This document explains what the different connection quality indicators on the video thumbnails actually mean."),(0,i.kt)("h2",{id:"good"},"GOOD"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"With video enabled, when the send bitrate for the video stream is at least 50% of the target bitrate expected for the stream. Please refer to the target bitrates table below."),(0,i.kt)("li",{parentName:"ul"},"With video disabled or screen sharing is in progress, when the downstream packet loss is less than 6%.")),(0,i.kt)("h2",{id:"non-optimal"},"NON-OPTIMAL"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"With video enabled, when the send bitrate for the video stream is at least 30% of the target bitrate expected for the stream. Please refer to the target bitrates table below."),(0,i.kt)("li",{parentName:"ul"},"With video disabled or screen sharing is in progress, when the downstream packet loss is between 6% and 8%.")),(0,i.kt)("h2",{id:"poor"},"POOR"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"With video enabled, when the send bitrate for the video stream is at least 10% of the target bitrate expected for the stream. Please refer to the target bitrates table below."),(0,i.kt)("li",{parentName:"ul"},"With video disabled or screen sharing is in progress, when the downstream packet loss is between 8% and 12%.")),(0,i.kt)("h2",{id:"lost"},"LOST"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"When the user stops receiving video for the remote endpoint even when the endpoint is not video muted and it is in LastN as indicated by the bridge\u2019s LastNEndpointChangeEvent."),(0,i.kt)("li",{parentName:"ul"},"When the bridge sends an EndpointConnectivityStatusChangeEvent indicating that the remote endpoint is no longer active, i.e., when the bridge has not received media from the remote endpoint for more than 3 secs.")),(0,i.kt)("h2",{id:"ghostninja"},"GHOST/NINJA"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"When the user stops receiving video for the remote endpoint even when the endpoint is not video muted and it is not in LastN as indicated by the bridge\u2019s LastNEndpointChangeEvent. This means that the bridge decided to suspend the video for this user. Bridge takes into consideration the available downlink bandwidth for the receiving endpoint and the number of video streams requested using the channelLast setting.")),(0,i.kt)("h2",{id:"target-bitrates-expected-for-the-video-streams"},"Target bitrates expected for the video streams"),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:null},"CodecType"),(0,i.kt)("th",{parentName:"tr",align:null},"180p (in Kbps)"),(0,i.kt)("th",{parentName:"tr",align:null},"360p (in Kbps)"),(0,i.kt)("th",{parentName:"tr",align:null},"720p (in Kbps)"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"VP8"),(0,i.kt)("td",{parentName:"tr",align:null},"200"),(0,i.kt)("td",{parentName:"tr",align:null},"500"),(0,i.kt)("td",{parentName:"tr",align:null},"1500")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"VP9"),(0,i.kt)("td",{parentName:"tr",align:null},"100"),(0,i.kt)("td",{parentName:"tr",align:null},"300"),(0,i.kt)("td",{parentName:"tr",align:null},"1200")))))}h.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[5817],{3905:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>h});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var l=r.createContext({}),d=function(e){var t=r.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},c=function(e){var t=d(e.components);return r.createElement(l.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},p=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,i=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),p=d(n),h=a,m=p["".concat(l,".").concat(h)]||p[h]||u[h]||i;return n?r.createElement(m,o(o({ref:t},c),{},{components:n})):r.createElement(m,o({ref:t},c))}));function h(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=n.length,o=new Array(i);o[0]=p;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:a,o[1]=s;for(var d=2;d{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>l,default:()=>h,frontMatter:()=>s,metadata:()=>d,toc:()=>u});var r=n(7462),a=n(3366),i=(n(7294),n(3905)),o=["components"],s={id:"client connection status indicators",title:"Client Connection Status Indicators"},l=void 0,d={unversionedId:"user-guide/client connection status indicators",id:"user-guide/client connection status indicators",title:"Client Connection Status Indicators",description:"This document explains what the different connection quality indicators on the video thumbnails actually mean.",source:"@site/docs/user-guide/client-connection-status-indicators.md",sourceDirName:"user-guide",slug:"/user-guide/client connection status indicators",permalink:"/handbook/docs/user-guide/client connection status indicators",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/user-guide/client-connection-status-indicators.md",tags:[],version:"current",lastUpdatedAt:1695203964,formattedLastUpdatedAt:"Sep 20, 2023",frontMatter:{id:"client connection status indicators",title:"Client Connection Status Indicators"}},c={},u=[{value:"GOOD",id:"good",level:2},{value:"NON-OPTIMAL",id:"non-optimal",level:2},{value:"POOR",id:"poor",level:2},{value:"LOST",id:"lost",level:2},{value:"GHOST/NINJA",id:"ghostninja",level:2},{value:"Target bitrates expected for the video streams",id:"target-bitrates-expected-for-the-video-streams",level:2}],p={toc:u};function h(e){var t=e.components,n=(0,a.Z)(e,o);return(0,i.kt)("wrapper",(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"This document explains what the different connection quality indicators on the video thumbnails actually mean."),(0,i.kt)("h2",{id:"good"},"GOOD"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"With video enabled, when the send bitrate for the video stream is at least 50% of the target bitrate expected for the stream. Please refer to the target bitrates table below."),(0,i.kt)("li",{parentName:"ul"},"With video disabled or screen sharing is in progress, when the downstream packet loss is less than 6%.")),(0,i.kt)("h2",{id:"non-optimal"},"NON-OPTIMAL"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"With video enabled, when the send bitrate for the video stream is at least 30% of the target bitrate expected for the stream. Please refer to the target bitrates table below."),(0,i.kt)("li",{parentName:"ul"},"With video disabled or screen sharing is in progress, when the downstream packet loss is between 6% and 8%.")),(0,i.kt)("h2",{id:"poor"},"POOR"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"With video enabled, when the send bitrate for the video stream is at least 10% of the target bitrate expected for the stream. Please refer to the target bitrates table below."),(0,i.kt)("li",{parentName:"ul"},"With video disabled or screen sharing is in progress, when the downstream packet loss is between 8% and 12%.")),(0,i.kt)("h2",{id:"lost"},"LOST"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"When the user stops receiving video for the remote endpoint even when the endpoint is not video muted and it is in LastN as indicated by the bridge\u2019s LastNEndpointChangeEvent."),(0,i.kt)("li",{parentName:"ul"},"When the bridge sends an EndpointConnectivityStatusChangeEvent indicating that the remote endpoint is no longer active, i.e., when the bridge has not received media from the remote endpoint for more than 3 secs.")),(0,i.kt)("h2",{id:"ghostninja"},"GHOST/NINJA"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"When the user stops receiving video for the remote endpoint even when the endpoint is not video muted and it is not in LastN as indicated by the bridge\u2019s LastNEndpointChangeEvent. This means that the bridge decided to suspend the video for this user. Bridge takes into consideration the available downlink bandwidth for the receiving endpoint and the number of video streams requested using the channelLast setting.")),(0,i.kt)("h2",{id:"target-bitrates-expected-for-the-video-streams"},"Target bitrates expected for the video streams"),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:null},"CodecType"),(0,i.kt)("th",{parentName:"tr",align:null},"180p (in Kbps)"),(0,i.kt)("th",{parentName:"tr",align:null},"360p (in Kbps)"),(0,i.kt)("th",{parentName:"tr",align:null},"720p (in Kbps)"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"VP8"),(0,i.kt)("td",{parentName:"tr",align:null},"200"),(0,i.kt)("td",{parentName:"tr",align:null},"500"),(0,i.kt)("td",{parentName:"tr",align:null},"1500")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"VP9"),(0,i.kt)("td",{parentName:"tr",align:null},"100"),(0,i.kt)("td",{parentName:"tr",align:null},"300"),(0,i.kt)("td",{parentName:"tr",align:null},"1200")))))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/5f85ed72.35fc98dc.js b/assets/js/5f85ed72.4301b5d0.js similarity index 99% rename from assets/js/5f85ed72.35fc98dc.js rename to assets/js/5f85ed72.4301b5d0.js index c49195596..dc29e9ba3 100644 --- a/assets/js/5f85ed72.35fc98dc.js +++ b/assets/js/5f85ed72.4301b5d0.js @@ -1 +1 @@ -"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[4051],{3905:(e,t,a)=>{a.d(t,{Zo:()=>d,kt:()=>k});var n=a(7294);function l(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function i(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function r(e){for(var t=1;t=0||(l[a]=e[a]);return l}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(l[a]=e[a])}return l}var s=n.createContext({}),p=function(e){var t=n.useContext(s),a=t;return e&&(a="function"==typeof e?e(t):r(r({},t),e)),a},d=function(e){var t=p(e.components);return n.createElement(s.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},c=n.forwardRef((function(e,t){var a=e.components,l=e.mdxType,i=e.originalType,s=e.parentName,d=o(e,["components","mdxType","originalType","parentName"]),c=p(a),k=l,m=c["".concat(s,".").concat(k)]||c[k]||u[k]||i;return a?n.createElement(m,r(r({ref:t},d),{},{components:a})):n.createElement(m,r({ref:t},d))}));function k(e,t){var a=arguments,l=t&&t.mdxType;if("string"==typeof e||l){var i=a.length,r=new Array(i);r[0]=c;var o={};for(var s in t)hasOwnProperty.call(t,s)&&(o[s]=t[s]);o.originalType=e,o.mdxType="string"==typeof e?e:l,r[1]=o;for(var p=2;p{a.r(t),a.d(t,{assets:()=>d,contentTitle:()=>s,default:()=>k,frontMatter:()=>o,metadata:()=>p,toc:()=>u});var n=a(7462),l=a(3366),i=(a(7294),a(3905)),r=["components"],o={id:"dev-guide-configuration",title:"Configuration"},s=void 0,p={unversionedId:"dev-guide/dev-guide-configuration",id:"dev-guide/dev-guide-configuration",title:"Configuration",description:"This page describes available configuration options for Jitsi Meet. These are either set in config.js on the server",source:"@site/docs/dev-guide/configuration.md",sourceDirName:"dev-guide",slug:"/dev-guide/dev-guide-configuration",permalink:"/handbook/docs/dev-guide/dev-guide-configuration",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/dev-guide/configuration.md",tags:[],version:"current",lastUpdatedAt:1694635654,formattedLastUpdatedAt:"Sep 13, 2023",frontMatter:{id:"dev-guide-configuration",title:"Configuration"},sidebar:"docs",previous:{title:"Flutter SDK",permalink:"/handbook/docs/dev-guide/dev-guide-flutter-sdk"},next:{title:"Overview",permalink:"/handbook/docs/devops-guide/"}},d={},u=[{value:"API",id:"api",level:2},{value:"apiLogLevels",id:"apiloglevels",level:3},{value:"buttonsWithNotifyClick",id:"buttonswithnotifyclick",level:3},{value:"customParticipantMenuButtons",id:"customparticipantmenubuttons",level:3},{value:"customToolbarButtons",id:"customtoolbarbuttons",level:3},{value:"mouseMoveCallbackInterval",id:"mousemovecallbackinterval",level:3},{value:"participantMenuButtonsWithNotifyClick",id:"participantmenubuttonswithnotifyclick",level:3},{value:"useHostPageLocalStorage",id:"usehostpagelocalstorage",level:3},{value:"Audio",id:"audio",level:2},{value:"audioLevelsInterval",id:"audiolevelsinterval",level:3},{value:"audioQuality",id:"audioquality",level:3},{value:"disableAudioLevels",id:"disableaudiolevels",level:3},{value:"disableSpeakerStatsSearch",id:"disablespeakerstatssearch",level:3},{value:"disabledSounds",id:"disabledsounds",level:3},{value:"enableNoAudioDetection",id:"enablenoaudiodetection",level:3},{value:"enableNoisyMicDetection",id:"enablenoisymicdetection",level:3},{value:"speakerStats",id:"speakerstats",level:3},{value:"speakerStatsOrder",id:"speakerstatsorder",level:3},{value:"startAudioMuted",id:"startaudiomuted",level:3},{value:"startAudioOnly",id:"startaudioonly",level:3},{value:"startSilent",id:"startsilent",level:3},{value:"startWithAudioMuted",id:"startwithaudiomuted",level:3},{value:"Breakout rooms",id:"breakout-rooms",level:2},{value:"breakoutRooms",id:"breakoutrooms",level:3},{value:"hideAddRoomButton",id:"hideaddroombutton",level:3},{value:"Callstats",id:"callstats",level:2},{value:"callStatsConfigParams",id:"callstatsconfigparams",level:3},{value:"callStatsID",id:"callstatsid",level:3},{value:"callStatsSecret",id:"callstatssecret",level:3},{value:"enableDisplayNameInStats",id:"enabledisplaynameinstats",level:3},{value:"enableEmailInStats",id:"enableemailinstats",level:3},{value:"feedbackPercentage",id:"feedbackpercentage",level:3},{value:"Closed captions",id:"closed-captions",level:2},{value:"autoCaptionOnRecord*",id:"autocaptiononrecord",level:3},{value:"preferredTranscribingLanguage \ud83d\udeab",id:"preferredtranscribinglanguage-",level:3},{value:"transcribeWithAppLanguage \ud83d\udeab",id:"transcribewithapplanguage-",level:3},{value:"transcribingEnabled",id:"transcribingenabled",level:3},{value:"Connection",id:"connection",level:2},{value:"bosh*",id:"bosh",level:3},{value:"disableRtx",id:"disablertx",level:3},{value:"disableSimulcast",id:"disablesimulcast",level:3},{value:"e2ee",id:"e2ee",level:3},{value:"e2eping",id:"e2eping",level:3},{value:"enableEncodedTransformSupport",id:"enableencodedtransformsupport",level:3},{value:"enableForcedReload \ud83d\udeab",id:"enableforcedreload-",level:3},{value:"enableIceRestart",id:"enableicerestart",level:3},{value:"gatherStats",id:"gatherstats",level:3},{value:"hosts",id:"hosts",level:3},{value:"p2p",id:"p2p",level:3},{value:"pcStatsInterval",id:"pcstatsinterval",level:3},{value:"useTurnUdp",id:"useturnudp",level:3},{value:"webrtcIceTcpDisable",id:"webrtcicetcpdisable",level:3},{value:"webrtcIceUdpDisable",id:"webrtciceudpdisable",level:3},{value:"websocket \ud83d\udeab",id:"websocket-",level:3},{value:"Etherpad",id:"etherpad",level:2},{value:"etherpad_base",id:"etherpad_base",level:3},{value:"openSharedDocumentOnJoin",id:"openshareddocumentonjoin",level:3},{value:"Filmstrip",id:"filmstrip",level:2},{value:"disableFilmstripAutohiding",id:"disablefilmstripautohiding",level:3},{value:"filmstrip",id:"filmstrip-1",level:3},{value:"Face Landmarks",id:"face-landmarks",level:2},{value:"faceLandmarks",id:"facelandmarks",level:3},{value:"Giphy",id:"giphy",level:2},{value:"giphy",id:"giphy-1",level:3},{value:"Gravatar",id:"gravatar",level:2},{value:"gravatar",id:"gravatar-1",level:3},{value:"gravatarBaseURL \ud83d\udeab",id:"gravatarbaseurl-",level:3},{value:"LastN",id:"lastn",level:2},{value:"channelLastN",id:"channellastn",level:3},{value:"lastNLimits \ud83d\udeab",id:"lastnlimits-",level:3},{value:"startLastN",id:"startlastn",level:3},{value:"Lobby",id:"lobby",level:2},{value:"autoKnockLobby",id:"autoknocklobby",level:3},{value:"enableLobbyChat",id:"enablelobbychat",level:3},{value:"hideLobbyButton",id:"hidelobbybutton",level:3},{value:"lobby",id:"lobby-1",level:3},{value:"Moderator",id:"moderator",level:2},{value:"disableModeratorIndicator",id:"disablemoderatorindicator",level:3},{value:"disableReactionsModeration",id:"disablereactionsmoderation",level:3},{value:"disableRemoteMute",id:"disableremotemute",level:3},{value:"Notifications",id:"notifications",level:2},{value:"notifications",id:"notifications-1",level:3},{value:"disabledNotifications",id:"disablednotifications",level:3},{value:"Participants Pane",id:"participants-pane",level:2},{value:"participantsPane",id:"participantspane",level:3},{value:"Recording",id:"recording",level:2},{value:"dropbox",id:"dropbox",level:3},{value:"fileRecordingsEnabled",id:"filerecordingsenabled",level:3},{value:"fileRecordingsServiceEnabled \ud83d\udeab",id:"filerecordingsserviceenabled-",level:3},{value:"fileRecordingsServiceSharingEnabled \ud83d\udeab",id:"filerecordingsservicesharingenabled-",level:3},{value:"hideRecordingLabel",id:"hiderecordinglabel",level:3},{value:"localRecording",id:"localrecording",level:3},{value:"recordingLimit \ud83d\udeab",id:"recordinglimit-",level:3},{value:"Screen Sharing",id:"screen-sharing",level:2},{value:"desktopSharingFrameRate",id:"desktopsharingframerate",level:3},{value:"disableScreensharingVirtualBackground",id:"disablescreensharingvirtualbackground",level:3},{value:"enableLayerSuspension",id:"enablelayersuspension",level:3},{value:"screenshotCapture",id:"screenshotcapture",level:3},{value:"Security UI",id:"security-ui",level:2},{value:"securityUi",id:"securityui",level:3},{value:"Video",id:"video",level:2},{value:"constraints",id:"constraints",level:3},{value:"disableAddingBackgroundImages",id:"disableaddingbackgroundimages",level:3},{value:"disableH264",id:"disableh264",level:3},{value:"disableLocalVideoFlip",id:"disablelocalvideoflip",level:3},{value:"disableSelfView",id:"disableselfview",level:3},{value:"doNotFlipLocalVideo",id:"donotfliplocalvideo",level:3},{value:"maxFullResolutionParticipants",id:"maxfullresolutionparticipants",level:3},{value:"preferH264",id:"preferh264",level:3},{value:"resolution",id:"resolution",level:3},{value:"startVideoMuted",id:"startvideomuted",level:3},{value:"startWithVideoMuted",id:"startwithvideomuted",level:3},{value:"videoQuality",id:"videoquality",level:3},{value:"Whiteboard",id:"whiteboard",level:2},{value:"whiteboard",id:"whiteboard-1",level:3}],c={toc:u};function k(e){var t=e.components,a=(0,l.Z)(e,r);return(0,i.kt)("wrapper",(0,n.Z)({},c,a,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"This page describes available configuration options for Jitsi Meet. These are either set in ",(0,i.kt)("inlineCode",{parentName:"p"},"config.js")," on the server\nside or overridden in the app."),(0,i.kt)("admonition",{type:"note"},(0,i.kt)("p",{parentName:"admonition"},"Options marked with \ud83d\udeab are not overwritable through ",(0,i.kt)("inlineCode",{parentName:"p"},"configOverwrite"))),(0,i.kt)("admonition",{type:"warning"},(0,i.kt)("p",{parentName:"admonition"},"This page is a work in progress. Not all options are described here yet.")),(0,i.kt)("h2",{id:"api"},"API"),(0,i.kt)("h3",{id:"apiloglevels"},"apiLogLevels"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Array")),(0,i.kt)("p",null,"Logs that should go be passed through the 'log' event if a handler is defined for it"),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"apiLogLevels: ['warn', 'log', 'error', 'info', 'debug']\n")),(0,i.kt)("h3",{id:"buttonswithnotifyclick"},"buttonsWithNotifyClick"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Array")),(0,i.kt)("p",null,"Toolbar buttons which have their click/tap event exposed through the API on ",(0,i.kt)("inlineCode",{parentName:"p"},"toolbarButtonClicked"),". Passing a string for the button key will prevent execution of the click/tap routine; passing an object with ",(0,i.kt)("inlineCode",{parentName:"p"},"key")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"preventExecution")," flag on false will not prevent execution of the click/tap routine. Below array with mixed mode for passing the buttons."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"buttonsWithNotifyClick: [\n 'camera',\n {\n key: 'chat',\n preventExecution: false\n },\n {\n key: 'closedcaptions',\n preventExecution: true\n },\n 'desktop',\n 'download',\n 'embedmeeting',\n 'etherpad',\n 'feedback',\n 'filmstrip',\n 'fullscreen',\n 'hangup',\n 'help',\n {\n key: 'invite',\n preventExecution: false\n },\n 'livestreaming',\n 'microphone',\n 'mute-everyone',\n 'mute-video-everyone',\n 'participants-pane',\n 'profile',\n {\n key: 'raisehand',\n preventExecution: true\n },\n 'recording',\n 'security',\n 'select-background',\n 'settings',\n 'shareaudio',\n 'sharedvideo',\n 'shortcuts',\n 'stats',\n 'tileview',\n 'toggle-camera',\n 'videoquality',\n // The add passcode button from the security dialog.\n {\n key: 'add-passcode',\n preventExecution: false\n },\n '__end'\n]\n")),(0,i.kt)("h3",{id:"customparticipantmenubuttons"},"customParticipantMenuButtons"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Array<{ icon: string; id: string; text: string; }>")),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("p",null,"A list of custom buttons that can be added to the Participant Context Menu. Each will have an icon, that can be either a base64 encoded image or the path to an image, a unique id, and a text that will be displayed alongside the icon in the menu. This custom button will trigger the ",(0,i.kt)("inlineCode",{parentName:"p"},"participantMenuButtonClick")," event that will have the id set to the button as the ",(0,i.kt)("inlineCode",{parentName:"p"},"key")," and the ",(0,i.kt)("inlineCode",{parentName:"p"},"participantId"),", representing the id of the participant for which the button was clicked."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"customParticipantMenuButtons: [\n {\n icon: 'data:image/svg+xml;base64,...',\n id: 'custom-button',\n text: 'Custom Button'\n }\n]\n")),(0,i.kt)("h3",{id:"customtoolbarbuttons"},"customToolbarButtons"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Array<{ icon: string; id: string; text: string; }>")),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("p",null,"A list of custom buttons that can be added to the Toolbar. Each will have an icon, that can be either a base64 encoded image or the path to an image, a unique id, and a text that will be displayed alongside the icon in the menu. This custom button will trigger the ",(0,i.kt)("inlineCode",{parentName:"p"},"toolbarButtonClicked")," event that will the id set to the button as the ",(0,i.kt)("inlineCode",{parentName:"p"},"key"),"."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"customToolbarButtons: [\n {\n icon: 'data:image/svg+xml;base64,...',\n id: 'custom-toolbar-button',\n text: 'Custom Toolbar Button'\n }\n]\n")),(0,i.kt)("h3",{id:"mousemovecallbackinterval"},"mouseMoveCallbackInterval"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Number")),(0,i.kt)("p",null,"Default interval (milliseconds) for triggering ",(0,i.kt)("inlineCode",{parentName:"p"},"mouseMoved")," iframe API event."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"1000")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"mouseMoveCallbackInterval: 1000\n")),(0,i.kt)("h3",{id:"participantmenubuttonswithnotifyclick"},"participantMenuButtonsWithNotifyClick"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Array")),(0,i.kt)("p",null,"Participant context menu buttons which have their click/tap event exposed through the API on ",(0,i.kt)("inlineCode",{parentName:"p"},"participantMenuButtonClick"),". Passing a string for the button key will prevent execution of the click/tap routine; passing an object with ",(0,i.kt)("inlineCode",{parentName:"p"},"key")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"preventExecution")," flag on false will not prevent execution of the click/tap routine. Below array with mixed mode for passing the buttons."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"participantMenuButtonsWithNotifyClick: [\n 'allow-video',\n {\n key: 'ask-unmute',\n preventExecution: false\n },\n 'conn-status',\n 'flip-local-video',\n 'grant-moderator',\n {\n key: 'kick',\n preventExecution: true\n },\n {\n key: 'hide-self-view',\n preventExecution: false\n },\n 'mute',\n 'mute-others',\n 'mute-others-video',\n 'mute-video',\n 'pinToStage',\n 'privateMessage',\n {\n key: 'remote-control',\n preventExecution: false\n },\n 'send-participant-to-room',\n 'verify',\n]\n")),(0,i.kt)("h3",{id:"usehostpagelocalstorage"},"useHostPageLocalStorage"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"This property is related to the use case when Jitsi Meet is used via the IFrame API. When the property is true\nJitsi Meet will use the local storage of the host page instead of its own. This option is useful if the browser\nis not persisting the local storage inside the iframe."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"useHostPageLocalStorage: true\n")),(0,i.kt)("h2",{id:"audio"},"Audio"),(0,i.kt)("h3",{id:"audiolevelsinterval"},"audioLevelsInterval"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Number")),(0,i.kt)("p",null,"The interval (milliseconds) at which the audio levels are calculated."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"200")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"audioLevelsInterval: 200\n")),(0,i.kt)("h3",{id:"audioquality"},"audioQuality"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Specify audio quality stereo and opusMaxAverageBitrate values in order to enable HD audio.\nBeware, by doing so, you are disabling echo cancellation, noise suppression and AGC."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"audioQuality: {\n stereo: false,\n opusMaxAverageBitrate: null // Value to fit the 6000 to 510000 range.\n}\n")),(0,i.kt)("h3",{id:"disableaudiolevels"},"disableAudioLevels"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Disable measuring of audio levels."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"false")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"disableAudioLevels: false\n")),(0,i.kt)("h3",{id:"disablespeakerstatssearch"},(0,i.kt)("del",{parentName:"h3"},"disableSpeakerStatsSearch")),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Specifies whether there will be a search field in speaker stats or not."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"DEPRECATED")," Use ",(0,i.kt)("inlineCode",{parentName:"p"},"speakerStats.disableSearch")," instead."),(0,i.kt)("p",null,"Default: false"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"disableSpeakerStatsSearch: false\n")),(0,i.kt)("h3",{id:"disabledsounds"},"disabledSounds"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Array")),(0,i.kt)("p",null,"The sounds passed in this array will be disabled."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"disabledSounds: [\n // 'ASKED_TO_UNMUTE_SOUND'\n // 'E2EE_OFF_SOUND'\n // 'E2EE_ON_SOUND'\n // 'INCOMING_MSG_SOUND'\n // 'KNOCKING_PARTICIPANT_SOUND'\n // 'LIVE_STREAMING_OFF_SOUND'\n // 'LIVE_STREAMING_ON_SOUND'\n // 'NO_AUDIO_SIGNAL_SOUND'\n // 'NOISY_AUDIO_INPUT_SOUND'\n // 'OUTGOING_CALL_EXPIRED_SOUND'\n // 'OUTGOING_CALL_REJECTED_SOUND'\n // 'OUTGOING_CALL_RINGING_SOUND'\n // 'OUTGOING_CALL_START_SOUND'\n // 'PARTICIPANT_JOINED_SOUND'\n // 'PARTICIPANT_LEFT_SOUND'\n // 'RAISE_HAND_SOUND'\n // 'REACTION_SOUND'\n // 'RECORDING_OFF_SOUND'\n // 'RECORDING_ON_SOUND'\n // 'TALK_WHILE_MUTED_SOUND'\n]\n")),(0,i.kt)("h3",{id:"enablenoaudiodetection"},"enableNoAudioDetection"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Enabling this will run the lib-jitsi-meet no audio detection module which\nwill notify the user if the current selected microphone has no audio\ninput and will suggest another valid device if one is present."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"true")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"enableNoAudioDetection: true\n")),(0,i.kt)("h3",{id:"enablenoisymicdetection"},"enableNoisyMicDetection"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Enabling this will run the lib-jitsi-meet noise detection module which will\nnotify the user if there is noise, other than voice, coming from the current\nselected microphone. The purpose it to let the user know that the input could\nbe potentially unpleasant for other meeting participants."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"true")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"enableNoisyMicDetection: true\n")),(0,i.kt)("h3",{id:"speakerstats"},"speakerStats"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Options related to the speaker stats feature."),(0,i.kt)("p",null,"Properties: "),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"disabled")," - Specifies whether the speaker stats is enable or not."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"disableSearch")," - Specifies whether there will be a search field in speaker stats or not."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"order")," - Specifies whether participants in speaker stats should be ordered or not, and with what priority.")),(0,i.kt)("p",null,"Default:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"speakerStats: {\n disabled: false,\n disableSearch: false,\n order: [\n 'role', // Moderators on top.\n 'name', // Alphabetically by name.\n 'hasLeft', // The ones that have left in the bottom.\n ], // the order of the array elements determines priority.\n}\n")),(0,i.kt)("h3",{id:"speakerstatsorder"},(0,i.kt)("del",{parentName:"h3"},"speakerStatsOrder")),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Array")),(0,i.kt)("p",null,"Specifies whether participants in speaker stats should be ordered or not, and with what priority."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"DEPRECATED")," Use ",(0,i.kt)("inlineCode",{parentName:"p"},"speakerStats.order")," instead."),(0,i.kt)("p",null,"Default:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"}," speakerStatsOrder: [\n 'role', // Moderators on top.\n 'name', // Alphabetically by name.\n 'hasLeft', // The ones that have left in the bottom.\n ], // the order of the array elements determines priority.\n")),(0,i.kt)("h3",{id:"startaudiomuted"},"startAudioMuted"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Number")),(0,i.kt)("p",null,"Every participant after the Nth will start audio muted."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"startAudioMuted: 10\n")),(0,i.kt)("h3",{id:"startaudioonly"},"startAudioOnly"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Start the conference in audio only mode (no video is being received nor sent)."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"startAudioOnly: false\n")),(0,i.kt)("h3",{id:"startsilent"},"startSilent"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Enabling it (with #params) will disable local audio output of remote\nparticipants and to enable it back a reload is needed."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"startSilent: false\n")),(0,i.kt)("h3",{id:"startwithaudiomuted"},"startWithAudioMuted"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Start calls with audio muted. This option is only applied locally."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"startWithAudioMuted: false\n")),(0,i.kt)("h2",{id:"breakout-rooms"},"Breakout rooms"),(0,i.kt)("h3",{id:"breakoutrooms"},"breakoutRooms"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Options related to the breakout rooms feature."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("p",null,"Properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"hideAddRoomButton")," - Hides the add breakout room button. This replaces ",(0,i.kt)("inlineCode",{parentName:"li"},"hideAddRoomButton"),"."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"hideAutoAssignButton")," - Hides the auto assign participants button."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"hideJoinRoomButton")," - Hides the join breakout room button."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"hideModeratorSettingsTab")," - Hides the button to open the moderator settings tab."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"hideMoreActionsButton")," - Hides the more actions button."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"hideMuteAllButton")," - Hides the mute all button.")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"breakoutRooms: {\n hideAddRoomButton: false,\n hideAutoAssignButton: false,\n hideJoinRoomButton: false\n}\n")),(0,i.kt)("h3",{id:"hideaddroombutton"},(0,i.kt)("del",{parentName:"h3"},"hideAddRoomButton")),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"DEPRECATED")," Use ",(0,i.kt)("inlineCode",{parentName:"p"},"breakoutRooms.hideAddRoomButton")," instead."),(0,i.kt)("p",null,"Hides add breakout room button."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"false")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"hideAddRoomButton: false\n")),(0,i.kt)("h2",{id:"callstats"},"Callstats"),(0,i.kt)("h3",{id:"callstatsconfigparams"},"callStatsConfigParams"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"The callstats initialize config params as described in the API ",(0,i.kt)("a",{parentName:"p",href:"https://docs.callstats.io/docs/javascript#callstatsinitialize-with-app-secret"},"here"),"."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},'callStatsConfigParams: {\n disableBeforeUnloadHandler: true, // disables callstats.js\'s window.onbeforeunload parameter.\n applicationVersion: "app_version", // Application version specified by the developer.\n disablePrecalltest: true, // disables the pre-call test, it is enabled by default.\n siteID: "siteID", // The name/ID of the site/campus from where the call/pre-call test is made.\n additionalIDs: { // additionalIDs object, contains application related IDs.\n customerID: "Customer Identifier. Example, walmart.",\n tenantID: "Tenant Identifier. Example, monster.",\n productName: "Product Name. Example, Jitsi.",\n meetingsName: "Meeting Name. Example, Jitsi loves callstats.",\n serverName: "Server/MiddleBox Name. Example, jvb-prod-us-east-mlkncws12.",\n pbxID: "PBX Identifier. Example, walmart.",\n pbxExtensionID: "PBX Extension Identifier. Example, 5625.",\n fqExtensionID: "Fully qualified Extension Identifier. Example, +71 (US) +5625.",\n sessionID: "Session Identifier. Example, session-12-34"\n },\n collectLegacyStats: true, //enables the collection of legacy stats in chrome browser\n collectIP: true //enables the collection localIP address\n}\n')),(0,i.kt)("h3",{id:"callstatsid"},"callStatsID"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"String")),(0,i.kt)("p",null,"You must provide the Application ID to enable sending statistics to callstats.io"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"callStatsID: 'my-callstats-app-id'\n")),(0,i.kt)("h3",{id:"callstatssecret"},"callStatsSecret"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"String")),(0,i.kt)("p",null,"You must provide the Secret to enable sending statistics to callstats.io"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"callStatsSecret: 'my-callstats-secret'\n")),(0,i.kt)("h3",{id:"enabledisplaynameinstats"},"enableDisplayNameInStats"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Enables sending participants' display names to callstats."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"enableDisplayNameInStats: false\n")),(0,i.kt)("h3",{id:"enableemailinstats"},"enableEmailInStats"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Enables sending participants' emails (if available) to callstats and other analytics"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"enableEmailInStats: false\n")),(0,i.kt)("h3",{id:"feedbackpercentage"},"feedbackPercentage"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Number")),(0,i.kt)("p",null,"Controls the percentage of automatic feedback shown to participants when callstats is enabled.\nThe default value is 100%. If set to 0, no automatic feedback will be requested"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"feedbackPercentage: 100\n")),(0,i.kt)("h2",{id:"closed-captions"},"Closed captions"),(0,i.kt)("h3",{id:"autocaptiononrecord"},"autoCaptionOnRecord*"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Enables turning captions on automatically when the recording starts"),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"false")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"autoCaptionOnRecord: false\n")),(0,i.kt)("h3",{id:"preferredtranscribinglanguage-"},"preferredTranscribingLanguage \ud83d\udeab"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Transcriber language. This settings will only work if ",(0,i.kt)("inlineCode",{parentName:"p"},"transcribeWithAppLanguage")," is explicitly set to ",(0,i.kt)("inlineCode",{parentName:"p"},"false"),".\nAvailable languages can be found ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet/blob/master/react/features/transcribing/transcriber-langs.json"},"here"),"."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"'en-US'")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"preferredTranscribeLanguage: 'en-CA'\n")),(0,i.kt)("h3",{id:"transcribewithapplanguage-"},"transcribeWithAppLanguage \ud83d\udeab"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"If ",(0,i.kt)("inlineCode",{parentName:"p"},"true")," the transcriber will use the application language.\nThe application language is either explicitly set by participants in their settings or automatically\ndetected based on the environment, e.g. if the app is opened in a chrome instance which is using french as its\ndefault language then transcriptions for that participant will be in french."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"true")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"transcribeWithAppLanguage: false\n")),(0,i.kt)("h3",{id:"transcribingenabled"},"transcribingEnabled"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Enable transcription (in interface_config, subtitles and buttons can be configured)."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"false")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"transcribingEnabled: true\n")),(0,i.kt)("h2",{id:"connection"},"Connection"),(0,i.kt)("h3",{id:"bosh"},"bosh*"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"String")),(0,i.kt)("p",null,"The BOSH URL."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"bosh: '//jitsi-meet.example.com/http-bind'\n")),(0,i.kt)("h3",{id:"disablertx"},"disableRtx"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Disables or enables RTX (RFC 4588)."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"false")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"disableRtx: true\n")),(0,i.kt)("h3",{id:"disablesimulcast"},"disableSimulcast"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Enable / disable simulcast support."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"false")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"disableSimulcast: true\n")),(0,i.kt)("h3",{id:"e2ee"},"e2ee"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Configure End-to-End Encryption."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"e2ee: {\n labels: {\n labelTooltip: 'Tooltip',\n description: 'Description',\n label: 'E2EE',\n warning: 'Warning'\n },\n externallyManagedKey: false\n}\n")),(0,i.kt)("h3",{id:"e2eping"},"e2eping"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Options related to end-to-end (participant to participant) ping."),(0,i.kt)("p",null,"Properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"enabled")," - Whether end-to-end pings should be enabled."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"numRequests")," - The number of responses to wait for."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"maxConferenceSize")," - The max conference size in which e2e pings will be sent."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"maxMessagesPerSecond")," - The maximum number of e2e ping messages per second for the whole conference to aim for.\nThis is used to contol the pacing of messages in order to reduce the load on the backend.")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"e2eping: {\n enabled: false,\n numRequests: 5,\n maxConferenceSize: 200,\n maxMessagesPerSecond: 250\n}\n")),(0,i.kt)("h3",{id:"enableencodedtransformsupport"},"enableEncodedTransformSupport"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Enable support for encoded transform in supported browsers. This allows\nE2EE to work in Safari if the corresponding flag is enabled in the browser.\n",(0,i.kt)("strong",{parentName:"p"},"Experimental"),"."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"enableEncodedTransformSupport: false\n")),(0,i.kt)("h3",{id:"enableforcedreload-"},"enableForcedReload \ud83d\udeab"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Enables forced reload of the client when the call is migrated as a result of\nthe bridge going down."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"enableForcedReload: true\n")),(0,i.kt)("h3",{id:"enableicerestart"},"enableIceRestart"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,'Enables ICE restart logic in LJM and displays the page reload overlay on\nICE failure. Current disabled by default because it\'s causing issues with\nsignaling when Octo is enabled. Also when we do an "ICE restart"(which is\nnot a real ICE restart), the client maintains the TCC sequence number\ncounter, but the bridge resets it. The bridge sends media packets with\nTCC sequence numbers starting from 0.'),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"enableIceRestart: true\n")),(0,i.kt)("h3",{id:"gatherstats"},"gatherStats"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Whether to enable stats collection or not in the ",(0,i.kt)("inlineCode",{parentName:"p"},"TraceablePeerConnection"),".\nThis can be useful for debugging purposes (post-processing/analysis of\nthe WebRTC stats) as it is done in the jitsi-meet-torture bandwidth\nestimation tests."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"gatherStats: false\n")),(0,i.kt)("h3",{id:"hosts"},"hosts"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"URLs for the app connection."),(0,i.kt)("p",null,"Properties"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"domain")," - XMPP domain"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"anonymousdomain")," - When using authentication, domain for guest users."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"authdomain")," - Domain for authenticated users. Defaults to ",(0,i.kt)("inlineCode",{parentName:"li"},"domain"),"."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"focus")," - Focus component domain. Defaults to ",(0,i.kt)("strong",{parentName:"li"},"focus.",(0,i.kt)("inlineCode",{parentName:"strong"},"domain")),"."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"muc")," - XMPP MUC domain.")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"hosts: {\n domain: 'jitsi-meet.example.com',\n anonymousdomain: 'guest.example.com',\n authdomain: 'jitsi-meet.example.com',\n focus: 'focus.jitsi-meet.example.com',\n muc: 'conference.jitsi-meet.example.com'\n}\n")),(0,i.kt)("h3",{id:"p2p"},"p2p"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Peer-To-Peer mode: used (if enabled) when there are just 2 participants."),(0,i.kt)("p",null,"Properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"enabled")," - Enables peer to peer mode. When enabled the system will try to\nestablish a direct connection when there are exactly 2 participants\nin the room. If that succeeds the conference will stop sending data\nthrough the JVB and use the peer to peer connection instead. When a\n3rd participant joins the conference will be moved back to the JVB\nconnection."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"iceTransportPolicy")," - Sets the ICE transport policy for the p2p connection. At the time\nof this writing the list of possible values are ",(0,i.kt)("inlineCode",{parentName:"li"},"all")," and ",(0,i.kt)("inlineCode",{parentName:"li"},"relay"),",\nbut that is subject to change in the future. The enum is defined in\nthe ",(0,i.kt)("a",{parentName:"li",href:"https://www.w3.org/TR/webrtc/#rtcicetransportpolicy-enum"},"WebRTC standard"),".\nIf not set, the effective value is ",(0,i.kt)("inlineCode",{parentName:"li"},"all"),"."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"codecPreferenceOrder")," - Provides a way to set the codec preference on desktop based endpoints."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"mobileCodecPreferenceOrder")," - Provides a way to set the codec preference on mobile devices, both on RN and mobile browser based endpoints."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"preferredCodec")," - ",(0,i.kt)("strong",{parentName:"li"},"DEPRECATED")," Use ",(0,i.kt)("inlineCode",{parentName:"li"},"codecPreferenceOrder")," or ",(0,i.kt)("inlineCode",{parentName:"li"},"mobileCodecPreferenceOrder")," instead."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"disabledCodec")," - ",(0,i.kt)("strong",{parentName:"li"},"DEPRECATED")," Use ",(0,i.kt)("inlineCode",{parentName:"li"},"codecPreferenceOrder")," or ",(0,i.kt)("inlineCode",{parentName:"li"},"mobileCodecPreferenceOrder")," instead."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"backToP2PDelay")," - How long we're going to wait, before going back to P2P after the 3rd\nparticipant has left the conference (to filter out page reload)."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"stunServers")," - The STUN servers that will be used in the peer to peer connections.")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"p2p: {\n enabled: true,\n enableUnifiedOnChrome: false,\n iceTransportPolicy: 'all',\n backToP2PDelay: 5,\n stunServers: [\n { urls: 'stun:jitsi-meet.example.com:3478' },\n { urls: 'stun:meet-jit-si-turnrelay.jitsi.net:443' }\n ]\n}\n")),(0,i.kt)("h3",{id:"pcstatsinterval"},"pcStatsInterval"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Number")),(0,i.kt)("p",null,"The interval at which PeerConnection.getStats() is called."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"10000")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"pcStatsInterval: 50000\n")),(0,i.kt)("h3",{id:"useturnudp"},"useTurnUdp"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Use TURN/UDP servers for the jitsi-videobridge connection (by default\nwe filter out TURN/UDP because it is usually not needed since the\nbridge itself is reachable via UDP)"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"useTurnUdp: false\n")),(0,i.kt)("h3",{id:"webrtcicetcpdisable"},"webrtcIceTcpDisable"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Disables ICE/TCP by filtering out local and remote TCP candidates in signalling."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"webrtcIceTcpDisable: false\n")),(0,i.kt)("h3",{id:"webrtciceudpdisable"},"webrtcIceUdpDisable"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Disables ICE/UDP by filtering out local and remote UDP candidates in signalling."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"webrtcIceUdpDisable: false\n")),(0,i.kt)("h3",{id:"websocket-"},"websocket \ud83d\udeab"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"String")),(0,i.kt)("p",null,"Websocket URL"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"websocket: 'wss://jitsi-meet.example.com/xmpp-websocket'\n")),(0,i.kt)("h2",{id:"etherpad"},"Etherpad"),(0,i.kt)("h3",{id:"etherpad_base"},"etherpad_base"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"String")),(0,i.kt)("p",null,'If set, it adds a "Open shared document" link to the bottom right menu that\nwill open an etherpad document.'),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"etherpad_base: 'https://your-etherpad-installati.on/p/'\n")),(0,i.kt)("h3",{id:"openshareddocumentonjoin"},"openSharedDocumentOnJoin"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"If etherpad integration is enabled, setting this to ",(0,i.kt)("inlineCode",{parentName:"p"},"true")," will\nautomatically open the etherpad when a participant joins. This\ndoes not affect the mobile app since opening an etherpad\nobscures the conference controls -- it's better to let users\nchoose to open the pad on their own in that case."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"openSharedDocumentOnJoin: false\n")),(0,i.kt)("h2",{id:"filmstrip"},"Filmstrip"),(0,i.kt)("h3",{id:"disablefilmstripautohiding"},"disableFilmstripAutohiding"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Prevents the filmstrip from autohiding when screen width is under a certain threshold"),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"false")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"disableFilmstripAutohiding: true\n")),(0,i.kt)("h3",{id:"filmstrip-1"},"filmstrip"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Options related to the filmstrip."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("p",null,"Properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"disableResizable")," - Disables user resizable filmstrip. This also allows configuration of the filmstrip\n(width, tiles aspect ratios) through the interfaceConfig options."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"disableStageFilmstrip")," - Disables the stage filmstrip (displaying multiple\nparticipants on stage besides the vertical filmstrip)")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"filmstrip: {\n disableResizable: true,\n disableStageFilmstrip: false\n}\n")),(0,i.kt)("h2",{id:"face-landmarks"},"Face Landmarks"),(0,i.kt)("h3",{id:"facelandmarks"},"faceLandmarks"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Options related to the face landmarks features."),(0,i.kt)("p",null,"Properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"enableFaceCentering")," - Enables centering faces within a video by sharing face coordinates."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"enableFaceExpressionsDetection")," - Enables detecting face expressions from video."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"enableDisplayFaceExpressions")," - Enables displaying face expressions in speaker stats."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"enableRTCStats")," - Enables anonymized stats collection for face landmarks."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"faceCenteringThreshold")," - Minimum required face movement percentage threshold for sending new face centering coordinates data."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"captureInterval")," - Milliseconds for processing a new image capture in order to detect face landmarks.")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"faceLandmarks: {\n enableFaceCentering: false,\n enableFaceExpressionsDetection: false,\n enableDisplayFaceExpressions: false,\n enableRTCStats: false,\n faceCenteringThreshold: 20,\n captureInterval: 1000\n},\n")),(0,i.kt)("h2",{id:"giphy"},"Giphy"),(0,i.kt)("h3",{id:"giphy-1"},"giphy"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Setup for the Giphy integration."),(0,i.kt)("p",null,"Properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"enabled")," - Whether the feature is enabled or not."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"sdkKey")," - SDK API Key from Giphy."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"displayMode")," - Display mode can be one of:",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"tile")," - show the GIF on the tile of the participant that sent it."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"chat")," - show the GIF as a message in chat."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"all")," - all of the above. This is the default option."))),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"tileTime")," - How long the GIF should be displayed on the tile (in milliseconds)."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"rating")," - Limit results by audience rating: ",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"g")," - broadly accepted as appropriate in a public environment. This is the default option."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"pg")," - commonly witnessed in a public environment, but not as broadly accepted as appropriate."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"pg-13")," - typically not seen unless sought out, but still commonly witnessed."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"r")," - typically not seen unless sought out, and could be considered alarming if witnessed.")))),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"giphy: {\n enabled: true,\n sdkKey: 'example-key',\n displayMode: 'tile',\n tileTime: 7000,\n rating: 'pg'\n}\n")),(0,i.kt)("h2",{id:"gravatar"},"Gravatar"),(0,i.kt)("h3",{id:"gravatar-1"},"gravatar"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Setup for Gravatar-compatible services."),(0,i.kt)("p",null,"Properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"baseUrl")," \ud83d\udeab - Base URL for a Gravatar-compatible service. Defaults to Gravatar."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"disabled")," - True if Gravatar should be disabled.")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"gravatar: {\n baseUrl: 'https://www.gravatar.com/avatar/',\n disabled: false\n}\n")),(0,i.kt)("h3",{id:"gravatarbaseurl-"},(0,i.kt)("del",{parentName:"h3"},"gravatarBaseURL")," \ud83d\udeab"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"String")),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"DEPRECATED")," Use ",(0,i.kt)("inlineCode",{parentName:"p"},"gravatar.baseUrl")," instead."),(0,i.kt)("p",null,"Base URL for a Gravatar-compatible service."),(0,i.kt)("p",null,"Default: '",(0,i.kt)("a",{parentName:"p",href:"https://www.gravatar.com/avatar/'"},"https://www.gravatar.com/avatar/'")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"gravatarBaseURL: 'https://www.gravatar.com/avatar/'\n")),(0,i.kt)("h2",{id:"lastn"},"LastN"),(0,i.kt)("h3",{id:"channellastn"},"channelLastN"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Number")),(0,i.kt)("p",null,'Default value for the channel "last N" attribute. -1 for unlimited.'),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"channelLastN: -1\n")),(0,i.kt)("h3",{id:"lastnlimits-"},"lastNLimits \ud83d\udeab"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,'Provides a way to use different "last N" values based on the number of participants in the conference.\nThe keys in an Object represent number of participants and the values are "last N" to be used when number of\nparticipants gets to or above the number.'),(0,i.kt)("p",null,"For the given example mapping, \"last N\" will be set to 20 as long as there are at least 5, but less than\n29 participants in the call and it will be lowered to 15 when the 30th participant joins. The 'channelLastN'\nwill be used as default until the first threshold is reached."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"lastNLimits: {\n 5: 20,\n 30: 15,\n 50: 10,\n 70: 5,\n 90: 2\n}\n")),(0,i.kt)("h3",{id:"startlastn"},"startLastN"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Number")),(0,i.kt)("p",null,'Provides a way for the lastN value to be controlled through the UI.\nWhen startLastN is present, conference starts with a last-n value of startLastN and channelLastN\nvalue will be used when the quality level is selected using "Manage Video Quality" slider.'),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"startLastN: 1\n")),(0,i.kt)("h2",{id:"lobby"},"Lobby"),(0,i.kt)("h3",{id:"autoknocklobby"},(0,i.kt)("del",{parentName:"h3"},"autoKnockLobby")),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"DEPRECATED")," Use ",(0,i.kt)("inlineCode",{parentName:"p"},"lobby.autoKnock")," instead."),(0,i.kt)("p",null,"If Lobby is enabled starts knocking automatically."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"autoKnockLobby: false\n")),(0,i.kt)("h3",{id:"enablelobbychat"},(0,i.kt)("del",{parentName:"h3"},"enableLobbyChat")),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"DEPRECATED")," Use ",(0,i.kt)("inlineCode",{parentName:"p"},"lobby.enableChat")," instead."),(0,i.kt)("p",null,"Enable lobby chat."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"enableLobbyChat: false\n")),(0,i.kt)("h3",{id:"hidelobbybutton"},(0,i.kt)("del",{parentName:"h3"},"hideLobbyButton")),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"DEPRECATED")," Use ",(0,i.kt)("inlineCode",{parentName:"p"},"securityUi.hideLobbyButton")," instead."),(0,i.kt)("p",null,"Hide the lobby button."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"hideLobbyButton: false\n")),(0,i.kt)("h3",{id:"lobby-1"},"lobby"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Options related to the lobby screen."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("p",null,"Properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"autoKnock")," - If the lobby is enabled, it starts knocking automatically. Replaces ",(0,i.kt)("inlineCode",{parentName:"li"},"autoKnockLobby"),"."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"enableChat")," - Enables the lobby chat. Replaces ",(0,i.kt)("inlineCode",{parentName:"li"},"enableLobbyChat"),".")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"lobby: {\n autoKnock: true,\n enableChat: false\n}\n")),(0,i.kt)("h2",{id:"moderator"},"Moderator"),(0,i.kt)("h3",{id:"disablemoderatorindicator"},"disableModeratorIndicator"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Hides the moderator indicators."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"false")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"disableModeratorIndicator: true\n")),(0,i.kt)("h3",{id:"disablereactionsmoderation"},"disableReactionsModeration"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Disables the moderation of reactions feature."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"false")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"disableReactionsModeration: true\n")),(0,i.kt)("h3",{id:"disableremotemute"},"disableRemoteMute"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Disables muting operations of remote participants."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"false")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"disableRemoteMute: true\n")),(0,i.kt)("h2",{id:"notifications"},"Notifications"),(0,i.kt)("h3",{id:"notifications-1"},"notifications"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Array")),(0,i.kt)("p",null,"Use this array to configure which notifications will be shown to the user.\nThe items correspond to the title or description key of that notification.\nSome of these notifications also depend on some other internal logic to be displayed or not,\nso adding them here will not ensure they will always be displayed."),(0,i.kt)("p",null,"A falsy value for this prop will result in having all notifications enabled (e.g null, undefined, false)."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"notifications: []\n")),(0,i.kt)("h3",{id:"disablednotifications"},"disabledNotifications"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Array")),(0,i.kt)("p",null,"List of notifications to be disabled. Works in tandem with the above setting."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"disabledNotifications: [\n 'notify.chatMessages', // shown when receiving chat messages while the chat window is closed\n 'notify.grantedTo', // shown when moderator rights were granted to a participant\n]\n")),(0,i.kt)("h2",{id:"participants-pane"},"Participants Pane"),(0,i.kt)("h3",{id:"participantspane"},"participantsPane"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Options related to the participants pane."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("p",null,"Properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"hideModeratorSettingsTab")," - Hides the button to open the moderator settings tab."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"hideMoreActionsButton")," - Hides the more actions button."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"hideMuteAllButton")," - Hides the mute all button.")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"participantsPane: {\n hideModeratorSettingsTab: false,\n hideMoreActionsButton: false,\n hideMuteAllButton: false\n}\n")),(0,i.kt)("h2",{id:"recording"},"Recording"),(0,i.kt)("h3",{id:"dropbox"},"dropbox"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Enable the dropbox integration."),(0,i.kt)("p",null,"Properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"appKey")," - Your APP Key."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"redirectURI")," - A URL to redirect the user to, after authenticating by default uses")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"dropbox: {\n appKey: 'DROPBOX_APP_KEY',\n redirectURI: 'https://jitsi-meet.example.com/subfolder/static/oauth.html'\n}\n")),(0,i.kt)("h3",{id:"filerecordingsenabled"},"fileRecordingsEnabled"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Whether to enable file recording or not."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"fileRecordingsEnabled: false\n")),(0,i.kt)("h3",{id:"filerecordingsserviceenabled-"},"fileRecordingsServiceEnabled \ud83d\udeab"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"When integrations like dropbox are enabled only that will be shown,\nby enabling fileRecordingsServiceEnabled, we show both the integrations\nand the generic recording service (its configuration and storage type\ndepends on jibri configuration)"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"fileRecordingsServiceEnabled: true\n")),(0,i.kt)("h3",{id:"filerecordingsservicesharingenabled-"},"fileRecordingsServiceSharingEnabled \ud83d\udeab"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Whether to show the possibility to share file recording with other people\n(e.g. meeting participants), based on the actual implementation\non the backend."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"fileRecordingsServiceSharingEnabled: false\n")),(0,i.kt)("h3",{id:"hiderecordinglabel"},"hideRecordingLabel"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Set recording label to auto hide instead of staying always on screen."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"false")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"hideRecordingLabel: true\n")),(0,i.kt)("h3",{id:"localrecording"},"localRecording"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Set local recording configuration."),(0,i.kt)("p",null,"Properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"disable")," - Whether to disable the feature or not."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"notifyAllParticipants")," - Whether to notify all the participants when a local recording is started.")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"localRecording: {\n disable: false,\n notifyAllParticipants: true\n}\n")),(0,i.kt)("h3",{id:"recordinglimit-"},"recordingLimit \ud83d\udeab"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Options for the recording limit notification."),(0,i.kt)("p",null,"Properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"limit")," - The recording limit in minutes. Note: This number appears in the notification text\nbut doesn't enforce the actual recording time limit. This should be configured in jibri!"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"appName")," = The name of the app with unlimited recordings."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"appURL")," - The URL of the app with unlimited recordings.")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"recordingLimit: {\n limit: 60,\n appName: 'Unlimited recordings APP',\n appURL: 'https://unlimited.recordings.app.com/'\n}\n")),(0,i.kt)("h2",{id:"screen-sharing"},"Screen Sharing"),(0,i.kt)("h3",{id:"desktopsharingframerate"},"desktopSharingFrameRate"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Optional desktop sharing frame rate options"),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"{\n min: 5,\n max: 5\n}")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"desktopSharingFrameRate: {\n min: 3,\n max: 10\n}\n")),(0,i.kt)("h3",{id:"disablescreensharingvirtualbackground"},"disableScreensharingVirtualBackground"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Disables using screensharing as virtual background."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"disableScreensharingVirtualBackground: false\n")),(0,i.kt)("h3",{id:"enablelayersuspension"},"enableLayerSuspension"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Enable layer suspension. If enabled, endpoints whose HD layers are not in use will be suspended\n(no longer sent) until they are requested again. This must be enabled for screen\nsharing to work as expected on Chrome. Disabling this might result in low resolution screenshare being sent\nby the client."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"true")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"enableLayerSuspension: false\n")),(0,i.kt)("h3",{id:"screenshotcapture"},"screenshotCapture"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Options for the screenshot capture feature."),(0,i.kt)("p",null,"Properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"enabled")," - Enables the feature"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"mode")," - The mode for the screenshot capture feature. Can be either 'recording' - screensharing screenshots\nare taken only when the recording is also on, or 'always' - screensharing screenshots are always taken.")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"screenshotCapture: {\n enabled: true,\n mode: 'recording'\n}\n")),(0,i.kt)("h2",{id:"security-ui"},"Security UI"),(0,i.kt)("h3",{id:"securityui"},"securityUi"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Options regarding the security related UI elements."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("p",null,"Properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"hideLobbyButton")," - Hides the lobby button. Replaces ",(0,i.kt)("inlineCode",{parentName:"li"},"hideLobbyButton"),"."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"disableLobbyPassword")," - Hides the possibility to set and enter a lobby password.")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"securityUi: {\n hideLobbyButton: true,\n disableLobbyPassword: false\n}\n")),(0,i.kt)("h2",{id:"video"},"Video"),(0,i.kt)("h3",{id:"constraints"},"constraints"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"W3C spec-compliant video constraints to use for video capture. Currently\nused by browsers that return true from lib-jitsi-meet's\n",(0,i.kt)("inlineCode",{parentName:"p"},"util#browser#usesNewGumFlow"),". The constraints are independent from\nthis config's resolution value. Defaults to requesting an ideal\nresolution of 720p."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"constraints: {\n video: {\n height: {\n ideal: 720,\n max: 720,\n min: 240\n }\n }\n}\n")),(0,i.kt)("h3",{id:"disableaddingbackgroundimages"},"disableAddingBackgroundImages"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"When true the user cannot add more images to be used as virtual background.\nOnly the default ones from will be available."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"disableAddingBackgroundImages: true\n")),(0,i.kt)("h3",{id:"disableh264"},"disableH264"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"If set to true, disable H.264 video codec by stripping it out of the SDP."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"disableH264: true\n")),(0,i.kt)("h3",{id:"disablelocalvideoflip"},"disableLocalVideoFlip"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Disable the Flip video option from the context menu for local video."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"disableLocalVideoFlip: true\n")),(0,i.kt)("h3",{id:"disableselfview"},"disableSelfView"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Disables self-view tile. (hides it from tile view and from filmstrip)"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"disableSelfView: true\n")),(0,i.kt)("h3",{id:"donotfliplocalvideo"},"doNotFlipLocalVideo"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"A property used to unset the default flip state of the local video.\nWhen it is set to ",(0,i.kt)("inlineCode",{parentName:"p"},"true"),", the local(self) video will not be mirrored anymore."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"doNotFlipLocalVideo: true\n")),(0,i.kt)("h3",{id:"maxfullresolutionparticipants"},"maxFullResolutionParticipants"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"How many participants while in the tile view mode, before the receiving video quality is reduced from HD to SD.\nUse ",(0,i.kt)("inlineCode",{parentName:"p"},"-1")," to disable."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"maxFullResolutionParticipants: 5\n")),(0,i.kt)("h3",{id:"preferh264"},(0,i.kt)("del",{parentName:"h3"},"preferH264")),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"DEPRECATED")," Use ",(0,i.kt)("inlineCode",{parentName:"p"},"preferredCodec")," under ",(0,i.kt)("inlineCode",{parentName:"p"},"videoQuality")," section instead."),(0,i.kt)("p",null,"Prefer to use the H.264 video codec (if supported).\nNote that it's not recommended to do this because simulcast is not\nsupported when using H.264. For 1-to-1 calls this setting is enabled by\ndefault and can be toggled in the p2p section."),(0,i.kt)("h3",{id:"resolution"},"resolution"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Number")),(0,i.kt)("p",null,"Sets the preferred resolution (height) for local video"),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"720")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"resolution: 1080\n")),(0,i.kt)("h3",{id:"startvideomuted"},"startVideoMuted"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Number")),(0,i.kt)("p",null,"Every participant after the Nth will start video muted."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"startVideoMuted: 5\n")),(0,i.kt)("h3",{id:"startwithvideomuted"},"startWithVideoMuted"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Start calls with video muted. Only applied locally."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"startWithVideoMuted: true\n")),(0,i.kt)("h3",{id:"videoquality"},"videoQuality"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Specify the settings for video quality optimizations on the client."),(0,i.kt)("p",null,"Properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},(0,i.kt)("inlineCode",{parentName:"p"},"codecPreferenceOrder")," - Provides a way to set the codec preference on desktop based endpoints.")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},(0,i.kt)("inlineCode",{parentName:"p"},"mobileCodecPreferenceOrder")," - Provides a way to set the codec preference on mobile devices, both on RN and mobile browser based endpoints.")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},(0,i.kt)("inlineCode",{parentName:"p"},"disabledCodec")," - ",(0,i.kt)("strong",{parentName:"p"},"DEPRECATED")," Use ",(0,i.kt)("inlineCode",{parentName:"p"},"codecPreferenceOrder")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"mobileCodecPreferenceOrder")," instead.")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},(0,i.kt)("inlineCode",{parentName:"p"},"preferredCodec")," - ",(0,i.kt)("strong",{parentName:"p"},"DEPRECATED")," Use ",(0,i.kt)("inlineCode",{parentName:"p"},"codecPreferenceOrder")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"mobileCodecPreferenceOrder")," instead.")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},(0,i.kt)("inlineCode",{parentName:"p"},"maxBitratesVideo")," - Provides a way to configure the maximum bitrates that will be enforced on the simulcast streams for\nvideo tracks. The keys in the object represent the type of the stream (LD, SD or HD) and the values\nare the max.bitrates to be set on that particular type of stream. The actual send may vary based on\nthe available bandwidth calculated by the browser, but it will be capped by the values specified here.\nThis is currently not implemented on app based clients on mobile.")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},(0,i.kt)("inlineCode",{parentName:"p"},"minHeightForQualityLvl")," - The options can be used to override default thresholds of video thumbnail heights corresponding to\nthe video quality levels used in the application. At the time of this writing the allowed levels are:"),(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},(0,i.kt)("inlineCode",{parentName:"p"},"low")," - for the low quality level (180p at the time of this writing)")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},(0,i.kt)("inlineCode",{parentName:"p"},"standard")," - for the medium quality level (360p)")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},(0,i.kt)("inlineCode",{parentName:"p"},"high")," - for the high quality level (720p)"),(0,i.kt)("p",{parentName:"li"},"The keys should be positive numbers which represent the minimal thumbnail height for the quality level.\nWith the default config value below the application will use 'low' quality until the thumbnails are\nat least 360 pixels tall. If the thumbnail height reaches 720 pixels then the application will switch to\nthe high quality."))))),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"videoQuality: {\n maxBitratesVideo: {\n H264: {\n low: 200000,\n standard: 500000,\n high: 1500000\n },\n VP8 : {\n low: 200000,\n standard: 500000,\n high: 1500000\n },\n VP9: {\n low: 100000,\n standard: 300000,\n high: 1200000\n }\n },\n minHeightForQualityLvl: {\n 360: 'standard',\n 720: 'high'\n },\n}\n")),(0,i.kt)("h2",{id:"whiteboard"},"Whiteboard"),(0,i.kt)("h3",{id:"whiteboard-1"},"whiteboard"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Options related to the Excalidraw whiteboard integration."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("p",null,"Properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"enabled")," - Whether the feature is enabled or not."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"collabServerBaseUrl")," - The ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/jitsi/excalidraw-backend"},"server")," used to support whiteboard collaboration.")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"whiteboard: {\n enabled: true,\n collabServerBaseUrl: 'https://excalidraw-backend.example.com'\n}\n")))}k.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[4051],{3905:(e,t,a)=>{a.d(t,{Zo:()=>d,kt:()=>k});var n=a(7294);function l(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function i(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function r(e){for(var t=1;t=0||(l[a]=e[a]);return l}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(l[a]=e[a])}return l}var s=n.createContext({}),p=function(e){var t=n.useContext(s),a=t;return e&&(a="function"==typeof e?e(t):r(r({},t),e)),a},d=function(e){var t=p(e.components);return n.createElement(s.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},c=n.forwardRef((function(e,t){var a=e.components,l=e.mdxType,i=e.originalType,s=e.parentName,d=o(e,["components","mdxType","originalType","parentName"]),c=p(a),k=l,m=c["".concat(s,".").concat(k)]||c[k]||u[k]||i;return a?n.createElement(m,r(r({ref:t},d),{},{components:a})):n.createElement(m,r({ref:t},d))}));function k(e,t){var a=arguments,l=t&&t.mdxType;if("string"==typeof e||l){var i=a.length,r=new Array(i);r[0]=c;var o={};for(var s in t)hasOwnProperty.call(t,s)&&(o[s]=t[s]);o.originalType=e,o.mdxType="string"==typeof e?e:l,r[1]=o;for(var p=2;p{a.r(t),a.d(t,{assets:()=>d,contentTitle:()=>s,default:()=>k,frontMatter:()=>o,metadata:()=>p,toc:()=>u});var n=a(7462),l=a(3366),i=(a(7294),a(3905)),r=["components"],o={id:"dev-guide-configuration",title:"Configuration"},s=void 0,p={unversionedId:"dev-guide/dev-guide-configuration",id:"dev-guide/dev-guide-configuration",title:"Configuration",description:"This page describes available configuration options for Jitsi Meet. These are either set in config.js on the server",source:"@site/docs/dev-guide/configuration.md",sourceDirName:"dev-guide",slug:"/dev-guide/dev-guide-configuration",permalink:"/handbook/docs/dev-guide/dev-guide-configuration",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/dev-guide/configuration.md",tags:[],version:"current",lastUpdatedAt:1695203964,formattedLastUpdatedAt:"Sep 20, 2023",frontMatter:{id:"dev-guide-configuration",title:"Configuration"},sidebar:"docs",previous:{title:"Flutter SDK",permalink:"/handbook/docs/dev-guide/dev-guide-flutter-sdk"},next:{title:"Overview",permalink:"/handbook/docs/devops-guide/"}},d={},u=[{value:"API",id:"api",level:2},{value:"apiLogLevels",id:"apiloglevels",level:3},{value:"buttonsWithNotifyClick",id:"buttonswithnotifyclick",level:3},{value:"customParticipantMenuButtons",id:"customparticipantmenubuttons",level:3},{value:"customToolbarButtons",id:"customtoolbarbuttons",level:3},{value:"mouseMoveCallbackInterval",id:"mousemovecallbackinterval",level:3},{value:"participantMenuButtonsWithNotifyClick",id:"participantmenubuttonswithnotifyclick",level:3},{value:"useHostPageLocalStorage",id:"usehostpagelocalstorage",level:3},{value:"Audio",id:"audio",level:2},{value:"audioLevelsInterval",id:"audiolevelsinterval",level:3},{value:"audioQuality",id:"audioquality",level:3},{value:"disableAudioLevels",id:"disableaudiolevels",level:3},{value:"disableSpeakerStatsSearch",id:"disablespeakerstatssearch",level:3},{value:"disabledSounds",id:"disabledsounds",level:3},{value:"enableNoAudioDetection",id:"enablenoaudiodetection",level:3},{value:"enableNoisyMicDetection",id:"enablenoisymicdetection",level:3},{value:"speakerStats",id:"speakerstats",level:3},{value:"speakerStatsOrder",id:"speakerstatsorder",level:3},{value:"startAudioMuted",id:"startaudiomuted",level:3},{value:"startAudioOnly",id:"startaudioonly",level:3},{value:"startSilent",id:"startsilent",level:3},{value:"startWithAudioMuted",id:"startwithaudiomuted",level:3},{value:"Breakout rooms",id:"breakout-rooms",level:2},{value:"breakoutRooms",id:"breakoutrooms",level:3},{value:"hideAddRoomButton",id:"hideaddroombutton",level:3},{value:"Callstats",id:"callstats",level:2},{value:"callStatsConfigParams",id:"callstatsconfigparams",level:3},{value:"callStatsID",id:"callstatsid",level:3},{value:"callStatsSecret",id:"callstatssecret",level:3},{value:"enableDisplayNameInStats",id:"enabledisplaynameinstats",level:3},{value:"enableEmailInStats",id:"enableemailinstats",level:3},{value:"feedbackPercentage",id:"feedbackpercentage",level:3},{value:"Closed captions",id:"closed-captions",level:2},{value:"autoCaptionOnRecord*",id:"autocaptiononrecord",level:3},{value:"preferredTranscribingLanguage \ud83d\udeab",id:"preferredtranscribinglanguage-",level:3},{value:"transcribeWithAppLanguage \ud83d\udeab",id:"transcribewithapplanguage-",level:3},{value:"transcribingEnabled",id:"transcribingenabled",level:3},{value:"Connection",id:"connection",level:2},{value:"bosh*",id:"bosh",level:3},{value:"disableRtx",id:"disablertx",level:3},{value:"disableSimulcast",id:"disablesimulcast",level:3},{value:"e2ee",id:"e2ee",level:3},{value:"e2eping",id:"e2eping",level:3},{value:"enableEncodedTransformSupport",id:"enableencodedtransformsupport",level:3},{value:"enableForcedReload \ud83d\udeab",id:"enableforcedreload-",level:3},{value:"enableIceRestart",id:"enableicerestart",level:3},{value:"gatherStats",id:"gatherstats",level:3},{value:"hosts",id:"hosts",level:3},{value:"p2p",id:"p2p",level:3},{value:"pcStatsInterval",id:"pcstatsinterval",level:3},{value:"useTurnUdp",id:"useturnudp",level:3},{value:"webrtcIceTcpDisable",id:"webrtcicetcpdisable",level:3},{value:"webrtcIceUdpDisable",id:"webrtciceudpdisable",level:3},{value:"websocket \ud83d\udeab",id:"websocket-",level:3},{value:"Etherpad",id:"etherpad",level:2},{value:"etherpad_base",id:"etherpad_base",level:3},{value:"openSharedDocumentOnJoin",id:"openshareddocumentonjoin",level:3},{value:"Filmstrip",id:"filmstrip",level:2},{value:"disableFilmstripAutohiding",id:"disablefilmstripautohiding",level:3},{value:"filmstrip",id:"filmstrip-1",level:3},{value:"Face Landmarks",id:"face-landmarks",level:2},{value:"faceLandmarks",id:"facelandmarks",level:3},{value:"Giphy",id:"giphy",level:2},{value:"giphy",id:"giphy-1",level:3},{value:"Gravatar",id:"gravatar",level:2},{value:"gravatar",id:"gravatar-1",level:3},{value:"gravatarBaseURL \ud83d\udeab",id:"gravatarbaseurl-",level:3},{value:"LastN",id:"lastn",level:2},{value:"channelLastN",id:"channellastn",level:3},{value:"lastNLimits \ud83d\udeab",id:"lastnlimits-",level:3},{value:"startLastN",id:"startlastn",level:3},{value:"Lobby",id:"lobby",level:2},{value:"autoKnockLobby",id:"autoknocklobby",level:3},{value:"enableLobbyChat",id:"enablelobbychat",level:3},{value:"hideLobbyButton",id:"hidelobbybutton",level:3},{value:"lobby",id:"lobby-1",level:3},{value:"Moderator",id:"moderator",level:2},{value:"disableModeratorIndicator",id:"disablemoderatorindicator",level:3},{value:"disableReactionsModeration",id:"disablereactionsmoderation",level:3},{value:"disableRemoteMute",id:"disableremotemute",level:3},{value:"Notifications",id:"notifications",level:2},{value:"notifications",id:"notifications-1",level:3},{value:"disabledNotifications",id:"disablednotifications",level:3},{value:"Participants Pane",id:"participants-pane",level:2},{value:"participantsPane",id:"participantspane",level:3},{value:"Recording",id:"recording",level:2},{value:"dropbox",id:"dropbox",level:3},{value:"fileRecordingsEnabled",id:"filerecordingsenabled",level:3},{value:"fileRecordingsServiceEnabled \ud83d\udeab",id:"filerecordingsserviceenabled-",level:3},{value:"fileRecordingsServiceSharingEnabled \ud83d\udeab",id:"filerecordingsservicesharingenabled-",level:3},{value:"hideRecordingLabel",id:"hiderecordinglabel",level:3},{value:"localRecording",id:"localrecording",level:3},{value:"recordingLimit \ud83d\udeab",id:"recordinglimit-",level:3},{value:"Screen Sharing",id:"screen-sharing",level:2},{value:"desktopSharingFrameRate",id:"desktopsharingframerate",level:3},{value:"disableScreensharingVirtualBackground",id:"disablescreensharingvirtualbackground",level:3},{value:"enableLayerSuspension",id:"enablelayersuspension",level:3},{value:"screenshotCapture",id:"screenshotcapture",level:3},{value:"Security UI",id:"security-ui",level:2},{value:"securityUi",id:"securityui",level:3},{value:"Video",id:"video",level:2},{value:"constraints",id:"constraints",level:3},{value:"disableAddingBackgroundImages",id:"disableaddingbackgroundimages",level:3},{value:"disableH264",id:"disableh264",level:3},{value:"disableLocalVideoFlip",id:"disablelocalvideoflip",level:3},{value:"disableSelfView",id:"disableselfview",level:3},{value:"doNotFlipLocalVideo",id:"donotfliplocalvideo",level:3},{value:"maxFullResolutionParticipants",id:"maxfullresolutionparticipants",level:3},{value:"preferH264",id:"preferh264",level:3},{value:"resolution",id:"resolution",level:3},{value:"startVideoMuted",id:"startvideomuted",level:3},{value:"startWithVideoMuted",id:"startwithvideomuted",level:3},{value:"videoQuality",id:"videoquality",level:3},{value:"Whiteboard",id:"whiteboard",level:2},{value:"whiteboard",id:"whiteboard-1",level:3}],c={toc:u};function k(e){var t=e.components,a=(0,l.Z)(e,r);return(0,i.kt)("wrapper",(0,n.Z)({},c,a,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"This page describes available configuration options for Jitsi Meet. These are either set in ",(0,i.kt)("inlineCode",{parentName:"p"},"config.js")," on the server\nside or overridden in the app."),(0,i.kt)("admonition",{type:"note"},(0,i.kt)("p",{parentName:"admonition"},"Options marked with \ud83d\udeab are not overwritable through ",(0,i.kt)("inlineCode",{parentName:"p"},"configOverwrite"))),(0,i.kt)("admonition",{type:"warning"},(0,i.kt)("p",{parentName:"admonition"},"This page is a work in progress. Not all options are described here yet.")),(0,i.kt)("h2",{id:"api"},"API"),(0,i.kt)("h3",{id:"apiloglevels"},"apiLogLevels"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Array")),(0,i.kt)("p",null,"Logs that should go be passed through the 'log' event if a handler is defined for it"),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"apiLogLevels: ['warn', 'log', 'error', 'info', 'debug']\n")),(0,i.kt)("h3",{id:"buttonswithnotifyclick"},"buttonsWithNotifyClick"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Array")),(0,i.kt)("p",null,"Toolbar buttons which have their click/tap event exposed through the API on ",(0,i.kt)("inlineCode",{parentName:"p"},"toolbarButtonClicked"),". Passing a string for the button key will prevent execution of the click/tap routine; passing an object with ",(0,i.kt)("inlineCode",{parentName:"p"},"key")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"preventExecution")," flag on false will not prevent execution of the click/tap routine. Below array with mixed mode for passing the buttons."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"buttonsWithNotifyClick: [\n 'camera',\n {\n key: 'chat',\n preventExecution: false\n },\n {\n key: 'closedcaptions',\n preventExecution: true\n },\n 'desktop',\n 'download',\n 'embedmeeting',\n 'etherpad',\n 'feedback',\n 'filmstrip',\n 'fullscreen',\n 'hangup',\n 'help',\n {\n key: 'invite',\n preventExecution: false\n },\n 'livestreaming',\n 'microphone',\n 'mute-everyone',\n 'mute-video-everyone',\n 'participants-pane',\n 'profile',\n {\n key: 'raisehand',\n preventExecution: true\n },\n 'recording',\n 'security',\n 'select-background',\n 'settings',\n 'shareaudio',\n 'sharedvideo',\n 'shortcuts',\n 'stats',\n 'tileview',\n 'toggle-camera',\n 'videoquality',\n // The add passcode button from the security dialog.\n {\n key: 'add-passcode',\n preventExecution: false\n },\n '__end'\n]\n")),(0,i.kt)("h3",{id:"customparticipantmenubuttons"},"customParticipantMenuButtons"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Array<{ icon: string; id: string; text: string; }>")),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("p",null,"A list of custom buttons that can be added to the Participant Context Menu. Each will have an icon, that can be either a base64 encoded image or the path to an image, a unique id, and a text that will be displayed alongside the icon in the menu. This custom button will trigger the ",(0,i.kt)("inlineCode",{parentName:"p"},"participantMenuButtonClick")," event that will have the id set to the button as the ",(0,i.kt)("inlineCode",{parentName:"p"},"key")," and the ",(0,i.kt)("inlineCode",{parentName:"p"},"participantId"),", representing the id of the participant for which the button was clicked."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"customParticipantMenuButtons: [\n {\n icon: 'data:image/svg+xml;base64,...',\n id: 'custom-button',\n text: 'Custom Button'\n }\n]\n")),(0,i.kt)("h3",{id:"customtoolbarbuttons"},"customToolbarButtons"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Array<{ icon: string; id: string; text: string; }>")),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("p",null,"A list of custom buttons that can be added to the Toolbar. Each will have an icon, that can be either a base64 encoded image or the path to an image, a unique id, and a text that will be displayed alongside the icon in the menu. This custom button will trigger the ",(0,i.kt)("inlineCode",{parentName:"p"},"toolbarButtonClicked")," event that will the id set to the button as the ",(0,i.kt)("inlineCode",{parentName:"p"},"key"),"."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"customToolbarButtons: [\n {\n icon: 'data:image/svg+xml;base64,...',\n id: 'custom-toolbar-button',\n text: 'Custom Toolbar Button'\n }\n]\n")),(0,i.kt)("h3",{id:"mousemovecallbackinterval"},"mouseMoveCallbackInterval"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Number")),(0,i.kt)("p",null,"Default interval (milliseconds) for triggering ",(0,i.kt)("inlineCode",{parentName:"p"},"mouseMoved")," iframe API event."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"1000")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"mouseMoveCallbackInterval: 1000\n")),(0,i.kt)("h3",{id:"participantmenubuttonswithnotifyclick"},"participantMenuButtonsWithNotifyClick"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Array")),(0,i.kt)("p",null,"Participant context menu buttons which have their click/tap event exposed through the API on ",(0,i.kt)("inlineCode",{parentName:"p"},"participantMenuButtonClick"),". Passing a string for the button key will prevent execution of the click/tap routine; passing an object with ",(0,i.kt)("inlineCode",{parentName:"p"},"key")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"preventExecution")," flag on false will not prevent execution of the click/tap routine. Below array with mixed mode for passing the buttons."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"participantMenuButtonsWithNotifyClick: [\n 'allow-video',\n {\n key: 'ask-unmute',\n preventExecution: false\n },\n 'conn-status',\n 'flip-local-video',\n 'grant-moderator',\n {\n key: 'kick',\n preventExecution: true\n },\n {\n key: 'hide-self-view',\n preventExecution: false\n },\n 'mute',\n 'mute-others',\n 'mute-others-video',\n 'mute-video',\n 'pinToStage',\n 'privateMessage',\n {\n key: 'remote-control',\n preventExecution: false\n },\n 'send-participant-to-room',\n 'verify',\n]\n")),(0,i.kt)("h3",{id:"usehostpagelocalstorage"},"useHostPageLocalStorage"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"This property is related to the use case when Jitsi Meet is used via the IFrame API. When the property is true\nJitsi Meet will use the local storage of the host page instead of its own. This option is useful if the browser\nis not persisting the local storage inside the iframe."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"useHostPageLocalStorage: true\n")),(0,i.kt)("h2",{id:"audio"},"Audio"),(0,i.kt)("h3",{id:"audiolevelsinterval"},"audioLevelsInterval"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Number")),(0,i.kt)("p",null,"The interval (milliseconds) at which the audio levels are calculated."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"200")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"audioLevelsInterval: 200\n")),(0,i.kt)("h3",{id:"audioquality"},"audioQuality"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Specify audio quality stereo and opusMaxAverageBitrate values in order to enable HD audio.\nBeware, by doing so, you are disabling echo cancellation, noise suppression and AGC."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"audioQuality: {\n stereo: false,\n opusMaxAverageBitrate: null // Value to fit the 6000 to 510000 range.\n}\n")),(0,i.kt)("h3",{id:"disableaudiolevels"},"disableAudioLevels"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Disable measuring of audio levels."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"false")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"disableAudioLevels: false\n")),(0,i.kt)("h3",{id:"disablespeakerstatssearch"},(0,i.kt)("del",{parentName:"h3"},"disableSpeakerStatsSearch")),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Specifies whether there will be a search field in speaker stats or not."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"DEPRECATED")," Use ",(0,i.kt)("inlineCode",{parentName:"p"},"speakerStats.disableSearch")," instead."),(0,i.kt)("p",null,"Default: false"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"disableSpeakerStatsSearch: false\n")),(0,i.kt)("h3",{id:"disabledsounds"},"disabledSounds"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Array")),(0,i.kt)("p",null,"The sounds passed in this array will be disabled."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"disabledSounds: [\n // 'ASKED_TO_UNMUTE_SOUND'\n // 'E2EE_OFF_SOUND'\n // 'E2EE_ON_SOUND'\n // 'INCOMING_MSG_SOUND'\n // 'KNOCKING_PARTICIPANT_SOUND'\n // 'LIVE_STREAMING_OFF_SOUND'\n // 'LIVE_STREAMING_ON_SOUND'\n // 'NO_AUDIO_SIGNAL_SOUND'\n // 'NOISY_AUDIO_INPUT_SOUND'\n // 'OUTGOING_CALL_EXPIRED_SOUND'\n // 'OUTGOING_CALL_REJECTED_SOUND'\n // 'OUTGOING_CALL_RINGING_SOUND'\n // 'OUTGOING_CALL_START_SOUND'\n // 'PARTICIPANT_JOINED_SOUND'\n // 'PARTICIPANT_LEFT_SOUND'\n // 'RAISE_HAND_SOUND'\n // 'REACTION_SOUND'\n // 'RECORDING_OFF_SOUND'\n // 'RECORDING_ON_SOUND'\n // 'TALK_WHILE_MUTED_SOUND'\n]\n")),(0,i.kt)("h3",{id:"enablenoaudiodetection"},"enableNoAudioDetection"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Enabling this will run the lib-jitsi-meet no audio detection module which\nwill notify the user if the current selected microphone has no audio\ninput and will suggest another valid device if one is present."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"true")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"enableNoAudioDetection: true\n")),(0,i.kt)("h3",{id:"enablenoisymicdetection"},"enableNoisyMicDetection"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Enabling this will run the lib-jitsi-meet noise detection module which will\nnotify the user if there is noise, other than voice, coming from the current\nselected microphone. The purpose it to let the user know that the input could\nbe potentially unpleasant for other meeting participants."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"true")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"enableNoisyMicDetection: true\n")),(0,i.kt)("h3",{id:"speakerstats"},"speakerStats"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Options related to the speaker stats feature."),(0,i.kt)("p",null,"Properties: "),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"disabled")," - Specifies whether the speaker stats is enable or not."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"disableSearch")," - Specifies whether there will be a search field in speaker stats or not."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"order")," - Specifies whether participants in speaker stats should be ordered or not, and with what priority.")),(0,i.kt)("p",null,"Default:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"speakerStats: {\n disabled: false,\n disableSearch: false,\n order: [\n 'role', // Moderators on top.\n 'name', // Alphabetically by name.\n 'hasLeft', // The ones that have left in the bottom.\n ], // the order of the array elements determines priority.\n}\n")),(0,i.kt)("h3",{id:"speakerstatsorder"},(0,i.kt)("del",{parentName:"h3"},"speakerStatsOrder")),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Array")),(0,i.kt)("p",null,"Specifies whether participants in speaker stats should be ordered or not, and with what priority."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"DEPRECATED")," Use ",(0,i.kt)("inlineCode",{parentName:"p"},"speakerStats.order")," instead."),(0,i.kt)("p",null,"Default:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"}," speakerStatsOrder: [\n 'role', // Moderators on top.\n 'name', // Alphabetically by name.\n 'hasLeft', // The ones that have left in the bottom.\n ], // the order of the array elements determines priority.\n")),(0,i.kt)("h3",{id:"startaudiomuted"},"startAudioMuted"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Number")),(0,i.kt)("p",null,"Every participant after the Nth will start audio muted."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"startAudioMuted: 10\n")),(0,i.kt)("h3",{id:"startaudioonly"},"startAudioOnly"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Start the conference in audio only mode (no video is being received nor sent)."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"startAudioOnly: false\n")),(0,i.kt)("h3",{id:"startsilent"},"startSilent"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Enabling it (with #params) will disable local audio output of remote\nparticipants and to enable it back a reload is needed."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"startSilent: false\n")),(0,i.kt)("h3",{id:"startwithaudiomuted"},"startWithAudioMuted"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Start calls with audio muted. This option is only applied locally."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"startWithAudioMuted: false\n")),(0,i.kt)("h2",{id:"breakout-rooms"},"Breakout rooms"),(0,i.kt)("h3",{id:"breakoutrooms"},"breakoutRooms"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Options related to the breakout rooms feature."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("p",null,"Properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"hideAddRoomButton")," - Hides the add breakout room button. This replaces ",(0,i.kt)("inlineCode",{parentName:"li"},"hideAddRoomButton"),"."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"hideAutoAssignButton")," - Hides the auto assign participants button."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"hideJoinRoomButton")," - Hides the join breakout room button."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"hideModeratorSettingsTab")," - Hides the button to open the moderator settings tab."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"hideMoreActionsButton")," - Hides the more actions button."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"hideMuteAllButton")," - Hides the mute all button.")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"breakoutRooms: {\n hideAddRoomButton: false,\n hideAutoAssignButton: false,\n hideJoinRoomButton: false\n}\n")),(0,i.kt)("h3",{id:"hideaddroombutton"},(0,i.kt)("del",{parentName:"h3"},"hideAddRoomButton")),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"DEPRECATED")," Use ",(0,i.kt)("inlineCode",{parentName:"p"},"breakoutRooms.hideAddRoomButton")," instead."),(0,i.kt)("p",null,"Hides add breakout room button."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"false")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"hideAddRoomButton: false\n")),(0,i.kt)("h2",{id:"callstats"},"Callstats"),(0,i.kt)("h3",{id:"callstatsconfigparams"},"callStatsConfigParams"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"The callstats initialize config params as described in the API ",(0,i.kt)("a",{parentName:"p",href:"https://docs.callstats.io/docs/javascript#callstatsinitialize-with-app-secret"},"here"),"."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},'callStatsConfigParams: {\n disableBeforeUnloadHandler: true, // disables callstats.js\'s window.onbeforeunload parameter.\n applicationVersion: "app_version", // Application version specified by the developer.\n disablePrecalltest: true, // disables the pre-call test, it is enabled by default.\n siteID: "siteID", // The name/ID of the site/campus from where the call/pre-call test is made.\n additionalIDs: { // additionalIDs object, contains application related IDs.\n customerID: "Customer Identifier. Example, walmart.",\n tenantID: "Tenant Identifier. Example, monster.",\n productName: "Product Name. Example, Jitsi.",\n meetingsName: "Meeting Name. Example, Jitsi loves callstats.",\n serverName: "Server/MiddleBox Name. Example, jvb-prod-us-east-mlkncws12.",\n pbxID: "PBX Identifier. Example, walmart.",\n pbxExtensionID: "PBX Extension Identifier. Example, 5625.",\n fqExtensionID: "Fully qualified Extension Identifier. Example, +71 (US) +5625.",\n sessionID: "Session Identifier. Example, session-12-34"\n },\n collectLegacyStats: true, //enables the collection of legacy stats in chrome browser\n collectIP: true //enables the collection localIP address\n}\n')),(0,i.kt)("h3",{id:"callstatsid"},"callStatsID"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"String")),(0,i.kt)("p",null,"You must provide the Application ID to enable sending statistics to callstats.io"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"callStatsID: 'my-callstats-app-id'\n")),(0,i.kt)("h3",{id:"callstatssecret"},"callStatsSecret"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"String")),(0,i.kt)("p",null,"You must provide the Secret to enable sending statistics to callstats.io"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"callStatsSecret: 'my-callstats-secret'\n")),(0,i.kt)("h3",{id:"enabledisplaynameinstats"},"enableDisplayNameInStats"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Enables sending participants' display names to callstats."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"enableDisplayNameInStats: false\n")),(0,i.kt)("h3",{id:"enableemailinstats"},"enableEmailInStats"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Enables sending participants' emails (if available) to callstats and other analytics"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"enableEmailInStats: false\n")),(0,i.kt)("h3",{id:"feedbackpercentage"},"feedbackPercentage"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Number")),(0,i.kt)("p",null,"Controls the percentage of automatic feedback shown to participants when callstats is enabled.\nThe default value is 100%. If set to 0, no automatic feedback will be requested"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"feedbackPercentage: 100\n")),(0,i.kt)("h2",{id:"closed-captions"},"Closed captions"),(0,i.kt)("h3",{id:"autocaptiononrecord"},"autoCaptionOnRecord*"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Enables turning captions on automatically when the recording starts"),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"false")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"autoCaptionOnRecord: false\n")),(0,i.kt)("h3",{id:"preferredtranscribinglanguage-"},"preferredTranscribingLanguage \ud83d\udeab"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Transcriber language. This settings will only work if ",(0,i.kt)("inlineCode",{parentName:"p"},"transcribeWithAppLanguage")," is explicitly set to ",(0,i.kt)("inlineCode",{parentName:"p"},"false"),".\nAvailable languages can be found ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet/blob/master/react/features/transcribing/transcriber-langs.json"},"here"),"."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"'en-US'")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"preferredTranscribeLanguage: 'en-CA'\n")),(0,i.kt)("h3",{id:"transcribewithapplanguage-"},"transcribeWithAppLanguage \ud83d\udeab"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"If ",(0,i.kt)("inlineCode",{parentName:"p"},"true")," the transcriber will use the application language.\nThe application language is either explicitly set by participants in their settings or automatically\ndetected based on the environment, e.g. if the app is opened in a chrome instance which is using french as its\ndefault language then transcriptions for that participant will be in french."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"true")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"transcribeWithAppLanguage: false\n")),(0,i.kt)("h3",{id:"transcribingenabled"},"transcribingEnabled"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Enable transcription (in interface_config, subtitles and buttons can be configured)."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"false")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"transcribingEnabled: true\n")),(0,i.kt)("h2",{id:"connection"},"Connection"),(0,i.kt)("h3",{id:"bosh"},"bosh*"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"String")),(0,i.kt)("p",null,"The BOSH URL."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"bosh: '//jitsi-meet.example.com/http-bind'\n")),(0,i.kt)("h3",{id:"disablertx"},"disableRtx"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Disables or enables RTX (RFC 4588)."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"false")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"disableRtx: true\n")),(0,i.kt)("h3",{id:"disablesimulcast"},"disableSimulcast"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Enable / disable simulcast support."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"false")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"disableSimulcast: true\n")),(0,i.kt)("h3",{id:"e2ee"},"e2ee"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Configure End-to-End Encryption."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"e2ee: {\n labels: {\n labelTooltip: 'Tooltip',\n description: 'Description',\n label: 'E2EE',\n warning: 'Warning'\n },\n externallyManagedKey: false\n}\n")),(0,i.kt)("h3",{id:"e2eping"},"e2eping"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Options related to end-to-end (participant to participant) ping."),(0,i.kt)("p",null,"Properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"enabled")," - Whether end-to-end pings should be enabled."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"numRequests")," - The number of responses to wait for."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"maxConferenceSize")," - The max conference size in which e2e pings will be sent."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"maxMessagesPerSecond")," - The maximum number of e2e ping messages per second for the whole conference to aim for.\nThis is used to contol the pacing of messages in order to reduce the load on the backend.")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"e2eping: {\n enabled: false,\n numRequests: 5,\n maxConferenceSize: 200,\n maxMessagesPerSecond: 250\n}\n")),(0,i.kt)("h3",{id:"enableencodedtransformsupport"},"enableEncodedTransformSupport"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Enable support for encoded transform in supported browsers. This allows\nE2EE to work in Safari if the corresponding flag is enabled in the browser.\n",(0,i.kt)("strong",{parentName:"p"},"Experimental"),"."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"enableEncodedTransformSupport: false\n")),(0,i.kt)("h3",{id:"enableforcedreload-"},"enableForcedReload \ud83d\udeab"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Enables forced reload of the client when the call is migrated as a result of\nthe bridge going down."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"enableForcedReload: true\n")),(0,i.kt)("h3",{id:"enableicerestart"},"enableIceRestart"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,'Enables ICE restart logic in LJM and displays the page reload overlay on\nICE failure. Current disabled by default because it\'s causing issues with\nsignaling when Octo is enabled. Also when we do an "ICE restart"(which is\nnot a real ICE restart), the client maintains the TCC sequence number\ncounter, but the bridge resets it. The bridge sends media packets with\nTCC sequence numbers starting from 0.'),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"enableIceRestart: true\n")),(0,i.kt)("h3",{id:"gatherstats"},"gatherStats"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Whether to enable stats collection or not in the ",(0,i.kt)("inlineCode",{parentName:"p"},"TraceablePeerConnection"),".\nThis can be useful for debugging purposes (post-processing/analysis of\nthe WebRTC stats) as it is done in the jitsi-meet-torture bandwidth\nestimation tests."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"gatherStats: false\n")),(0,i.kt)("h3",{id:"hosts"},"hosts"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"URLs for the app connection."),(0,i.kt)("p",null,"Properties"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"domain")," - XMPP domain"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"anonymousdomain")," - When using authentication, domain for guest users."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"authdomain")," - Domain for authenticated users. Defaults to ",(0,i.kt)("inlineCode",{parentName:"li"},"domain"),"."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"focus")," - Focus component domain. Defaults to ",(0,i.kt)("strong",{parentName:"li"},"focus.",(0,i.kt)("inlineCode",{parentName:"strong"},"domain")),"."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"muc")," - XMPP MUC domain.")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"hosts: {\n domain: 'jitsi-meet.example.com',\n anonymousdomain: 'guest.example.com',\n authdomain: 'jitsi-meet.example.com',\n focus: 'focus.jitsi-meet.example.com',\n muc: 'conference.jitsi-meet.example.com'\n}\n")),(0,i.kt)("h3",{id:"p2p"},"p2p"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Peer-To-Peer mode: used (if enabled) when there are just 2 participants."),(0,i.kt)("p",null,"Properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"enabled")," - Enables peer to peer mode. When enabled the system will try to\nestablish a direct connection when there are exactly 2 participants\nin the room. If that succeeds the conference will stop sending data\nthrough the JVB and use the peer to peer connection instead. When a\n3rd participant joins the conference will be moved back to the JVB\nconnection."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"iceTransportPolicy")," - Sets the ICE transport policy for the p2p connection. At the time\nof this writing the list of possible values are ",(0,i.kt)("inlineCode",{parentName:"li"},"all")," and ",(0,i.kt)("inlineCode",{parentName:"li"},"relay"),",\nbut that is subject to change in the future. The enum is defined in\nthe ",(0,i.kt)("a",{parentName:"li",href:"https://www.w3.org/TR/webrtc/#rtcicetransportpolicy-enum"},"WebRTC standard"),".\nIf not set, the effective value is ",(0,i.kt)("inlineCode",{parentName:"li"},"all"),"."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"codecPreferenceOrder")," - Provides a way to set the codec preference on desktop based endpoints."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"mobileCodecPreferenceOrder")," - Provides a way to set the codec preference on mobile devices, both on RN and mobile browser based endpoints."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"preferredCodec")," - ",(0,i.kt)("strong",{parentName:"li"},"DEPRECATED")," Use ",(0,i.kt)("inlineCode",{parentName:"li"},"codecPreferenceOrder")," or ",(0,i.kt)("inlineCode",{parentName:"li"},"mobileCodecPreferenceOrder")," instead."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"disabledCodec")," - ",(0,i.kt)("strong",{parentName:"li"},"DEPRECATED")," Use ",(0,i.kt)("inlineCode",{parentName:"li"},"codecPreferenceOrder")," or ",(0,i.kt)("inlineCode",{parentName:"li"},"mobileCodecPreferenceOrder")," instead."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"backToP2PDelay")," - How long we're going to wait, before going back to P2P after the 3rd\nparticipant has left the conference (to filter out page reload)."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"stunServers")," - The STUN servers that will be used in the peer to peer connections.")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"p2p: {\n enabled: true,\n enableUnifiedOnChrome: false,\n iceTransportPolicy: 'all',\n backToP2PDelay: 5,\n stunServers: [\n { urls: 'stun:jitsi-meet.example.com:3478' },\n { urls: 'stun:meet-jit-si-turnrelay.jitsi.net:443' }\n ]\n}\n")),(0,i.kt)("h3",{id:"pcstatsinterval"},"pcStatsInterval"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Number")),(0,i.kt)("p",null,"The interval at which PeerConnection.getStats() is called."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"10000")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"pcStatsInterval: 50000\n")),(0,i.kt)("h3",{id:"useturnudp"},"useTurnUdp"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Use TURN/UDP servers for the jitsi-videobridge connection (by default\nwe filter out TURN/UDP because it is usually not needed since the\nbridge itself is reachable via UDP)"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"useTurnUdp: false\n")),(0,i.kt)("h3",{id:"webrtcicetcpdisable"},"webrtcIceTcpDisable"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Disables ICE/TCP by filtering out local and remote TCP candidates in signalling."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"webrtcIceTcpDisable: false\n")),(0,i.kt)("h3",{id:"webrtciceudpdisable"},"webrtcIceUdpDisable"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Disables ICE/UDP by filtering out local and remote UDP candidates in signalling."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"webrtcIceUdpDisable: false\n")),(0,i.kt)("h3",{id:"websocket-"},"websocket \ud83d\udeab"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"String")),(0,i.kt)("p",null,"Websocket URL"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"websocket: 'wss://jitsi-meet.example.com/xmpp-websocket'\n")),(0,i.kt)("h2",{id:"etherpad"},"Etherpad"),(0,i.kt)("h3",{id:"etherpad_base"},"etherpad_base"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"String")),(0,i.kt)("p",null,'If set, it adds a "Open shared document" link to the bottom right menu that\nwill open an etherpad document.'),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"etherpad_base: 'https://your-etherpad-installati.on/p/'\n")),(0,i.kt)("h3",{id:"openshareddocumentonjoin"},"openSharedDocumentOnJoin"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"If etherpad integration is enabled, setting this to ",(0,i.kt)("inlineCode",{parentName:"p"},"true")," will\nautomatically open the etherpad when a participant joins. This\ndoes not affect the mobile app since opening an etherpad\nobscures the conference controls -- it's better to let users\nchoose to open the pad on their own in that case."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"openSharedDocumentOnJoin: false\n")),(0,i.kt)("h2",{id:"filmstrip"},"Filmstrip"),(0,i.kt)("h3",{id:"disablefilmstripautohiding"},"disableFilmstripAutohiding"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Prevents the filmstrip from autohiding when screen width is under a certain threshold"),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"false")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"disableFilmstripAutohiding: true\n")),(0,i.kt)("h3",{id:"filmstrip-1"},"filmstrip"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Options related to the filmstrip."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("p",null,"Properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"disableResizable")," - Disables user resizable filmstrip. This also allows configuration of the filmstrip\n(width, tiles aspect ratios) through the interfaceConfig options."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"disableStageFilmstrip")," - Disables the stage filmstrip (displaying multiple\nparticipants on stage besides the vertical filmstrip)")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"filmstrip: {\n disableResizable: true,\n disableStageFilmstrip: false\n}\n")),(0,i.kt)("h2",{id:"face-landmarks"},"Face Landmarks"),(0,i.kt)("h3",{id:"facelandmarks"},"faceLandmarks"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Options related to the face landmarks features."),(0,i.kt)("p",null,"Properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"enableFaceCentering")," - Enables centering faces within a video by sharing face coordinates."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"enableFaceExpressionsDetection")," - Enables detecting face expressions from video."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"enableDisplayFaceExpressions")," - Enables displaying face expressions in speaker stats."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"enableRTCStats")," - Enables anonymized stats collection for face landmarks."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"faceCenteringThreshold")," - Minimum required face movement percentage threshold for sending new face centering coordinates data."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"captureInterval")," - Milliseconds for processing a new image capture in order to detect face landmarks.")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"faceLandmarks: {\n enableFaceCentering: false,\n enableFaceExpressionsDetection: false,\n enableDisplayFaceExpressions: false,\n enableRTCStats: false,\n faceCenteringThreshold: 20,\n captureInterval: 1000\n},\n")),(0,i.kt)("h2",{id:"giphy"},"Giphy"),(0,i.kt)("h3",{id:"giphy-1"},"giphy"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Setup for the Giphy integration."),(0,i.kt)("p",null,"Properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"enabled")," - Whether the feature is enabled or not."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"sdkKey")," - SDK API Key from Giphy."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"displayMode")," - Display mode can be one of:",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"tile")," - show the GIF on the tile of the participant that sent it."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"chat")," - show the GIF as a message in chat."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"all")," - all of the above. This is the default option."))),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"tileTime")," - How long the GIF should be displayed on the tile (in milliseconds)."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"rating")," - Limit results by audience rating: ",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"g")," - broadly accepted as appropriate in a public environment. This is the default option."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"pg")," - commonly witnessed in a public environment, but not as broadly accepted as appropriate."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"pg-13")," - typically not seen unless sought out, but still commonly witnessed."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"r")," - typically not seen unless sought out, and could be considered alarming if witnessed.")))),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"giphy: {\n enabled: true,\n sdkKey: 'example-key',\n displayMode: 'tile',\n tileTime: 7000,\n rating: 'pg'\n}\n")),(0,i.kt)("h2",{id:"gravatar"},"Gravatar"),(0,i.kt)("h3",{id:"gravatar-1"},"gravatar"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Setup for Gravatar-compatible services."),(0,i.kt)("p",null,"Properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"baseUrl")," \ud83d\udeab - Base URL for a Gravatar-compatible service. Defaults to Gravatar."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"disabled")," - True if Gravatar should be disabled.")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"gravatar: {\n baseUrl: 'https://www.gravatar.com/avatar/',\n disabled: false\n}\n")),(0,i.kt)("h3",{id:"gravatarbaseurl-"},(0,i.kt)("del",{parentName:"h3"},"gravatarBaseURL")," \ud83d\udeab"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"String")),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"DEPRECATED")," Use ",(0,i.kt)("inlineCode",{parentName:"p"},"gravatar.baseUrl")," instead."),(0,i.kt)("p",null,"Base URL for a Gravatar-compatible service."),(0,i.kt)("p",null,"Default: '",(0,i.kt)("a",{parentName:"p",href:"https://www.gravatar.com/avatar/'"},"https://www.gravatar.com/avatar/'")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"gravatarBaseURL: 'https://www.gravatar.com/avatar/'\n")),(0,i.kt)("h2",{id:"lastn"},"LastN"),(0,i.kt)("h3",{id:"channellastn"},"channelLastN"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Number")),(0,i.kt)("p",null,'Default value for the channel "last N" attribute. -1 for unlimited.'),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"channelLastN: -1\n")),(0,i.kt)("h3",{id:"lastnlimits-"},"lastNLimits \ud83d\udeab"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,'Provides a way to use different "last N" values based on the number of participants in the conference.\nThe keys in an Object represent number of participants and the values are "last N" to be used when number of\nparticipants gets to or above the number.'),(0,i.kt)("p",null,"For the given example mapping, \"last N\" will be set to 20 as long as there are at least 5, but less than\n29 participants in the call and it will be lowered to 15 when the 30th participant joins. The 'channelLastN'\nwill be used as default until the first threshold is reached."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"lastNLimits: {\n 5: 20,\n 30: 15,\n 50: 10,\n 70: 5,\n 90: 2\n}\n")),(0,i.kt)("h3",{id:"startlastn"},"startLastN"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Number")),(0,i.kt)("p",null,'Provides a way for the lastN value to be controlled through the UI.\nWhen startLastN is present, conference starts with a last-n value of startLastN and channelLastN\nvalue will be used when the quality level is selected using "Manage Video Quality" slider.'),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"startLastN: 1\n")),(0,i.kt)("h2",{id:"lobby"},"Lobby"),(0,i.kt)("h3",{id:"autoknocklobby"},(0,i.kt)("del",{parentName:"h3"},"autoKnockLobby")),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"DEPRECATED")," Use ",(0,i.kt)("inlineCode",{parentName:"p"},"lobby.autoKnock")," instead."),(0,i.kt)("p",null,"If Lobby is enabled starts knocking automatically."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"autoKnockLobby: false\n")),(0,i.kt)("h3",{id:"enablelobbychat"},(0,i.kt)("del",{parentName:"h3"},"enableLobbyChat")),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"DEPRECATED")," Use ",(0,i.kt)("inlineCode",{parentName:"p"},"lobby.enableChat")," instead."),(0,i.kt)("p",null,"Enable lobby chat."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"enableLobbyChat: false\n")),(0,i.kt)("h3",{id:"hidelobbybutton"},(0,i.kt)("del",{parentName:"h3"},"hideLobbyButton")),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"DEPRECATED")," Use ",(0,i.kt)("inlineCode",{parentName:"p"},"securityUi.hideLobbyButton")," instead."),(0,i.kt)("p",null,"Hide the lobby button."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"hideLobbyButton: false\n")),(0,i.kt)("h3",{id:"lobby-1"},"lobby"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Options related to the lobby screen."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("p",null,"Properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"autoKnock")," - If the lobby is enabled, it starts knocking automatically. Replaces ",(0,i.kt)("inlineCode",{parentName:"li"},"autoKnockLobby"),"."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"enableChat")," - Enables the lobby chat. Replaces ",(0,i.kt)("inlineCode",{parentName:"li"},"enableLobbyChat"),".")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"lobby: {\n autoKnock: true,\n enableChat: false\n}\n")),(0,i.kt)("h2",{id:"moderator"},"Moderator"),(0,i.kt)("h3",{id:"disablemoderatorindicator"},"disableModeratorIndicator"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Hides the moderator indicators."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"false")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"disableModeratorIndicator: true\n")),(0,i.kt)("h3",{id:"disablereactionsmoderation"},"disableReactionsModeration"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Disables the moderation of reactions feature."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"false")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"disableReactionsModeration: true\n")),(0,i.kt)("h3",{id:"disableremotemute"},"disableRemoteMute"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Disables muting operations of remote participants."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"false")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"disableRemoteMute: true\n")),(0,i.kt)("h2",{id:"notifications"},"Notifications"),(0,i.kt)("h3",{id:"notifications-1"},"notifications"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Array")),(0,i.kt)("p",null,"Use this array to configure which notifications will be shown to the user.\nThe items correspond to the title or description key of that notification.\nSome of these notifications also depend on some other internal logic to be displayed or not,\nso adding them here will not ensure they will always be displayed."),(0,i.kt)("p",null,"A falsy value for this prop will result in having all notifications enabled (e.g null, undefined, false)."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"notifications: []\n")),(0,i.kt)("h3",{id:"disablednotifications"},"disabledNotifications"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Array")),(0,i.kt)("p",null,"List of notifications to be disabled. Works in tandem with the above setting."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"disabledNotifications: [\n 'notify.chatMessages', // shown when receiving chat messages while the chat window is closed\n 'notify.grantedTo', // shown when moderator rights were granted to a participant\n]\n")),(0,i.kt)("h2",{id:"participants-pane"},"Participants Pane"),(0,i.kt)("h3",{id:"participantspane"},"participantsPane"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Options related to the participants pane."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("p",null,"Properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"hideModeratorSettingsTab")," - Hides the button to open the moderator settings tab."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"hideMoreActionsButton")," - Hides the more actions button."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"hideMuteAllButton")," - Hides the mute all button.")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"participantsPane: {\n hideModeratorSettingsTab: false,\n hideMoreActionsButton: false,\n hideMuteAllButton: false\n}\n")),(0,i.kt)("h2",{id:"recording"},"Recording"),(0,i.kt)("h3",{id:"dropbox"},"dropbox"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Enable the dropbox integration."),(0,i.kt)("p",null,"Properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"appKey")," - Your APP Key."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"redirectURI")," - A URL to redirect the user to, after authenticating by default uses")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"dropbox: {\n appKey: 'DROPBOX_APP_KEY',\n redirectURI: 'https://jitsi-meet.example.com/subfolder/static/oauth.html'\n}\n")),(0,i.kt)("h3",{id:"filerecordingsenabled"},"fileRecordingsEnabled"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Whether to enable file recording or not."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"fileRecordingsEnabled: false\n")),(0,i.kt)("h3",{id:"filerecordingsserviceenabled-"},"fileRecordingsServiceEnabled \ud83d\udeab"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"When integrations like dropbox are enabled only that will be shown,\nby enabling fileRecordingsServiceEnabled, we show both the integrations\nand the generic recording service (its configuration and storage type\ndepends on jibri configuration)"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"fileRecordingsServiceEnabled: true\n")),(0,i.kt)("h3",{id:"filerecordingsservicesharingenabled-"},"fileRecordingsServiceSharingEnabled \ud83d\udeab"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Whether to show the possibility to share file recording with other people\n(e.g. meeting participants), based on the actual implementation\non the backend."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"fileRecordingsServiceSharingEnabled: false\n")),(0,i.kt)("h3",{id:"hiderecordinglabel"},"hideRecordingLabel"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Set recording label to auto hide instead of staying always on screen."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"false")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"hideRecordingLabel: true\n")),(0,i.kt)("h3",{id:"localrecording"},"localRecording"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Set local recording configuration."),(0,i.kt)("p",null,"Properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"disable")," - Whether to disable the feature or not."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"notifyAllParticipants")," - Whether to notify all the participants when a local recording is started.")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"localRecording: {\n disable: false,\n notifyAllParticipants: true\n}\n")),(0,i.kt)("h3",{id:"recordinglimit-"},"recordingLimit \ud83d\udeab"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Options for the recording limit notification."),(0,i.kt)("p",null,"Properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"limit")," - The recording limit in minutes. Note: This number appears in the notification text\nbut doesn't enforce the actual recording time limit. This should be configured in jibri!"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"appName")," = The name of the app with unlimited recordings."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"appURL")," - The URL of the app with unlimited recordings.")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"recordingLimit: {\n limit: 60,\n appName: 'Unlimited recordings APP',\n appURL: 'https://unlimited.recordings.app.com/'\n}\n")),(0,i.kt)("h2",{id:"screen-sharing"},"Screen Sharing"),(0,i.kt)("h3",{id:"desktopsharingframerate"},"desktopSharingFrameRate"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Optional desktop sharing frame rate options"),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"{\n min: 5,\n max: 5\n}")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"desktopSharingFrameRate: {\n min: 3,\n max: 10\n}\n")),(0,i.kt)("h3",{id:"disablescreensharingvirtualbackground"},"disableScreensharingVirtualBackground"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Disables using screensharing as virtual background."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"disableScreensharingVirtualBackground: false\n")),(0,i.kt)("h3",{id:"enablelayersuspension"},"enableLayerSuspension"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Enable layer suspension. If enabled, endpoints whose HD layers are not in use will be suspended\n(no longer sent) until they are requested again. This must be enabled for screen\nsharing to work as expected on Chrome. Disabling this might result in low resolution screenshare being sent\nby the client."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"true")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"enableLayerSuspension: false\n")),(0,i.kt)("h3",{id:"screenshotcapture"},"screenshotCapture"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Options for the screenshot capture feature."),(0,i.kt)("p",null,"Properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"enabled")," - Enables the feature"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"mode")," - The mode for the screenshot capture feature. Can be either 'recording' - screensharing screenshots\nare taken only when the recording is also on, or 'always' - screensharing screenshots are always taken.")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"screenshotCapture: {\n enabled: true,\n mode: 'recording'\n}\n")),(0,i.kt)("h2",{id:"security-ui"},"Security UI"),(0,i.kt)("h3",{id:"securityui"},"securityUi"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Options regarding the security related UI elements."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("p",null,"Properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"hideLobbyButton")," - Hides the lobby button. Replaces ",(0,i.kt)("inlineCode",{parentName:"li"},"hideLobbyButton"),"."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"disableLobbyPassword")," - Hides the possibility to set and enter a lobby password.")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"securityUi: {\n hideLobbyButton: true,\n disableLobbyPassword: false\n}\n")),(0,i.kt)("h2",{id:"video"},"Video"),(0,i.kt)("h3",{id:"constraints"},"constraints"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"W3C spec-compliant video constraints to use for video capture. Currently\nused by browsers that return true from lib-jitsi-meet's\n",(0,i.kt)("inlineCode",{parentName:"p"},"util#browser#usesNewGumFlow"),". The constraints are independent from\nthis config's resolution value. Defaults to requesting an ideal\nresolution of 720p."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"constraints: {\n video: {\n height: {\n ideal: 720,\n max: 720,\n min: 240\n }\n }\n}\n")),(0,i.kt)("h3",{id:"disableaddingbackgroundimages"},"disableAddingBackgroundImages"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"When true the user cannot add more images to be used as virtual background.\nOnly the default ones from will be available."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"disableAddingBackgroundImages: true\n")),(0,i.kt)("h3",{id:"disableh264"},"disableH264"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"If set to true, disable H.264 video codec by stripping it out of the SDP."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"disableH264: true\n")),(0,i.kt)("h3",{id:"disablelocalvideoflip"},"disableLocalVideoFlip"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Disable the Flip video option from the context menu for local video."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"disableLocalVideoFlip: true\n")),(0,i.kt)("h3",{id:"disableselfview"},"disableSelfView"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Disables self-view tile. (hides it from tile view and from filmstrip)"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"disableSelfView: true\n")),(0,i.kt)("h3",{id:"donotfliplocalvideo"},"doNotFlipLocalVideo"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"A property used to unset the default flip state of the local video.\nWhen it is set to ",(0,i.kt)("inlineCode",{parentName:"p"},"true"),", the local(self) video will not be mirrored anymore."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"doNotFlipLocalVideo: true\n")),(0,i.kt)("h3",{id:"maxfullresolutionparticipants"},"maxFullResolutionParticipants"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"How many participants while in the tile view mode, before the receiving video quality is reduced from HD to SD.\nUse ",(0,i.kt)("inlineCode",{parentName:"p"},"-1")," to disable."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"maxFullResolutionParticipants: 5\n")),(0,i.kt)("h3",{id:"preferh264"},(0,i.kt)("del",{parentName:"h3"},"preferH264")),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"DEPRECATED")," Use ",(0,i.kt)("inlineCode",{parentName:"p"},"preferredCodec")," under ",(0,i.kt)("inlineCode",{parentName:"p"},"videoQuality")," section instead."),(0,i.kt)("p",null,"Prefer to use the H.264 video codec (if supported).\nNote that it's not recommended to do this because simulcast is not\nsupported when using H.264. For 1-to-1 calls this setting is enabled by\ndefault and can be toggled in the p2p section."),(0,i.kt)("h3",{id:"resolution"},"resolution"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Number")),(0,i.kt)("p",null,"Sets the preferred resolution (height) for local video"),(0,i.kt)("p",null,"Default: ",(0,i.kt)("inlineCode",{parentName:"p"},"720")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"resolution: 1080\n")),(0,i.kt)("h3",{id:"startvideomuted"},"startVideoMuted"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Number")),(0,i.kt)("p",null,"Every participant after the Nth will start video muted."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"startVideoMuted: 5\n")),(0,i.kt)("h3",{id:"startwithvideomuted"},"startWithVideoMuted"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Boolean")),(0,i.kt)("p",null,"Start calls with video muted. Only applied locally."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"startWithVideoMuted: true\n")),(0,i.kt)("h3",{id:"videoquality"},"videoQuality"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Specify the settings for video quality optimizations on the client."),(0,i.kt)("p",null,"Properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},(0,i.kt)("inlineCode",{parentName:"p"},"codecPreferenceOrder")," - Provides a way to set the codec preference on desktop based endpoints.")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},(0,i.kt)("inlineCode",{parentName:"p"},"mobileCodecPreferenceOrder")," - Provides a way to set the codec preference on mobile devices, both on RN and mobile browser based endpoints.")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},(0,i.kt)("inlineCode",{parentName:"p"},"disabledCodec")," - ",(0,i.kt)("strong",{parentName:"p"},"DEPRECATED")," Use ",(0,i.kt)("inlineCode",{parentName:"p"},"codecPreferenceOrder")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"mobileCodecPreferenceOrder")," instead.")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},(0,i.kt)("inlineCode",{parentName:"p"},"preferredCodec")," - ",(0,i.kt)("strong",{parentName:"p"},"DEPRECATED")," Use ",(0,i.kt)("inlineCode",{parentName:"p"},"codecPreferenceOrder")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"mobileCodecPreferenceOrder")," instead.")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},(0,i.kt)("inlineCode",{parentName:"p"},"maxBitratesVideo")," - Provides a way to configure the maximum bitrates that will be enforced on the simulcast streams for\nvideo tracks. The keys in the object represent the type of the stream (LD, SD or HD) and the values\nare the max.bitrates to be set on that particular type of stream. The actual send may vary based on\nthe available bandwidth calculated by the browser, but it will be capped by the values specified here.\nThis is currently not implemented on app based clients on mobile.")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},(0,i.kt)("inlineCode",{parentName:"p"},"minHeightForQualityLvl")," - The options can be used to override default thresholds of video thumbnail heights corresponding to\nthe video quality levels used in the application. At the time of this writing the allowed levels are:"),(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},(0,i.kt)("inlineCode",{parentName:"p"},"low")," - for the low quality level (180p at the time of this writing)")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},(0,i.kt)("inlineCode",{parentName:"p"},"standard")," - for the medium quality level (360p)")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},(0,i.kt)("inlineCode",{parentName:"p"},"high")," - for the high quality level (720p)"),(0,i.kt)("p",{parentName:"li"},"The keys should be positive numbers which represent the minimal thumbnail height for the quality level.\nWith the default config value below the application will use 'low' quality until the thumbnails are\nat least 360 pixels tall. If the thumbnail height reaches 720 pixels then the application will switch to\nthe high quality."))))),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"videoQuality: {\n maxBitratesVideo: {\n H264: {\n low: 200000,\n standard: 500000,\n high: 1500000\n },\n VP8 : {\n low: 200000,\n standard: 500000,\n high: 1500000\n },\n VP9: {\n low: 100000,\n standard: 300000,\n high: 1200000\n }\n },\n minHeightForQualityLvl: {\n 360: 'standard',\n 720: 'high'\n },\n}\n")),(0,i.kt)("h2",{id:"whiteboard"},"Whiteboard"),(0,i.kt)("h3",{id:"whiteboard-1"},"whiteboard"),(0,i.kt)("p",null,"type: ",(0,i.kt)("inlineCode",{parentName:"p"},"Object")),(0,i.kt)("p",null,"Options related to the Excalidraw whiteboard integration."),(0,i.kt)("p",null,"Default: ",(0,i.kt)("strong",{parentName:"p"},"unset")),(0,i.kt)("p",null,"Properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"enabled")," - Whether the feature is enabled or not."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"collabServerBaseUrl")," - The ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/jitsi/excalidraw-backend"},"server")," used to support whiteboard collaboration.")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},"whiteboard: {\n enabled: true,\n collabServerBaseUrl: 'https://excalidraw-backend.example.com'\n}\n")))}k.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/619256ff.d8501c82.js b/assets/js/619256ff.e350c2b8.js similarity index 99% rename from assets/js/619256ff.d8501c82.js rename to assets/js/619256ff.e350c2b8.js index 72bfdded6..7cf25e88e 100644 --- a/assets/js/619256ff.d8501c82.js +++ b/assets/js/619256ff.e350c2b8.js @@ -1 +1 @@ -"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[5742],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>h});var i=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function r(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=i.createContext({}),d=function(e){var t=i.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},p=function(e){var t=d(e.components);return i.createElement(s.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},u=i.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=d(n),h=a,m=u["".concat(s,".").concat(h)]||u[h]||c[h]||o;return n?i.createElement(m,r(r({ref:t},p),{},{components:n})):i.createElement(m,r({ref:t},p))}));function h(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,r=new Array(o);r[0]=u;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:a,r[1]=l;for(var d=2;d{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>s,default:()=>h,frontMatter:()=>l,metadata:()=>d,toc:()=>c});var i=n(7462),a=n(3366),o=(n(7294),n(3905)),r=["components"],l={id:"dev-guide-android-sdk",title:"Android SDK"},s=void 0,d={unversionedId:"dev-guide/dev-guide-android-sdk",id:"dev-guide/dev-guide-android-sdk",title:"Android SDK",description:"The Jitsi Meet Android SDK provides the same user experience as the Jitsi Meet app,",source:"@site/docs/dev-guide/android-sdk.md",sourceDirName:"dev-guide",slug:"/dev-guide/dev-guide-android-sdk",permalink:"/handbook/docs/dev-guide/dev-guide-android-sdk",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/dev-guide/android-sdk.md",tags:[],version:"current",lastUpdatedAt:1694635654,formattedLastUpdatedAt:"Sep 13, 2023",frontMatter:{id:"dev-guide-android-sdk",title:"Android SDK"},sidebar:"docs",previous:{title:"React SDK",permalink:"/handbook/docs/dev-guide/dev-guide-react-sdk"},next:{title:"iOS SDK",permalink:"/handbook/docs/dev-guide/dev-guide-ios-sdk"}},p={},c=[{value:"Sample applications using the SDK",id:"sample-applications-using-the-sdk",level:2},{value:"Build your own, or use a pre-build SDK artifacts/binaries",id:"build-your-own-or-use-a-pre-build-sdk-artifactsbinaries",level:2},{value:"Use pre-build SDK artifacts/binaries",id:"use-pre-build-sdk-artifactsbinaries",level:3},{value:"Build and use your own SDK artifacts/binaries",id:"build-and-use-your-own-sdk-artifactsbinaries",level:3},{value:"Using the API",id:"using-the-api",level:2},{value:"JitsiMeetActivity",id:"jitsimeetactivity",level:3},{value:"JitsiMeetView",id:"jitsimeetview",level:3},{value:"join(options)",id:"joinoptions",level:4},{value:"dispose()",id:"dispose",level:4},{value:"JitsiMeetConferenceOptions",id:"jitsimeetconferenceoptions",level:3},{value:"JitsiMeetActivityDelegate",id:"jitsimeetactivitydelegate",level:3},{value:"onActivityResult(...)",id:"onactivityresult",level:4},{value:"onBackPressed()",id:"onbackpressed",level:4},{value:"onHostDestroy(...)",id:"onhostdestroy",level:4},{value:"onHostResume(...)",id:"onhostresume",level:4},{value:"onHostStop(...)",id:"onhoststop",level:4},{value:"onNewIntent(...)",id:"onnewintent",level:4},{value:"onRequestPermissionsResult(...)",id:"onrequestpermissionsresult",level:4},{value:"onUserLeaveHint()",id:"onuserleavehint",level:4},{value:"Listening for broadcasted events",id:"listening-for-broadcasted-events",level:3},{value:"Supported events",id:"supported-events",level:4},{value:"CONFERENCE_JOINED",id:"conference_joined",level:5},{value:"CONFERENCE_TERMINATED",id:"conference_terminated",level:5},{value:"CONFERENCE_WILL_JOIN",id:"conference_will_join",level:5},{value:"AUDIO_MUTED_CHANGED",id:"audio_muted_changed",level:5},{value:"PARTICIPANT_JOINED",id:"participant_joined",level:5},{value:"PARTICIPANT_LEFT",id:"participant_left",level:5},{value:"ENDPOINT_TEXT_MESSAGE_RECEIVED",id:"endpoint_text_message_received",level:5},{value:"SCREEN_SHARE_TOGGLED",id:"screen_share_toggled",level:4},{value:"PARTICIPANTS_INFO_RETRIEVED",id:"participants_info_retrieved",level:5},{value:"CHAT_MESSAGE_RECEIVED",id:"chat_message_received",level:5},{value:"CHAT_TOGGLED",id:"chat_toggled",level:5},{value:"VIDEO_MUTED_CHANGED",id:"video_muted_changed",level:5},{value:"READY_TO_CLOSE",id:"ready_to_close",level:5},{value:"Broadcasting Actions",id:"broadcasting-actions",level:3},{value:"Supported actions",id:"supported-actions",level:4},{value:"SET_AUDIO_MUTED",id:"set_audio_muted",level:5},{value:"SET_VIDEO_MUTED",id:"set_video_muted",level:5},{value:"HANG_UP",id:"hang_up",level:5},{value:"SEND_ENDPOINT_TEXT_MESSAGE",id:"send_endpoint_text_message",level:5},{value:"TOGGLE_SCREEN_SHARE",id:"toggle_screen_share",level:5},{value:"RETRIEVE_PARTICIPANTS_INFO",id:"retrieve_participants_info",level:5},{value:"OPEN_CHAT",id:"open_chat",level:5},{value:"CLOSE_CHAT",id:"close_chat",level:5},{value:"SEND_CHAT_MESSAGE",id:"send_chat_message",level:5},{value:"ProGuard rules",id:"proguard-rules",level:2},{value:"Picture-in-Picture",id:"picture-in-picture",level:2},{value:"Dropbox integration",id:"dropbox-integration",level:2}],u={toc:c};function h(e){var t=e.components,n=(0,a.Z)(e,r);return(0,o.kt)("wrapper",(0,i.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("p",null,"The Jitsi Meet Android SDK provides the same user experience as the Jitsi Meet app,\nin a customizable way which you can embed in your apps."),(0,o.kt)("admonition",{type:"important"},(0,o.kt)("p",{parentName:"admonition"},"Android 6.0 (API level 23) or higher is required.")),(0,o.kt)("h2",{id:"sample-applications-using-the-sdk"},"Sample applications using the SDK"),(0,o.kt)("p",null,"If you want to see how easy integrating the Jitsi Meet SDK into a native application is, take a look at the\n",(0,o.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet-sdk-samples#android"},"sample applications repository"),"."),(0,o.kt)("h2",{id:"build-your-own-or-use-a-pre-build-sdk-artifactsbinaries"},"Build your own, or use a pre-build SDK artifacts/binaries"),(0,o.kt)("p",null,"Jitsi conveniently provides a pre-build SDK artifacts/binaries in its Maven repository. When you do not require any\nmodification to the SDK itself or any of its dependencies, it's suggested to use the pre-build SDK. This avoids the\ncomplexity of building and installing your own SDK artifacts/binaries."),(0,o.kt)("h3",{id:"use-pre-build-sdk-artifactsbinaries"},"Use pre-build SDK artifacts/binaries"),(0,o.kt)("p",null,"In your project, add the Maven repository\n",(0,o.kt)("inlineCode",{parentName:"p"},"https://github.com/jitsi/jitsi-maven-repository/raw/master/releases")," and the\ndependency ",(0,o.kt)("inlineCode",{parentName:"p"},"org.jitsi.react:jitsi-meet-sdk")," into your ",(0,o.kt)("inlineCode",{parentName:"p"},"build.gradle")," files."),(0,o.kt)("p",null,"The repository typically goes into the ",(0,o.kt)("inlineCode",{parentName:"p"},"build.gradle")," file in the root of your project:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-gradle",metastring:'title="build.gradle"',title:'"build.gradle"'},"allprojects {\n repositories {\n maven {\n url \"https://github.com/jitsi/jitsi-maven-repository/raw/master/releases\"\n }\n google()\n mavenCentral()\n maven { url 'https://www.jitpack.io' }\n }\n}\n")),(0,o.kt)("p",null,"In recent versions of Android Studios, ",(0,o.kt)("inlineCode",{parentName:"p"},"allprojects{}")," might not be found in ",(0,o.kt)("inlineCode",{parentName:"p"},"build.gradle"),". In that case, the repository goes into the ",(0,o.kt)("inlineCode",{parentName:"p"},"settings.gradle")," file in the root of your project:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-gradle",metastring:'title="settings.gradle"',title:'"settings.gradle"'},'dependencyResolutionManagement {\n repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n repositories {\n google()\n mavenCentral()\n maven {\n url "https://github.com/jitsi/jitsi-maven-repository/raw/master/releases"\n }\n maven {\n url "https://maven.google.com"\n }\n }\n}\n')),(0,o.kt)("p",null,"Dependency definitions belong in the individual module ",(0,o.kt)("inlineCode",{parentName:"p"},"build.gradle")," files:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-gradle"},"dependencies {\n // (other dependencies)\n implementation ('org.jitsi.react:jitsi-meet-sdk:+') { transitive = true }\n}\n")),(0,o.kt)("admonition",{type:"warning"},(0,o.kt)("p",{parentName:"admonition"},"Make sure you pin your dependency by checking the ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet-release-notes/blob/master/CHANGELOG-MOBILE-SDKS.md"},"releases page"),".")),(0,o.kt)("h3",{id:"build-and-use-your-own-sdk-artifactsbinaries"},"Build and use your own SDK artifacts/binaries"),(0,o.kt)("details",null,(0,o.kt)("summary",null,"Show building instructions"),(0,o.kt)("p",null,"Start by making sure that your development environment ",(0,o.kt)("a",{parentName:"p",href:"/docs/category/mobile"},"is set up correctly"),"."),(0,o.kt)("admonition",{title:"A Note on Dependencies",type:"note"},(0,o.kt)("p",{parentName:"admonition"},"Apart from the SDK, Jitsi also publishes a binary Maven artifact for some of the SDK dependencies (that are not otherwise publicly available) to the Jitsi Maven repository. When you're planning to use a SDK that is built from source, you'll likely use a version of the source code that is newer (or at least ",(0,o.kt)("em",{parentName:"p"},"different"),") than the version of the source that was used to create the binary SDK artifact. As a consequence, the dependencies that your project will need, might also be different from those that are published in the Jitsi Maven repository. This might lead to build problems, caused by dependencies that are unavailable.")),(0,o.kt)("p",null,"If you want to use a SDK that is built from source, you will likely benefit from composing a local Maven repository that contains these dependencies. The text below describes how you create a repository that includes both the SDK as well as these dependencies. For illustration purposes, we'll define the location of this local Maven repository as ",(0,o.kt)("inlineCode",{parentName:"p"},"/tmp/repo")),(0,o.kt)("p",null,"In source code form, the Android SDK dependencies are locked/pinned by ",(0,o.kt)("inlineCode",{parentName:"p"},"package.json")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"package-lock.json")," of the Jitsi Meet project. To obtain the data, execute NPM in the jitsi-meet project directory:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"npm install\n")),(0,o.kt)("p",null,"This will pull in the dependencies in either binary format, or in source code format, somewhere under /node_modules/"),(0,o.kt)("p",null,"Third-party React Native ",(0,o.kt)("em",{parentName:"p"},"modules"),", which Jitsi Meet SDK for Android depends on, are download by NPM in source code\nor binary form. These need to be assembled into Maven artifacts, and then published to your local Maven repository.\nA script is provided to facilitate this. From the root of the jitsi-meet project repository, run:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"./android/scripts/release-sdk.sh /tmp/repo\n")),(0,o.kt)("p",null,"This will build and publish the SDK, and all of its dependencies to the specified Maven repository (",(0,o.kt)("inlineCode",{parentName:"p"},"/tmp/repo"),") in\nthis example."),(0,o.kt)("p",null,"You're now ready to use the artifacts. In ",(0,o.kt)("em",{parentName:"p"},"your")," project, add the Maven repository that you used above (",(0,o.kt)("inlineCode",{parentName:"p"},"/tmp/repo"),") into your top-level ",(0,o.kt)("inlineCode",{parentName:"p"},"build.gradle")," file:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"allprojects {\n repositories {\n maven { url \"file:/tmp/repo\" }\n google()\n mavenCentral()\n maven { url 'https://www.jitpack.io' }\n }\n}\n")),(0,o.kt)("p",null,"You can use your local repository to replace the Jitsi repository (",(0,o.kt)("inlineCode",{parentName:"p"},'maven { url "https://github.com/jitsi/jitsi-maven-repository/raw/master/releases" }'),") when you published ",(0,o.kt)("em",{parentName:"p"},"all")," subprojects. If you didn't do that, you'll have to add both repositories. Make sure your local repository is listed first!"),(0,o.kt)("p",null,"Then, define the dependency ",(0,o.kt)("inlineCode",{parentName:"p"},"org.jitsi.react:jitsi-meet-sdk")," into the ",(0,o.kt)("inlineCode",{parentName:"p"},"build.gradle")," file of your module:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"implementation ('org.jitsi.react:jitsi-meet-sdk:+') { transitive = true }\n")),(0,o.kt)("p",null,"Note that there should not be a need to explicitly add the other dependencies, as they will be pulled in as transitive\ndependencies of ",(0,o.kt)("inlineCode",{parentName:"p"},"jitsi-meet-sdk"),".")),(0,o.kt)("h2",{id:"using-the-api"},"Using the API"),(0,o.kt)("p",null,"Jitsi Meet SDK is an Android library which embodies the whole Jitsi Meet\nexperience and makes it reusable by third-party apps."),(0,o.kt)("p",null,"First, add Java 1.8 compatibility support to your project by adding the\nfollowing lines into your ",(0,o.kt)("inlineCode",{parentName:"p"},"build.gradle")," file:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"compileOptions {\n sourceCompatibility JavaVersion.VERSION_1_8\n targetCompatibility JavaVersion.VERSION_1_8\n}\n")),(0,o.kt)("p",null,"To get started, just launch ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetActivity")," pointing to the room you want:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-java"},'// Somewhere early in your app.\nJitsiMeetConferenceOptions defaultOptions\n = new JitsiMeetConferenceOptions.Builder()\n .setServerURL(serverURL)\n // When using JaaS, set the obtained JWT here\n //.setToken("MyJWT")\n // Different features flags can be set\n // .setFeatureFlag("toolbox.enabled", false)\n // .setFeatureFlag("filmstrip.enabled", false)\n .setFeatureFlag("welcomepage.enabled", false)\n .build();\nJitsiMeet.setDefaultConferenceOptions(defaultOptions);\n// ...\n// Build options object for joining the conference. The SDK will merge the default\n// one we set earlier and this one when joining.\nJitsiMeetConferenceOptions options\n = new JitsiMeetConferenceOptions.Builder()\n .setRoom(roomName)\n // Settings for audio and video\n //.setAudioMuted(true)\n //.setVideoMuted(true)\n .build();\n// Launch the new activity with the given options. The launch() method takes care\n// of creating the required Intent and passing the options.\nJitsiMeetActivity.launch(this, options);\n')),(0,o.kt)("p",null,"Alternatively, you can use the ",(0,o.kt)("inlineCode",{parentName:"p"},"org.jitsi.meet.sdk.JitsiMeetView")," class which\nextends ",(0,o.kt)("inlineCode",{parentName:"p"},"android.view.View"),"."),(0,o.kt)("p",null,"Note that this should only be needed when ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetActivity")," cannot be used for\nsome reason. Extending ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetView")," requires manual wiring of the view to\nthe activity, using a lot of boilerplate code. Using the Activity instead of the\nView is strongly recommended."),(0,o.kt)("details",null,(0,o.kt)("summary",null,"Show example"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-java"},'package org.jitsi.example;\n\nimport android.os.Bundle;\nimport android.support.v4.app.FragmentActivity;\n\nimport org.jitsi.meet.sdk.JitsiMeetView;\nimport org.jitsi.meet.sdk.ReactActivityLifecycleCallbacks;\n\n// Example\n//\npublic class MainActivity extends FragmentActivity implements JitsiMeetActivityInterface {\n private JitsiMeetView view;\n\n @Override\n protected void onActivityResult(\n int requestCode,\n int resultCode,\n Intent data) {\n JitsiMeetActivityDelegate.onActivityResult(\n this, requestCode, resultCode, data);\n }\n\n @Override\n public void onBackPressed() {\n JitsiMeetActivityDelegate.onBackPressed();\n }\n\n @Override\n protected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n\n view = new JitsiMeetView(this);\n JitsiMeetConferenceOptions options = new JitsiMeetConferenceOptions.Builder()\n .setRoom("https://meet.jit.si/test123")\n .build();\n view.join(options);\n\n setContentView(view);\n }\n\n @Override\n protected void onDestroy() {\n super.onDestroy();\n\n view.dispose();\n view = null;\n\n JitsiMeetActivityDelegate.onHostDestroy(this);\n }\n\n @Override\n public void onNewIntent(Intent intent) {\n JitsiMeetActivityDelegate.onNewIntent(intent);\n }\n\n @Override\n public void onRequestPermissionsResult(\n final int requestCode,\n final String[] permissions,\n final int[] grantResults) {\n JitsiMeetActivityDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults);\n }\n\n @Override\n protected void onResume() {\n super.onResume();\n\n JitsiMeetActivityDelegate.onHostResume(this);\n }\n\n @Override\n protected void onStop() {\n super.onStop();\n\n JitsiMeetActivityDelegate.onHostPause(this);\n }\n}\n'))),(0,o.kt)("h3",{id:"jitsimeetactivity"},"JitsiMeetActivity"),(0,o.kt)("p",null,"This class encapsulates a high level API in the form of an Android ",(0,o.kt)("inlineCode",{parentName:"p"},"FragmentActivity"),"\nwhich displays a single ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetView"),". You can pass a URL as a ",(0,o.kt)("inlineCode",{parentName:"p"},"ACTION_VIEW"),"\non the Intent when starting it and it will join the conference, and will be\nautomatically terminated (finish() will be called on the activity) when the\nconference ends or fails."),(0,o.kt)("h3",{id:"jitsimeetview"},"JitsiMeetView"),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetView")," class is the core of Jitsi Meet SDK. It's designed to\ndisplay a Jitsi Meet conference (or a welcome page)."),(0,o.kt)("h4",{id:"joinoptions"},"join(options)"),(0,o.kt)("p",null,"Joins the conference specified by the given ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetConferenceOptions"),"."),(0,o.kt)("h4",{id:"dispose"},"dispose()"),(0,o.kt)("p",null,"Releases all resources associated with this view. This method MUST be called\nwhen the Activity holding this view is going to be destroyed, usually in the\n",(0,o.kt)("inlineCode",{parentName:"p"},"onDestroy()")," method."),(0,o.kt)("h3",{id:"jitsimeetconferenceoptions"},"JitsiMeetConferenceOptions"),(0,o.kt)("p",null,"This object encapsulates all the options that can be tweaked when joining\na conference."),(0,o.kt)("p",null,"Example:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-java"},'JitsiMeetConferenceOptions options = new JitsiMeetConferenceOptions.Builder()\n .setServerURL(new URL("https://meet.jit.si"))\n .setRoom("test123")\n .setAudioMuted(false)\n .setVideoMuted(false)\n .setAudioOnly(false)\n .setWelcomePageEnabled(false)\n .setConfigOverride("requireDisplayName", true)\n .build();\n')),(0,o.kt)("p",null,"See the ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetConferenceOptions")," implementation for all available options."),(0,o.kt)("h3",{id:"jitsimeetactivitydelegate"},"JitsiMeetActivityDelegate"),(0,o.kt)("p",null,"This class handles the interaction between ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetView")," and its enclosing\n",(0,o.kt)("inlineCode",{parentName:"p"},"Activity"),". Generally this shouldn't be consumed by users, because they'd be\nusing ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetActivity")," instead, which is already completely integrated."),(0,o.kt)("p",null,"All its methods are static."),(0,o.kt)("h4",{id:"onactivityresult"},"onActivityResult(...)"),(0,o.kt)("p",null,"Helper method to handle results of auxiliary activities launched by the SDK.\nShould be called from the activity method of the same name."),(0,o.kt)("h4",{id:"onbackpressed"},"onBackPressed()"),(0,o.kt)("p",null,"Helper method which should be called from the activity's ",(0,o.kt)("inlineCode",{parentName:"p"},"onBackPressed")," method.\nIf this function returns ",(0,o.kt)("inlineCode",{parentName:"p"},"true"),", it means the action was handled and thus no\nextra processing is required; otherwise the app should call the parent's\n",(0,o.kt)("inlineCode",{parentName:"p"},"onBackPressed")," method."),(0,o.kt)("h4",{id:"onhostdestroy"},"onHostDestroy(...)"),(0,o.kt)("p",null,"Helper method which should be called from the activity's ",(0,o.kt)("inlineCode",{parentName:"p"},"onDestroy")," method."),(0,o.kt)("h4",{id:"onhostresume"},"onHostResume(...)"),(0,o.kt)("p",null,"Helper method which should be called from the activity's ",(0,o.kt)("inlineCode",{parentName:"p"},"onResume")," or ",(0,o.kt)("inlineCode",{parentName:"p"},"onStop"),"\nmethod."),(0,o.kt)("h4",{id:"onhoststop"},"onHostStop(...)"),(0,o.kt)("p",null,"Helper method which should be called from the activity's ",(0,o.kt)("inlineCode",{parentName:"p"},"onSstop")," method."),(0,o.kt)("h4",{id:"onnewintent"},"onNewIntent(...)"),(0,o.kt)("p",null,"Helper method for integrating the ",(0,o.kt)("em",{parentName:"p"},"deep linking")," functionality. If your app's\nactivity is launched in \"singleTask\" mode this method should be called from the\nactivity's ",(0,o.kt)("inlineCode",{parentName:"p"},"onNewIntent")," method."),(0,o.kt)("h4",{id:"onrequestpermissionsresult"},"onRequestPermissionsResult(...)"),(0,o.kt)("p",null,"Helper method to handle permission requests inside the SDK. It should be called\nfrom the activity method of the same name."),(0,o.kt)("h4",{id:"onuserleavehint"},"onUserLeaveHint()"),(0,o.kt)("p",null,"Helper method for integrating automatic Picture-in-Picture. It should be called\nfrom the activity's ",(0,o.kt)("inlineCode",{parentName:"p"},"onUserLeaveHint")," method."),(0,o.kt)("p",null,"This is a static method."),(0,o.kt)("h3",{id:"listening-for-broadcasted-events"},"Listening for broadcasted events"),(0,o.kt)("p",null,"The SDK broadcasts several events that the users can listen for."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-java"}," IntentFilter intentFilter = new IntentFilter();\n intentFilter.addAction(BroadcastEvent.Type.CONFERENCE_JOINED.getAction());\n LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, intentFilter);\n")),(0,o.kt)("p",null,"Please see ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetActivity"),", which registers for all the events and can serve as an example."),(0,o.kt)("h4",{id:"supported-events"},"Supported events"),(0,o.kt)("h5",{id:"conference_joined"},"CONFERENCE_JOINED"),(0,o.kt)("p",null,"Broadcasted when a conference was joined. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"url"),": the conference URL")),(0,o.kt)("h5",{id:"conference_terminated"},"CONFERENCE_TERMINATED"),(0,o.kt)("p",null,"Broadcasted when the active conference ends, be it because of user choice or because of a failure. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the\nfollowing information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"url"),": the conference URL"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"error"),": missing if the conference finished gracefully, otherwise contains the error message")),(0,o.kt)("h5",{id:"conference_will_join"},"CONFERENCE_WILL_JOIN"),(0,o.kt)("p",null,"Broadcasted before a conference is joined. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"url"),": the conference URL")),(0,o.kt)("h5",{id:"audio_muted_changed"},"AUDIO_MUTED_CHANGED"),(0,o.kt)("p",null,"Broadcasted when the local participant's audio is muted or unmuted. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"muted"),": a boolean indicating whether the audio is muted or not.")),(0,o.kt)("h5",{id:"participant_joined"},"PARTICIPANT_JOINED"),(0,o.kt)("p",null,"Broadcasted when a participant has joined the conference. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"email"),": the email of the participant. It may not be set if the remote participant didn't set one."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"name"),": the name of the participant."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"role"),": the role of the participant."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"participantId"),": the id of the participant.")),(0,o.kt)("h5",{id:"participant_left"},"PARTICIPANT_LEFT"),(0,o.kt)("p",null,"Called when a participant has left the conference. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"participantId"),": the id of the participant that left.")),(0,o.kt)("h5",{id:"endpoint_text_message_received"},"ENDPOINT_TEXT_MESSAGE_RECEIVED"),(0,o.kt)("p",null,"Broadcasted when an endpoint text message is received. The ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," HashMap contains a ",(0,o.kt)("inlineCode",{parentName:"p"},"senderId")," key with the\nparticipantId of the sender and a ",(0,o.kt)("inlineCode",{parentName:"p"},"message")," key with the content."),(0,o.kt)("h4",{id:"screen_share_toggled"},"SCREEN_SHARE_TOGGLED"),(0,o.kt)("p",null,"Broadcasted when a participant starts or stops sharing his screen. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"participantId"),": Id of the participant that started or stopped sharing his screen."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"sharing"),": True if the participant is sharing his screen, false otherwise.")),(0,o.kt)("h5",{id:"participants_info_retrieved"},"PARTICIPANTS_INFO_RETRIEVED"),(0,o.kt)("p",null,"Broadcasted when a RETRIEVE_PARTICIPANTS_INFO action is called. The ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," HashMap contains a ",(0,o.kt)("inlineCode",{parentName:"p"},"participantsInfo")," key\nwith a list of participants information and a ",(0,o.kt)("inlineCode",{parentName:"p"},"requestId")," key with the id that was sent in the\nRETRIEVE_PARTICIPANTS_INFO action."),(0,o.kt)("h5",{id:"chat_message_received"},"CHAT_MESSAGE_RECEIVED"),(0,o.kt)("p",null,"Broadcasted when a chat text message is received. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"senderId"),": the id of the participant that sent the message."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"message"),": the content of the message."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"isPrivate"),": true if the message is private, false otherwise."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"timestamp"),": the (optional) timestamp of the message.")),(0,o.kt)("h5",{id:"chat_toggled"},"CHAT_TOGGLED"),(0,o.kt)("p",null,"Broadcasted when the chat dialog is opened or closed. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"isOpen"),": true if the chat dialog is open, false otherwise.")),(0,o.kt)("h5",{id:"video_muted_changed"},"VIDEO_MUTED_CHANGED"),(0,o.kt)("p",null,"Broadcasted when the local participant's video is muted or unmuted. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"muted"),": an integer indicating whether the video is muted or not. 0 means unmuted, 4 means muted.")),(0,o.kt)("h5",{id:"ready_to_close"},"READY_TO_CLOSE"),(0,o.kt)("p",null,"The SDK is ready to be closed / dismised."),(0,o.kt)("h3",{id:"broadcasting-actions"},"Broadcasting Actions"),(0,o.kt)("p",null,"The SDK listens for broadcasted actions from the users and reacts accordingly."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-java"},' Intent muteBroadcastIntent = new Intent(BroadcastAction.Type.SET_AUDIO_MUTED.getAction());\n muteBroadcastIntent.putExtra("muted", muted);\n LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(muteBroadcastIntent);\n')),(0,o.kt)("p",null,"The intents can be build manually (as shown above) or through the methods in ",(0,o.kt)("inlineCode",{parentName:"p"},"BroadcastIntentHelper"),"."),(0,o.kt)("p",null,"Please see ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetOngoingConferenceService")," for more examples of sending actions."),(0,o.kt)("h4",{id:"supported-actions"},"Supported actions"),(0,o.kt)("h5",{id:"set_audio_muted"},"SET_AUDIO_MUTED"),(0,o.kt)("p",null,"Sets the state of the localParticipant audio muted according to the ",(0,o.kt)("inlineCode",{parentName:"p"},"muted")," parameter.\nExpects a ",(0,o.kt)("inlineCode",{parentName:"p"},"muted")," key on the intent extra with a boolean value."),(0,o.kt)("h5",{id:"set_video_muted"},"SET_VIDEO_MUTED"),(0,o.kt)("p",null,"Sets the state of the localParticipant video muted according to the ",(0,o.kt)("inlineCode",{parentName:"p"},"muted")," parameter.\nExpects a ",(0,o.kt)("inlineCode",{parentName:"p"},"muted")," key on the intent extra with a boolean value."),(0,o.kt)("h5",{id:"hang_up"},"HANG_UP"),(0,o.kt)("p",null,"The localParticipant leaves the current conference.\nDoes not expect any extra value."),(0,o.kt)("h5",{id:"send_endpoint_text_message"},"SEND_ENDPOINT_TEXT_MESSAGE"),(0,o.kt)("p",null,"Sends a message via the data channel to one particular participant or to all of them.\nExpects a ",(0,o.kt)("inlineCode",{parentName:"p"},"to")," key on the intent extra with the id of the participant to which the message\nis meant and a ",(0,o.kt)("inlineCode",{parentName:"p"},"message")," key with a string value, the actual content of the message.\nIf the ",(0,o.kt)("inlineCode",{parentName:"p"},"to")," key is not present or it's value is empty, the message will be sent\nto all the participants in the conference."),(0,o.kt)("p",null,"In order to get the participantId, the ",(0,o.kt)("inlineCode",{parentName:"p"},"PARTICIPANT_JOINED")," event should be listened for,\nwhich ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," includes the id and this should be stored somehow."),(0,o.kt)("h5",{id:"toggle_screen_share"},"TOGGLE_SCREEN_SHARE"),(0,o.kt)("p",null,"Sets the state of the localParticipant screen share according to the ",(0,o.kt)("inlineCode",{parentName:"p"},"enabled")," parameter.\nExpects a ",(0,o.kt)("inlineCode",{parentName:"p"},"enabled")," key on the intent extra with a boolean value."),(0,o.kt)("h5",{id:"retrieve_participants_info"},"RETRIEVE_PARTICIPANTS_INFO"),(0,o.kt)("p",null,"Signals the SDK to retrieve a list with the participants information. The SDK will emit a PARTICIPANTS_INFO_RETRIEVED event.\nExpects a ",(0,o.kt)("inlineCode",{parentName:"p"},"requestId")," key on the intent extra with a string value, this parameter will be present on the PARTICIPANTS_INFO_RETRIEVED event."),(0,o.kt)("h5",{id:"open_chat"},"OPEN_CHAT"),(0,o.kt)("p",null,"Opens the chat dialog. If a ",(0,o.kt)("inlineCode",{parentName:"p"},"to")," key is present with a valid participantId, the private chat for that particular participant will be opened."),(0,o.kt)("h5",{id:"close_chat"},"CLOSE_CHAT"),(0,o.kt)("p",null,"Closes the chat dialog.\nDoes not expect any extra value."),(0,o.kt)("h5",{id:"send_chat_message"},"SEND_CHAT_MESSAGE"),(0,o.kt)("p",null,"Sends a chat message, either a private one if a ",(0,o.kt)("inlineCode",{parentName:"p"},"to")," key is present with a valid participantId and to everybody otherwise.\nExpects a ",(0,o.kt)("inlineCode",{parentName:"p"},"message")," key with a string value."),(0,o.kt)("h2",{id:"proguard-rules"},"ProGuard rules"),(0,o.kt)("p",null,"When using the SDK on a project some proguard rules have to be added in order\nto avoid necessary code being stripped. Add the following to your project's\nrules file: ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet/blob/master/android/app/proguard-rules.pro"},"https://github.com/jitsi/jitsi-meet/blob/master/android/app/proguard-rules.pro")),(0,o.kt)("h2",{id:"picture-in-picture"},"Picture-in-Picture"),(0,o.kt)("p",null,(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetView"),' will automatically adjust its UI when presented in a\nPicture-in-Picture style scenario, in a rectangle too small to accommodate its\n"full" UI.'),(0,o.kt)("h2",{id:"dropbox-integration"},"Dropbox integration"),(0,o.kt)("p",null,"To setup the Dropbox integration, follow these steps:"),(0,o.kt)("ol",null,(0,o.kt)("li",{parentName:"ol"},"Add the following to the app's AndroidManifest.xml and change ",(0,o.kt)("inlineCode",{parentName:"li"},"")," to\nyour Dropbox app key:")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'\n \n \n \n \n \n \n\n')),(0,o.kt)("ol",{start:2},(0,o.kt)("li",{parentName:"ol"},"Add the following to the app's strings.xml and change ",(0,o.kt)("inlineCode",{parentName:"li"},"")," to your\nDropbox app key:")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'\n')))}h.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[5742],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>h});var i=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function r(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=i.createContext({}),d=function(e){var t=i.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},p=function(e){var t=d(e.components);return i.createElement(s.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},u=i.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=d(n),h=a,m=u["".concat(s,".").concat(h)]||u[h]||c[h]||o;return n?i.createElement(m,r(r({ref:t},p),{},{components:n})):i.createElement(m,r({ref:t},p))}));function h(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,r=new Array(o);r[0]=u;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:a,r[1]=l;for(var d=2;d{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>s,default:()=>h,frontMatter:()=>l,metadata:()=>d,toc:()=>c});var i=n(7462),a=n(3366),o=(n(7294),n(3905)),r=["components"],l={id:"dev-guide-android-sdk",title:"Android SDK"},s=void 0,d={unversionedId:"dev-guide/dev-guide-android-sdk",id:"dev-guide/dev-guide-android-sdk",title:"Android SDK",description:"The Jitsi Meet Android SDK provides the same user experience as the Jitsi Meet app,",source:"@site/docs/dev-guide/android-sdk.md",sourceDirName:"dev-guide",slug:"/dev-guide/dev-guide-android-sdk",permalink:"/handbook/docs/dev-guide/dev-guide-android-sdk",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/dev-guide/android-sdk.md",tags:[],version:"current",lastUpdatedAt:1695203964,formattedLastUpdatedAt:"Sep 20, 2023",frontMatter:{id:"dev-guide-android-sdk",title:"Android SDK"},sidebar:"docs",previous:{title:"React SDK",permalink:"/handbook/docs/dev-guide/dev-guide-react-sdk"},next:{title:"iOS SDK",permalink:"/handbook/docs/dev-guide/dev-guide-ios-sdk"}},p={},c=[{value:"Sample applications using the SDK",id:"sample-applications-using-the-sdk",level:2},{value:"Build your own, or use a pre-build SDK artifacts/binaries",id:"build-your-own-or-use-a-pre-build-sdk-artifactsbinaries",level:2},{value:"Use pre-build SDK artifacts/binaries",id:"use-pre-build-sdk-artifactsbinaries",level:3},{value:"Build and use your own SDK artifacts/binaries",id:"build-and-use-your-own-sdk-artifactsbinaries",level:3},{value:"Using the API",id:"using-the-api",level:2},{value:"JitsiMeetActivity",id:"jitsimeetactivity",level:3},{value:"JitsiMeetView",id:"jitsimeetview",level:3},{value:"join(options)",id:"joinoptions",level:4},{value:"dispose()",id:"dispose",level:4},{value:"JitsiMeetConferenceOptions",id:"jitsimeetconferenceoptions",level:3},{value:"JitsiMeetActivityDelegate",id:"jitsimeetactivitydelegate",level:3},{value:"onActivityResult(...)",id:"onactivityresult",level:4},{value:"onBackPressed()",id:"onbackpressed",level:4},{value:"onHostDestroy(...)",id:"onhostdestroy",level:4},{value:"onHostResume(...)",id:"onhostresume",level:4},{value:"onHostStop(...)",id:"onhoststop",level:4},{value:"onNewIntent(...)",id:"onnewintent",level:4},{value:"onRequestPermissionsResult(...)",id:"onrequestpermissionsresult",level:4},{value:"onUserLeaveHint()",id:"onuserleavehint",level:4},{value:"Listening for broadcasted events",id:"listening-for-broadcasted-events",level:3},{value:"Supported events",id:"supported-events",level:4},{value:"CONFERENCE_JOINED",id:"conference_joined",level:5},{value:"CONFERENCE_TERMINATED",id:"conference_terminated",level:5},{value:"CONFERENCE_WILL_JOIN",id:"conference_will_join",level:5},{value:"AUDIO_MUTED_CHANGED",id:"audio_muted_changed",level:5},{value:"PARTICIPANT_JOINED",id:"participant_joined",level:5},{value:"PARTICIPANT_LEFT",id:"participant_left",level:5},{value:"ENDPOINT_TEXT_MESSAGE_RECEIVED",id:"endpoint_text_message_received",level:5},{value:"SCREEN_SHARE_TOGGLED",id:"screen_share_toggled",level:4},{value:"PARTICIPANTS_INFO_RETRIEVED",id:"participants_info_retrieved",level:5},{value:"CHAT_MESSAGE_RECEIVED",id:"chat_message_received",level:5},{value:"CHAT_TOGGLED",id:"chat_toggled",level:5},{value:"VIDEO_MUTED_CHANGED",id:"video_muted_changed",level:5},{value:"READY_TO_CLOSE",id:"ready_to_close",level:5},{value:"Broadcasting Actions",id:"broadcasting-actions",level:3},{value:"Supported actions",id:"supported-actions",level:4},{value:"SET_AUDIO_MUTED",id:"set_audio_muted",level:5},{value:"SET_VIDEO_MUTED",id:"set_video_muted",level:5},{value:"HANG_UP",id:"hang_up",level:5},{value:"SEND_ENDPOINT_TEXT_MESSAGE",id:"send_endpoint_text_message",level:5},{value:"TOGGLE_SCREEN_SHARE",id:"toggle_screen_share",level:5},{value:"RETRIEVE_PARTICIPANTS_INFO",id:"retrieve_participants_info",level:5},{value:"OPEN_CHAT",id:"open_chat",level:5},{value:"CLOSE_CHAT",id:"close_chat",level:5},{value:"SEND_CHAT_MESSAGE",id:"send_chat_message",level:5},{value:"ProGuard rules",id:"proguard-rules",level:2},{value:"Picture-in-Picture",id:"picture-in-picture",level:2},{value:"Dropbox integration",id:"dropbox-integration",level:2}],u={toc:c};function h(e){var t=e.components,n=(0,a.Z)(e,r);return(0,o.kt)("wrapper",(0,i.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("p",null,"The Jitsi Meet Android SDK provides the same user experience as the Jitsi Meet app,\nin a customizable way which you can embed in your apps."),(0,o.kt)("admonition",{type:"important"},(0,o.kt)("p",{parentName:"admonition"},"Android 6.0 (API level 23) or higher is required.")),(0,o.kt)("h2",{id:"sample-applications-using-the-sdk"},"Sample applications using the SDK"),(0,o.kt)("p",null,"If you want to see how easy integrating the Jitsi Meet SDK into a native application is, take a look at the\n",(0,o.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet-sdk-samples#android"},"sample applications repository"),"."),(0,o.kt)("h2",{id:"build-your-own-or-use-a-pre-build-sdk-artifactsbinaries"},"Build your own, or use a pre-build SDK artifacts/binaries"),(0,o.kt)("p",null,"Jitsi conveniently provides a pre-build SDK artifacts/binaries in its Maven repository. When you do not require any\nmodification to the SDK itself or any of its dependencies, it's suggested to use the pre-build SDK. This avoids the\ncomplexity of building and installing your own SDK artifacts/binaries."),(0,o.kt)("h3",{id:"use-pre-build-sdk-artifactsbinaries"},"Use pre-build SDK artifacts/binaries"),(0,o.kt)("p",null,"In your project, add the Maven repository\n",(0,o.kt)("inlineCode",{parentName:"p"},"https://github.com/jitsi/jitsi-maven-repository/raw/master/releases")," and the\ndependency ",(0,o.kt)("inlineCode",{parentName:"p"},"org.jitsi.react:jitsi-meet-sdk")," into your ",(0,o.kt)("inlineCode",{parentName:"p"},"build.gradle")," files."),(0,o.kt)("p",null,"The repository typically goes into the ",(0,o.kt)("inlineCode",{parentName:"p"},"build.gradle")," file in the root of your project:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-gradle",metastring:'title="build.gradle"',title:'"build.gradle"'},"allprojects {\n repositories {\n maven {\n url \"https://github.com/jitsi/jitsi-maven-repository/raw/master/releases\"\n }\n google()\n mavenCentral()\n maven { url 'https://www.jitpack.io' }\n }\n}\n")),(0,o.kt)("p",null,"In recent versions of Android Studios, ",(0,o.kt)("inlineCode",{parentName:"p"},"allprojects{}")," might not be found in ",(0,o.kt)("inlineCode",{parentName:"p"},"build.gradle"),". In that case, the repository goes into the ",(0,o.kt)("inlineCode",{parentName:"p"},"settings.gradle")," file in the root of your project:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-gradle",metastring:'title="settings.gradle"',title:'"settings.gradle"'},'dependencyResolutionManagement {\n repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n repositories {\n google()\n mavenCentral()\n maven {\n url "https://github.com/jitsi/jitsi-maven-repository/raw/master/releases"\n }\n maven {\n url "https://maven.google.com"\n }\n }\n}\n')),(0,o.kt)("p",null,"Dependency definitions belong in the individual module ",(0,o.kt)("inlineCode",{parentName:"p"},"build.gradle")," files:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-gradle"},"dependencies {\n // (other dependencies)\n implementation ('org.jitsi.react:jitsi-meet-sdk:+') { transitive = true }\n}\n")),(0,o.kt)("admonition",{type:"warning"},(0,o.kt)("p",{parentName:"admonition"},"Make sure you pin your dependency by checking the ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet-release-notes/blob/master/CHANGELOG-MOBILE-SDKS.md"},"releases page"),".")),(0,o.kt)("h3",{id:"build-and-use-your-own-sdk-artifactsbinaries"},"Build and use your own SDK artifacts/binaries"),(0,o.kt)("details",null,(0,o.kt)("summary",null,"Show building instructions"),(0,o.kt)("p",null,"Start by making sure that your development environment ",(0,o.kt)("a",{parentName:"p",href:"/docs/category/mobile"},"is set up correctly"),"."),(0,o.kt)("admonition",{title:"A Note on Dependencies",type:"note"},(0,o.kt)("p",{parentName:"admonition"},"Apart from the SDK, Jitsi also publishes a binary Maven artifact for some of the SDK dependencies (that are not otherwise publicly available) to the Jitsi Maven repository. When you're planning to use a SDK that is built from source, you'll likely use a version of the source code that is newer (or at least ",(0,o.kt)("em",{parentName:"p"},"different"),") than the version of the source that was used to create the binary SDK artifact. As a consequence, the dependencies that your project will need, might also be different from those that are published in the Jitsi Maven repository. This might lead to build problems, caused by dependencies that are unavailable.")),(0,o.kt)("p",null,"If you want to use a SDK that is built from source, you will likely benefit from composing a local Maven repository that contains these dependencies. The text below describes how you create a repository that includes both the SDK as well as these dependencies. For illustration purposes, we'll define the location of this local Maven repository as ",(0,o.kt)("inlineCode",{parentName:"p"},"/tmp/repo")),(0,o.kt)("p",null,"In source code form, the Android SDK dependencies are locked/pinned by ",(0,o.kt)("inlineCode",{parentName:"p"},"package.json")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"package-lock.json")," of the Jitsi Meet project. To obtain the data, execute NPM in the jitsi-meet project directory:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"npm install\n")),(0,o.kt)("p",null,"This will pull in the dependencies in either binary format, or in source code format, somewhere under /node_modules/"),(0,o.kt)("p",null,"Third-party React Native ",(0,o.kt)("em",{parentName:"p"},"modules"),", which Jitsi Meet SDK for Android depends on, are download by NPM in source code\nor binary form. These need to be assembled into Maven artifacts, and then published to your local Maven repository.\nA script is provided to facilitate this. From the root of the jitsi-meet project repository, run:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"./android/scripts/release-sdk.sh /tmp/repo\n")),(0,o.kt)("p",null,"This will build and publish the SDK, and all of its dependencies to the specified Maven repository (",(0,o.kt)("inlineCode",{parentName:"p"},"/tmp/repo"),") in\nthis example."),(0,o.kt)("p",null,"You're now ready to use the artifacts. In ",(0,o.kt)("em",{parentName:"p"},"your")," project, add the Maven repository that you used above (",(0,o.kt)("inlineCode",{parentName:"p"},"/tmp/repo"),") into your top-level ",(0,o.kt)("inlineCode",{parentName:"p"},"build.gradle")," file:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"allprojects {\n repositories {\n maven { url \"file:/tmp/repo\" }\n google()\n mavenCentral()\n maven { url 'https://www.jitpack.io' }\n }\n}\n")),(0,o.kt)("p",null,"You can use your local repository to replace the Jitsi repository (",(0,o.kt)("inlineCode",{parentName:"p"},'maven { url "https://github.com/jitsi/jitsi-maven-repository/raw/master/releases" }'),") when you published ",(0,o.kt)("em",{parentName:"p"},"all")," subprojects. If you didn't do that, you'll have to add both repositories. Make sure your local repository is listed first!"),(0,o.kt)("p",null,"Then, define the dependency ",(0,o.kt)("inlineCode",{parentName:"p"},"org.jitsi.react:jitsi-meet-sdk")," into the ",(0,o.kt)("inlineCode",{parentName:"p"},"build.gradle")," file of your module:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"implementation ('org.jitsi.react:jitsi-meet-sdk:+') { transitive = true }\n")),(0,o.kt)("p",null,"Note that there should not be a need to explicitly add the other dependencies, as they will be pulled in as transitive\ndependencies of ",(0,o.kt)("inlineCode",{parentName:"p"},"jitsi-meet-sdk"),".")),(0,o.kt)("h2",{id:"using-the-api"},"Using the API"),(0,o.kt)("p",null,"Jitsi Meet SDK is an Android library which embodies the whole Jitsi Meet\nexperience and makes it reusable by third-party apps."),(0,o.kt)("p",null,"First, add Java 1.8 compatibility support to your project by adding the\nfollowing lines into your ",(0,o.kt)("inlineCode",{parentName:"p"},"build.gradle")," file:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"compileOptions {\n sourceCompatibility JavaVersion.VERSION_1_8\n targetCompatibility JavaVersion.VERSION_1_8\n}\n")),(0,o.kt)("p",null,"To get started, just launch ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetActivity")," pointing to the room you want:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-java"},'// Somewhere early in your app.\nJitsiMeetConferenceOptions defaultOptions\n = new JitsiMeetConferenceOptions.Builder()\n .setServerURL(serverURL)\n // When using JaaS, set the obtained JWT here\n //.setToken("MyJWT")\n // Different features flags can be set\n // .setFeatureFlag("toolbox.enabled", false)\n // .setFeatureFlag("filmstrip.enabled", false)\n .setFeatureFlag("welcomepage.enabled", false)\n .build();\nJitsiMeet.setDefaultConferenceOptions(defaultOptions);\n// ...\n// Build options object for joining the conference. The SDK will merge the default\n// one we set earlier and this one when joining.\nJitsiMeetConferenceOptions options\n = new JitsiMeetConferenceOptions.Builder()\n .setRoom(roomName)\n // Settings for audio and video\n //.setAudioMuted(true)\n //.setVideoMuted(true)\n .build();\n// Launch the new activity with the given options. The launch() method takes care\n// of creating the required Intent and passing the options.\nJitsiMeetActivity.launch(this, options);\n')),(0,o.kt)("p",null,"Alternatively, you can use the ",(0,o.kt)("inlineCode",{parentName:"p"},"org.jitsi.meet.sdk.JitsiMeetView")," class which\nextends ",(0,o.kt)("inlineCode",{parentName:"p"},"android.view.View"),"."),(0,o.kt)("p",null,"Note that this should only be needed when ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetActivity")," cannot be used for\nsome reason. Extending ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetView")," requires manual wiring of the view to\nthe activity, using a lot of boilerplate code. Using the Activity instead of the\nView is strongly recommended."),(0,o.kt)("details",null,(0,o.kt)("summary",null,"Show example"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-java"},'package org.jitsi.example;\n\nimport android.os.Bundle;\nimport android.support.v4.app.FragmentActivity;\n\nimport org.jitsi.meet.sdk.JitsiMeetView;\nimport org.jitsi.meet.sdk.ReactActivityLifecycleCallbacks;\n\n// Example\n//\npublic class MainActivity extends FragmentActivity implements JitsiMeetActivityInterface {\n private JitsiMeetView view;\n\n @Override\n protected void onActivityResult(\n int requestCode,\n int resultCode,\n Intent data) {\n JitsiMeetActivityDelegate.onActivityResult(\n this, requestCode, resultCode, data);\n }\n\n @Override\n public void onBackPressed() {\n JitsiMeetActivityDelegate.onBackPressed();\n }\n\n @Override\n protected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n\n view = new JitsiMeetView(this);\n JitsiMeetConferenceOptions options = new JitsiMeetConferenceOptions.Builder()\n .setRoom("https://meet.jit.si/test123")\n .build();\n view.join(options);\n\n setContentView(view);\n }\n\n @Override\n protected void onDestroy() {\n super.onDestroy();\n\n view.dispose();\n view = null;\n\n JitsiMeetActivityDelegate.onHostDestroy(this);\n }\n\n @Override\n public void onNewIntent(Intent intent) {\n JitsiMeetActivityDelegate.onNewIntent(intent);\n }\n\n @Override\n public void onRequestPermissionsResult(\n final int requestCode,\n final String[] permissions,\n final int[] grantResults) {\n JitsiMeetActivityDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults);\n }\n\n @Override\n protected void onResume() {\n super.onResume();\n\n JitsiMeetActivityDelegate.onHostResume(this);\n }\n\n @Override\n protected void onStop() {\n super.onStop();\n\n JitsiMeetActivityDelegate.onHostPause(this);\n }\n}\n'))),(0,o.kt)("h3",{id:"jitsimeetactivity"},"JitsiMeetActivity"),(0,o.kt)("p",null,"This class encapsulates a high level API in the form of an Android ",(0,o.kt)("inlineCode",{parentName:"p"},"FragmentActivity"),"\nwhich displays a single ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetView"),". You can pass a URL as a ",(0,o.kt)("inlineCode",{parentName:"p"},"ACTION_VIEW"),"\non the Intent when starting it and it will join the conference, and will be\nautomatically terminated (finish() will be called on the activity) when the\nconference ends or fails."),(0,o.kt)("h3",{id:"jitsimeetview"},"JitsiMeetView"),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetView")," class is the core of Jitsi Meet SDK. It's designed to\ndisplay a Jitsi Meet conference (or a welcome page)."),(0,o.kt)("h4",{id:"joinoptions"},"join(options)"),(0,o.kt)("p",null,"Joins the conference specified by the given ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetConferenceOptions"),"."),(0,o.kt)("h4",{id:"dispose"},"dispose()"),(0,o.kt)("p",null,"Releases all resources associated with this view. This method MUST be called\nwhen the Activity holding this view is going to be destroyed, usually in the\n",(0,o.kt)("inlineCode",{parentName:"p"},"onDestroy()")," method."),(0,o.kt)("h3",{id:"jitsimeetconferenceoptions"},"JitsiMeetConferenceOptions"),(0,o.kt)("p",null,"This object encapsulates all the options that can be tweaked when joining\na conference."),(0,o.kt)("p",null,"Example:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-java"},'JitsiMeetConferenceOptions options = new JitsiMeetConferenceOptions.Builder()\n .setServerURL(new URL("https://meet.jit.si"))\n .setRoom("test123")\n .setAudioMuted(false)\n .setVideoMuted(false)\n .setAudioOnly(false)\n .setWelcomePageEnabled(false)\n .setConfigOverride("requireDisplayName", true)\n .build();\n')),(0,o.kt)("p",null,"See the ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetConferenceOptions")," implementation for all available options."),(0,o.kt)("h3",{id:"jitsimeetactivitydelegate"},"JitsiMeetActivityDelegate"),(0,o.kt)("p",null,"This class handles the interaction between ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetView")," and its enclosing\n",(0,o.kt)("inlineCode",{parentName:"p"},"Activity"),". Generally this shouldn't be consumed by users, because they'd be\nusing ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetActivity")," instead, which is already completely integrated."),(0,o.kt)("p",null,"All its methods are static."),(0,o.kt)("h4",{id:"onactivityresult"},"onActivityResult(...)"),(0,o.kt)("p",null,"Helper method to handle results of auxiliary activities launched by the SDK.\nShould be called from the activity method of the same name."),(0,o.kt)("h4",{id:"onbackpressed"},"onBackPressed()"),(0,o.kt)("p",null,"Helper method which should be called from the activity's ",(0,o.kt)("inlineCode",{parentName:"p"},"onBackPressed")," method.\nIf this function returns ",(0,o.kt)("inlineCode",{parentName:"p"},"true"),", it means the action was handled and thus no\nextra processing is required; otherwise the app should call the parent's\n",(0,o.kt)("inlineCode",{parentName:"p"},"onBackPressed")," method."),(0,o.kt)("h4",{id:"onhostdestroy"},"onHostDestroy(...)"),(0,o.kt)("p",null,"Helper method which should be called from the activity's ",(0,o.kt)("inlineCode",{parentName:"p"},"onDestroy")," method."),(0,o.kt)("h4",{id:"onhostresume"},"onHostResume(...)"),(0,o.kt)("p",null,"Helper method which should be called from the activity's ",(0,o.kt)("inlineCode",{parentName:"p"},"onResume")," or ",(0,o.kt)("inlineCode",{parentName:"p"},"onStop"),"\nmethod."),(0,o.kt)("h4",{id:"onhoststop"},"onHostStop(...)"),(0,o.kt)("p",null,"Helper method which should be called from the activity's ",(0,o.kt)("inlineCode",{parentName:"p"},"onSstop")," method."),(0,o.kt)("h4",{id:"onnewintent"},"onNewIntent(...)"),(0,o.kt)("p",null,"Helper method for integrating the ",(0,o.kt)("em",{parentName:"p"},"deep linking")," functionality. If your app's\nactivity is launched in \"singleTask\" mode this method should be called from the\nactivity's ",(0,o.kt)("inlineCode",{parentName:"p"},"onNewIntent")," method."),(0,o.kt)("h4",{id:"onrequestpermissionsresult"},"onRequestPermissionsResult(...)"),(0,o.kt)("p",null,"Helper method to handle permission requests inside the SDK. It should be called\nfrom the activity method of the same name."),(0,o.kt)("h4",{id:"onuserleavehint"},"onUserLeaveHint()"),(0,o.kt)("p",null,"Helper method for integrating automatic Picture-in-Picture. It should be called\nfrom the activity's ",(0,o.kt)("inlineCode",{parentName:"p"},"onUserLeaveHint")," method."),(0,o.kt)("p",null,"This is a static method."),(0,o.kt)("h3",{id:"listening-for-broadcasted-events"},"Listening for broadcasted events"),(0,o.kt)("p",null,"The SDK broadcasts several events that the users can listen for."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-java"}," IntentFilter intentFilter = new IntentFilter();\n intentFilter.addAction(BroadcastEvent.Type.CONFERENCE_JOINED.getAction());\n LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, intentFilter);\n")),(0,o.kt)("p",null,"Please see ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetActivity"),", which registers for all the events and can serve as an example."),(0,o.kt)("h4",{id:"supported-events"},"Supported events"),(0,o.kt)("h5",{id:"conference_joined"},"CONFERENCE_JOINED"),(0,o.kt)("p",null,"Broadcasted when a conference was joined. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"url"),": the conference URL")),(0,o.kt)("h5",{id:"conference_terminated"},"CONFERENCE_TERMINATED"),(0,o.kt)("p",null,"Broadcasted when the active conference ends, be it because of user choice or because of a failure. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the\nfollowing information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"url"),": the conference URL"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"error"),": missing if the conference finished gracefully, otherwise contains the error message")),(0,o.kt)("h5",{id:"conference_will_join"},"CONFERENCE_WILL_JOIN"),(0,o.kt)("p",null,"Broadcasted before a conference is joined. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"url"),": the conference URL")),(0,o.kt)("h5",{id:"audio_muted_changed"},"AUDIO_MUTED_CHANGED"),(0,o.kt)("p",null,"Broadcasted when the local participant's audio is muted or unmuted. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"muted"),": a boolean indicating whether the audio is muted or not.")),(0,o.kt)("h5",{id:"participant_joined"},"PARTICIPANT_JOINED"),(0,o.kt)("p",null,"Broadcasted when a participant has joined the conference. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"email"),": the email of the participant. It may not be set if the remote participant didn't set one."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"name"),": the name of the participant."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"role"),": the role of the participant."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"participantId"),": the id of the participant.")),(0,o.kt)("h5",{id:"participant_left"},"PARTICIPANT_LEFT"),(0,o.kt)("p",null,"Called when a participant has left the conference. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"participantId"),": the id of the participant that left.")),(0,o.kt)("h5",{id:"endpoint_text_message_received"},"ENDPOINT_TEXT_MESSAGE_RECEIVED"),(0,o.kt)("p",null,"Broadcasted when an endpoint text message is received. The ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," HashMap contains a ",(0,o.kt)("inlineCode",{parentName:"p"},"senderId")," key with the\nparticipantId of the sender and a ",(0,o.kt)("inlineCode",{parentName:"p"},"message")," key with the content."),(0,o.kt)("h4",{id:"screen_share_toggled"},"SCREEN_SHARE_TOGGLED"),(0,o.kt)("p",null,"Broadcasted when a participant starts or stops sharing his screen. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"participantId"),": Id of the participant that started or stopped sharing his screen."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"sharing"),": True if the participant is sharing his screen, false otherwise.")),(0,o.kt)("h5",{id:"participants_info_retrieved"},"PARTICIPANTS_INFO_RETRIEVED"),(0,o.kt)("p",null,"Broadcasted when a RETRIEVE_PARTICIPANTS_INFO action is called. The ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," HashMap contains a ",(0,o.kt)("inlineCode",{parentName:"p"},"participantsInfo")," key\nwith a list of participants information and a ",(0,o.kt)("inlineCode",{parentName:"p"},"requestId")," key with the id that was sent in the\nRETRIEVE_PARTICIPANTS_INFO action."),(0,o.kt)("h5",{id:"chat_message_received"},"CHAT_MESSAGE_RECEIVED"),(0,o.kt)("p",null,"Broadcasted when a chat text message is received. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"senderId"),": the id of the participant that sent the message."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"message"),": the content of the message."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"isPrivate"),": true if the message is private, false otherwise."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"timestamp"),": the (optional) timestamp of the message.")),(0,o.kt)("h5",{id:"chat_toggled"},"CHAT_TOGGLED"),(0,o.kt)("p",null,"Broadcasted when the chat dialog is opened or closed. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"isOpen"),": true if the chat dialog is open, false otherwise.")),(0,o.kt)("h5",{id:"video_muted_changed"},"VIDEO_MUTED_CHANGED"),(0,o.kt)("p",null,"Broadcasted when the local participant's video is muted or unmuted. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"muted"),": an integer indicating whether the video is muted or not. 0 means unmuted, 4 means muted.")),(0,o.kt)("h5",{id:"ready_to_close"},"READY_TO_CLOSE"),(0,o.kt)("p",null,"The SDK is ready to be closed / dismised."),(0,o.kt)("h3",{id:"broadcasting-actions"},"Broadcasting Actions"),(0,o.kt)("p",null,"The SDK listens for broadcasted actions from the users and reacts accordingly."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-java"},' Intent muteBroadcastIntent = new Intent(BroadcastAction.Type.SET_AUDIO_MUTED.getAction());\n muteBroadcastIntent.putExtra("muted", muted);\n LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(muteBroadcastIntent);\n')),(0,o.kt)("p",null,"The intents can be build manually (as shown above) or through the methods in ",(0,o.kt)("inlineCode",{parentName:"p"},"BroadcastIntentHelper"),"."),(0,o.kt)("p",null,"Please see ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetOngoingConferenceService")," for more examples of sending actions."),(0,o.kt)("h4",{id:"supported-actions"},"Supported actions"),(0,o.kt)("h5",{id:"set_audio_muted"},"SET_AUDIO_MUTED"),(0,o.kt)("p",null,"Sets the state of the localParticipant audio muted according to the ",(0,o.kt)("inlineCode",{parentName:"p"},"muted")," parameter.\nExpects a ",(0,o.kt)("inlineCode",{parentName:"p"},"muted")," key on the intent extra with a boolean value."),(0,o.kt)("h5",{id:"set_video_muted"},"SET_VIDEO_MUTED"),(0,o.kt)("p",null,"Sets the state of the localParticipant video muted according to the ",(0,o.kt)("inlineCode",{parentName:"p"},"muted")," parameter.\nExpects a ",(0,o.kt)("inlineCode",{parentName:"p"},"muted")," key on the intent extra with a boolean value."),(0,o.kt)("h5",{id:"hang_up"},"HANG_UP"),(0,o.kt)("p",null,"The localParticipant leaves the current conference.\nDoes not expect any extra value."),(0,o.kt)("h5",{id:"send_endpoint_text_message"},"SEND_ENDPOINT_TEXT_MESSAGE"),(0,o.kt)("p",null,"Sends a message via the data channel to one particular participant or to all of them.\nExpects a ",(0,o.kt)("inlineCode",{parentName:"p"},"to")," key on the intent extra with the id of the participant to which the message\nis meant and a ",(0,o.kt)("inlineCode",{parentName:"p"},"message")," key with a string value, the actual content of the message.\nIf the ",(0,o.kt)("inlineCode",{parentName:"p"},"to")," key is not present or it's value is empty, the message will be sent\nto all the participants in the conference."),(0,o.kt)("p",null,"In order to get the participantId, the ",(0,o.kt)("inlineCode",{parentName:"p"},"PARTICIPANT_JOINED")," event should be listened for,\nwhich ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," includes the id and this should be stored somehow."),(0,o.kt)("h5",{id:"toggle_screen_share"},"TOGGLE_SCREEN_SHARE"),(0,o.kt)("p",null,"Sets the state of the localParticipant screen share according to the ",(0,o.kt)("inlineCode",{parentName:"p"},"enabled")," parameter.\nExpects a ",(0,o.kt)("inlineCode",{parentName:"p"},"enabled")," key on the intent extra with a boolean value."),(0,o.kt)("h5",{id:"retrieve_participants_info"},"RETRIEVE_PARTICIPANTS_INFO"),(0,o.kt)("p",null,"Signals the SDK to retrieve a list with the participants information. The SDK will emit a PARTICIPANTS_INFO_RETRIEVED event.\nExpects a ",(0,o.kt)("inlineCode",{parentName:"p"},"requestId")," key on the intent extra with a string value, this parameter will be present on the PARTICIPANTS_INFO_RETRIEVED event."),(0,o.kt)("h5",{id:"open_chat"},"OPEN_CHAT"),(0,o.kt)("p",null,"Opens the chat dialog. If a ",(0,o.kt)("inlineCode",{parentName:"p"},"to")," key is present with a valid participantId, the private chat for that particular participant will be opened."),(0,o.kt)("h5",{id:"close_chat"},"CLOSE_CHAT"),(0,o.kt)("p",null,"Closes the chat dialog.\nDoes not expect any extra value."),(0,o.kt)("h5",{id:"send_chat_message"},"SEND_CHAT_MESSAGE"),(0,o.kt)("p",null,"Sends a chat message, either a private one if a ",(0,o.kt)("inlineCode",{parentName:"p"},"to")," key is present with a valid participantId and to everybody otherwise.\nExpects a ",(0,o.kt)("inlineCode",{parentName:"p"},"message")," key with a string value."),(0,o.kt)("h2",{id:"proguard-rules"},"ProGuard rules"),(0,o.kt)("p",null,"When using the SDK on a project some proguard rules have to be added in order\nto avoid necessary code being stripped. Add the following to your project's\nrules file: ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet/blob/master/android/app/proguard-rules.pro"},"https://github.com/jitsi/jitsi-meet/blob/master/android/app/proguard-rules.pro")),(0,o.kt)("h2",{id:"picture-in-picture"},"Picture-in-Picture"),(0,o.kt)("p",null,(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetView"),' will automatically adjust its UI when presented in a\nPicture-in-Picture style scenario, in a rectangle too small to accommodate its\n"full" UI.'),(0,o.kt)("h2",{id:"dropbox-integration"},"Dropbox integration"),(0,o.kt)("p",null,"To setup the Dropbox integration, follow these steps:"),(0,o.kt)("ol",null,(0,o.kt)("li",{parentName:"ol"},"Add the following to the app's AndroidManifest.xml and change ",(0,o.kt)("inlineCode",{parentName:"li"},"")," to\nyour Dropbox app key:")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'\n \n \n \n \n \n \n\n')),(0,o.kt)("ol",{start:2},(0,o.kt)("li",{parentName:"ol"},"Add the following to the app's strings.xml and change ",(0,o.kt)("inlineCode",{parentName:"li"},"")," to your\nDropbox app key:")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'\n')))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/6506de4b.ac31cc8d.js b/assets/js/6506de4b.fc39c370.js similarity index 99% rename from assets/js/6506de4b.ac31cc8d.js rename to assets/js/6506de4b.fc39c370.js index 778650bae..ebd89800b 100644 --- a/assets/js/6506de4b.ac31cc8d.js +++ b/assets/js/6506de4b.fc39c370.js @@ -1 +1 @@ -"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[2987],{3905:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>h});var i=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function r(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=i.createContext({}),p=function(e){var t=i.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},d=function(e){var t=p(e.components);return i.createElement(s.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},u=i.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,s=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),u=p(n),h=a,m=u["".concat(s,".").concat(h)]||u[h]||c[h]||o;return n?i.createElement(m,r(r({ref:t},d),{},{components:n})):i.createElement(m,r({ref:t},d))}));function h(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,r=new Array(o);r[0]=u;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:a,r[1]=l;for(var p=2;p{n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>s,default:()=>h,frontMatter:()=>l,metadata:()=>p,toc:()=>c});var i=n(7462),a=n(3366),o=(n(7294),n(3905)),r=["components"],l={id:"dev-guide-ios-sdk",title:"iOS SDK"},s=void 0,p={unversionedId:"dev-guide/dev-guide-ios-sdk",id:"dev-guide/dev-guide-ios-sdk",title:"iOS SDK",description:"The Jitsi Meet iOS SDK provides the same user experience as the Jitsi Meet app,",source:"@site/docs/dev-guide/ios-sdk.md",sourceDirName:"dev-guide",slug:"/dev-guide/dev-guide-ios-sdk",permalink:"/handbook/docs/dev-guide/dev-guide-ios-sdk",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/dev-guide/ios-sdk.md",tags:[],version:"current",lastUpdatedAt:1694635654,formattedLastUpdatedAt:"Sep 13, 2023",frontMatter:{id:"dev-guide-ios-sdk",title:"iOS SDK"},sidebar:"docs",previous:{title:"Android SDK",permalink:"/handbook/docs/dev-guide/dev-guide-android-sdk"},next:{title:"React Native SDK",permalink:"/handbook/docs/dev-guide/dev-guide-react-native-sdk"}},d={},c=[{value:"Sample applications using the SDK",id:"sample-applications-using-the-sdk",level:2},{value:"Usage",id:"usage",level:2},{value:"Using CocoaPods",id:"using-cocoapods",level:3},{value:"Building it yourself",id:"building-it-yourself",level:3},{value:"API",id:"api",level:2},{value:"JitsiMeetView class",id:"jitsimeetview-class",level:3},{value:"delegate",id:"delegate",level:4},{value:"join:JitsiMeetConferenceOptions",id:"joinjitsimeetconferenceoptions",level:4},{value:"leave",id:"leave",level:4},{value:"hangUp",id:"hangup",level:4},{value:"setAudioMuted",id:"setaudiomuted",level:4},{value:"setVideoMuted",id:"setvideomuted",level:4},{value:"sendEndpointTextMessage",id:"sendendpointtextmessage",level:4},{value:"toggleScreenShare",id:"togglescreenshare",level:4},{value:"openChat",id:"openchat",level:4},{value:"sendChatMessage",id:"sendchatmessage",level:4},{value:"closeChat",id:"closechat",level:4},{value:"retrieveParticipantsInfo",id:"retrieveparticipantsinfo",level:4},{value:"Universal / deep linking",id:"universal--deep-linking",level:4},{value:"JitsiMeetViewDelegate",id:"jitsimeetviewdelegate",level:3},{value:"conferenceJoined",id:"conferencejoined",level:4},{value:"conferenceTerminated",id:"conferenceterminated",level:4},{value:"conferenceWillJoin",id:"conferencewilljoin",level:4},{value:"enterPictureInPicture",id:"enterpictureinpicture",level:4},{value:"participantJoined",id:"participantjoined",level:4},{value:"participantLeft",id:"participantleft",level:4},{value:"audioMutedChanged",id:"audiomutedchanged",level:4},{value:"videoMutedChanged",id:"videomutedchanged",level:4},{value:"endpointTextMessageReceived",id:"endpointtextmessagereceived",level:4},{value:"screenShareToggled",id:"screensharetoggled",level:4},{value:"chatMessageReceived",id:"chatmessagereceived",level:4},{value:"chatToggled",id:"chattoggled",level:4},{value:"readyToClose",id:"readytoclose",level:4},{value:"Picture-in-Picture",id:"picture-in-picture",level:3},{value:"Dropbox integration",id:"dropbox-integration",level:2},{value:"Screen Sharing integration",id:"screen-sharing-integration",level:2},{value:"Creating the Broadcast Upload Extension",id:"creating-the-broadcast-upload-extension",level:3},{value:"Setting up the socket connection",id:"setting-up-the-socket-connection",level:3},{value:"Opening the socket connection",id:"opening-the-socket-connection",level:3},{value:"Sending video frames",id:"sending-video-frames",level:3},{value:"Handling stop screen sharing",id:"handling-stop-screen-sharing",level:3},{value:"TL;DR",id:"tldr",level:3}],u={toc:c};function h(e){var t=e.components,l=(0,a.Z)(e,r);return(0,o.kt)("wrapper",(0,i.Z)({},u,l,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("p",null,"The Jitsi Meet iOS SDK provides the same user experience as the Jitsi Meet app,\nin a customizable way which you can embed in your apps."),(0,o.kt)("admonition",{type:"important"},(0,o.kt)("p",{parentName:"admonition"},"iOS 12.4 or higher is required.")),(0,o.kt)("h2",{id:"sample-applications-using-the-sdk"},"Sample applications using the SDK"),(0,o.kt)("p",null,"If you want to see how easy integrating the Jitsi Meet SDK into a native application is, take a look at the\n",(0,o.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet-sdk-samples#ios"},"sample applications repository"),"."),(0,o.kt)("h2",{id:"usage"},"Usage"),(0,o.kt)("p",null,"There are 2 ways to integrate the SDK into your project:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Using CocoaPods"),(0,o.kt)("li",{parentName:"ul"},"Building it yourself")),(0,o.kt)("h3",{id:"using-cocoapods"},"Using CocoaPods"),(0,o.kt)("p",null,"The recommended way for using the SDK is by using CocoaPods. In order to\ndo so, add the ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetSDK")," dependency to your existing ",(0,o.kt)("inlineCode",{parentName:"p"},"Podfile")," or create\na new one following this example:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"platform :ios, '12.4'\n\nworkspace 'JitsiMeetSDKTest.xcworkspace'\n\ntarget 'JitsiMeetSDKTest' do\n project 'JitsiMeetSDKTest.xcodeproj'\n\n pod 'JitsiMeetSDK'\nend\n")),(0,o.kt)("p",null,"Replace ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetSDKTest")," with your project and target names."),(0,o.kt)("p",null,"Bitcode is not provided in the prebuilt SDK, so turn it off for your project."),(0,o.kt)("p",null,"The SDK uses Swift code, so make sure you select ",(0,o.kt)("inlineCode",{parentName:"p"},"Always Embed Swift Standard Libraries"),"\nin your project."),(0,o.kt)("p",null,"Since the SDK requests camera and microphone access, make sure to include the\nrequired entries for ",(0,o.kt)("inlineCode",{parentName:"p"},"NSCameraUsageDescription")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"NSMicrophoneUsageDescription"),"\nin your ",(0,o.kt)("inlineCode",{parentName:"p"},"Info.plist")," file."),(0,o.kt)("p",null,'In order for app to properly work in the background, select the "audio" and "voip"\nbackground modes.'),(0,o.kt)("p",null,"Last, since the SDK shows and hides the status bar based on the conference state,\nyou may want to set ",(0,o.kt)("inlineCode",{parentName:"p"},"UIViewControllerBasedStatusBarAppearance")," to ",(0,o.kt)("inlineCode",{parentName:"p"},"NO")," in your\n",(0,o.kt)("inlineCode",{parentName:"p"},"Info.plist")," file."),(0,o.kt)("h3",{id:"building-it-yourself"},"Building it yourself"),(0,o.kt)("ol",null,(0,o.kt)("li",{parentName:"ol"},(0,o.kt)("p",{parentName:"li"},"Install all required ",(0,o.kt)("a",{parentName:"p",href:"https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-mobile-jitsi-meet"},"dependencies"),".")),(0,o.kt)("li",{parentName:"ol"},(0,o.kt)("p",{parentName:"li"},"Build it:"))),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"mkdir -p ios/sdk/out\nxcodebuild clean \\\n -workspace ios/jitsi-meet.xcworkspace \\\n -scheme JitsiMeetSDK\nxcodebuild archive \\\n -workspace ios/jitsi-meet.xcworkspace \\\n -scheme JitsiMeetSDK \\\n -configuration Release \\\n -sdk iphonesimulator \\\n -destination='generic/platform=iOS Simulator' \\\n -archivePath ios/sdk/out/ios-simulator \\\n SKIP_INSTALL=NO \\\n BUILD_LIBRARY_FOR_DISTRIBUTION=YES\nxcodebuild archive \\\n -workspace ios/jitsi-meet.xcworkspace \\\n -scheme JitsiMeetSDK \\\n -configuration Release \\\n -sdk iphoneos \\\n -destination='generic/platform=iOS' \\\n -archivePath ios/sdk/out/ios-device \\\n SKIP_INSTALL=NO \\\n BUILD_LIBRARY_FOR_DISTRIBUTION=YES\nxcodebuild -create-xcframework \\\n -framework ios/sdk/out/ios-device.xcarchive/Products/Library/Frameworks/JitsiMeetSDK.framework \\\n -framework ios/sdk/out/ios-simulator.xcarchive/Products/Library/Frameworks/JitsiMeetSDK.framework \\\n -output ios/sdk/out/JitsiMeetSDK.xcframework\n")),(0,o.kt)("p",null,"After successfully building Jitsi Meet SDK for iOS, the resulting XCFramework will be in the ios/sdk/out directory."),(0,o.kt)("p",null,"NOTE: Your app will need to depend on the JitsiWebRTC CocoaPod."),(0,o.kt)("h2",{id:"api"},"API"),(0,o.kt)("p",null,"JitsiMeet is an iOS framework which embodies the whole Jitsi Meet experience and\nmakes it reusable by third-party apps."),(0,o.kt)("p",null,"To get started:"),(0,o.kt)("ol",null,(0,o.kt)("li",{parentName:"ol"},(0,o.kt)("p",{parentName:"li"},"Add a ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetView")," to your app using a Storyboard or Interface Builder,\nfor example.")),(0,o.kt)("li",{parentName:"ol"},(0,o.kt)("p",{parentName:"li"},"Then, once the view has loaded, set the delegate in your controller and load\nthe desired URL:"))),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-objc"},'- (void)viewDidLoad {\n [super viewDidLoad];\n\n JitsiMeetView *jitsiMeetView = (JitsiMeetView *) self.view;\n jitsiMeetView.delegate = self;\n\n JitsiMeetConferenceOptions *options = [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {\n builder.serverURL = [NSURL URLWithString:@"https://meet.jit.si"];\n builder.room = @"test123";\n builder.audioOnly = YES;\n }];\n\n [jitsiMeetView join:options];\n}\n')),(0,o.kt)("h3",{id:"jitsimeetview-class"},"JitsiMeetView class"),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetView")," class is the entry point to the SDK. It a subclass of\n",(0,o.kt)("inlineCode",{parentName:"p"},"UIView")," which renders a full conference in the designated area."),(0,o.kt)("h4",{id:"delegate"},"delegate"),(0,o.kt)("p",null,"Property to get/set the ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetViewDelegate")," on ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetView"),"."),(0,o.kt)("h4",{id:"joinjitsimeetconferenceoptions"},"join:JitsiMeetConferenceOptions"),(0,o.kt)("p",null,"Joins the conference specified by the given options."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-objc"},' JitsiMeetConferenceOptions *options = [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {\n builder.serverURL = [NSURL URLWithString:@"https://meet.jit.si"];\n builder.room = @"test123";\n builder.audioOnly = NO;\n builder.audioMuted = NO;\n builder.videoMuted = NO;\n builder.welcomePageEnabled = NO;\n [builder setConfigOverride:@"requireDisplayName" withBoolean:YES];\n }];\n\n [jitsiMeetView join:options];\n')),(0,o.kt)("h4",{id:"leave"},"leave"),(0,o.kt)("p",null,"Leaves the currently active conference."),(0,o.kt)("h4",{id:"hangup"},"hangUp"),(0,o.kt)("p",null,"The localParticipant leaves the current conference."),(0,o.kt)("h4",{id:"setaudiomuted"},"setAudioMuted"),(0,o.kt)("p",null,"Sets the state of the localParticipant audio muted according to the ",(0,o.kt)("inlineCode",{parentName:"p"},"muted")," parameter."),(0,o.kt)("h4",{id:"setvideomuted"},"setVideoMuted"),(0,o.kt)("p",null,"Sets the state of the localParticipant video muted according to the ",(0,o.kt)("inlineCode",{parentName:"p"},"muted")," parameter."),(0,o.kt)("h4",{id:"sendendpointtextmessage"},"sendEndpointTextMessage"),(0,o.kt)("p",null,"Sends a message via the data channel to one particular participant or to all of them.\nIf the ",(0,o.kt)("inlineCode",{parentName:"p"},"to")," param is empty, the message will be sent to all the participants in the conference."),(0,o.kt)("p",null,"In order to get the participantId, the ",(0,o.kt)("inlineCode",{parentName:"p"},"PARTICIPANT_JOINED")," event should be listened for,\nwhich ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," includes the id and this should be stored somehow."),(0,o.kt)("h4",{id:"togglescreenshare"},"toggleScreenShare"),(0,o.kt)("p",null,"Sets the state of the localParticipant screen sharing according to the ",(0,o.kt)("inlineCode",{parentName:"p"},"enabled")," parameter."),(0,o.kt)("h4",{id:"openchat"},"openChat"),(0,o.kt)("p",null,"Opens the chat dialog. If ",(0,o.kt)("inlineCode",{parentName:"p"},"to")," contains a valid participantId, the private chat with that particular participant will be opened."),(0,o.kt)("h4",{id:"sendchatmessage"},"sendChatMessage"),(0,o.kt)("p",null,"Sends a chat message via to one particular participant or to all of them.\nIf the ",(0,o.kt)("inlineCode",{parentName:"p"},"to")," param is empty, the message will be sent to all the participants in the conference."),(0,o.kt)("p",null,"In order to get the participantId, the ",(0,o.kt)("inlineCode",{parentName:"p"},"PARTICIPANT_JOINED")," event should be listened for,\nwhich ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," includes the id and this should be stored somehow."),(0,o.kt)("h4",{id:"closechat"},"closeChat"),(0,o.kt)("p",null,"Closes the chat dialog."),(0,o.kt)("h4",{id:"retrieveparticipantsinfo"},"retrieveParticipantsInfo"),(0,o.kt)("p",null,"Retrieves the participants information in the completionHandler sent as parameter."),(0,o.kt)("h4",{id:"universal--deep-linking"},"Universal / deep linking"),(0,o.kt)("p",null,"In order to support Universal / deep linking, ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeet")," offers 2 class\nmethods that you app's delegate should call in order for the app to follow those\nlinks."),(0,o.kt)("p",null,"If these functions return NO it means the URL wasn't handled by the SDK. This\nis useful when the host application uses other SDKs which also use linking."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-objc"},"- (BOOL)application:(UIApplication *)application\ncontinueUserActivity:(NSUserActivity *)userActivity\n restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler\n{\n return [[JitsiMeet sharedInstance] application:application\n continueUserActivity:userActivity\n restorationHandler:restorationHandler];\n}\n")),(0,o.kt)("p",null,"And also one of the following:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-objc"},"// See https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623073-application?language=objc\n- (BOOL)application:(UIApplication *)app\n openURL:(NSURL *)url\n options:(NSDictionary *)options {\n return [[JitsiMeet sharedInstance] application:app\n openURL:url\n options: options];\n}\n")),(0,o.kt)("h3",{id:"jitsimeetviewdelegate"},"JitsiMeetViewDelegate"),(0,o.kt)("p",null,"This delegate is optional, and can be set on the ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetView")," instance using\nthe ",(0,o.kt)("inlineCode",{parentName:"p"},"delegate")," property."),(0,o.kt)("p",null,"It provides information about the conference state: was it joined, left, did it\nfail?"),(0,o.kt)("p",null,"All methods in this delegate are optional."),(0,o.kt)("h4",{id:"conferencejoined"},"conferenceJoined"),(0,o.kt)("p",null,"Called when a conference was joined. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"url"),": the conference URL")),(0,o.kt)("h4",{id:"conferenceterminated"},"conferenceTerminated"),(0,o.kt)("p",null,"Called when the active conference ends, be it because of user choice or because of a failure. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the\nfollowing information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"url"),": the conference URL"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"error"),": missing if the conference finished gracefully, otherwise contains the error message")),(0,o.kt)("h4",{id:"conferencewilljoin"},"conferenceWillJoin"),(0,o.kt)("p",null,"Called before a conference is joined. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"url"),": the conference URL")),(0,o.kt)("h4",{id:"enterpictureinpicture"},"enterPictureInPicture"),(0,o.kt)("p",null,"Called when entering Picture-in-Picture is requested by the user. The app should\nnow activate its Picture-in-Picture implementation (and resize the associated\n",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetView"),". The latter will automatically detect its new size and adjust\nits user interface to a variant appropriate for the small size ordinarily\nassociated with Picture-in-Picture.)"),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," dictionary is empty."),(0,o.kt)("h4",{id:"participantjoined"},"participantJoined"),(0,o.kt)("p",null,"Called when a participant has joined the conference. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"email"),": the email of the participant. It may not be set if the remote participant didn't set one."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"name"),": the name of the participant."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"role"),": the role of the participant."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"participantId"),": the id of the participant.")),(0,o.kt)("h4",{id:"participantleft"},"participantLeft"),(0,o.kt)("p",null,"Called when a participant has left the conference. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"participantId"),": the id of the participant that left.")),(0,o.kt)("h4",{id:"audiomutedchanged"},"audioMutedChanged"),(0,o.kt)("p",null,"Called when the local participant's audio is muted or unmuted. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"muted"),": a boolean indicating whether the audio is muted or not.")),(0,o.kt)("h4",{id:"videomutedchanged"},"videoMutedChanged"),(0,o.kt)("p",null,"Called when the local participant's video is muted or unmuted. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"muted"),": an integer indicating whether the video is muted or not. 0 means unmuted, 4 means muted.")),(0,o.kt)("h4",{id:"endpointtextmessagereceived"},"endpointTextMessageReceived"),(0,o.kt)("p",null,"Called when an endpoint text message is received."),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," dictionary contains a ",(0,o.kt)("inlineCode",{parentName:"p"},"senderId")," key with the participantId of the sender and a ",(0,o.kt)("inlineCode",{parentName:"p"},"message")," key with the\ncontent."),(0,o.kt)("h4",{id:"screensharetoggled"},"screenShareToggled"),(0,o.kt)("p",null,"Called when a participant starts or stops sharing his screen."),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," dictionary contains a ",(0,o.kt)("inlineCode",{parentName:"p"},"participantId")," key with the id of the participant and a 'sharing' key with boolean\nvalue."),(0,o.kt)("h4",{id:"chatmessagereceived"},"chatMessageReceived"),(0,o.kt)("p",null,"Called when a chat text message is received. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"senderId"),": the id of the participant that sent the message."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"message"),": the content of the message."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"isPrivate"),": true if the message is private, false otherwise."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"timestamp"),": the (optional) timestamp of the message.")),(0,o.kt)("h4",{id:"chattoggled"},"chatToggled"),(0,o.kt)("p",null,"Called when the chat dialog is opened or closed. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"isOpen"),": true if the chat dialog is open, false otherwise.")),(0,o.kt)("h4",{id:"readytoclose"},"readyToClose"),(0,o.kt)("p",null,"Called when the SDK is ready to be closed. No meeting is happening at this point."),(0,o.kt)("h3",{id:"picture-in-picture"},"Picture-in-Picture"),(0,o.kt)("p",null,(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetView"),' will automatically adjust its UI when presented in a\nPicture-in-Picture style scenario, in a rectangle too small to accommodate its\n"full" UI.'),(0,o.kt)("p",null,"Jitsi Meet SDK does not currently implement native Picture-in-Picture on iOS. If\ndesired, apps need to implement non-native Picture-in-Picture themselves and\nresize ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetView"),"."),(0,o.kt)("p",null,"If ",(0,o.kt)("inlineCode",{parentName:"p"},"delegate")," implements ",(0,o.kt)("inlineCode",{parentName:"p"},"enterPictureInPicture:"),", the in-call toolbar will\nrender a button to afford the user to request entering Picture-in-Picture."),(0,o.kt)("h2",{id:"dropbox-integration"},"Dropbox integration"),(0,o.kt)("p",null,"To setup the Dropbox integration, follow these steps:"),(0,o.kt)("ol",null,(0,o.kt)("li",{parentName:"ol"},"Add the following to the app's Info.plist and change ",(0,o.kt)("inlineCode",{parentName:"li"},"")," to your\nDropbox app key:")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"CFBundleURLTypes\n\n \n CFBundleURLName\n \n CFBundleURLSchemes\n \n db-\n \n \n\nLSApplicationQueriesSchemes\n\n dbapi-2\n dbapi-8-emm\n\n")),(0,o.kt)("ol",{start:2},(0,o.kt)("li",{parentName:"ol"},"Make sure your app calls the Jitsi Meet SDK universal / deep linking delegate methods.")),(0,o.kt)("h2",{id:"screen-sharing-integration"},"Screen Sharing integration"),(0,o.kt)("p",null,"The screen sharing functionality for iOS was added to Jitsi starting with JitsiMeetSDK version 3.3.0. It is available for applications running on iOS 14 or newer."),(0,o.kt)("p",null,"For achieving this we are using the ",(0,o.kt)("inlineCode",{parentName:"p"},"Broadcast Upload Extension")," for capturing the contents of the user's screen. Passing the frames to the RN WebRTC is done using Unix stream-oriented sockets communication, the extension acting as the client and the React Native WebRTC being the server."),(0,o.kt)("p",null,"The following documentation covers the code provided in the ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet-sdk-samples/tree/master/ios/swift-screensharing/JitsiSDKScreenSharingTest"},"sample app"),"."),(0,o.kt)("h3",{id:"creating-the-broadcast-upload-extension"},"Creating the Broadcast Upload Extension"),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"Broadcast Upload Extension")," is one of the App Extensions types defined in iOS and is used for capturing the contents of the user's screen."),(0,o.kt)("p",null,"For creating the extension you need to add a new target to your application, selecting the ",(0,o.kt)("inlineCode",{parentName:"p"},"Broadcast Upload Extension")," template. Fill in the desired name, change the language to Swift, make sure ",(0,o.kt)("inlineCode",{parentName:"p"},"Include UI Extension")," is not selected, as we don't need custom UI for our case, then press Finish (screenshot 1). You will see that a new folder with the extension's name was added to the project's tree, containing the ",(0,o.kt)("inlineCode",{parentName:"p"},"SampleHandler.swift")," class. Also, make sure to update the ",(0,o.kt)("inlineCode",{parentName:"p"},"Deployment Info"),", for the newly created extension, to iOS 14 or newer. To learn more about creating App Extensions check the ",(0,o.kt)("a",{parentName:"p",href:"https://developer.apple.com/library/archive/documentation/General/Conceptual/ExtensibilityPG/ExtensionCreation.html"},"official documentation"),"."),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"screenshot 1",src:n(1798).Z,title:"screenshot 1",width:"594",height:"402"})),(0,o.kt)("p",null,"With the extension created the next steps are to set up the socket connection, add the functionality for handling the received frames, and send them to RN WebRTC for processing. We will be using the code provided with the sample project for this. Copy ",(0,o.kt)("inlineCode",{parentName:"p"},"SampleUploader.swift"),", ",(0,o.kt)("inlineCode",{parentName:"p"},"SocketConnection.swift"),", ",(0,o.kt)("inlineCode",{parentName:"p"},"DarwinNotificationCenter.swift"),", and ",(0,o.kt)("inlineCode",{parentName:"p"},"Atomic.swift")," files to your extension's folder and make sure they're added to the target."),(0,o.kt)("h3",{id:"setting-up-the-socket-connection"},"Setting up the socket connection"),(0,o.kt)("p",null,"Sending the recorded frames to RN WebRTC is done via Unix SOCK_STREAM sockets. The extension needs to be set up as the client endpoint for this."),(0,o.kt)("p",null,"We will update ",(0,o.kt)("inlineCode",{parentName:"p"},"SampleHandler.swift")," to initiate the socket connection with RN WebRTC, using the ",(0,o.kt)("inlineCode",{parentName:"p"},"SocketConnection")," class. But before, we have to set up the file that the sockets will use for communication."),(0,o.kt)("p",null,"Even though an app extension bundle is nested within its containing app\u2019s bundle, the running app extension and containing app have no direct access to each other\u2019s containers. We will address this by enabling data sharing. To enable data sharing, use Xcode or the Developer portal to enable app groups for the containing app and its contained app extensions. Next, register the app group in the portal and specify the app group to use in the containing app. To learn about working with app groups, see ",(0,o.kt)("a",{parentName:"p",href:"https://developer.apple.com/library/archive/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html#//apple_ref/doc/uid/TP40011195-CH4-SW19"},"Adding an App to an App Group"),"."),(0,o.kt)("p",null,"Now, add a ",(0,o.kt)("inlineCode",{parentName:"p"},"private var socketFilePath: String")," to your ",(0,o.kt)("inlineCode",{parentName:"p"},"SampleHandler")," class and set it up with a shared file named ",(0,o.kt)("inlineCode",{parentName:"p"},"rtc_SSFD"),", using the newly registered app group, like this:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'private enum Constants {\n static let appGroupIdentifier = "my.custom.app.group"\n}\n\nprivate var socketFilePath: String {\n let sharedContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: Constants.appGroupIdentifier)\n \n return sharedContainer?.appendingPathComponent("rtc_SSFD").path ?? ""\n}\n')),(0,o.kt)("p",null,"Next, we will configure the ",(0,o.kt)("inlineCode",{parentName:"p"},"SocketConnection")," to use the shared file. Add a ",(0,o.kt)("inlineCode",{parentName:"p"},"private var clientConnection: SocketConnection?")," to the ",(0,o.kt)("inlineCode",{parentName:"p"},"SampleHandler")," class and override ",(0,o.kt)("inlineCode",{parentName:"p"},"init")," to set it up, like this:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"override init() {\n super.init()\n if let connection = SocketConnection(filePath: socketFilePath) {\n clientConnection = connection\n }\n} \n")),(0,o.kt)("p",null,"In order for this to work, the RN WebRTC end needs to know about the app group identifier we have configured the app with. We are doing this by adding a new key named ",(0,o.kt)("inlineCode",{parentName:"p"},"RTCAppGroupIdentifier")," to the app's ",(0,o.kt)("inlineCode",{parentName:"p"},"Info.plist")," with the app group identifier as the value."),(0,o.kt)("h3",{id:"opening-the-socket-connection"},"Opening the socket connection"),(0,o.kt)("p",null,"For starting screen sharing JitsiMeet SDK provides the UI to present the ",(0,o.kt)("inlineCode",{parentName:"p"},"RPSystemBroadcastPickerView")," to the user. By default, the picker will display a list of all the available broadcast providers. In order to limit the picker to our particular broadcast provider, we have to set ",(0,o.kt)("inlineCode",{parentName:"p"},"preferredExtension")," to the bundle identifier of the broadcast extension. We are doing this by adding a new key named ",(0,o.kt)("inlineCode",{parentName:"p"},"RTCScreenSharingExtension")," to the app's Info.plist and setting the broadcast extension bundle identifier as the value."),(0,o.kt)("p",null,"Once screen recording has started ReplayKit invokes the methods to handle video buffers, as well as the methods to handle starting and stopping the broadcast, from the ",(0,o.kt)("inlineCode",{parentName:"p"},"SampleHandler")," class. The ",(0,o.kt)("inlineCode",{parentName:"p"},"broadcastStarted(withSetupInfo:)")," method is our entry point for opening the socket connection with the RN WebRTC server. To do this we have to post the ",(0,o.kt)("inlineCode",{parentName:"p"},"broadcastStarted")," notification the server is listening for, in order to start the connection, and we are ready to connect. Add a new method ",(0,o.kt)("inlineCode",{parentName:"p"},"openConnection()")," to the ",(0,o.kt)("inlineCode",{parentName:"p"},"SampleHandler")," class which will repeatedly attempt connecting to the server, for cases when the server connection start is delayed:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'func openConnection() {\n let queue = DispatchQueue(label: "broadcast.connectTimer")\n let timer = DispatchSource.makeTimerSource(queue: queue)\n timer.schedule(deadline: .now(), repeating: .milliseconds(100), leeway: .milliseconds(500))\n timer.setEventHandler { [weak self] in\n guard self?.clientConnection?.open() == true else {\n return\n }\n \n timer.cancel()\n }\n \n timer.resume()\n}\n')),(0,o.kt)("p",null,"Next, update the ",(0,o.kt)("inlineCode",{parentName:"p"},"broadcastStarted(withSetupInfo:)")," method to post the notification and connect:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"override func broadcastStarted(withSetupInfo setupInfo: [String: NSObject]?) {\n DarwinNotificationCenter.shared.postNotification(.broadcastStarted)\n openConnection()\n}\n")),(0,o.kt)("p",null,(0,o.kt)("inlineCode",{parentName:"p"},"DarwinNotificationCenter")," is a simple helper class for broadcasting system-wide notifications, instead of delivering only within a single program, as ",(0,o.kt)("inlineCode",{parentName:"p"},"NSNotificationCenter")," does. This mechanism allows the app to register for notifications sent from the extension."),(0,o.kt)("p",null,"Now we are ready to start sending video frames."),(0,o.kt)("h3",{id:"sending-video-frames"},"Sending video frames"),(0,o.kt)("p",null,"RN WebRTC is designed to work with jpeg encoded images framed in a ",(0,o.kt)("inlineCode",{parentName:"p"},"CFHTTPMessage")," object. The following header fields are required:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"Content-Length")," - the size of the jpeg data"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"Buffer-Width")," - the width of the buffer, in pixels"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"Buffer-Height")," - the buffer height, in pixels"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"Buffer-Orientation")," - the value for the ",(0,o.kt)("inlineCode",{parentName:"li"},"RPVideoSampleOrientationKey")," that describes the video orientation.")),(0,o.kt)("p",null,"We are going to prepare and send our video frames using the ",(0,o.kt)("inlineCode",{parentName:"p"},"SampleUploader")," class. Add a new ",(0,o.kt)("inlineCode",{parentName:"p"},"private var uploader: SampleUploader?")," to the SampleHandler class and update ",(0,o.kt)("inlineCode",{parentName:"p"},"init()")," to initialize it:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"override init() {\n super.init()\n if let connection = SocketConnection(filePath: socketFilePath) {\n clientConnection = connection\n uploader = SampleUploader(connection: connection)\n }\n}\n")),(0,o.kt)("p",null,"Next, we are going to update the ",(0,o.kt)("inlineCode",{parentName:"p"},"processSampleBuffer(_ sampleBuffer: CMSampleBuffer, with sampleBufferType: RPSampleBufferType)")," method to send our video frames. For performance reasons, we'll also implement a very simple mechanism for adjusting the frame rate by using every third frame. Add a new ",(0,o.kt)("inlineCode",{parentName:"p"},"private var frameCount = 0")," and update the above-mentioned method like this:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"override func processSampleBuffer(_ sampleBuffer: CMSampleBuffer, with sampleBufferType: RPSampleBufferType) {\n switch sampleBufferType {\n case .video:\n // very simple mechanism for adjusting frame rate by using every third frame\n frameCount += 1\n if frameCount % 3 == 0 {\n uploader?.send(sample: sampleBuffer)\n }\n default:\n break\n }\n}\n")),(0,o.kt)("p",null,"Also, update ",(0,o.kt)("inlineCode",{parentName:"p"},"broadcastStarted(withSetupInfo setupInfo: [String: NSObject]?)")," to reset the ",(0,o.kt)("inlineCode",{parentName:"p"},"frameCount")," every time screen sharing is started:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"override func broadcastStarted(withSetupInfo setupInfo: [String: NSObject]?) {\n frameCount = 0\n \n DarwinNotificationCenter.shared.postNotification(.broadcastStarted)\n openConnection()\n}\n")),(0,o.kt)("p",null,"With this, we've concluded sending the video frames and we can move to the last step, handling stop screen sharing."),(0,o.kt)("h3",{id:"handling-stop-screen-sharing"},"Handling stop screen sharing"),(0,o.kt)("p",null,"Besides the in-meeting UI (screenshot 2), ReplayKit integration with iOS provides support for stopping screen recording outside of the app's control, from the status bar (screenshot 3) or using the Control Center (screenshot 4)."),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"ios-screensharing",src:n(1082).Z,title:"screenshot 2",width:"226",height:"487"})," ",(0,o.kt)("img",{alt:"ios-screensharing",src:n(6195).Z,title:"screenshot 3",width:"226",height:"487"})," ",(0,o.kt)("img",{alt:"ios-screensharing",src:n(9166).Z,title:"screenshot 4",width:"226",height:"487"})," "),(0,o.kt)("p",null,"Any of these actions will trigger ",(0,o.kt)("inlineCode",{parentName:"p"},"broadcastFinished")," in our ",(0,o.kt)("inlineCode",{parentName:"p"},"SampleHandler")," implementation. This is our entry point for closing the connection and cleaning up. We will update ",(0,o.kt)("inlineCode",{parentName:"p"},"broadcastFinished")," to post a ",(0,o.kt)("inlineCode",{parentName:"p"},"DarwinNotification.broadcastStopped")," system-wide notification and close the connection:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"override func broadcastFinished() {\n DarwinNotificationCenter.shared.postNotification(.broadcastStopped)\n clientConnection?.close()\n}\n")),(0,o.kt)("p",null,"Another scenario we need to take care of is when the server connection is dropped, like when leaving a meeting while screen sharing or an error is encountered. We will address this by handling ",(0,o.kt)("inlineCode",{parentName:"p"},"clientConnection.didClose")," event. Add a new method ",(0,o.kt)("inlineCode",{parentName:"p"},"setupConnection")," to the ",(0,o.kt)("inlineCode",{parentName:"p"},"SampleHandler")," class and update ",(0,o.kt)("inlineCode",{parentName:"p"},"init")," to call it:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'func setupConnection() {\n clientConnection?.didClose = { [weak self] error in \n if let error = error {\n self?.finishBroadcastWithError(error)\n } else {\n // the displayed failure message is more user friendly when using NSError instead of Error\n let JMScreenSharingStopped = 10001\n let customError = NSError(domain: RPRecordingErrorDomain, code: JMScreenSharingStopped, userInfo: [NSLocalizedDescriptionKey: "Screen sharing stopped"])\n self?.finishBroadcastWithError(customError)\n }\n }\n}\n\noverride init() {\n super.init()\n if let connection = SocketConnection(filePath: socketFilePath) {\n clientConnection = connection\n setupConnection()\n \n uploader = SampleUploader(connection: connection)\n }\n}\n')),(0,o.kt)("p",null,"Now, that we are done writing the implementation, we just need to enable the functionality in Jitsi. We are doing this by configuring ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetConferenceOptionsBuilder")," with the ",(0,o.kt)("inlineCode",{parentName:"p"},"ios.screensharing.enabled feature")," flag, like this:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'let options = JitsiMeetConferenceOptions.fromBuilder { [weak self] builder in\n ...\n builder.setFeatureFlag("ios.screensharing.enabled", withBoolean: true)\n}\nmeetView.join(options)\n')),(0,o.kt)("p",null,"Finally, we are ready to test the implementation. Before doing so, make sure voip is added to ",(0,o.kt)("inlineCode",{parentName:"p"},"UIBackgroundModes"),", in the app's ",(0,o.kt)("inlineCode",{parentName:"p"},"Info.playlist"),", in order to work when the app is in the background."),(0,o.kt)("h3",{id:"tldr"},"TL;DR"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Add a ",(0,o.kt)("inlineCode",{parentName:"li"},"Broadcast Upload Extension"),", without UI, to your app. Update deployment info to run in iOS 14 or newer."),(0,o.kt)("li",{parentName:"ul"},"Copy ",(0,o.kt)("inlineCode",{parentName:"li"},"SampleUploader.swift"),", ",(0,o.kt)("inlineCode",{parentName:"li"},"SocketConnection.swift"),", ",(0,o.kt)("inlineCode",{parentName:"li"},"DarwinNotificationCenter.swift")," and ",(0,o.kt)("inlineCode",{parentName:"li"},"Atomic.swift")," files from the sample project to your extension. Make sure they are added to the extension's target."),(0,o.kt)("li",{parentName:"ul"},"Add both the app and the extension to the same App Group. Next, add the app group id value to the app's ",(0,o.kt)("inlineCode",{parentName:"li"},"Info.plist")," for the ",(0,o.kt)("inlineCode",{parentName:"li"},"RTCAppGroupIdentifier")," key."),(0,o.kt)("li",{parentName:"ul"},"Add a new key ",(0,o.kt)("inlineCode",{parentName:"li"},"RTCScreenSharingExtension")," to the app's ",(0,o.kt)("inlineCode",{parentName:"li"},"Info.plist")," with the extension's ",(0,o.kt)("inlineCode",{parentName:"li"},"Bundle Identifier")," as the value."),(0,o.kt)("li",{parentName:"ul"},"Update ",(0,o.kt)("inlineCode",{parentName:"li"},"SampleHandler.swift")," with the code from the sample project. Update ",(0,o.kt)("inlineCode",{parentName:"li"},"appGroupIdentifier")," constant with the App Group name your app and extension are both registered to."),(0,o.kt)("li",{parentName:"ul"},"Update ",(0,o.kt)("inlineCode",{parentName:"li"},"JitsiMeetConferenceOptions")," to enable screen sharing using the ",(0,o.kt)("inlineCode",{parentName:"li"},"ios.screensharing.enabled")," feature flag."),(0,o.kt)("li",{parentName:"ul"},"Make sure ",(0,o.kt)("inlineCode",{parentName:"li"},"voip")," is added to ",(0,o.kt)("inlineCode",{parentName:"li"},"UIBackgroundModes"),", in the app's ",(0,o.kt)("inlineCode",{parentName:"li"},"Info.plist"),", in order to work when the app is in the background.")))}h.isMDXComponent=!0},1798:(e,t,n)=>{n.d(t,{Z:()=>i});const i=n.p+"assets/images/iOS_screensharing_1-9fc051cab7c6c45813ac86a09d9be7f8.png"},1082:(e,t,n)=>{n.d(t,{Z:()=>i});const i=n.p+"assets/images/iOS_screensharing_2-e4305e7a7935b01689f316310cae41e9.png"},6195:(e,t,n)=>{n.d(t,{Z:()=>i});const i=n.p+"assets/images/iOS_screensharing_3-83343a2407deba42d29b363470fa4680.png"},9166:(e,t,n)=>{n.d(t,{Z:()=>i});const i=n.p+"assets/images/iOS_screensharing_4-985c0a05d859624dab3201410a425ca1.png"}}]); \ No newline at end of file +"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[2987],{3905:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>h});var i=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function r(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=i.createContext({}),p=function(e){var t=i.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},d=function(e){var t=p(e.components);return i.createElement(s.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},u=i.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,s=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),u=p(n),h=a,m=u["".concat(s,".").concat(h)]||u[h]||c[h]||o;return n?i.createElement(m,r(r({ref:t},d),{},{components:n})):i.createElement(m,r({ref:t},d))}));function h(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,r=new Array(o);r[0]=u;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:a,r[1]=l;for(var p=2;p{n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>s,default:()=>h,frontMatter:()=>l,metadata:()=>p,toc:()=>c});var i=n(7462),a=n(3366),o=(n(7294),n(3905)),r=["components"],l={id:"dev-guide-ios-sdk",title:"iOS SDK"},s=void 0,p={unversionedId:"dev-guide/dev-guide-ios-sdk",id:"dev-guide/dev-guide-ios-sdk",title:"iOS SDK",description:"The Jitsi Meet iOS SDK provides the same user experience as the Jitsi Meet app,",source:"@site/docs/dev-guide/ios-sdk.md",sourceDirName:"dev-guide",slug:"/dev-guide/dev-guide-ios-sdk",permalink:"/handbook/docs/dev-guide/dev-guide-ios-sdk",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/dev-guide/ios-sdk.md",tags:[],version:"current",lastUpdatedAt:1695203964,formattedLastUpdatedAt:"Sep 20, 2023",frontMatter:{id:"dev-guide-ios-sdk",title:"iOS SDK"},sidebar:"docs",previous:{title:"Android SDK",permalink:"/handbook/docs/dev-guide/dev-guide-android-sdk"},next:{title:"React Native SDK",permalink:"/handbook/docs/dev-guide/dev-guide-react-native-sdk"}},d={},c=[{value:"Sample applications using the SDK",id:"sample-applications-using-the-sdk",level:2},{value:"Usage",id:"usage",level:2},{value:"Using CocoaPods",id:"using-cocoapods",level:3},{value:"Building it yourself",id:"building-it-yourself",level:3},{value:"API",id:"api",level:2},{value:"JitsiMeetView class",id:"jitsimeetview-class",level:3},{value:"delegate",id:"delegate",level:4},{value:"join:JitsiMeetConferenceOptions",id:"joinjitsimeetconferenceoptions",level:4},{value:"leave",id:"leave",level:4},{value:"hangUp",id:"hangup",level:4},{value:"setAudioMuted",id:"setaudiomuted",level:4},{value:"setVideoMuted",id:"setvideomuted",level:4},{value:"sendEndpointTextMessage",id:"sendendpointtextmessage",level:4},{value:"toggleScreenShare",id:"togglescreenshare",level:4},{value:"openChat",id:"openchat",level:4},{value:"sendChatMessage",id:"sendchatmessage",level:4},{value:"closeChat",id:"closechat",level:4},{value:"retrieveParticipantsInfo",id:"retrieveparticipantsinfo",level:4},{value:"Universal / deep linking",id:"universal--deep-linking",level:4},{value:"JitsiMeetViewDelegate",id:"jitsimeetviewdelegate",level:3},{value:"conferenceJoined",id:"conferencejoined",level:4},{value:"conferenceTerminated",id:"conferenceterminated",level:4},{value:"conferenceWillJoin",id:"conferencewilljoin",level:4},{value:"enterPictureInPicture",id:"enterpictureinpicture",level:4},{value:"participantJoined",id:"participantjoined",level:4},{value:"participantLeft",id:"participantleft",level:4},{value:"audioMutedChanged",id:"audiomutedchanged",level:4},{value:"videoMutedChanged",id:"videomutedchanged",level:4},{value:"endpointTextMessageReceived",id:"endpointtextmessagereceived",level:4},{value:"screenShareToggled",id:"screensharetoggled",level:4},{value:"chatMessageReceived",id:"chatmessagereceived",level:4},{value:"chatToggled",id:"chattoggled",level:4},{value:"readyToClose",id:"readytoclose",level:4},{value:"Picture-in-Picture",id:"picture-in-picture",level:3},{value:"Dropbox integration",id:"dropbox-integration",level:2},{value:"Screen Sharing integration",id:"screen-sharing-integration",level:2},{value:"Creating the Broadcast Upload Extension",id:"creating-the-broadcast-upload-extension",level:3},{value:"Setting up the socket connection",id:"setting-up-the-socket-connection",level:3},{value:"Opening the socket connection",id:"opening-the-socket-connection",level:3},{value:"Sending video frames",id:"sending-video-frames",level:3},{value:"Handling stop screen sharing",id:"handling-stop-screen-sharing",level:3},{value:"TL;DR",id:"tldr",level:3}],u={toc:c};function h(e){var t=e.components,l=(0,a.Z)(e,r);return(0,o.kt)("wrapper",(0,i.Z)({},u,l,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("p",null,"The Jitsi Meet iOS SDK provides the same user experience as the Jitsi Meet app,\nin a customizable way which you can embed in your apps."),(0,o.kt)("admonition",{type:"important"},(0,o.kt)("p",{parentName:"admonition"},"iOS 12.4 or higher is required.")),(0,o.kt)("h2",{id:"sample-applications-using-the-sdk"},"Sample applications using the SDK"),(0,o.kt)("p",null,"If you want to see how easy integrating the Jitsi Meet SDK into a native application is, take a look at the\n",(0,o.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet-sdk-samples#ios"},"sample applications repository"),"."),(0,o.kt)("h2",{id:"usage"},"Usage"),(0,o.kt)("p",null,"There are 2 ways to integrate the SDK into your project:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Using CocoaPods"),(0,o.kt)("li",{parentName:"ul"},"Building it yourself")),(0,o.kt)("h3",{id:"using-cocoapods"},"Using CocoaPods"),(0,o.kt)("p",null,"The recommended way for using the SDK is by using CocoaPods. In order to\ndo so, add the ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetSDK")," dependency to your existing ",(0,o.kt)("inlineCode",{parentName:"p"},"Podfile")," or create\na new one following this example:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"platform :ios, '12.4'\n\nworkspace 'JitsiMeetSDKTest.xcworkspace'\n\ntarget 'JitsiMeetSDKTest' do\n project 'JitsiMeetSDKTest.xcodeproj'\n\n pod 'JitsiMeetSDK'\nend\n")),(0,o.kt)("p",null,"Replace ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetSDKTest")," with your project and target names."),(0,o.kt)("p",null,"Bitcode is not provided in the prebuilt SDK, so turn it off for your project."),(0,o.kt)("p",null,"The SDK uses Swift code, so make sure you select ",(0,o.kt)("inlineCode",{parentName:"p"},"Always Embed Swift Standard Libraries"),"\nin your project."),(0,o.kt)("p",null,"Since the SDK requests camera and microphone access, make sure to include the\nrequired entries for ",(0,o.kt)("inlineCode",{parentName:"p"},"NSCameraUsageDescription")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"NSMicrophoneUsageDescription"),"\nin your ",(0,o.kt)("inlineCode",{parentName:"p"},"Info.plist")," file."),(0,o.kt)("p",null,'In order for app to properly work in the background, select the "audio" and "voip"\nbackground modes.'),(0,o.kt)("p",null,"Last, since the SDK shows and hides the status bar based on the conference state,\nyou may want to set ",(0,o.kt)("inlineCode",{parentName:"p"},"UIViewControllerBasedStatusBarAppearance")," to ",(0,o.kt)("inlineCode",{parentName:"p"},"NO")," in your\n",(0,o.kt)("inlineCode",{parentName:"p"},"Info.plist")," file."),(0,o.kt)("h3",{id:"building-it-yourself"},"Building it yourself"),(0,o.kt)("ol",null,(0,o.kt)("li",{parentName:"ol"},(0,o.kt)("p",{parentName:"li"},"Install all required ",(0,o.kt)("a",{parentName:"p",href:"https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-mobile-jitsi-meet"},"dependencies"),".")),(0,o.kt)("li",{parentName:"ol"},(0,o.kt)("p",{parentName:"li"},"Build it:"))),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"mkdir -p ios/sdk/out\nxcodebuild clean \\\n -workspace ios/jitsi-meet.xcworkspace \\\n -scheme JitsiMeetSDK\nxcodebuild archive \\\n -workspace ios/jitsi-meet.xcworkspace \\\n -scheme JitsiMeetSDK \\\n -configuration Release \\\n -sdk iphonesimulator \\\n -destination='generic/platform=iOS Simulator' \\\n -archivePath ios/sdk/out/ios-simulator \\\n SKIP_INSTALL=NO \\\n BUILD_LIBRARY_FOR_DISTRIBUTION=YES\nxcodebuild archive \\\n -workspace ios/jitsi-meet.xcworkspace \\\n -scheme JitsiMeetSDK \\\n -configuration Release \\\n -sdk iphoneos \\\n -destination='generic/platform=iOS' \\\n -archivePath ios/sdk/out/ios-device \\\n SKIP_INSTALL=NO \\\n BUILD_LIBRARY_FOR_DISTRIBUTION=YES\nxcodebuild -create-xcframework \\\n -framework ios/sdk/out/ios-device.xcarchive/Products/Library/Frameworks/JitsiMeetSDK.framework \\\n -framework ios/sdk/out/ios-simulator.xcarchive/Products/Library/Frameworks/JitsiMeetSDK.framework \\\n -output ios/sdk/out/JitsiMeetSDK.xcframework\n")),(0,o.kt)("p",null,"After successfully building Jitsi Meet SDK for iOS, the resulting XCFramework will be in the ios/sdk/out directory."),(0,o.kt)("p",null,"NOTE: Your app will need to depend on the JitsiWebRTC CocoaPod."),(0,o.kt)("h2",{id:"api"},"API"),(0,o.kt)("p",null,"JitsiMeet is an iOS framework which embodies the whole Jitsi Meet experience and\nmakes it reusable by third-party apps."),(0,o.kt)("p",null,"To get started:"),(0,o.kt)("ol",null,(0,o.kt)("li",{parentName:"ol"},(0,o.kt)("p",{parentName:"li"},"Add a ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetView")," to your app using a Storyboard or Interface Builder,\nfor example.")),(0,o.kt)("li",{parentName:"ol"},(0,o.kt)("p",{parentName:"li"},"Then, once the view has loaded, set the delegate in your controller and load\nthe desired URL:"))),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-objc"},'- (void)viewDidLoad {\n [super viewDidLoad];\n\n JitsiMeetView *jitsiMeetView = (JitsiMeetView *) self.view;\n jitsiMeetView.delegate = self;\n\n JitsiMeetConferenceOptions *options = [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {\n builder.serverURL = [NSURL URLWithString:@"https://meet.jit.si"];\n builder.room = @"test123";\n builder.audioOnly = YES;\n }];\n\n [jitsiMeetView join:options];\n}\n')),(0,o.kt)("h3",{id:"jitsimeetview-class"},"JitsiMeetView class"),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetView")," class is the entry point to the SDK. It a subclass of\n",(0,o.kt)("inlineCode",{parentName:"p"},"UIView")," which renders a full conference in the designated area."),(0,o.kt)("h4",{id:"delegate"},"delegate"),(0,o.kt)("p",null,"Property to get/set the ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetViewDelegate")," on ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetView"),"."),(0,o.kt)("h4",{id:"joinjitsimeetconferenceoptions"},"join:JitsiMeetConferenceOptions"),(0,o.kt)("p",null,"Joins the conference specified by the given options."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-objc"},' JitsiMeetConferenceOptions *options = [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {\n builder.serverURL = [NSURL URLWithString:@"https://meet.jit.si"];\n builder.room = @"test123";\n builder.audioOnly = NO;\n builder.audioMuted = NO;\n builder.videoMuted = NO;\n builder.welcomePageEnabled = NO;\n [builder setConfigOverride:@"requireDisplayName" withBoolean:YES];\n }];\n\n [jitsiMeetView join:options];\n')),(0,o.kt)("h4",{id:"leave"},"leave"),(0,o.kt)("p",null,"Leaves the currently active conference."),(0,o.kt)("h4",{id:"hangup"},"hangUp"),(0,o.kt)("p",null,"The localParticipant leaves the current conference."),(0,o.kt)("h4",{id:"setaudiomuted"},"setAudioMuted"),(0,o.kt)("p",null,"Sets the state of the localParticipant audio muted according to the ",(0,o.kt)("inlineCode",{parentName:"p"},"muted")," parameter."),(0,o.kt)("h4",{id:"setvideomuted"},"setVideoMuted"),(0,o.kt)("p",null,"Sets the state of the localParticipant video muted according to the ",(0,o.kt)("inlineCode",{parentName:"p"},"muted")," parameter."),(0,o.kt)("h4",{id:"sendendpointtextmessage"},"sendEndpointTextMessage"),(0,o.kt)("p",null,"Sends a message via the data channel to one particular participant or to all of them.\nIf the ",(0,o.kt)("inlineCode",{parentName:"p"},"to")," param is empty, the message will be sent to all the participants in the conference."),(0,o.kt)("p",null,"In order to get the participantId, the ",(0,o.kt)("inlineCode",{parentName:"p"},"PARTICIPANT_JOINED")," event should be listened for,\nwhich ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," includes the id and this should be stored somehow."),(0,o.kt)("h4",{id:"togglescreenshare"},"toggleScreenShare"),(0,o.kt)("p",null,"Sets the state of the localParticipant screen sharing according to the ",(0,o.kt)("inlineCode",{parentName:"p"},"enabled")," parameter."),(0,o.kt)("h4",{id:"openchat"},"openChat"),(0,o.kt)("p",null,"Opens the chat dialog. If ",(0,o.kt)("inlineCode",{parentName:"p"},"to")," contains a valid participantId, the private chat with that particular participant will be opened."),(0,o.kt)("h4",{id:"sendchatmessage"},"sendChatMessage"),(0,o.kt)("p",null,"Sends a chat message via to one particular participant or to all of them.\nIf the ",(0,o.kt)("inlineCode",{parentName:"p"},"to")," param is empty, the message will be sent to all the participants in the conference."),(0,o.kt)("p",null,"In order to get the participantId, the ",(0,o.kt)("inlineCode",{parentName:"p"},"PARTICIPANT_JOINED")," event should be listened for,\nwhich ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," includes the id and this should be stored somehow."),(0,o.kt)("h4",{id:"closechat"},"closeChat"),(0,o.kt)("p",null,"Closes the chat dialog."),(0,o.kt)("h4",{id:"retrieveparticipantsinfo"},"retrieveParticipantsInfo"),(0,o.kt)("p",null,"Retrieves the participants information in the completionHandler sent as parameter."),(0,o.kt)("h4",{id:"universal--deep-linking"},"Universal / deep linking"),(0,o.kt)("p",null,"In order to support Universal / deep linking, ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeet")," offers 2 class\nmethods that you app's delegate should call in order for the app to follow those\nlinks."),(0,o.kt)("p",null,"If these functions return NO it means the URL wasn't handled by the SDK. This\nis useful when the host application uses other SDKs which also use linking."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-objc"},"- (BOOL)application:(UIApplication *)application\ncontinueUserActivity:(NSUserActivity *)userActivity\n restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler\n{\n return [[JitsiMeet sharedInstance] application:application\n continueUserActivity:userActivity\n restorationHandler:restorationHandler];\n}\n")),(0,o.kt)("p",null,"And also one of the following:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-objc"},"// See https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623073-application?language=objc\n- (BOOL)application:(UIApplication *)app\n openURL:(NSURL *)url\n options:(NSDictionary *)options {\n return [[JitsiMeet sharedInstance] application:app\n openURL:url\n options: options];\n}\n")),(0,o.kt)("h3",{id:"jitsimeetviewdelegate"},"JitsiMeetViewDelegate"),(0,o.kt)("p",null,"This delegate is optional, and can be set on the ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetView")," instance using\nthe ",(0,o.kt)("inlineCode",{parentName:"p"},"delegate")," property."),(0,o.kt)("p",null,"It provides information about the conference state: was it joined, left, did it\nfail?"),(0,o.kt)("p",null,"All methods in this delegate are optional."),(0,o.kt)("h4",{id:"conferencejoined"},"conferenceJoined"),(0,o.kt)("p",null,"Called when a conference was joined. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"url"),": the conference URL")),(0,o.kt)("h4",{id:"conferenceterminated"},"conferenceTerminated"),(0,o.kt)("p",null,"Called when the active conference ends, be it because of user choice or because of a failure. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the\nfollowing information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"url"),": the conference URL"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"error"),": missing if the conference finished gracefully, otherwise contains the error message")),(0,o.kt)("h4",{id:"conferencewilljoin"},"conferenceWillJoin"),(0,o.kt)("p",null,"Called before a conference is joined. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"url"),": the conference URL")),(0,o.kt)("h4",{id:"enterpictureinpicture"},"enterPictureInPicture"),(0,o.kt)("p",null,"Called when entering Picture-in-Picture is requested by the user. The app should\nnow activate its Picture-in-Picture implementation (and resize the associated\n",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetView"),". The latter will automatically detect its new size and adjust\nits user interface to a variant appropriate for the small size ordinarily\nassociated with Picture-in-Picture.)"),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," dictionary is empty."),(0,o.kt)("h4",{id:"participantjoined"},"participantJoined"),(0,o.kt)("p",null,"Called when a participant has joined the conference. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"email"),": the email of the participant. It may not be set if the remote participant didn't set one."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"name"),": the name of the participant."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"role"),": the role of the participant."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"participantId"),": the id of the participant.")),(0,o.kt)("h4",{id:"participantleft"},"participantLeft"),(0,o.kt)("p",null,"Called when a participant has left the conference. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"participantId"),": the id of the participant that left.")),(0,o.kt)("h4",{id:"audiomutedchanged"},"audioMutedChanged"),(0,o.kt)("p",null,"Called when the local participant's audio is muted or unmuted. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"muted"),": a boolean indicating whether the audio is muted or not.")),(0,o.kt)("h4",{id:"videomutedchanged"},"videoMutedChanged"),(0,o.kt)("p",null,"Called when the local participant's video is muted or unmuted. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"muted"),": an integer indicating whether the video is muted or not. 0 means unmuted, 4 means muted.")),(0,o.kt)("h4",{id:"endpointtextmessagereceived"},"endpointTextMessageReceived"),(0,o.kt)("p",null,"Called when an endpoint text message is received."),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," dictionary contains a ",(0,o.kt)("inlineCode",{parentName:"p"},"senderId")," key with the participantId of the sender and a ",(0,o.kt)("inlineCode",{parentName:"p"},"message")," key with the\ncontent."),(0,o.kt)("h4",{id:"screensharetoggled"},"screenShareToggled"),(0,o.kt)("p",null,"Called when a participant starts or stops sharing his screen."),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," dictionary contains a ",(0,o.kt)("inlineCode",{parentName:"p"},"participantId")," key with the id of the participant and a 'sharing' key with boolean\nvalue."),(0,o.kt)("h4",{id:"chatmessagereceived"},"chatMessageReceived"),(0,o.kt)("p",null,"Called when a chat text message is received. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"senderId"),": the id of the participant that sent the message."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"message"),": the content of the message."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"isPrivate"),": true if the message is private, false otherwise."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"timestamp"),": the (optional) timestamp of the message.")),(0,o.kt)("h4",{id:"chattoggled"},"chatToggled"),(0,o.kt)("p",null,"Called when the chat dialog is opened or closed. ",(0,o.kt)("inlineCode",{parentName:"p"},"data")," contains the following information:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"isOpen"),": true if the chat dialog is open, false otherwise.")),(0,o.kt)("h4",{id:"readytoclose"},"readyToClose"),(0,o.kt)("p",null,"Called when the SDK is ready to be closed. No meeting is happening at this point."),(0,o.kt)("h3",{id:"picture-in-picture"},"Picture-in-Picture"),(0,o.kt)("p",null,(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetView"),' will automatically adjust its UI when presented in a\nPicture-in-Picture style scenario, in a rectangle too small to accommodate its\n"full" UI.'),(0,o.kt)("p",null,"Jitsi Meet SDK does not currently implement native Picture-in-Picture on iOS. If\ndesired, apps need to implement non-native Picture-in-Picture themselves and\nresize ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetView"),"."),(0,o.kt)("p",null,"If ",(0,o.kt)("inlineCode",{parentName:"p"},"delegate")," implements ",(0,o.kt)("inlineCode",{parentName:"p"},"enterPictureInPicture:"),", the in-call toolbar will\nrender a button to afford the user to request entering Picture-in-Picture."),(0,o.kt)("h2",{id:"dropbox-integration"},"Dropbox integration"),(0,o.kt)("p",null,"To setup the Dropbox integration, follow these steps:"),(0,o.kt)("ol",null,(0,o.kt)("li",{parentName:"ol"},"Add the following to the app's Info.plist and change ",(0,o.kt)("inlineCode",{parentName:"li"},"")," to your\nDropbox app key:")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"CFBundleURLTypes\n\n \n CFBundleURLName\n \n CFBundleURLSchemes\n \n db-\n \n \n\nLSApplicationQueriesSchemes\n\n dbapi-2\n dbapi-8-emm\n\n")),(0,o.kt)("ol",{start:2},(0,o.kt)("li",{parentName:"ol"},"Make sure your app calls the Jitsi Meet SDK universal / deep linking delegate methods.")),(0,o.kt)("h2",{id:"screen-sharing-integration"},"Screen Sharing integration"),(0,o.kt)("p",null,"The screen sharing functionality for iOS was added to Jitsi starting with JitsiMeetSDK version 3.3.0. It is available for applications running on iOS 14 or newer."),(0,o.kt)("p",null,"For achieving this we are using the ",(0,o.kt)("inlineCode",{parentName:"p"},"Broadcast Upload Extension")," for capturing the contents of the user's screen. Passing the frames to the RN WebRTC is done using Unix stream-oriented sockets communication, the extension acting as the client and the React Native WebRTC being the server."),(0,o.kt)("p",null,"The following documentation covers the code provided in the ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/jitsi/jitsi-meet-sdk-samples/tree/master/ios/swift-screensharing/JitsiSDKScreenSharingTest"},"sample app"),"."),(0,o.kt)("h3",{id:"creating-the-broadcast-upload-extension"},"Creating the Broadcast Upload Extension"),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"Broadcast Upload Extension")," is one of the App Extensions types defined in iOS and is used for capturing the contents of the user's screen."),(0,o.kt)("p",null,"For creating the extension you need to add a new target to your application, selecting the ",(0,o.kt)("inlineCode",{parentName:"p"},"Broadcast Upload Extension")," template. Fill in the desired name, change the language to Swift, make sure ",(0,o.kt)("inlineCode",{parentName:"p"},"Include UI Extension")," is not selected, as we don't need custom UI for our case, then press Finish (screenshot 1). You will see that a new folder with the extension's name was added to the project's tree, containing the ",(0,o.kt)("inlineCode",{parentName:"p"},"SampleHandler.swift")," class. Also, make sure to update the ",(0,o.kt)("inlineCode",{parentName:"p"},"Deployment Info"),", for the newly created extension, to iOS 14 or newer. To learn more about creating App Extensions check the ",(0,o.kt)("a",{parentName:"p",href:"https://developer.apple.com/library/archive/documentation/General/Conceptual/ExtensibilityPG/ExtensionCreation.html"},"official documentation"),"."),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"screenshot 1",src:n(1798).Z,title:"screenshot 1",width:"594",height:"402"})),(0,o.kt)("p",null,"With the extension created the next steps are to set up the socket connection, add the functionality for handling the received frames, and send them to RN WebRTC for processing. We will be using the code provided with the sample project for this. Copy ",(0,o.kt)("inlineCode",{parentName:"p"},"SampleUploader.swift"),", ",(0,o.kt)("inlineCode",{parentName:"p"},"SocketConnection.swift"),", ",(0,o.kt)("inlineCode",{parentName:"p"},"DarwinNotificationCenter.swift"),", and ",(0,o.kt)("inlineCode",{parentName:"p"},"Atomic.swift")," files to your extension's folder and make sure they're added to the target."),(0,o.kt)("h3",{id:"setting-up-the-socket-connection"},"Setting up the socket connection"),(0,o.kt)("p",null,"Sending the recorded frames to RN WebRTC is done via Unix SOCK_STREAM sockets. The extension needs to be set up as the client endpoint for this."),(0,o.kt)("p",null,"We will update ",(0,o.kt)("inlineCode",{parentName:"p"},"SampleHandler.swift")," to initiate the socket connection with RN WebRTC, using the ",(0,o.kt)("inlineCode",{parentName:"p"},"SocketConnection")," class. But before, we have to set up the file that the sockets will use for communication."),(0,o.kt)("p",null,"Even though an app extension bundle is nested within its containing app\u2019s bundle, the running app extension and containing app have no direct access to each other\u2019s containers. We will address this by enabling data sharing. To enable data sharing, use Xcode or the Developer portal to enable app groups for the containing app and its contained app extensions. Next, register the app group in the portal and specify the app group to use in the containing app. To learn about working with app groups, see ",(0,o.kt)("a",{parentName:"p",href:"https://developer.apple.com/library/archive/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html#//apple_ref/doc/uid/TP40011195-CH4-SW19"},"Adding an App to an App Group"),"."),(0,o.kt)("p",null,"Now, add a ",(0,o.kt)("inlineCode",{parentName:"p"},"private var socketFilePath: String")," to your ",(0,o.kt)("inlineCode",{parentName:"p"},"SampleHandler")," class and set it up with a shared file named ",(0,o.kt)("inlineCode",{parentName:"p"},"rtc_SSFD"),", using the newly registered app group, like this:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'private enum Constants {\n static let appGroupIdentifier = "my.custom.app.group"\n}\n\nprivate var socketFilePath: String {\n let sharedContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: Constants.appGroupIdentifier)\n \n return sharedContainer?.appendingPathComponent("rtc_SSFD").path ?? ""\n}\n')),(0,o.kt)("p",null,"Next, we will configure the ",(0,o.kt)("inlineCode",{parentName:"p"},"SocketConnection")," to use the shared file. Add a ",(0,o.kt)("inlineCode",{parentName:"p"},"private var clientConnection: SocketConnection?")," to the ",(0,o.kt)("inlineCode",{parentName:"p"},"SampleHandler")," class and override ",(0,o.kt)("inlineCode",{parentName:"p"},"init")," to set it up, like this:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"override init() {\n super.init()\n if let connection = SocketConnection(filePath: socketFilePath) {\n clientConnection = connection\n }\n} \n")),(0,o.kt)("p",null,"In order for this to work, the RN WebRTC end needs to know about the app group identifier we have configured the app with. We are doing this by adding a new key named ",(0,o.kt)("inlineCode",{parentName:"p"},"RTCAppGroupIdentifier")," to the app's ",(0,o.kt)("inlineCode",{parentName:"p"},"Info.plist")," with the app group identifier as the value."),(0,o.kt)("h3",{id:"opening-the-socket-connection"},"Opening the socket connection"),(0,o.kt)("p",null,"For starting screen sharing JitsiMeet SDK provides the UI to present the ",(0,o.kt)("inlineCode",{parentName:"p"},"RPSystemBroadcastPickerView")," to the user. By default, the picker will display a list of all the available broadcast providers. In order to limit the picker to our particular broadcast provider, we have to set ",(0,o.kt)("inlineCode",{parentName:"p"},"preferredExtension")," to the bundle identifier of the broadcast extension. We are doing this by adding a new key named ",(0,o.kt)("inlineCode",{parentName:"p"},"RTCScreenSharingExtension")," to the app's Info.plist and setting the broadcast extension bundle identifier as the value."),(0,o.kt)("p",null,"Once screen recording has started ReplayKit invokes the methods to handle video buffers, as well as the methods to handle starting and stopping the broadcast, from the ",(0,o.kt)("inlineCode",{parentName:"p"},"SampleHandler")," class. The ",(0,o.kt)("inlineCode",{parentName:"p"},"broadcastStarted(withSetupInfo:)")," method is our entry point for opening the socket connection with the RN WebRTC server. To do this we have to post the ",(0,o.kt)("inlineCode",{parentName:"p"},"broadcastStarted")," notification the server is listening for, in order to start the connection, and we are ready to connect. Add a new method ",(0,o.kt)("inlineCode",{parentName:"p"},"openConnection()")," to the ",(0,o.kt)("inlineCode",{parentName:"p"},"SampleHandler")," class which will repeatedly attempt connecting to the server, for cases when the server connection start is delayed:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'func openConnection() {\n let queue = DispatchQueue(label: "broadcast.connectTimer")\n let timer = DispatchSource.makeTimerSource(queue: queue)\n timer.schedule(deadline: .now(), repeating: .milliseconds(100), leeway: .milliseconds(500))\n timer.setEventHandler { [weak self] in\n guard self?.clientConnection?.open() == true else {\n return\n }\n \n timer.cancel()\n }\n \n timer.resume()\n}\n')),(0,o.kt)("p",null,"Next, update the ",(0,o.kt)("inlineCode",{parentName:"p"},"broadcastStarted(withSetupInfo:)")," method to post the notification and connect:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"override func broadcastStarted(withSetupInfo setupInfo: [String: NSObject]?) {\n DarwinNotificationCenter.shared.postNotification(.broadcastStarted)\n openConnection()\n}\n")),(0,o.kt)("p",null,(0,o.kt)("inlineCode",{parentName:"p"},"DarwinNotificationCenter")," is a simple helper class for broadcasting system-wide notifications, instead of delivering only within a single program, as ",(0,o.kt)("inlineCode",{parentName:"p"},"NSNotificationCenter")," does. This mechanism allows the app to register for notifications sent from the extension."),(0,o.kt)("p",null,"Now we are ready to start sending video frames."),(0,o.kt)("h3",{id:"sending-video-frames"},"Sending video frames"),(0,o.kt)("p",null,"RN WebRTC is designed to work with jpeg encoded images framed in a ",(0,o.kt)("inlineCode",{parentName:"p"},"CFHTTPMessage")," object. The following header fields are required:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"Content-Length")," - the size of the jpeg data"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"Buffer-Width")," - the width of the buffer, in pixels"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"Buffer-Height")," - the buffer height, in pixels"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"Buffer-Orientation")," - the value for the ",(0,o.kt)("inlineCode",{parentName:"li"},"RPVideoSampleOrientationKey")," that describes the video orientation.")),(0,o.kt)("p",null,"We are going to prepare and send our video frames using the ",(0,o.kt)("inlineCode",{parentName:"p"},"SampleUploader")," class. Add a new ",(0,o.kt)("inlineCode",{parentName:"p"},"private var uploader: SampleUploader?")," to the SampleHandler class and update ",(0,o.kt)("inlineCode",{parentName:"p"},"init()")," to initialize it:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"override init() {\n super.init()\n if let connection = SocketConnection(filePath: socketFilePath) {\n clientConnection = connection\n uploader = SampleUploader(connection: connection)\n }\n}\n")),(0,o.kt)("p",null,"Next, we are going to update the ",(0,o.kt)("inlineCode",{parentName:"p"},"processSampleBuffer(_ sampleBuffer: CMSampleBuffer, with sampleBufferType: RPSampleBufferType)")," method to send our video frames. For performance reasons, we'll also implement a very simple mechanism for adjusting the frame rate by using every third frame. Add a new ",(0,o.kt)("inlineCode",{parentName:"p"},"private var frameCount = 0")," and update the above-mentioned method like this:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"override func processSampleBuffer(_ sampleBuffer: CMSampleBuffer, with sampleBufferType: RPSampleBufferType) {\n switch sampleBufferType {\n case .video:\n // very simple mechanism for adjusting frame rate by using every third frame\n frameCount += 1\n if frameCount % 3 == 0 {\n uploader?.send(sample: sampleBuffer)\n }\n default:\n break\n }\n}\n")),(0,o.kt)("p",null,"Also, update ",(0,o.kt)("inlineCode",{parentName:"p"},"broadcastStarted(withSetupInfo setupInfo: [String: NSObject]?)")," to reset the ",(0,o.kt)("inlineCode",{parentName:"p"},"frameCount")," every time screen sharing is started:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"override func broadcastStarted(withSetupInfo setupInfo: [String: NSObject]?) {\n frameCount = 0\n \n DarwinNotificationCenter.shared.postNotification(.broadcastStarted)\n openConnection()\n}\n")),(0,o.kt)("p",null,"With this, we've concluded sending the video frames and we can move to the last step, handling stop screen sharing."),(0,o.kt)("h3",{id:"handling-stop-screen-sharing"},"Handling stop screen sharing"),(0,o.kt)("p",null,"Besides the in-meeting UI (screenshot 2), ReplayKit integration with iOS provides support for stopping screen recording outside of the app's control, from the status bar (screenshot 3) or using the Control Center (screenshot 4)."),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"ios-screensharing",src:n(1082).Z,title:"screenshot 2",width:"226",height:"487"})," ",(0,o.kt)("img",{alt:"ios-screensharing",src:n(6195).Z,title:"screenshot 3",width:"226",height:"487"})," ",(0,o.kt)("img",{alt:"ios-screensharing",src:n(9166).Z,title:"screenshot 4",width:"226",height:"487"})," "),(0,o.kt)("p",null,"Any of these actions will trigger ",(0,o.kt)("inlineCode",{parentName:"p"},"broadcastFinished")," in our ",(0,o.kt)("inlineCode",{parentName:"p"},"SampleHandler")," implementation. This is our entry point for closing the connection and cleaning up. We will update ",(0,o.kt)("inlineCode",{parentName:"p"},"broadcastFinished")," to post a ",(0,o.kt)("inlineCode",{parentName:"p"},"DarwinNotification.broadcastStopped")," system-wide notification and close the connection:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"override func broadcastFinished() {\n DarwinNotificationCenter.shared.postNotification(.broadcastStopped)\n clientConnection?.close()\n}\n")),(0,o.kt)("p",null,"Another scenario we need to take care of is when the server connection is dropped, like when leaving a meeting while screen sharing or an error is encountered. We will address this by handling ",(0,o.kt)("inlineCode",{parentName:"p"},"clientConnection.didClose")," event. Add a new method ",(0,o.kt)("inlineCode",{parentName:"p"},"setupConnection")," to the ",(0,o.kt)("inlineCode",{parentName:"p"},"SampleHandler")," class and update ",(0,o.kt)("inlineCode",{parentName:"p"},"init")," to call it:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'func setupConnection() {\n clientConnection?.didClose = { [weak self] error in \n if let error = error {\n self?.finishBroadcastWithError(error)\n } else {\n // the displayed failure message is more user friendly when using NSError instead of Error\n let JMScreenSharingStopped = 10001\n let customError = NSError(domain: RPRecordingErrorDomain, code: JMScreenSharingStopped, userInfo: [NSLocalizedDescriptionKey: "Screen sharing stopped"])\n self?.finishBroadcastWithError(customError)\n }\n }\n}\n\noverride init() {\n super.init()\n if let connection = SocketConnection(filePath: socketFilePath) {\n clientConnection = connection\n setupConnection()\n \n uploader = SampleUploader(connection: connection)\n }\n}\n')),(0,o.kt)("p",null,"Now, that we are done writing the implementation, we just need to enable the functionality in Jitsi. We are doing this by configuring ",(0,o.kt)("inlineCode",{parentName:"p"},"JitsiMeetConferenceOptionsBuilder")," with the ",(0,o.kt)("inlineCode",{parentName:"p"},"ios.screensharing.enabled feature")," flag, like this:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'let options = JitsiMeetConferenceOptions.fromBuilder { [weak self] builder in\n ...\n builder.setFeatureFlag("ios.screensharing.enabled", withBoolean: true)\n}\nmeetView.join(options)\n')),(0,o.kt)("p",null,"Finally, we are ready to test the implementation. Before doing so, make sure voip is added to ",(0,o.kt)("inlineCode",{parentName:"p"},"UIBackgroundModes"),", in the app's ",(0,o.kt)("inlineCode",{parentName:"p"},"Info.playlist"),", in order to work when the app is in the background."),(0,o.kt)("h3",{id:"tldr"},"TL;DR"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Add a ",(0,o.kt)("inlineCode",{parentName:"li"},"Broadcast Upload Extension"),", without UI, to your app. Update deployment info to run in iOS 14 or newer."),(0,o.kt)("li",{parentName:"ul"},"Copy ",(0,o.kt)("inlineCode",{parentName:"li"},"SampleUploader.swift"),", ",(0,o.kt)("inlineCode",{parentName:"li"},"SocketConnection.swift"),", ",(0,o.kt)("inlineCode",{parentName:"li"},"DarwinNotificationCenter.swift")," and ",(0,o.kt)("inlineCode",{parentName:"li"},"Atomic.swift")," files from the sample project to your extension. Make sure they are added to the extension's target."),(0,o.kt)("li",{parentName:"ul"},"Add both the app and the extension to the same App Group. Next, add the app group id value to the app's ",(0,o.kt)("inlineCode",{parentName:"li"},"Info.plist")," for the ",(0,o.kt)("inlineCode",{parentName:"li"},"RTCAppGroupIdentifier")," key."),(0,o.kt)("li",{parentName:"ul"},"Add a new key ",(0,o.kt)("inlineCode",{parentName:"li"},"RTCScreenSharingExtension")," to the app's ",(0,o.kt)("inlineCode",{parentName:"li"},"Info.plist")," with the extension's ",(0,o.kt)("inlineCode",{parentName:"li"},"Bundle Identifier")," as the value."),(0,o.kt)("li",{parentName:"ul"},"Update ",(0,o.kt)("inlineCode",{parentName:"li"},"SampleHandler.swift")," with the code from the sample project. Update ",(0,o.kt)("inlineCode",{parentName:"li"},"appGroupIdentifier")," constant with the App Group name your app and extension are both registered to."),(0,o.kt)("li",{parentName:"ul"},"Update ",(0,o.kt)("inlineCode",{parentName:"li"},"JitsiMeetConferenceOptions")," to enable screen sharing using the ",(0,o.kt)("inlineCode",{parentName:"li"},"ios.screensharing.enabled")," feature flag."),(0,o.kt)("li",{parentName:"ul"},"Make sure ",(0,o.kt)("inlineCode",{parentName:"li"},"voip")," is added to ",(0,o.kt)("inlineCode",{parentName:"li"},"UIBackgroundModes"),", in the app's ",(0,o.kt)("inlineCode",{parentName:"li"},"Info.plist"),", in order to work when the app is in the background.")))}h.isMDXComponent=!0},1798:(e,t,n)=>{n.d(t,{Z:()=>i});const i=n.p+"assets/images/iOS_screensharing_1-9fc051cab7c6c45813ac86a09d9be7f8.png"},1082:(e,t,n)=>{n.d(t,{Z:()=>i});const i=n.p+"assets/images/iOS_screensharing_2-e4305e7a7935b01689f316310cae41e9.png"},6195:(e,t,n)=>{n.d(t,{Z:()=>i});const i=n.p+"assets/images/iOS_screensharing_3-83343a2407deba42d29b363470fa4680.png"},9166:(e,t,n)=>{n.d(t,{Z:()=>i});const i=n.p+"assets/images/iOS_screensharing_4-985c0a05d859624dab3201410a425ca1.png"}}]); \ No newline at end of file diff --git a/assets/js/6d2ef372.29ac1429.js b/assets/js/6d2ef372.f4a03730.js similarity index 99% rename from assets/js/6d2ef372.29ac1429.js rename to assets/js/6d2ef372.f4a03730.js index 5af0bfe73..27f2f7350 100644 --- a/assets/js/6d2ef372.29ac1429.js +++ b/assets/js/6d2ef372.f4a03730.js @@ -1 +1 @@ -"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[5505],{3905:(e,t,a)=>{a.d(t,{Zo:()=>m,kt:()=>k});var n=a(7294);function i(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function r(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function l(e){for(var t=1;t=0||(i[a]=e[a]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(i[a]=e[a])}return i}var p=n.createContext({}),s=function(e){var t=n.useContext(p),a=t;return e&&(a="function"==typeof e?e(t):l(l({},t),e)),a},m=function(e){var t=s(e.components);return n.createElement(p.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},c=n.forwardRef((function(e,t){var a=e.components,i=e.mdxType,r=e.originalType,p=e.parentName,m=o(e,["components","mdxType","originalType","parentName"]),c=s(a),k=i,N=c["".concat(p,".").concat(k)]||c[k]||d[k]||r;return a?n.createElement(N,l(l({ref:t},m),{},{components:a})):n.createElement(N,l({ref:t},m))}));function k(e,t){var a=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=a.length,l=new Array(r);l[0]=c;var o={};for(var p in t)hasOwnProperty.call(t,p)&&(o[p]=t[p]);o.originalType=e,o.mdxType="string"==typeof e?e:i,l[1]=o;for(var s=2;s{a.r(t),a.d(t,{assets:()=>m,contentTitle:()=>p,default:()=>k,frontMatter:()=>o,metadata:()=>s,toc:()=>d});var n=a(7462),i=a(3366),r=(a(7294),a(3905)),l=["components"],o={id:"dev-guide-ljm-api",title:"lib-jitsi-meet API (low level)"},p=void 0,s={unversionedId:"dev-guide/dev-guide-ljm-api",id:"dev-guide/dev-guide-ljm-api",title:"lib-jitsi-meet API (low level)",description:"You can use Jitsi Meet API to create Jitsi Meet video conferences with a custom GUI.",source:"@site/docs/dev-guide/ljm-api.md",sourceDirName:"dev-guide",slug:"/dev-guide/dev-guide-ljm-api",permalink:"/handbook/docs/dev-guide/dev-guide-ljm-api",draft:!1,editUrl:"https://github.com/jitsi/handbook/edit/master/docs/dev-guide/ljm-api.md",tags:[],version:"current",lastUpdatedAt:1694635654,formattedLastUpdatedAt:"Sep 13, 2023",frontMatter:{id:"dev-guide-ljm-api",title:"lib-jitsi-meet API (low level)"},sidebar:"docs",previous:{title:"IFrame API",permalink:"/handbook/docs/dev-guide/dev-guide-iframe"},next:{title:"React SDK",permalink:"/handbook/docs/dev-guide/dev-guide-react-sdk"}},m={},d=[{value:"Installation",id:"installation",level:2},{value:"Getting Started",id:"getting-started",level:2},{value:"Components",id:"components",level:2},{value:"Usage",id:"usage",level:2},{value:"JitsiMeetJS",id:"jitsimeetjs",level:3},{value:"JitsiConnection",id:"jitsiconnection",level:3},{value:"JitsiConference",id:"jitsiconference",level:3},{value:"JitsiTrack",id:"jitsitrack",level:3},{value:"JitsiTrackError",id:"jitsitrackerror",level:3}],c={toc:d};function k(e){var t=e.components,a=(0,i.Z)(e,l);return(0,r.kt)("wrapper",(0,n.Z)({},c,a,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("p",null,"You can use Jitsi Meet API to create Jitsi Meet video conferences with a custom GUI."),(0,r.kt)("h2",{id:"installation"},"Installation"),(0,r.kt)("p",null,"To embed Jitsi Meet API in your application you need to add Jitsi Meet API library"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-html"},' +This folder contains all the css that is used in the project. The files (mostly .scss filesscss) are split up into features like the React features that they are used in.

Testing

The main form of testing code changes is done through torture tests, next to this the code is tested manually.

The torture tests are located in a separate repository, Jitsi Meet Torture. The project contains end to end tests for several key functions such as peer to peer and invites. The testing can be done for iOS, Android and web, which are all the platforms that Jitsi Meet can be used on. The testing is done automatically for pull requests by project members, where it is used in combination with the continuous integration by a Jenkins instance running the tests, testing on the meet.jit.si instance. Other members can run the tests locally. The test results can be viewed on an automatically generated web page.

Manual testing is performed while doing code reviews, however there are also testing releases that can be freely downloaded and deployed, or can be used on the beta test server.

+ \ No newline at end of file diff --git a/docs/category/configuration/index.html b/docs/category/configuration/index.html index 8657263ea..0fadfa748 100644 --- a/docs/category/configuration/index.html +++ b/docs/category/configuration/index.html @@ -4,13 +4,13 @@ Configuration | Jitsi Meet - +

Configuration

- + \ No newline at end of file diff --git a/docs/category/deployment/index.html b/docs/category/deployment/index.html index 128ca9c0c..99c0dabfd 100644 --- a/docs/category/deployment/index.html +++ b/docs/category/deployment/index.html @@ -4,13 +4,13 @@ Deployment | Jitsi Meet - + - + \ No newline at end of file diff --git a/docs/category/developer-guide/index.html b/docs/category/developer-guide/index.html index 1b608c751..e26ff0733 100644 --- a/docs/category/developer-guide/index.html +++ b/docs/category/developer-guide/index.html @@ -4,13 +4,13 @@ Developer Guide | Jitsi Meet - + - + \ No newline at end of file diff --git a/docs/category/mobile/index.html b/docs/category/mobile/index.html index 8bda87176..8d245b32f 100644 --- a/docs/category/mobile/index.html +++ b/docs/category/mobile/index.html @@ -4,13 +4,13 @@ Mobile | Jitsi Meet - + - + \ No newline at end of file diff --git a/docs/category/sdks/index.html b/docs/category/sdks/index.html index dcdc7a593..f1146eecb 100644 --- a/docs/category/sdks/index.html +++ b/docs/category/sdks/index.html @@ -4,13 +4,13 @@ SDKs | Jitsi Meet - +
- + \ No newline at end of file diff --git a/docs/category/user-guide/index.html b/docs/category/user-guide/index.html index 762840053..f4cf82e7f 100644 --- a/docs/category/user-guide/index.html +++ b/docs/category/user-guide/index.html @@ -4,13 +4,13 @@ User Guide | Jitsi Meet - + - + \ No newline at end of file diff --git a/docs/category/web/index.html b/docs/category/web/index.html index 4c7f6d3d7..903540edf 100644 --- a/docs/category/web/index.html +++ b/docs/category/web/index.html @@ -4,13 +4,13 @@ Web | Jitsi Meet - + - + \ No newline at end of file diff --git a/docs/community/breakout-rooms/index.html b/docs/community/breakout-rooms/index.html index babc10506..f8dd501ea 100644 --- a/docs/community/breakout-rooms/index.html +++ b/docs/community/breakout-rooms/index.html @@ -4,13 +4,13 @@ Breakout rooms | Jitsi Meet - +
-

Breakout rooms

note

Jitsi Meet now has native support for breakout rooms. You can see it in action here. This page is kept here for historical purposes and will receive no further updates.

Jitsi does not natively support breakout rooms (e.g. see #4787 and #5550); however there are various solutions for this functionality built on top of Jitsi:

NameLicenseCost modelAdmin can move participantsComments
"DIY" - manually combine Jitsi with other comms toolsDepends on what you useDepends on what you useNoNot a great solution, since no native integration; everyone needs to follow a pre-agreed workflow and which Jitsi rooms to use, e.g. https://meet.jit.si/<eventname>-<roomname>
VeertlyApache 2.0Cannot currently be self-hosted as it requires firebase (also SaaS - free for now?)NoTry the online demo
Wurk.appApache 2.0Open SourceYesOriginal comment Uses a Main room. The meeting host can add unlimited other rooms and move web-based participants to them. Moving participants away from the main room is not yet supported on mobile. Supports timeboxes and broadcast messages. Meeting hosts can unmute / mute others. Wurk uses a self hosted Jitsi meet installation
VideoFacilitatorProprietary SaaSSubscription (see pricing)?Issues with navigating rooms on mobile?
Plugin for Openfire MeetingsApache 2.0Free (self-hosting)?More info here
- +

Breakout rooms

note

Jitsi Meet now has native support for breakout rooms. You can see it in action here. This page is kept here for historical purposes and will receive no further updates.

Jitsi does not natively support breakout rooms (e.g. see #4787 and #5550); however there are various solutions for this functionality built on top of Jitsi:

NameLicenseCost modelAdmin can move participantsComments
"DIY" - manually combine Jitsi with other comms toolsDepends on what you useDepends on what you useNoNot a great solution, since no native integration; everyone needs to follow a pre-agreed workflow and which Jitsi rooms to use, e.g. https://meet.jit.si/<eventname>-<roomname>
VeertlyApache 2.0Cannot currently be self-hosted as it requires firebase (also SaaS - free for now?)NoTry the online demo
Wurk.appApache 2.0Open SourceYesOriginal comment Uses a Main room. The meeting host can add unlimited other rooms and move web-based participants to them. Moving participants away from the main room is not yet supported on mobile. Supports timeboxes and broadcast messages. Meeting hosts can unmute / mute others. Wurk uses a self hosted Jitsi meet installation
VideoFacilitatorProprietary SaaSSubscription (see pricing)?Issues with navigating rooms on mobile?
Plugin for Openfire MeetingsApache 2.0Free (self-hosting)?More info here
+ \ No newline at end of file diff --git a/docs/community/community-instances/index.html b/docs/community/community-instances/index.html index 2f315f798..54525ceb7 100644 --- a/docs/community/community-instances/index.html +++ b/docs/community/community-instances/index.html @@ -4,13 +4,13 @@ Community-run instances | Jitsi Meet - +
-

Community-run instances

Official Servers

info

Any other server listed in this page, with the exception of the ones above, has no affiliation with the Jitsi project or 8x8, Inc.

Misc. info & other server lists

You can use DNS Lookup, GeoIP Lookup, and KeyCDN Geo Tool to find out more about each server's location.

Jitsi Meet apps

Next to its native webclient interface, Jitsi Meet is also available as

User- and/or community-run jit.si instances by region

✅ = this jitsi setup does not use Google's STUN/TURN servers

❌ = hosted on Amazon, Google, Cloudflare or Microsoft servers

📞 = Dial-in

⏺️ = Recording


Europe

Austria

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
easyconference.uibk.ac.atrun SSL check!University of Innsbruck
meet.epicenter.worksrun SSL check!Nessus GmbH
fairmeeting.net✅ 📞run SSL check!fairkom Gesellschaft
pro.fairmeeting.net✅ 📞 ⏺️run SSL check!fairkom Gesellschaft
meet.graz.socialrun SSL check!
jitsi.projectsegfau.ltrun SSL check!Project Segfault

Belarus

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
meet.naveksoft.comrun SSL check!Navek Soft & Mobile Service LTD

Belgium

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
jitsi.belnet.berun SSL check!BELNET
praatbox.berun SSL check!

Czech Republic

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
meet.vpsfree.czrun SSL check!Master Internet s.r.o.

Finland

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
jitsi.riot.imrun SSL check!UpCloud Ltd, hosted by New Vector on behalf of the Matrix.org Foundation

France

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
allo.bim.landSSL checkILOTH
blabla.dedidream.comSSL checkOnline S.a.s.
conf.domainepublic.netSSL checkBouygues Telecom SA
conf.underworld.frSSL checkBouygues Telecom SA
meet.kbu.freifunk.netrun SSL check!Online S.a.s.
jump.chatrun SSL check!Online S.a.s.
jitsi.cheezecake.ovhSSL checkOnline S.A.S.
jitsi.hadoly.frSSL checkGrenode
jitsi.hivane.netSSL checkHivane
jitsi.milkywan.frSSL checkMilkyWan
jitsi.nextmap.ioSSL checkNXT Initiative SAS
jitsi.q2r.netSSL checkOnline S.a.s.
jitsi.tetaneutral.netSSL checkTetaneutral.net
meet.digdeo.frSSL checkOVH SAS
meet.evolix.orgSSL checkEvolix SARL
meet.lenalio.frSSL checkOceanet Technology SAS
meet.tedomum.netSSL checkOnline S.a.s.
meet.yapbreak.frSSL checkOVH SAS
meeting.rezopole.netSSL checkRezopole A.D.
rendez-vous.renater.frSSL checkRenater
jitsi.debamax.comrun SSL check!Online S.a.s.
talk.fdn.frSSL checkAssociation Gitoyen
talk.ouvaton.coopSSL checkInulogic Sarl
jitsi.laas.frSSL check
video.devloprog.orgSSL checkBouygues Telecom SA
video.omicro.orgSSL checkOVH SAS
jitsi.komuniki.frSSL checkOVH SAS
visio.lafranceinsoumise.frSSL checkOnline S.a.s.
webcall.paulla.asso.frSSL checkAxione S.a.s.
webconf.viviers-fibre.netSSL checkOVH SAS

special cases (limited access)

Germany

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
meet.ffmuc.net❌ (only Frontend no videotraffic through CF)run SSL check!CLOUDFLARENET
meet.teamcloud.workSSL checkHetzner Online GmbH
besprechung.netSSL checkHost Europe GmbH
meet.physnet.uni-hamburg.deSSL check
meet.in-berlin.deSSL check
meet.jotbe.ioSSL check
meet.nerd.reSSL checkContabo GmbH
framatalk.orgSSL checkHetzner Online GmbH
freejitsi01.netcup.netSSL checknetcup GmbH
jitsi.arl.wtfSSL checkfux eG
jitsi.debian.socialSSL checkHetzner Online GmbH
jitsi.dorf-post.deSSL check
jitsi.fem.tu-ilmenau.deSSL checkDFN
jitsi.flyingcircus.ioSSL checkKAMP Netzwerkdienste GmbH
jitsi.freifunk-duesseldorf.deSSL checkFreifunk Duesseldorf e.V.
jitsi.hamburg.ccc.deSSL checkHetzner Online GmbH
jitsi.hs-anhalt.deSSL check
jitsi.komun.orgSSL checkContabo GmbH
jitsi.netways.deSSL checkNETWAYS GmbH
meet.osna.socialSSL checkVerein zur Foerderung eines Deutschen Forschungsnetzes e.V.
jitsi.piratenpartei-duesseldorf.deSSL checkContabo GmbH
jitsi.sixtopia.netSSL checkAlexej Senderkin
jitsi.uni-kl.deSSL checkTechnische Universitaet Kaiserslautern
jitsi.zfn.uni-bremen.deSSL checkDFN
konferenz.netzbegruenung.de✅ -- 📞SSL checkHetzner Online GmbH
meet.alpha.berlinSSL checkmanitu GmbH
meet.alps.oneSSL checkHetzner Online GmbH
meet.darmstadt.socialSSL checknetcup GmbH
meet.ffrn.deSSL checkHetzner Online GmbH
meet.ffbrb.deSSL checkHetzner Online GmbH
meet.freifunk-aachen.deSSL checkRelAix Networks GmbH
meet.freifunk-rhein-sieg.netSSL checkHetzner Online GmbH
meet.golem.deSSL checkHetzner Online GmbH
meet.hackerspace-bremen.deSSL check
meet.isf.esSSL checkHetzner Online GmbH
meet.jugendrecht.orgSSL checkHetzner Online GmbH
meet.lrz.deSSL checkLeibniz-Rechenzentrum
meet.lug-stormarn.deSSL check
meet.meerfarbig.netSSL checkmeerfarbig GmbH & Co. KG
meet.no42.orgSSL check1&1 Ionos Se
meet.opensuse.orgSSL check
meet.piraten-witten.deSSL checknetcup GmbH
meet.piratenpartei-sh.deSSL checkISPpro Internet KG
meet.roflcopter.frSSL checkHetzner Online GmbH
meet.rollenspiel.monsterSSL checkHetzner Online GmbH
meet.scheible.itSSL checkHetzner Online GmbH
meet.stura.uni-heidelberg.deSSL check
meet.systemli.orgSSL checkDigitale Partizipation e.V.
meet.weimarnetz.deSSL checkFoerderverein Freie Netzwerke e.V.
teamjoin.deSSL checkHetzner Online GmbH
talk.snopyta.orgSSL checkHetzner Online GmbH
vc.autistici.orgSSL checkHetzner Online GmbH
viko.extinctionrebellion.deSSL checkSysEleven GmbH
webmeeting.opensolution.itSSL checkDIGITALOCEAN-ASN
virtual.chaosdorf.spaceSSL check
jitsi.eichstaett.socialrun SSL check!Severloft GmbH
konferenz.buehl.digitalrun SSL check!
meet.stuvus.uni-stuttgart.derun SSL check!Universität Stuttgart
jitsi.hs-anhalt.derun SSL check!Hetzner Online GmbH
jitsi.php-friends.derun SSL check!PhP-Friends GmbH
meet.studiumdigitale.uni-frankfurt.derun SSL check!Johann Wolfgang Goethe-Universitat Frankfurt am Main
onlinetreff.ash-berlin.eurun SSL check!netcup GmbH
meet.nerd.rerun SSL check!Hetzner Online GmbH
jitsi.mpi-bremen.de✅ - 📞run SSL check!Verein zur Foerderung eines Deutschen Forschungsnetzes e.V.
meet.rdi.zimt.uni-siegen.derun SSL check!Verein zur Foerderung eines Deutschen Forschungsnetzes e.V.
meet.ur.derun SSL check!Verein zur Foerderung eines Deutschen Forschungsnetzes e.V.
jitsi-01.csn.tu-chemnitz.derun SSL check!Verein zur Foerderung eines Deutschen Forschungsnetzes e.V.
jitsi.hs-nb.derun SSL check!Verein zur Foerderung eines Deutschen Forschungsnetzes e.V.
llamada.comun.alrun SSL check!1&1 IONOS SE

special cases (limited access, mostly HEI)

Italy

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
jitsi.area.fi.cnr.itrun SSL check!Consortium GARR
jitsi.cedrc.cnr.itrun SSL check!Consortium GARR
smaug.lixper.itrun SSL check!Telecom Italia
iorestoacasa.unicam.itrun SSL check!Consortium GARR
balin.siriustec.itrun SSL check!Sirius Technology SRL

Netherlands

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
beeldbellen.vc4all.nlrun SSL check!Transip B.V.
jitsi.tuxis.netrun SSL check!
meet.greenmini.hostrun SSL check!
meetme.bit.nlrun SSL check!BIT BV
calls.disroot.orgrun SSL check!Serverius Holding B.V.
jitsi.hivos.orgrun SSL check!WorldStream B.V.
meet.waag.orgrun SSL check!
jitsi.nluug.nlrun SSL check!ProcoliX B.V.
vdc.dyne.orgrun SSL check!
meet.greenhost.netrun SSL check!
meet.domov.deSSL checkRouteLabel V.O.F.
meet.speakup.nlrun SSL check!

Russian Federation

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
conf.edu-kuban.rurun SSL check!
jitsi.ufanet.rurun SSL check!

Spain

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
meet.guifi.netrun SSL check!Fundacio Privada per a la Xarxa Lliure, Oberta i Neutral, guifi.net
videoconferencia.valenciatech.comrun SSL check!VALENCIATECH - Servicios de Informática con Software Libre - Administración y provisión de servidores GNU/Linux.

Sweden

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
jitsi.brainmill.comrun SSL check!Stiftelsen Chalmers Studenthem
meet.operationtulip.comrun SSL check!Bahnhof AB

Switzerland

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
open.meet.switch.chrun SSL check!SWITCH
swiss-meet.hidora.comrun SSL check!HIDORA
kmeet.infomaniak.comrun SSL check!
jitsi.math.uzh.chrun SSL check!SWITCH
jitsi.ff3l.netrun SSL check!Init7
unibe.meet.switch.chrun SSL check!SWITCH
unifr.meet.switch.chrun SSL check!SWITCH
meet-7.immerda.chrun SSL check!
hosttech.chatrun SSL check!
meet.cyon.toolsrun SSL check!
meet.init7.netrun SSL check!Init 7 AG
meet.hostpoint.chrun SSL check!Hostpoint AG
meet.coredump.chrun SSL check!Nine Internet Solutions AG

South & North America

Argentina

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
jitsi.unp.edu.arrun SSL check!Red de Interconexion Universitaria
jitsi.uner.edu.arrun SSL check!Universidad Nacional de Entre Rios
jitsi.hostcero.comrun SSL check!Servicios y Telecomunicaciones S.A.
jitsi.justiciajujuy.gov.arrun SSL check!Telecom Argentina S.A.

Canada

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
conference.facil.servicesrun SSL check!OVH SAS
meet-6.immerda.chrun SSL check!KOUMBIT

Chile

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
vc.sanclemente.clrun SSL check!ENTEL CHILE S.A.

El Salvador

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
jmeet.uca.edu.svrun SSL check!GOOGLE

United States

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
gruveo.comrun SSL check!AMAZON-AES
meet.jit.sirun SSL check!AMAZON-02
meet.mayfirst.orgrun SSL check!HURRICANE
team.videorun SSL check!HURRICANE
swrtc.talky.iorun SSL check!AMAZON-02
jitsi.member.fsf.orgrun SSL check!Free Software Foundation
jitsi.vern.ccrun SSL check!~vern

Africa

South Africa

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
noumeet.comrun SSL check!Teraco Johannesburg
meet.qix.co.zarun SSL check!Host Africa

Asia

Singapore

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
meet.engagemedia.orgSSL checkMicrosoft Azure

Sri Lanka

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
meet.gov.lkrun SSL check!Information and Communication Technology Agency of Sri Lanka

Thailand

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
vroom.truevirtualworld.comrun SSL check!Huawei International Pte. Ltd.

- +

Community-run instances

Official Servers

info

Any other server listed in this page, with the exception of the ones above, has no affiliation with the Jitsi project or 8x8, Inc.

Misc. info & other server lists

You can use DNS Lookup, GeoIP Lookup, and KeyCDN Geo Tool to find out more about each server's location.

Jitsi Meet apps

Next to its native webclient interface, Jitsi Meet is also available as

User- and/or community-run jit.si instances by region

✅ = this jitsi setup does not use Google's STUN/TURN servers

❌ = hosted on Amazon, Google, Cloudflare or Microsoft servers

📞 = Dial-in

⏺️ = Recording


Europe

Austria

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
easyconference.uibk.ac.atrun SSL check!University of Innsbruck
meet.epicenter.worksrun SSL check!Nessus GmbH
fairmeeting.net✅ 📞run SSL check!fairkom Gesellschaft
pro.fairmeeting.net✅ 📞 ⏺️run SSL check!fairkom Gesellschaft
meet.graz.socialrun SSL check!
jitsi.projectsegfau.ltrun SSL check!Project Segfault

Belarus

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
meet.naveksoft.comrun SSL check!Navek Soft & Mobile Service LTD

Belgium

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
jitsi.belnet.berun SSL check!BELNET
praatbox.berun SSL check!

Czech Republic

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
meet.vpsfree.czrun SSL check!Master Internet s.r.o.

Finland

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
jitsi.riot.imrun SSL check!UpCloud Ltd, hosted by New Vector on behalf of the Matrix.org Foundation

France

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
allo.bim.landSSL checkILOTH
blabla.dedidream.comSSL checkOnline S.a.s.
conf.domainepublic.netSSL checkBouygues Telecom SA
conf.underworld.frSSL checkBouygues Telecom SA
meet.kbu.freifunk.netrun SSL check!Online S.a.s.
jump.chatrun SSL check!Online S.a.s.
jitsi.cheezecake.ovhSSL checkOnline S.A.S.
jitsi.hadoly.frSSL checkGrenode
jitsi.hivane.netSSL checkHivane
jitsi.milkywan.frSSL checkMilkyWan
jitsi.nextmap.ioSSL checkNXT Initiative SAS
jitsi.q2r.netSSL checkOnline S.a.s.
jitsi.tetaneutral.netSSL checkTetaneutral.net
meet.digdeo.frSSL checkOVH SAS
meet.evolix.orgSSL checkEvolix SARL
meet.lenalio.frSSL checkOceanet Technology SAS
meet.tedomum.netSSL checkOnline S.a.s.
meet.yapbreak.frSSL checkOVH SAS
meeting.rezopole.netSSL checkRezopole A.D.
rendez-vous.renater.frSSL checkRenater
jitsi.debamax.comrun SSL check!Online S.a.s.
talk.fdn.frSSL checkAssociation Gitoyen
talk.ouvaton.coopSSL checkInulogic Sarl
jitsi.laas.frSSL check
video.devloprog.orgSSL checkBouygues Telecom SA
video.omicro.orgSSL checkOVH SAS
jitsi.komuniki.frSSL checkOVH SAS
visio.lafranceinsoumise.frSSL checkOnline S.a.s.
webcall.paulla.asso.frSSL checkAxione S.a.s.
webconf.viviers-fibre.netSSL checkOVH SAS

special cases (limited access)

Germany

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
meet.ffmuc.net❌ (only Frontend no videotraffic through CF)run SSL check!CLOUDFLARENET
meet.teamcloud.workSSL checkHetzner Online GmbH
besprechung.netSSL checkHost Europe GmbH
meet.physnet.uni-hamburg.deSSL check
meet.in-berlin.deSSL check
meet.jotbe.ioSSL check
meet.nerd.reSSL checkContabo GmbH
framatalk.orgSSL checkHetzner Online GmbH
freejitsi01.netcup.netSSL checknetcup GmbH
jitsi.arl.wtfSSL checkfux eG
jitsi.debian.socialSSL checkHetzner Online GmbH
jitsi.dorf-post.deSSL check
jitsi.fem.tu-ilmenau.deSSL checkDFN
jitsi.flyingcircus.ioSSL checkKAMP Netzwerkdienste GmbH
jitsi.freifunk-duesseldorf.deSSL checkFreifunk Duesseldorf e.V.
jitsi.hamburg.ccc.deSSL checkHetzner Online GmbH
jitsi.hs-anhalt.deSSL check
jitsi.komun.orgSSL checkContabo GmbH
jitsi.netways.deSSL checkNETWAYS GmbH
meet.osna.socialSSL checkVerein zur Foerderung eines Deutschen Forschungsnetzes e.V.
jitsi.piratenpartei-duesseldorf.deSSL checkContabo GmbH
jitsi.sixtopia.netSSL checkAlexej Senderkin
jitsi.uni-kl.deSSL checkTechnische Universitaet Kaiserslautern
jitsi.zfn.uni-bremen.deSSL checkDFN
konferenz.netzbegruenung.de✅ -- 📞SSL checkHetzner Online GmbH
meet.alpha.berlinSSL checkmanitu GmbH
meet.alps.oneSSL checkHetzner Online GmbH
meet.darmstadt.socialSSL checknetcup GmbH
meet.ffrn.deSSL checkHetzner Online GmbH
meet.ffbrb.deSSL checkHetzner Online GmbH
meet.freifunk-aachen.deSSL checkRelAix Networks GmbH
meet.freifunk-rhein-sieg.netSSL checkHetzner Online GmbH
meet.golem.deSSL checkHetzner Online GmbH
meet.hackerspace-bremen.deSSL check
meet.isf.esSSL checkHetzner Online GmbH
meet.jugendrecht.orgSSL checkHetzner Online GmbH
meet.lrz.deSSL checkLeibniz-Rechenzentrum
meet.lug-stormarn.deSSL check
meet.meerfarbig.netSSL checkmeerfarbig GmbH & Co. KG
meet.no42.orgSSL check1&1 Ionos Se
meet.opensuse.orgSSL check
meet.piraten-witten.deSSL checknetcup GmbH
meet.piratenpartei-sh.deSSL checkISPpro Internet KG
meet.roflcopter.frSSL checkHetzner Online GmbH
meet.rollenspiel.monsterSSL checkHetzner Online GmbH
meet.scheible.itSSL checkHetzner Online GmbH
meet.stura.uni-heidelberg.deSSL check
meet.systemli.orgSSL checkDigitale Partizipation e.V.
meet.weimarnetz.deSSL checkFoerderverein Freie Netzwerke e.V.
teamjoin.deSSL checkHetzner Online GmbH
talk.snopyta.orgSSL checkHetzner Online GmbH
vc.autistici.orgSSL checkHetzner Online GmbH
viko.extinctionrebellion.deSSL checkSysEleven GmbH
webmeeting.opensolution.itSSL checkDIGITALOCEAN-ASN
virtual.chaosdorf.spaceSSL check
jitsi.eichstaett.socialrun SSL check!Severloft GmbH
konferenz.buehl.digitalrun SSL check!
meet.stuvus.uni-stuttgart.derun SSL check!Universität Stuttgart
jitsi.hs-anhalt.derun SSL check!Hetzner Online GmbH
jitsi.php-friends.derun SSL check!PhP-Friends GmbH
meet.studiumdigitale.uni-frankfurt.derun SSL check!Johann Wolfgang Goethe-Universitat Frankfurt am Main
onlinetreff.ash-berlin.eurun SSL check!netcup GmbH
meet.nerd.rerun SSL check!Hetzner Online GmbH
jitsi.mpi-bremen.de✅ - 📞run SSL check!Verein zur Foerderung eines Deutschen Forschungsnetzes e.V.
meet.rdi.zimt.uni-siegen.derun SSL check!Verein zur Foerderung eines Deutschen Forschungsnetzes e.V.
meet.ur.derun SSL check!Verein zur Foerderung eines Deutschen Forschungsnetzes e.V.
jitsi-01.csn.tu-chemnitz.derun SSL check!Verein zur Foerderung eines Deutschen Forschungsnetzes e.V.
jitsi.hs-nb.derun SSL check!Verein zur Foerderung eines Deutschen Forschungsnetzes e.V.
llamada.comun.alrun SSL check!1&1 IONOS SE

special cases (limited access, mostly HEI)

Italy

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
jitsi.area.fi.cnr.itrun SSL check!Consortium GARR
jitsi.cedrc.cnr.itrun SSL check!Consortium GARR
smaug.lixper.itrun SSL check!Telecom Italia
iorestoacasa.unicam.itrun SSL check!Consortium GARR
balin.siriustec.itrun SSL check!Sirius Technology SRL

Netherlands

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
beeldbellen.vc4all.nlrun SSL check!Transip B.V.
jitsi.tuxis.netrun SSL check!
meet.greenmini.hostrun SSL check!
meetme.bit.nlrun SSL check!BIT BV
calls.disroot.orgrun SSL check!Serverius Holding B.V.
jitsi.hivos.orgrun SSL check!WorldStream B.V.
meet.waag.orgrun SSL check!
jitsi.nluug.nlrun SSL check!ProcoliX B.V.
vdc.dyne.orgrun SSL check!
meet.greenhost.netrun SSL check!
meet.domov.deSSL checkRouteLabel V.O.F.
meet.speakup.nlrun SSL check!

Russian Federation

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
conf.edu-kuban.rurun SSL check!
jitsi.ufanet.rurun SSL check!

Spain

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
meet.guifi.netrun SSL check!Fundacio Privada per a la Xarxa Lliure, Oberta i Neutral, guifi.net
videoconferencia.valenciatech.comrun SSL check!VALENCIATECH - Servicios de Informática con Software Libre - Administración y provisión de servidores GNU/Linux.

Sweden

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
jitsi.brainmill.comrun SSL check!Stiftelsen Chalmers Studenthem
meet.operationtulip.comrun SSL check!Bahnhof AB

Switzerland

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
open.meet.switch.chrun SSL check!SWITCH
swiss-meet.hidora.comrun SSL check!HIDORA
kmeet.infomaniak.comrun SSL check!
jitsi.math.uzh.chrun SSL check!SWITCH
jitsi.ff3l.netrun SSL check!Init7
unibe.meet.switch.chrun SSL check!SWITCH
unifr.meet.switch.chrun SSL check!SWITCH
meet-7.immerda.chrun SSL check!
hosttech.chatrun SSL check!
meet.cyon.toolsrun SSL check!
meet.init7.netrun SSL check!Init 7 AG
meet.hostpoint.chrun SSL check!Hostpoint AG
meet.coredump.chrun SSL check!Nine Internet Solutions AG

South & North America

Argentina

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
jitsi.unp.edu.arrun SSL check!Red de Interconexion Universitaria
jitsi.uner.edu.arrun SSL check!Universidad Nacional de Entre Rios
jitsi.hostcero.comrun SSL check!Servicios y Telecomunicaciones S.A.
jitsi.justiciajujuy.gov.arrun SSL check!Telecom Argentina S.A.

Canada

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
conference.facil.servicesrun SSL check!OVH SAS
meet-6.immerda.chrun SSL check!KOUMBIT

Chile

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
vc.sanclemente.clrun SSL check!ENTEL CHILE S.A.

El Salvador

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
jmeet.uca.edu.svrun SSL check!GOOGLE

United States

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
gruveo.comrun SSL check!AMAZON-AES
meet.jit.sirun SSL check!AMAZON-02
meet.mayfirst.orgrun SSL check!HURRICANE
team.videorun SSL check!HURRICANE
swrtc.talky.iorun SSL check!AMAZON-02
jitsi.member.fsf.orgrun SSL check!Free Software Foundation
jitsi.vern.ccrun SSL check!~vern

Africa

South Africa

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
noumeet.comrun SSL check!Teraco Johannesburg
meet.qix.co.zarun SSL check!Host Africa

Asia

Singapore

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
meet.engagemedia.orgSSL checkMicrosoft Azure

Sri Lanka

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
meet.gov.lkrun SSL check!Information and Communication Technology Agency of Sri Lanka

Thailand

URLNon-Google STUN/TURNOn Amazon, Google, Cloudflare or MicrosoftSSL Testhoster
vroom.truevirtualworld.comrun SSL check!Huawei International Pte. Ltd.

+ \ No newline at end of file diff --git a/docs/community/community-intro/index.html b/docs/community/community-intro/index.html index 660b8412e..0dd7a00a8 100644 --- a/docs/community/community-intro/index.html +++ b/docs/community/community-intro/index.html @@ -4,15 +4,15 @@ Our community | Jitsi Meet - + - +here.


+ \ No newline at end of file diff --git a/docs/community/third-party-software/index.html b/docs/community/third-party-software/index.html index 7b3865cfc..9bc2d0f44 100644 --- a/docs/community/third-party-software/index.html +++ b/docs/community/third-party-software/index.html @@ -4,7 +4,7 @@ Third-Party Software | Jitsi Meet - + @@ -29,8 +29,8 @@ token (JWT).
  • token_lobby_ondemand: Activates lobby based on a flag in token (JWT).
  • token_owner_party: Prevents unauthorized users from create a room and terminates the conference when the owner leaves.
  • SAML to Jitsi JWT Authentification

    Intergration of SAML authentification with Shibboleth for a Jitsi Meet JWT -generator.

    Github: https://github.com/Renater/Jitsi-SAML2JWT

    Unity plugin

    Plugin for using lib-jitsi-meet in a Unity environment (WebGL).

    https://github.com/avstack/jitsi-meet-unity-demo

    Plugin for using lib-jitsi-meet in a Unity environment (Android and iOS).

    https://github.com/SariskaIO/Sariska-Media-Unity-Demo

    - +generator.

    Github: https://github.com/Renater/Jitsi-SAML2JWT

    Unity plugin

    Plugin for using lib-jitsi-meet in a Unity environment (WebGL).

    https://github.com/avstack/jitsi-meet-unity-demo

    Plugin for using lib-jitsi-meet in a Unity environment (Android and iOS).

    https://github.com/SariskaIO/Sariska-Media-Unity-Demo

    + \ No newline at end of file diff --git a/docs/dev-guide/dev-guide-android-sdk/index.html b/docs/dev-guide/dev-guide-android-sdk/index.html index 98d0c5a10..020556f19 100644 --- a/docs/dev-guide/dev-guide-android-sdk/index.html +++ b/docs/dev-guide/dev-guide-android-sdk/index.html @@ -4,7 +4,7 @@ Android SDK | Jitsi Meet - + @@ -66,8 +66,8 @@ Picture-in-Picture style scenario, in a rectangle too small to accommodate its "full" UI.

    Dropbox integration

    To setup the Dropbox integration, follow these steps:

    1. Add the following to the app's AndroidManifest.xml and change <APP_KEY> to your Dropbox app key:
    <activity
    android:configChanges="keyboard|orientation"
    android:launchMode="singleTask"
    android:name="com.dropbox.core.android.AuthActivity">
    <intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.BROWSABLE" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:scheme="db-<APP_KEY>" />
    </intent-filter>
    </activity>
    1. Add the following to the app's strings.xml and change <APP_KEY> to your -Dropbox app key:
    <string name="dropbox_app_key"><APP_KEY></string>
    - +Dropbox app key:
    <string name="dropbox_app_key"><APP_KEY></string>
    + \ No newline at end of file diff --git a/docs/dev-guide/dev-guide-configuration/index.html b/docs/dev-guide/dev-guide-configuration/index.html index a46c25c72..5e45d346c 100644 --- a/docs/dev-guide/dev-guide-configuration/index.html +++ b/docs/dev-guide/dev-guide-configuration/index.html @@ -4,7 +4,7 @@ Configuration | Jitsi Meet - + @@ -95,8 +95,8 @@ the video quality levels used in the application. At the time of this writing the allowed levels are:

    • low - for the low quality level (180p at the time of this writing)

    • standard - for the medium quality level (360p)

    • high - for the high quality level (720p)

      The keys should be positive numbers which represent the minimal thumbnail height for the quality level. With the default config value below the application will use 'low' quality until the thumbnails are at least 360 pixels tall. If the thumbnail height reaches 720 pixels then the application will switch to -the high quality.

    videoQuality: {
    maxBitratesVideo: {
    H264: {
    low: 200000,
    standard: 500000,
    high: 1500000
    },
    VP8 : {
    low: 200000,
    standard: 500000,
    high: 1500000
    },
    VP9: {
    low: 100000,
    standard: 300000,
    high: 1200000
    }
    },
    minHeightForQualityLvl: {
    360: 'standard',
    720: 'high'
    },
    }

    Whiteboard

    whiteboard

    type: Object

    Options related to the Excalidraw whiteboard integration.

    Default: unset

    Properties:

    • enabled - Whether the feature is enabled or not.
    • collabServerBaseUrl - The server used to support whiteboard collaboration.
    whiteboard: {
    enabled: true,
    collabServerBaseUrl: 'https://excalidraw-backend.example.com'
    }
    - +the high quality.

    videoQuality: {
    maxBitratesVideo: {
    H264: {
    low: 200000,
    standard: 500000,
    high: 1500000
    },
    VP8 : {
    low: 200000,
    standard: 500000,
    high: 1500000
    },
    VP9: {
    low: 100000,
    standard: 300000,
    high: 1200000
    }
    },
    minHeightForQualityLvl: {
    360: 'standard',
    720: 'high'
    },
    }

    Whiteboard

    whiteboard

    type: Object

    Options related to the Excalidraw whiteboard integration.

    Default: unset

    Properties:

    • enabled - Whether the feature is enabled or not.
    • collabServerBaseUrl - The server used to support whiteboard collaboration.
    whiteboard: {
    enabled: true,
    collabServerBaseUrl: 'https://excalidraw-backend.example.com'
    }
    + \ No newline at end of file diff --git a/docs/dev-guide/dev-guide-contributing/index.html b/docs/dev-guide/dev-guide-contributing/index.html index 31d0005d4..846d70edc 100644 --- a/docs/dev-guide/dev-guide-contributing/index.html +++ b/docs/dev-guide/dev-guide-contributing/index.html @@ -4,7 +4,7 @@ Contributing Guidelines | Jitsi Meet - + @@ -50,8 +50,8 @@ In this case we create two index files in components/: index.native.ts and index.web.ts and export just the component we need. The common file should then import from components/index.

    This has not always been the case and the entire codebase hasn't been migrated to this model but new features should follow this new layout.

    When working on an old feature, adding the necessary changes to migrate to the new -model is encouraged.

    Avoiding bundle bloat

    When adding a new feature it's possible that it triggers a build failure due to the increased bundle size. We have safeguards inplace to avoid bundles growing disproportionatelly. While there are legit reasons for increasing the limits, please analyze the bundle first to make sure no unintended dependencies have been included, causing the increase in size.

    First, make a production build with bundle-analysis enabled:

    npx webpack -p --analyze-bundle

    Then open the interactive bundle analyzer tool:

    npx webpack-bundle-analyzer build/app-stats.json

    Kotlin

    For Kotlin code we use the standard convention and limit line length to 120 characters. We use ktlint to enforce formatting.

    - +model is encouraged.

    Avoiding bundle bloat

    When adding a new feature it's possible that it triggers a build failure due to the increased bundle size. We have safeguards inplace to avoid bundles growing disproportionatelly. While there are legit reasons for increasing the limits, please analyze the bundle first to make sure no unintended dependencies have been included, causing the increase in size.

    First, make a production build with bundle-analysis enabled:

    npx webpack -p --analyze-bundle

    Then open the interactive bundle analyzer tool:

    npx webpack-bundle-analyzer build/app-stats.json

    Kotlin

    For Kotlin code we use the standard convention and limit line length to 120 characters. We use ktlint to enforce formatting.

    + \ No newline at end of file diff --git a/docs/dev-guide/dev-guide-flutter-sdk/index.html b/docs/dev-guide/dev-guide-flutter-sdk/index.html index 0f63c31cb..e582d8d1e 100644 --- a/docs/dev-guide/dev-guide-flutter-sdk/index.html +++ b/docs/dev-guide/dev-guide-flutter-sdk/index.html @@ -4,14 +4,14 @@ Flutter SDK | Jitsi Meet - +

    Flutter SDK

    The Jitsi Meet Flutter SDK provides the same user experience as the Jitsi Meet app, in the form of a Flutter plugin so that you can embed and customize Jitsi Meet in your own Flutter app.

    Sample application using the Flutter

    If you want to see how easy integrating the Jitsi Meet Flutter SDK into a Flutter application is, take a look at the
    -sample applications repository.

    Installation

    Add dependency

    Add the dependency from command-line

    $ flutter pub add jitsi_meet_flutter_sdk

    The command above will add this to the pubspec.yaml file in your project (you can do this manually):

    dependencies:
    jitsi_meet_flutter_sdk: ^0.1.4

    Install

    Install the packages from the terminal:

    $ flutter pub get

    Import files

    Import the following files into your dart code:

    import 'package:jitsi_meet_flutter_sdk/jitsi_meet_flutter_sdk.dart';

    Usage

    Join meeting

    Firstly, create a JitsiMeet object, then call the method join from it with a JitsiMeetConferenceOptions object

    var jitsiMeet = JitsiMeet();
    var options = JitsiMeetConferenceOptions(room: 'jitsiIsAwesome');
    jitsiMeet.join(options);

    Configuration

    iOS

    Make sure in Podfile from ios directory you set the ios version 12.4 or higher

    platform :ios, '12.4'

    The plugin requests camera and microphone access, make sure to include the required entries for NSCameraUsageDescription and NSMicrophoneUsageDescription in your Info.plist file from the ios/Runner directory.

    <key>NSCameraUsageDescription</key>
    <string>The app needs access to your camera for meetings.</string>
    <key>NSMicrophoneUsageDescription</key>
    <string>The app needs access to your microphone for meetings.</string>

    Android

    Go to android/app/build.gradle and make sure that the minSdkVersion is set to at lest 24

    android {
    ...
    defaultConfig {
    ...
    minSdkVersion 24
    }
    }

    The application:label field from the Jitsi Meet Android SDK will conflict with your application's one . Go to android/app/src/main/AndroidManifest.xml and add the tools library and tools:replace="android:label" to the application tag.

    <manifest xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:tools="http://schemas.android.com/tools">
    <application
    tools:replace="android:label"
    android:label="sample_app"
    android:name="${applicationName}"
    android:icon="@mipmap/ic_launcher">
    ...
    </application>
    </manifest>

    Using the API

    JitsiMeet

    The JitsiMeet class is the entry point for the sdk. It is used to launch the meeting screen, to send and receive all the events.

    1. JitsiMeet()

      The constructor for the class.
    1. join(JitsiMeetConferenceOptions options, [JitsiMeetEventListener? listener])

      Joins a meeting with the given options and optionally a listener is given

      • options : meeting options
      • listener : event listener for events triggered by the native sdks
    2. hangUp()

      The localParticipant leaves the current meeting.

    3. setAudioMuted(bool muted)

      Sets the state of the localParticipant audio muted according to the muted parameter.

    4. setVideoMuted(bool muted)

      Sets the state of the localParticipant video muted according to the muted parameter.

    5. sendEndpointTextMessage({String? to, required String message})

      Sends a message via the data channel to one particular participant or to all of them. If the to param is empty, the message will be sent to all the participants in the conference.

      In order to get the participantId, the participantsJoined event should be listened for, which have as a parameter the participantId and this should be stored somehow.

    6. toggleScreenShare(bool enabled)

      Sets the state of the localParticipant screen sharing according to the enabled parameter.

    7. openChat([String? to])

      Opens the chat dialog. If to contains a valid participantId, the private chat with that particular participant will be opened.

    8. sendChatMessage({String? to, required String message})

      Sends a chat message via to one particular participant or to all of them. If the to param is empty, the message will be sent to all the participants in the conference.

      In order to get the participantId, the participantsJoined event should be listened for, which have as a parameter the participantId and this should be stored somehow.

    9. closeChat()

      Closes the chat dialog.

    10. retrieveParticipantsInfo()

      Sends and event that will trigger the participantsInfoRetrieved event which will contain participants information

    JitsiMeetConferenceOptions

    This object encapsulates all the options that can be tweaked when joining a conference.

    Example:

    var options = JitsiMeetConferenceOptions(
    serverURL: "https://meet.jit.si",
    room: "jitsiIsAwesomeWithFlutter",
    configOverrides: {
    "startWithAudioMuted": false,
    "startWithVideoMuted": false,
    "subject" : "Jitsi with Flutter",
    },
    featureFlags: {
    "unsaferoomwarning.enabled": false
    },
    userInfo: JitsiMeetUserInfo(
    displayName: "Flutter user",
    email: "user@example.com"
    ),
    );
    • All the values that can be added to the configOverrides can be found here.

    • All the values that can be added to the featureFlags can be found here.

    • JitsiMeetUserInfo({String displayName, String email, String avatar})

      The constructor for the JitsiMeetUserInfo. 

      P.S. the avatar should be an url.

    JitsiMeetEventListener

    This class intends to be used as a listener for events that come from the native sdks. It will receive as arguments the event handlers

    1. conferenceJoined(String url)

      Called when a conference was joined.

      • url : the conference URL
    2. conferenceTerminated(String url, Object? error)

      Called when the active conference ends, be it because of user choice or because of a failure.

      • url : the conference URL
      • error : missing if the conference finished gracefully, otherwise contains the error message
    3. conferenceWillJoin(String url)

      Called before a conference is joined.

      • url: the conference URL
    4. participantJoined(String? email, String? name, String? role, String? participantId)

      Called when a participant has joined the conference.

      • email : the email of the participant. It may not be set if the remote participant didn't set one.
      • name : the name of the participant.
      • role : the role of the participant.
      • participantId : the id of the participant.
    5. participantLeft(String? participantId)

      Called when a participant has left the conference.

      • participantId : the id of the participant that left.
    6. audioMutedChanged(bool muted)

      Called when the local participant's audio is muted or unmuted.

      • muted : a boolean indicating whether the audio is muted or not.
    7. videoMutedChanged(bool muted)

      Called when the local participant's video is muted or unmuted.

      • muted : a boolean indicating whether the video is muted or not.
    8. endpointTextMessageReceived(String senderId, String message)

      Called when an endpoint text message is received.

      • senderId : the participantId of the sender
      • message : the content.
    9. screenShareToggled(String participantId, bool sharing)

      Called when a participant starts or stops sharing his screen.

      • participantId : the id of the participant
      • sharing : the state of screen share
    10. chatMessageReceived(String senderId, String message, bool isPrivate, String? timestamp)

      Called when a chat text message is received.

      • senderId : the id of the participant that sent the message.
      • message : the content of the message.
      • isPrivate : true if the message is private, false otherwise.
      • timestamp : the (optional) timestamp of the message.
    11. chatToggled(bool isOpen)

      Called when the chat dialog is opened or closed.

      • isOpen : true if the chat dialog is open, false otherwise.
    12. participantsInfoRetrieved(String participantsInfo)

      Called when retrieveParticipantsInfo action is called

      • participantsInfo : a list of participants information as a string.
    13. readyToClose()

      Called when the SDK is ready to be closed. No meeting is happening at this point.

    Example of listener:

    var listener = JitsiMeetEventListener(
    conferenceJoined: (url) {
    debugPrint("conferenceJoined: url: $url");
    },

    participantJoined: (email, name, role, participantId) {
    debugPrint(
    "participantJoined: email: $email, name: $name, role: $role, "
    "participantId: $participantId",
    );
    participants.add(participantId!);
    },

    chatMessageReceived: (senderId, message, isPrivate) {
    debugPrint(
    "chatMessageReceived: senderId: $senderId, message: $message, "
    "isPrivate: $isPrivate",
    );
    },

    readyToClose: () {
    debugPrint("readyToClose");
    },
    );
    - +sample applications repository.

    Installation

    Add dependency

    Add the dependency from command-line

    $ flutter pub add jitsi_meet_flutter_sdk

    The command above will add this to the pubspec.yaml file in your project (you can do this manually):

    dependencies:
    jitsi_meet_flutter_sdk: ^0.1.4

    Install

    Install the packages from the terminal:

    $ flutter pub get

    Import files

    Import the following files into your dart code:

    import 'package:jitsi_meet_flutter_sdk/jitsi_meet_flutter_sdk.dart';

    Usage

    Join meeting

    Firstly, create a JitsiMeet object, then call the method join from it with a JitsiMeetConferenceOptions object

    var jitsiMeet = JitsiMeet();
    var options = JitsiMeetConferenceOptions(room: 'jitsiIsAwesome');
    jitsiMeet.join(options);

    Configuration

    iOS

    Make sure in Podfile from ios directory you set the ios version 12.4 or higher

    platform :ios, '12.4'

    The plugin requests camera and microphone access, make sure to include the required entries for NSCameraUsageDescription and NSMicrophoneUsageDescription in your Info.plist file from the ios/Runner directory.

    <key>NSCameraUsageDescription</key>
    <string>The app needs access to your camera for meetings.</string>
    <key>NSMicrophoneUsageDescription</key>
    <string>The app needs access to your microphone for meetings.</string>

    Android

    Go to android/app/build.gradle and make sure that the minSdkVersion is set to at lest 24

    android {
    ...
    defaultConfig {
    ...
    minSdkVersion 24
    }
    }

    The application:label field from the Jitsi Meet Android SDK will conflict with your application's one . Go to android/app/src/main/AndroidManifest.xml and add the tools library and tools:replace="android:label" to the application tag.

    <manifest xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:tools="http://schemas.android.com/tools">
    <application
    tools:replace="android:label"
    android:label="sample_app"
    android:name="${applicationName}"
    android:icon="@mipmap/ic_launcher">
    ...
    </application>
    </manifest>

    Using the API

    JitsiMeet

    The JitsiMeet class is the entry point for the sdk. It is used to launch the meeting screen, to send and receive all the events.

    1. JitsiMeet()

      The constructor for the class.
    1. join(JitsiMeetConferenceOptions options, [JitsiMeetEventListener? listener])

      Joins a meeting with the given options and optionally a listener is given

      • options : meeting options
      • listener : event listener for events triggered by the native sdks
    2. hangUp()

      The localParticipant leaves the current meeting.

    3. setAudioMuted(bool muted)

      Sets the state of the localParticipant audio muted according to the muted parameter.

    4. setVideoMuted(bool muted)

      Sets the state of the localParticipant video muted according to the muted parameter.

    5. sendEndpointTextMessage({String? to, required String message})

      Sends a message via the data channel to one particular participant or to all of them. If the to param is empty, the message will be sent to all the participants in the conference.

      In order to get the participantId, the participantsJoined event should be listened for, which have as a parameter the participantId and this should be stored somehow.

    6. toggleScreenShare(bool enabled)

      Sets the state of the localParticipant screen sharing according to the enabled parameter.

    7. openChat([String? to])

      Opens the chat dialog. If to contains a valid participantId, the private chat with that particular participant will be opened.

    8. sendChatMessage({String? to, required String message})

      Sends a chat message via to one particular participant or to all of them. If the to param is empty, the message will be sent to all the participants in the conference.

      In order to get the participantId, the participantsJoined event should be listened for, which have as a parameter the participantId and this should be stored somehow.

    9. closeChat()

      Closes the chat dialog.

    10. retrieveParticipantsInfo()

      Sends and event that will trigger the participantsInfoRetrieved event which will contain participants information

    JitsiMeetConferenceOptions

    This object encapsulates all the options that can be tweaked when joining a conference.

    Example:

    var options = JitsiMeetConferenceOptions(
    serverURL: "https://meet.jit.si",
    room: "jitsiIsAwesomeWithFlutter",
    configOverrides: {
    "startWithAudioMuted": false,
    "startWithVideoMuted": false,
    "subject" : "Jitsi with Flutter",
    },
    featureFlags: {
    "unsaferoomwarning.enabled": false
    },
    userInfo: JitsiMeetUserInfo(
    displayName: "Flutter user",
    email: "user@example.com"
    ),
    );
    • All the values that can be added to the configOverrides can be found here.

    • All the values that can be added to the featureFlags can be found here.

    • JitsiMeetUserInfo({String displayName, String email, String avatar})

      The constructor for the JitsiMeetUserInfo. 

      P.S. the avatar should be an url.

    JitsiMeetEventListener

    This class intends to be used as a listener for events that come from the native sdks. It will receive as arguments the event handlers

    1. conferenceJoined(String url)

      Called when a conference was joined.

      • url : the conference URL
    2. conferenceTerminated(String url, Object? error)

      Called when the active conference ends, be it because of user choice or because of a failure.

      • url : the conference URL
      • error : missing if the conference finished gracefully, otherwise contains the error message
    3. conferenceWillJoin(String url)

      Called before a conference is joined.

      • url: the conference URL
    4. participantJoined(String? email, String? name, String? role, String? participantId)

      Called when a participant has joined the conference.

      • email : the email of the participant. It may not be set if the remote participant didn't set one.
      • name : the name of the participant.
      • role : the role of the participant.
      • participantId : the id of the participant.
    5. participantLeft(String? participantId)

      Called when a participant has left the conference.

      • participantId : the id of the participant that left.
    6. audioMutedChanged(bool muted)

      Called when the local participant's audio is muted or unmuted.

      • muted : a boolean indicating whether the audio is muted or not.
    7. videoMutedChanged(bool muted)

      Called when the local participant's video is muted or unmuted.

      • muted : a boolean indicating whether the video is muted or not.
    8. endpointTextMessageReceived(String senderId, String message)

      Called when an endpoint text message is received.

      • senderId : the participantId of the sender
      • message : the content.
    9. screenShareToggled(String participantId, bool sharing)

      Called when a participant starts or stops sharing his screen.

      • participantId : the id of the participant
      • sharing : the state of screen share
    10. chatMessageReceived(String senderId, String message, bool isPrivate, String? timestamp)

      Called when a chat text message is received.

      • senderId : the id of the participant that sent the message.
      • message : the content of the message.
      • isPrivate : true if the message is private, false otherwise.
      • timestamp : the (optional) timestamp of the message.
    11. chatToggled(bool isOpen)

      Called when the chat dialog is opened or closed.

      • isOpen : true if the chat dialog is open, false otherwise.
    12. participantsInfoRetrieved(String participantsInfo)

      Called when retrieveParticipantsInfo action is called

      • participantsInfo : a list of participants information as a string.
    13. readyToClose()

      Called when the SDK is ready to be closed. No meeting is happening at this point.

    Example of listener:

    var listener = JitsiMeetEventListener(
    conferenceJoined: (url) {
    debugPrint("conferenceJoined: url: $url");
    },

    participantJoined: (email, name, role, participantId) {
    debugPrint(
    "participantJoined: email: $email, name: $name, role: $role, "
    "participantId: $participantId",
    );
    participants.add(participantId!);
    },

    chatMessageReceived: (senderId, message, isPrivate) {
    debugPrint(
    "chatMessageReceived: senderId: $senderId, message: $message, "
    "isPrivate: $isPrivate",
    );
    },

    readyToClose: () {
    debugPrint("readyToClose");
    },
    );
    + \ No newline at end of file diff --git a/docs/dev-guide/dev-guide-iframe-commands/index.html b/docs/dev-guide/dev-guide-iframe-commands/index.html index 3d38ca45a..202990e8d 100644 --- a/docs/dev-guide/dev-guide-iframe-commands/index.html +++ b/docs/dev-guide/dev-guide-iframe-commands/index.html @@ -4,7 +4,7 @@ Commands | Jitsi Meet - + @@ -12,8 +12,8 @@

    Commands

    You can control the embedded Jitsi Meet conference by calling executeCommand on the JitsiMeetExternalAPI object:

    api.executeCommand(command, ...arguments);

    The command parameter is a string which contains the command name.

    You can also execute multiple commands using the executeCommands method:

    api.executeCommands(commands);

    The commands parameter is an object with the names of the commands as keys and the arguments for the commands as values:

    api.executeCommands({
    displayName: [ 'nickname' ],
    toggleAudio: []
    });

    The following commands are supported:

    displayName

    Sets the display name of the local participant.

    This command requires one argument to set the new display name.

    api.executeCommand('displayName', 'New Nickname');

    password

    Sets the password for the room.

    // set new password for channel
    api.addEventListener('participantRoleChanged', function(event) {
    if (event.role === "moderator") {
    api.executeCommand('password', 'The Password');
    }
    });
    // join a protected channel
    api.on('passwordRequired', function ()
    {
    api.executeCommand('password', 'The Password');
    });

    toggleLobby

    Toggles the lobby mode on or off.

    This command requires the desired lobby mode state as the argument.

    api.addEventListener('participantRoleChanged', function (event) {
    if(event.role === 'moderator') {
    api.executeCommand('toggleLobby', true);
    }
    });

    sendTones

    Touch tone playback.

    This command requires the selected touch tone dial pads to play as well as the length of and time gap between tone play as the arguments.

    api.executeCommand('sendTones', {
    tones: string, // The dial pad touch tones to play. For example, '12345#'.
    duration: number, // Optional. The number of milliseconds each tone should play. The default is 200.
    pause: number // Optional. The number of milliseconds between each tone. The default is 200.
    });

    startShareVideo

    Starts sharing a video

    This command requires the an url pointing to either a youtube video or a video to be streamed from web (e.g an mp4 file)

    api.executeCommand('startShareVideo', url);

    stopShareVideo

    Stops sharing a video (if the user is the one who started the video)

    No arguments are required.

    api.executeCommand('stopShareVideo');

    subject

    Sets the subject of the conference.

    This command requires the new subject to be set as the argument and it will be applied only if the participant has the moderator role or after they receive that role later on.

    api.executeCommand('subject', 'New Conference Subject');

    localSubject

    Sets the local subject of the conference.

    This command requires the new local subject to be set as the argument and it can be applied by all participants regardless of their role.

    api.executeCommand('localSubject', 'New Conference Local Subject');

    toggleAudio

    Mutes / unmutes the audio for the local participant.

    No arguments are required.

    api.executeCommand('toggleAudio');

    toggleVideo

    Mutes / unmutes the video for the local participant.

    No arguments are required.

    api.executeCommand('toggleVideo');

    toggleFilmStrip

    Hide or show the filmstrip.

    No arguments are required.

    api.executeCommand('toggleFilmStrip');

    toggleChat

    Hide or show chat messaging.

    No arguments are required.

    api.executeCommand('toggleChat');

    toggleRaiseHand

    Hide or show the raised hand.

    No arguments are required.

    api.executeCommand('toggleRaiseHand')

    toggleShareScreen

    Start or stop screen sharing.

    No arguments are required.

    api.executeCommand('toggleShareScreen');

    setNoiseSuppressionEnabled

    Enable or disable noise suppression on the current audio track.

    api.executeCommand('setNoiseSuppressionEnabled', {
    enabled: boolean // Enable or disable noise suppression.
    });

    toggleSubtitles

    Start or stop subtitles.

    No arguments are required.

    api.executeCommand('toggleSubtitles');

    toggleTileView

    Enter or exit the tile view layout mode.

    No arguments are required.

    api.executeCommand('toggleTileView');

    hangup

    Ends the call.

    No arguments are required.

    api.executeCommand('hangup');

    endConference

    Ends the current conference for everyone.

    This command can only be executed by a meeting moderator, and requires End Conference support to be enabled for the deployment.

    api.executeCommand('endConference');

    email

    Changes the local email address.

    This command requires the new email address as the single argument.

    api.executeCommand('email', 'example@example.com');

    avatarUrl

    Changes the local avatar URL.

    This command requires the new avatar URL to be set as the single argument.

    api.executeCommand('avatarUrl', 'https://avatars0.githubusercontent.com/u/3671647');

    sendEndpointTextMessage

    Sends a text message to another participant through the data channels.

    api.executeCommand('sendEndpointTextMessage', 'receiverParticipantId', 'text');

    setLargeVideoParticipant

    Displays the participant on the large video display.

    The participant ID, if specified, is displayed on the large video. If no argument is passed, the participant to be displayed on the large video is automatically selected based on the dominant/pinned speaker settings.

    The second parameter is optional and can be used to specify a videoType. When multistream support is enabled by passing this parameter you can specify whether the desktop or the camera video for the specified participant should be selected. The accepted values are 'camera' and 'desktop'. The default is 'camera'. Any invalid values will be ignored and default will be used.

    api.executeCommand('setLargeVideoParticipant', 'abcd1234', 'desktop');

    setVideoQuality

    Sets the send and receive video resolution.

    The resolution height setting is implemented using a single argument.

    api.executeCommand('setVideoQuality', 720);

    muteEveryone

    Mute all meeting participants.

    This command can only be executed by the meeting moderator and can take one argument: mediaType - for which media type to mute everyone.

    mediaType can be either 'audio' (default) or 'video'.

    api.executeCommand('muteEveryone', 'video');

    startRecording

    Starts a local recording, file recording or streaming session using passed parameters:

    • RTMP streaming - Recording mode set to stream with an rtmpStreamKey. The rtmpBroadcastID value is optional.

    • YouTube streams - Recording mode set to stream with an youtubeStreamKey. The youtubeBroadcastID value is optional.

    • Local Recording - Recording mode set to local. The onlySelf value is optional.

    • Dropbox recording - Recording mode set to file with a Dropbox OAuth2 token.

      Additionally, Dropbox saving should be enabled on the Jitsi meet deploy config you are using.

    • File recording - Recording mode set to file.

      Optionally, shouldShare should be passed on. No other params are required.

    api.executeCommand('startRecording', {
    mode: string, //recording mode, either `local`, `file` or `stream`.
    dropboxToken: string, //dropbox oauth2 token.
    onlySelf: boolean, //Whether to only record the local streams. Only applies to `local` recording mode.
    shouldShare: boolean, //whether the recording should be shared with the participants or not. Only applies to certain jitsi meet deploys.
    rtmpStreamKey: string, //the RTMP stream key.
    rtmpBroadcastID: string, //the RTMP broadcast ID.
    youtubeStreamKey: string, //the youtube stream key.
    youtubeBroadcastID: string //the youtube broacast ID.
    });

    stopRecording

    Stops an ongoing local, stream or file recording.

    The mode in which the recording was started must be specified.

    api.executeCommand('stopRecording',
    mode: string //recording mode to stop, `local`, `stream` or `file`
    );

    initiatePrivateChat

    Opens the chat window and sets the participant with the given participant ID as the messages recipient.

    api.executeCommand('initiatePrivateChat',
    participantID: string
    );

    cancelPrivateChat

    Removes the private chat participant thus it resets the chat window to group chat.

    api.executeCommand('cancelPrivateChat');

    kickParticipant

    Kicks the participant with the given participant ID from the meeting.

    api.executeCommand('kickParticipant',
    participantID: string
    );

    grantModerator

    Grants moderator rights to the participant with the given ID.

    api.executeCommand('grantModerator',
    participantID: string
    );

    overwriteConfig

    Overwrite config.js props with values from the config object passed on to the command.

    api.executeCommand('overwriteConfig',
    config: Object
    );

    For example:

    api.executeCommand('overwriteConfig',
    {
    toolbarButtons: ['chat']
    }
    );

    will overwrite the toolbarButtons config value with [chat], resulting in UI only showing the chat button.

    sendChatMessage

    Sends a chat message either to a specific participant or as a group chat message.

    api.executeCommand('sendChatMessage',
    message: string, //the text message
    to: string, // the receiving participant ID or empty string/undefined for group chat.
    ignorePrivacy: boolean // true if the privacy notification should be ignored. Defaulted to false.
    );

    setFollowMe

    Allows moderators to toggle the follow me functionality

    api.executeCommand('setFollowMe',
    value: boolean, // set to true if participants should be following you, false otherwise
    );

    setSubtitles

    Enables or disables the subtitles.

    api.executeCommand('setSubtitles',
    enabled: boolean
    );

    setTileView

    Enables or disables the tileview mode.

    api.executeCommand('setTileView',
    enabled: boolean
    );

    answerKnockingParticipant

    Approves or rejects the knocking participant in the lobby.

    api.executeCommand('answerKnockingParticipant',
    id: string, // the participant id
    approved: boolean
    );

    toggleCamera

    Toggles the front/back camera on mobile web.

    api.executeCommand('toggleCamera');

    toggleCameraMirror

    Toggles the mirroring of the local video.

    api.executeCommand('toggleCameraMirror');

    toggleVirtualBackgroundDialog

    Toggles the virtual background selection dialog.

    api.executeCommand('toggleVirtualBackgroundDialog');

    pinParticipant

    Pins a conference participant.

    api.executeCommand('pinParticipant',
    id?: string // The ID of the conference participant to pin or null to unpin all
    );

    setParticipantVolume

    Change volume of the participant with the given participant ID.

    api.executeCommand('setParticipantVolume',
    participantID: string,
    volume: number // number between 0 and 1
    );

    toggleParticipantsPane

    Changes the visibility status of the participants pane.

    api.executeCommand('toggleParticipantsPane',
    enabled: boolean // The visibility status of the participants pane.
    );

    toggleModeration

    Changes moderation status of the given media type.

    This command requires two arguments: enable - whether to enable it or not, and mediaType - the media type for which to change moderation.

    api.executeCommand('toggleModeration',
    enable: Boolean,
    mediaType: String // can be 'audio' (default) or 'video'
    );

    askToUnmute

    Asks the participant with the given ID to unmute. If audio moderation is on it also approves the participant for audio.

    api.executeCommand('askToUnmute',
    participantId: String
    );

    approveVideo

    If video moderation is on it approves the participant with the given ID for video.

    api.executeCommand('approveVideo',
    participantId: String
    );

    rejectParticipant

    Rejects the participant with the given ID from moderation of the given media type.

    api.executeCommand('rejectParticipant',
    participantId: String,
    mediaType: String // can be 'audio' (default) or 'video'
    );

    addBreakoutRoom

    Creates a breakout room.

    This command can only be executed by the meeting moderator.

    api.executeCommand('addBreakoutRoom',
    name: String // Optional. The name or subject of the new room.
    );

    autoAssignToBreakoutRooms

    Auto-assigns the participants to breakout rooms.

    This command can only be executed by the meeting moderator.

    api.executeCommand('autoAssignToBreakoutRooms');

    closeBreakoutRoom

    Closes the breakout room and sends participants to the main room.

    This command can only be executed by the meeting moderator.

    api.executeCommand('closeBreakoutRoom',
    roomId: String // The id of the room to close.
    );

    joinBreakoutRoom

    Joins a breakout room. If the argument is omitted, joins the main room.

    api.executeCommand('joinBreakoutRoom',
    roomId: String // Optional. The id of the room to join.
    );

    removeBreakoutRoom

    Removes the breakout room.

    This command can only be executed by the meeting moderator.

    api.executeCommand('removeBreakoutRoom',
    breakoutRoomJid: String // The jid of the breakout room to remove.
    );

    resizeFilmStrip

    Resizes the filmstrip.

    api.executeCommand('resizeFilmStrip', {
    width: number // The desired filmstrip width
    });

    resizeLargeVideo

    Resizes the large video container based on the dimensions provided.

    api.executeCommand('resizeLargeVideo',
    width: number, // The desired width
    height: number // The desired height
    );

    sendParticipantToRoom

    Sends a participant to a room.

    This command can only be executed by the meeting moderator.

    api.executeCommand('sendParticipantToRoom',
    participantId: String, // The id of the participant.
    roomId: String // The id of the room.
    );

    overwriteNames

    Overwrites the names of the given participants to the given names. (locally for the participant that send the command)

    api.executeCommand('overwriteNames', [{
    id: String, // The id of the participant.
    name: String // The new name.
    }]
    );

    showNotification

    Shows a custom notification. This affects only the local user.

    If uid is provided, the notification will replace existing notification with the same uid. The uid can also be -passed to the hideNotification command to programmatically hide the notification.

    api.executeCommand('showNotification', {
    title: String, // Title of the notification.
    description: String, // Content of the notification.
    uid: String, // Optional. Unique identifier for the notification.
    type: String, // Optional. Can be 'info', 'normal', 'success', 'warning' or 'error'. Defaults to 'normal'.
    timeout: String // optional. Can be 'short', 'medium', 'long', or 'sticky'. Defaults to 'short'.
    });

    hideNotification

    Hides the notification which has the given uid.

    api.executeCommand('hideNotification',
    uid: String // Unique identifier for the notification to be removed.
    );

    toggleWhiteboard

    Toggles the whiteboard to open, repeated toggling hidden the whiteboard

    api.executeCommand('toggleWhiteboard');
    - +passed to the hideNotification command to programmatically hide the notification.

    api.executeCommand('showNotification', {
    title: String, // Title of the notification.
    description: String, // Content of the notification.
    uid: String, // Optional. Unique identifier for the notification.
    type: String, // Optional. Can be 'info', 'normal', 'success', 'warning' or 'error'. Defaults to 'normal'.
    timeout: String // optional. Can be 'short', 'medium', 'long', or 'sticky'. Defaults to 'short'.
    });

    hideNotification

    Hides the notification which has the given uid.

    api.executeCommand('hideNotification',
    uid: String // Unique identifier for the notification to be removed.
    );

    toggleWhiteboard

    Toggles the whiteboard to open, repeated toggling hidden the whiteboard

    api.executeCommand('toggleWhiteboard');
    + \ No newline at end of file diff --git a/docs/dev-guide/dev-guide-iframe-events/index.html b/docs/dev-guide/dev-guide-iframe-events/index.html index ee7f294f3..3682d9c1c 100644 --- a/docs/dev-guide/dev-guide-iframe-events/index.html +++ b/docs/dev-guide/dev-guide-iframe-events/index.html @@ -4,7 +4,7 @@ Events | Jitsi Meet - + @@ -17,8 +17,8 @@ https://github.com/jitsi/jitsi-meet/blob/042a2cb447bd9ff39ab3904e493952787bd30924/config.js#L547

    The listener receives an object with the following structure:

    {
    key: string, // the pressed button's key. The key is as defined in `toolbarButtons` config,
    preventExecution: boolean // whether the click routine execution was prevented or not.
    }

    outgoingMessage

    Provides event notifications about outgoing chat messages.

    The listener receives an object with the following structure:

    {
    message: string, // the text of the message
    privateMessage: boolean // whether this is a private or group message
    }

    displayNameChange

    Provides event notifications about display name changes.

    The listener receives an object with the following structure:

    {
    id: string, // the id of the participant that changed their display name
    displayname: string // the new display name
    }

    deviceListChanged

    Provides event notifications about device list changes.

    The listener receives an object with the following structure:

    {
    devices: Object // the new list of available devices.
    }

    NOTE: The device object has the same format as the getAvailableDevices result format.

    emailChange

    Provides event notifications about email changes.

    The listener receives an object with the following structure:

    {
    id: string, // the id of the participant that changed his email
    email: string // the new email
    }

    feedbackSubmitted

    Provides event notifications about conference feedback submissions:

    {
    error: string // The error which occurred during submission, if any.
    }

    filmstripDisplayChanged

    Provides event visibility notifications for the filmstrip that is being updated:

    {
    visible: boolean // Whether or not the filmstrip is displayed or hidden.
    }

    moderationStatusChanged

    Provides event notifications about changes to moderation status.

    {
    mediaType: string, // The media type for which moderation changed.
    enabled: boolean // Whether or not moderation changed to enabled.
    }

    moderationParticipantApproved

    Provides event notifications about participants approvals for moderation.

    {
    id: string, // The ID of the participant that got approved.
    mediaType: string // The media type for which the participant was approved.
    }

    moderationParticipantRejected

    Provides event notifications about participants rejections for moderation.

    {
    id: string, // The ID of the participant that got rejected.
    mediaType: string // The media type for which the participant was rejected.
    }

    notificationTriggered

    Provides event notifications when an application notification occurs.

    {
    title: string, // The notification title.
    description: string // The notification description.
    }

    participantJoined

    Provides event notifications about new participants who join the room.

    The listener receives an object with the following structure:

    {
    id: string, // the id of the participant
    displayName: string // the display name of the participant
    }

    participantKickedOut

    Provides event notifications about participants being removed from the room.

    The listener receives an object with the following structure:

    {
    kicked: {
    id: string, // the id of the participant removed from the room
    local: boolean // whether or not the participant is the local particiapnt
    },
    kicker: {
    id: string // the id of the participant who kicked out the other participant
    }
    }

    participantLeft

    Provides event notifications about participants that leave the meeting room.

    The listener receives an object with the following structure:

    {
    id: string // the id of the participant
    }

    participantRoleChanged

    Provides event notifications that fire when the local user role has changed (e.g., none, moderator, participant).

    The listener receives an object with the following structure:

    {
    id: string // the id of the participant
    role: string // the new role of the participant
    }

    participantsPaneToggled

    Provides event notifications that fire when the participants pane status changes.

    The listener receives an object with the following structure:

    {
    open: boolean // whether the pane is open or not
    }

    passwordRequired

    Provides event notifications that fire when participants fail to join a password protected room.

    videoConferenceJoined

    Provides event notifications that fire when the local user has joined the video conference.

    The listener receives an object with the following structure:

    {
    roomName: string, // the room name of the conference
    id: string, // the id of the local participant
    displayName: string, // the display name of the local participant
    avatarURL: string, // the avatar URL of the local participant
    breakoutRoom: boolean // whether the current room is a breakout room
    }

    videoConferenceLeft

    Provides event notifications that fire when the local user has left the video conference.

    The listener receives an object with the following structure:

    {
    roomName: string // the room name of the conference
    }

    videoAvailabilityChanged

    Provides event notifications about video availability status changes.

    The listener receives an object with the following structure:

    {
    available: boolean // new available status - boolean
    }

    videoMuteStatusChanged

    Provides event notifications about video mute status changes.

    The listener receives an object with the following structure:

    {
    muted: boolean // new muted status - boolean
    }

    videoQualityChanged

    Provides event notifications about changes to video quality settings.

    The listener receives an object with the following structure:

    {
    videoQuality: number // the height of the resolution related to the new video quality setting.
    }

    readyToClose

    Provides event notifications that fire when Jitsi Meet is ready to be closed (i.e., hangup operations are completed).

    recordingLinkAvailable

    Provides event notifications about recording link becoming available.

    The listener receives an object with the following structure:

    {
    link: string, // the recording link
    ttl: number // the time to live of the recording link
    }

    recordingStatusChanged

    Provides event notifications about recording status changes.

    The listener receives an object with the following structure:

    {
    on: boolean // new recording status - boolean,
    mode: string // recording mode, `local`, `stream` or `file`,
    error: string | undefined // error type if recording fails, undefined otherwise
    }

    subjectChange

    Provides event notifications regarding the change of subject for a conference.

    The listener receives an object with the following structure:

    {
    subject: string // the new subject
    }

    suspendDetected

    Provides notifications about detecting suspended events in the host computer.

    peerConnectionFailure

    Notify the external application that a PeerConnection lost connectivity. This event is fired only if a PC failed but connectivity to the rtcstats server is still maintained signaling that there is a problem establishing a link between the app and the JVB server or the remote peer in case of P2P. -Will only fire if rtcstats is enabled.

    {
    // Type of PC, Peer2Peer or JVB connection.
    isP2P: boolean,

    // Was this connection previously connected. If it was it could mean
    // that connectivity was disrupted, if not it most likely means that the app could not reach
    // the JVB server, or the other peer in case of P2P.
    wasConnected: boolean
    }

    whiteboardStatusChanged

    Provides event notifications about changes to the whiteboard.

    The listener receives an object with the following structure:

    {
    status: string // new whiteboard status
    }

    p2pStatusChanged

    Provides event notifications about changes to the connection type.

    The listener receives an object with the following structure:

    {
    isP2p: boolean|null // whether the new connection type is P2P
    }
    - +Will only fire if rtcstats is enabled.

    {
    // Type of PC, Peer2Peer or JVB connection.
    isP2P: boolean,

    // Was this connection previously connected. If it was it could mean
    // that connectivity was disrupted, if not it most likely means that the app could not reach
    // the JVB server, or the other peer in case of P2P.
    wasConnected: boolean
    }

    whiteboardStatusChanged

    Provides event notifications about changes to the whiteboard.

    The listener receives an object with the following structure:

    {
    status: string // new whiteboard status
    }

    p2pStatusChanged

    Provides event notifications about changes to the connection type.

    The listener receives an object with the following structure:

    {
    isP2p: boolean|null // whether the new connection type is P2P
    }
    + \ No newline at end of file diff --git a/docs/dev-guide/dev-guide-iframe-functions/index.html b/docs/dev-guide/dev-guide-iframe-functions/index.html index 0ced21fc1..111abb8d2 100644 --- a/docs/dev-guide/dev-guide-iframe-functions/index.html +++ b/docs/dev-guide/dev-guide-iframe-functions/index.html @@ -4,13 +4,13 @@ Functions | Jitsi Meet - +
    -

    Functions

    Use the following API functions to control your embedded Jitsi Meet Conference.

    captureLargeVideoScreenshot

    Captures a screenshot for the participant in the large video view (on stage).

    api.captureLargeVideoScreenshot().then(data => {
    // data is an Object with only one param, dataURL
    // data.dataURL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABQAA..."
    });

    getAvailableDevices

    Retrieves a list of available devices.

    api.getAvailableDevices().then(devices => {
    // devices = {
    // audioInput: [{
    // deviceId: 'ID'
    // groupId: 'grpID'
    // kind: 'audioinput'
    // label: 'label'
    // },....],
    // audioOutput: [{
    // deviceId: 'ID'
    // groupId: 'grpID'
    // kind: 'audioOutput'
    // label: 'label'
    // },....],
    // videoInput: [{
    // deviceId: 'ID'
    // groupId: 'grpID'
    // kind: 'videoInput'
    // label: 'label'
    // },....]
    // }
    ...
    });

    getContentSharingParticipants

    Returns a promise which resolves with an array of currently sharing participants ID's.

    api.getContentSharingParticipants().then(res => {
    //res.sharingParticipantIds = [particId1, particId2, ...]
    });

    getCurrentDevices

    Retrieves a list of currently selected devices.

    api.getCurrentDevices().then(devices => {
    // devices = {
    // audioInput: {
    // deviceId: 'ID'
    // groupId: 'grpID'
    // kind: 'videoInput'
    // label: 'label'
    // },
    // audioOutput: {
    // deviceId: 'ID'
    // groupId: 'grpID'
    // kind: 'videoInput'
    // label: 'label'
    // },
    // videoInput: {
    // deviceId: 'ID'
    // groupId: 'grpID'
    // kind: 'videoInput'
    // label: 'label'
    // }
    // }
    ...
    });

    getDeploymentInfo

    Retrieves an object containing information about the deployment.

    api.getDeploymentInfo().then(deploymentInfo => {
    // deploymentInfo = {
    // region: 'deployment-region',
    // shard: 'deployment-shard',
    // ...
    // }
    ...
    });

    getLivestreamUrl

    Retrieves an object containing information about livestreamUrl of the current live stream.

    api.getLivestreamUrl().then(livestreamData => {
    // livestreamData = {
    // livestreamUrl: 'livestreamUrl'
    // }
    ...
    });

    getParticipantsInfo

    DEPRECATED Use getRoomsInfo instead.

    Returns an array containing participant information such as ID, display name, avatar URL, and email.

    api.getParticipantsInfo();

    getRoomsInfo

    Returns an array of available rooms and details of it:

    • isMainRoom (true,false), id, jid
    • participants: Participant[]
      • id
      • jid
      • role
      • displayName
    api.getRoomsInfo().then(rooms => {
    ... // see response example structure
    })

    Response example structure:

    {
    "rooms": [
    {
    "isMainRoom": true,
    "id": "room_name@conference.jitsi",
    "jid": "room_name@conference.jitsi/aaaaaa",
    "participants": [
    {
    "jid": "room_name@conference.jitsi/bbbbbb",
    "role": "participant",
    "displayName": "p1",
    "id": "bbbbbb"
    },
    {
    "jid": "room_name@conference.jitsi/cccccc",
    "role": "participant",
    "displayName": "p2",
    "id": "cccccc"
    }
    ]
    },
    {
    "isMainRoom": false,
    "id": "aaaaaa-bbb-cccc-dddd-qwertyuiopas",
    "jid": "aaaaaa-bbb-cccc-dddd-qwertyuiopas@breakout.jitsi",
    "participants": [{
    "jid": "aaaaaa-cccc-dddd-eeee-qwertyuiopas@jitsi/abcd1234",
    "role": "moderator",
    "displayName": "Participant name",
    "avatarUrl": "",
    "id": "abcd1234"
    }]
    },
    ]
    }

    getVideoQuality

    Returns the current video quality setting.

    api.getVideoQuality();

    getSupportedCommands

    Returns array of commands supported by api.executeCommand(command, ...arguments);

    api.getSupportedCommands();

    getSupportedEvents

    Returns array of events supported by api.addListener(event, listener);

    api.getSupportedEvents();

    isDeviceChangeAvailable

    Resolves to true if the device change is available and to false if not.

    // The accepted deviceType values are - 'output', 'input' or undefined.
    api.isDeviceChangeAvailable(deviceType).then(isDeviceChangeAvailable => {
    ...
    });

    isDeviceListAvailable

    Resolves to true if the device list is available and to false if not.

    api.isDeviceListAvailable().then(isDeviceListAvailable => {
    ...
    });

    isMultipleAudioInputSupported

    Resolves to true if multiple audio input is supported and to false if not.

    api.isMultipleAudioInputSupported().then(isMultipleAudioInputSupported => {
    ...
    });

    pinParticipant

    Selects the participant ID to be the pinned participant in order to always receive video for this participant.

    The second parameter is optional and can be used to specify a videoType. When multistream support is enabled by passing this parameter you can specify whether the desktop or the camera video for the specified participant should be pinned. The accepted values are 'camera' and 'desktop'. The default is 'camera'. Any invalid values will be ignored and default will be used.

    api.pinParticipant(participantId, videoType);

    resizeLargeVideo

    Resizes the large video container per the provided dimensions.

    api.resizeLargeVideo(width, height);

    setAudioInputDevice

    Sets the audio input device to the one with the passed label or ID.

    api.setAudioInputDevice(deviceLabel, deviceId);

    setAudioOutputDevice

    Sets the audio output device to the one with the passed label or ID.

    api.setAudioOutputDevice(deviceLabel, deviceId);

    setLargeVideoParticipant

    Displays the participant with the given participant ID on the large video.

    If no participant ID is given, a participant is picked based on the dominant, pinned speaker settings.

    api.setLargeVideoParticipant(participantId);

    setVideoInputDevice

    Sets the video input device to the one with the passed label or ID.

    api.setVideoInputDevice(deviceLabel, deviceId);

    startRecording

    Starts a file recording or streaming session. See the startRecording command for more details.

    api.startRecording(options);

    stopRecording

    Stops an ongoing file recording or streaming session. See the stopRecording command for more details.

    api.stopRecording(mode);

    getNumberOfParticipants

    Returns the number of conference participants:

    const numberOfParticipants = api.getNumberOfParticipants();

    getAvatarURL

    DEPRECATED Use getRoomsInfo instead.

    Returns a participant's avatar URL:

    const avatarURL = api.getAvatarURL(participantId);

    getDisplayName

    Returns a participant's display name:

    const displayName = api.getDisplayName(participantId);

    getEmail

    Returns a participant's email:

    const email = api.getEmail(participantId);

    getIFrame

    Returns the IFrame HTML element which is used to load the Jitsi Meet conference:

    const iframe = api.getIFrame();

    isAudioDisabled

    Returns a Promise which resolves to the current audio disabled state:

    api.isAudioDisabled().then(disabled => {
    ...
    });

    isAudioMuted

    Returns a Promise which resolves to the current audio muted state:

    api.isAudioMuted().then(muted => {
    ...
    });

    isVideoMuted

    Returns a Promise which resolves to the current video muted state:

    api.isVideoMuted().then(muted => {
    ...
    });

    isAudioAvailable

    Returns a Promise which resolves to the current audio availability state:

    api.isAudioAvailable().then(available => {
    ...
    });

    isVideoAvailable

    Returns a Promise which resolves to the current video availability state:

    api.isVideoAvailable().then(available => {
    ...
    });

    isModerationOn

    Returns a Promise which resolves to the current moderation state of the given media type.

    mediaType can be either audio (default) or video.

    api.isModerationOn(mediaType).then(isModerationOn => {
    ...
    });

    isP2pActive

    Returns a Promise which resolves to a Boolean or null, when there is no conference.

    api.isP2pActive().then(isP2p => {
    ...
    });

    isParticipantForceMuted

    Returns a Promise which resolves to the current force mute state of the given participant for the given media type.

    mediaType can be either audio (default) or video.

    Force muted - moderation is on and participant is not allowed to unmute the given media type.

    api.isParticipantForceMuted(participantId, mediaType).then(isForceMuted => {
    ...
    });

    isParticipantsPaneOpen

    Returns a Promise which resolves with the current participants pane state.

    api.isParticipantsPaneOpen().then(state => {
    ...
    });

    isStartSilent

    Returns a Promise which resolves with whether meeting was started in view only.

    api.isStartSilent().then(startSilent => {
    ...
    });

    listBreakoutRooms

    Returns a Promise which resolves with the map of breakout rooms.

    api.listBreakoutRooms().then(breakoutRooms => {
    ...
    });

    invite

    Invite the given array of participants to the meeting:

    api.invite([ {...}, {...}, {...} ]).then(() => {
    // success
    }).catch(() => {
    // failure
    });

    NOTE: The invitee format in the array depends on the invite service used in the deployment.

    PSTN invite objects have the following structure:

    {
    type: 'phone',
    number: <string> // the phone number in E.164 format (ex. +31201234567)
    }

    SIP invite objects have the following structure:

    {
    type: 'sip',
    address: <string> // the sip address
    }

    dispose

    Removes the embedded Jitsi Meet conference:

    api.dispose();

    NOTE: Jitsi recommends removing the conference before the page is unloaded.

    - +

    Functions

    Use the following API functions to control your embedded Jitsi Meet Conference.

    captureLargeVideoScreenshot

    Captures a screenshot for the participant in the large video view (on stage).

    api.captureLargeVideoScreenshot().then(data => {
    // data is an Object with only one param, dataURL
    // data.dataURL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABQAA..."
    });

    getAvailableDevices

    Retrieves a list of available devices.

    api.getAvailableDevices().then(devices => {
    // devices = {
    // audioInput: [{
    // deviceId: 'ID'
    // groupId: 'grpID'
    // kind: 'audioinput'
    // label: 'label'
    // },....],
    // audioOutput: [{
    // deviceId: 'ID'
    // groupId: 'grpID'
    // kind: 'audioOutput'
    // label: 'label'
    // },....],
    // videoInput: [{
    // deviceId: 'ID'
    // groupId: 'grpID'
    // kind: 'videoInput'
    // label: 'label'
    // },....]
    // }
    ...
    });

    getContentSharingParticipants

    Returns a promise which resolves with an array of currently sharing participants ID's.

    api.getContentSharingParticipants().then(res => {
    //res.sharingParticipantIds = [particId1, particId2, ...]
    });

    getCurrentDevices

    Retrieves a list of currently selected devices.

    api.getCurrentDevices().then(devices => {
    // devices = {
    // audioInput: {
    // deviceId: 'ID'
    // groupId: 'grpID'
    // kind: 'videoInput'
    // label: 'label'
    // },
    // audioOutput: {
    // deviceId: 'ID'
    // groupId: 'grpID'
    // kind: 'videoInput'
    // label: 'label'
    // },
    // videoInput: {
    // deviceId: 'ID'
    // groupId: 'grpID'
    // kind: 'videoInput'
    // label: 'label'
    // }
    // }
    ...
    });

    getDeploymentInfo

    Retrieves an object containing information about the deployment.

    api.getDeploymentInfo().then(deploymentInfo => {
    // deploymentInfo = {
    // region: 'deployment-region',
    // shard: 'deployment-shard',
    // ...
    // }
    ...
    });

    getLivestreamUrl

    Retrieves an object containing information about livestreamUrl of the current live stream.

    api.getLivestreamUrl().then(livestreamData => {
    // livestreamData = {
    // livestreamUrl: 'livestreamUrl'
    // }
    ...
    });

    getParticipantsInfo

    DEPRECATED Use getRoomsInfo instead.

    Returns an array containing participant information such as ID, display name, avatar URL, and email.

    api.getParticipantsInfo();

    getRoomsInfo

    Returns an array of available rooms and details of it:

    • isMainRoom (true,false), id, jid
    • participants: Participant[]
      • id
      • jid
      • role
      • displayName
    api.getRoomsInfo().then(rooms => {
    ... // see response example structure
    })

    Response example structure:

    {
    "rooms": [
    {
    "isMainRoom": true,
    "id": "room_name@conference.jitsi",
    "jid": "room_name@conference.jitsi/aaaaaa",
    "participants": [
    {
    "jid": "room_name@conference.jitsi/bbbbbb",
    "role": "participant",
    "displayName": "p1",
    "id": "bbbbbb"
    },
    {
    "jid": "room_name@conference.jitsi/cccccc",
    "role": "participant",
    "displayName": "p2",
    "id": "cccccc"
    }
    ]
    },
    {
    "isMainRoom": false,
    "id": "aaaaaa-bbb-cccc-dddd-qwertyuiopas",
    "jid": "aaaaaa-bbb-cccc-dddd-qwertyuiopas@breakout.jitsi",
    "participants": [{
    "jid": "aaaaaa-cccc-dddd-eeee-qwertyuiopas@jitsi/abcd1234",
    "role": "moderator",
    "displayName": "Participant name",
    "avatarUrl": "",
    "id": "abcd1234"
    }]
    },
    ]
    }

    getVideoQuality

    Returns the current video quality setting.

    api.getVideoQuality();

    getSupportedCommands

    Returns array of commands supported by api.executeCommand(command, ...arguments);

    api.getSupportedCommands();

    getSupportedEvents

    Returns array of events supported by api.addListener(event, listener);

    api.getSupportedEvents();

    isDeviceChangeAvailable

    Resolves to true if the device change is available and to false if not.

    // The accepted deviceType values are - 'output', 'input' or undefined.
    api.isDeviceChangeAvailable(deviceType).then(isDeviceChangeAvailable => {
    ...
    });

    isDeviceListAvailable

    Resolves to true if the device list is available and to false if not.

    api.isDeviceListAvailable().then(isDeviceListAvailable => {
    ...
    });

    isMultipleAudioInputSupported

    Resolves to true if multiple audio input is supported and to false if not.

    api.isMultipleAudioInputSupported().then(isMultipleAudioInputSupported => {
    ...
    });

    pinParticipant

    Selects the participant ID to be the pinned participant in order to always receive video for this participant.

    The second parameter is optional and can be used to specify a videoType. When multistream support is enabled by passing this parameter you can specify whether the desktop or the camera video for the specified participant should be pinned. The accepted values are 'camera' and 'desktop'. The default is 'camera'. Any invalid values will be ignored and default will be used.

    api.pinParticipant(participantId, videoType);

    resizeLargeVideo

    Resizes the large video container per the provided dimensions.

    api.resizeLargeVideo(width, height);

    setAudioInputDevice

    Sets the audio input device to the one with the passed label or ID.

    api.setAudioInputDevice(deviceLabel, deviceId);

    setAudioOutputDevice

    Sets the audio output device to the one with the passed label or ID.

    api.setAudioOutputDevice(deviceLabel, deviceId);

    setLargeVideoParticipant

    Displays the participant with the given participant ID on the large video.

    If no participant ID is given, a participant is picked based on the dominant, pinned speaker settings.

    api.setLargeVideoParticipant(participantId);

    setVideoInputDevice

    Sets the video input device to the one with the passed label or ID.

    api.setVideoInputDevice(deviceLabel, deviceId);

    startRecording

    Starts a file recording or streaming session. See the startRecording command for more details.

    api.startRecording(options);

    stopRecording

    Stops an ongoing file recording or streaming session. See the stopRecording command for more details.

    api.stopRecording(mode);

    getNumberOfParticipants

    Returns the number of conference participants:

    const numberOfParticipants = api.getNumberOfParticipants();

    getAvatarURL

    DEPRECATED Use getRoomsInfo instead.

    Returns a participant's avatar URL:

    const avatarURL = api.getAvatarURL(participantId);

    getDisplayName

    Returns a participant's display name:

    const displayName = api.getDisplayName(participantId);

    getEmail

    Returns a participant's email:

    const email = api.getEmail(participantId);

    getIFrame

    Returns the IFrame HTML element which is used to load the Jitsi Meet conference:

    const iframe = api.getIFrame();

    isAudioDisabled

    Returns a Promise which resolves to the current audio disabled state:

    api.isAudioDisabled().then(disabled => {
    ...
    });

    isAudioMuted

    Returns a Promise which resolves to the current audio muted state:

    api.isAudioMuted().then(muted => {
    ...
    });

    isVideoMuted

    Returns a Promise which resolves to the current video muted state:

    api.isVideoMuted().then(muted => {
    ...
    });

    isAudioAvailable

    Returns a Promise which resolves to the current audio availability state:

    api.isAudioAvailable().then(available => {
    ...
    });

    isVideoAvailable

    Returns a Promise which resolves to the current video availability state:

    api.isVideoAvailable().then(available => {
    ...
    });

    isModerationOn

    Returns a Promise which resolves to the current moderation state of the given media type.

    mediaType can be either audio (default) or video.

    api.isModerationOn(mediaType).then(isModerationOn => {
    ...
    });

    isP2pActive

    Returns a Promise which resolves to a Boolean or null, when there is no conference.

    api.isP2pActive().then(isP2p => {
    ...
    });

    isParticipantForceMuted

    Returns a Promise which resolves to the current force mute state of the given participant for the given media type.

    mediaType can be either audio (default) or video.

    Force muted - moderation is on and participant is not allowed to unmute the given media type.

    api.isParticipantForceMuted(participantId, mediaType).then(isForceMuted => {
    ...
    });

    isParticipantsPaneOpen

    Returns a Promise which resolves with the current participants pane state.

    api.isParticipantsPaneOpen().then(state => {
    ...
    });

    isStartSilent

    Returns a Promise which resolves with whether meeting was started in view only.

    api.isStartSilent().then(startSilent => {
    ...
    });

    listBreakoutRooms

    Returns a Promise which resolves with the map of breakout rooms.

    api.listBreakoutRooms().then(breakoutRooms => {
    ...
    });

    invite

    Invite the given array of participants to the meeting:

    api.invite([ {...}, {...}, {...} ]).then(() => {
    // success
    }).catch(() => {
    // failure
    });

    NOTE: The invitee format in the array depends on the invite service used in the deployment.

    PSTN invite objects have the following structure:

    {
    type: 'phone',
    number: <string> // the phone number in E.164 format (ex. +31201234567)
    }

    SIP invite objects have the following structure:

    {
    type: 'sip',
    address: <string> // the sip address
    }

    dispose

    Removes the embedded Jitsi Meet conference:

    api.dispose();

    NOTE: Jitsi recommends removing the conference before the page is unloaded.

    + \ No newline at end of file diff --git a/docs/dev-guide/dev-guide-iframe/index.html b/docs/dev-guide/dev-guide-iframe/index.html index 3b313fc21..eaa7bafce 100644 --- a/docs/dev-guide/dev-guide-iframe/index.html +++ b/docs/dev-guide/dev-guide-iframe/index.html @@ -4,13 +4,13 @@ IFrame API | Jitsi Meet - +
    -

    IFrame API

    Embedding the Jitsi Meet API into your site or app enables you to host and provide secure video meetings with your colleagues, teams, and stakeholders. The Meet API provides a full complement of comprehensive meeting features.

    Your Jitsi meetings can be hosted and attended using any device while keeping your data and privacy protected. You can reach your meeting participants anywhere in the world eliminating the need for travel and the associated inconvenience.

    The IFrame API enables you to embed Jitsi Meet functionality into your meeting application so you can experience the full functionality of the globally distributed and highly available deployment available with meet.jit.si.

    You can also embed and integrate the globally distributed and highly available deployment on the meet.jit.si platform itself.

    NOTE

    JaaS customers, please make sure you also read this!

    tip

    If you use React in your web application you might want to use our React SDK instead.

    Integration

    To enable the Jitsi Meet API in your application you must use one of the following JavaScript (JS) Jitsi Meet API library scripts and integrate it into your application:

    For self-hosting in your domain:

    <script src='https://<your-domain>/external_api.js'></script>

    meet.jit.si:

    <script src='https://meet.jit.si/external_api.js'></script>

    Mobile support

    The iframe API works on mobile browsers the same way as it does on desktop browsers.

    Opening meetings in the Jitsi Meet app

    In order to open meetings with the Jitsi Meet app you can use our custom URL scheme as follows:

    (let's assume the meeting is https://meet.jit.si/test123)

    • Android: intent://meet.jit.si/test123#Intent;scheme=org.jitsi.meet;package=org.jitsi.meet;end
    • iOS: org.jitsi.meet://meet.jit.si/test123

    This works with custom servers too, just replace meet.jit.si with your custom server URL.

    Creating the Jitsi Meet API object

    After you have integrated the Meet API library, you must then create the Jitsi Meet API object.

    The Meet API object takes the following form:

    api = new JitsiMeetExternalAPI(domain, options)

    The API object constructor uses the following options:

    • domain: The domain used to build the conference URL (e.g., meet.jit.si).

    • options: The object with properties.

      Optional arguments include:

      • roomName: The name of the room to join.

      • width: The created IFrame width.

        The width argument has the following characteristics:

        • A numerical value indicates the width in pixel units.

        • If a string is specified the format is a number followed by px, em, pt, or %.

      • height: The height for the created IFrame.

        The height argument has the following characteristics:

        • A numerical value indicates the height in pixel units.

        • If a string is specified the format is a number followed by px, em, pt, or %.

      • parentNode: The HTML DOM Element where the IFrame is added as a child.

      • configOverwrite: The JS object with overrides for options defined in the config.js file.

      • interfaceConfigOverwrite: The JS object with overrides for options defined in the interface_config.js file.

      • jwt: The JWT token.

      • onload: The IFrame onload event handler.

      • invitees: Object arrays that contain information about participants invited to a call.

      • devices: Information map about the devices used in a call.

      • userInfo: The JS object that contains information about the participant starting or joining the meeting (e.g., email).

      • lang: The default meeting language.

        For example:

    const domain = 'meet.jit.si';
    const options = {
    roomName: 'JitsiMeetAPIExample',
    width: 700,
    height: 700,
    parentNode: document.querySelector('#meet'),
    lang: 'de'
    };
    const api = new JitsiMeetExternalAPI(domain, options);

    You can set the initial media devices for the call using the following:

    const domain = 'meet.jit.si';
    const options = {
    ...
    devices: {
    audioInput: '<deviceLabel>',
    audioOutput: '<deviceLabel>',
    videoInput: '<deviceLabel>'
    },
    ...
    };
    const api = new JitsiMeetExternalAPI(domain, options);

    You can override options set in the config.js file and the interface_config.js file using the configOverwrite and interfaceConfigOverwrite objects, respectively.

    For example:

    const options = {
    ...
    configOverwrite: { startWithAudioMuted: true },
    interfaceConfigOverwrite: { DISABLE_DOMINANT_SPEAKER_INDICATOR: true },
    ...
    };
    const api = new JitsiMeetExternalAPI(domain, options);

    To pass a JWT token to Jitsi Meet use the following:

    const options = {
    ...
    jwt: '<jwt_token>',
    ...
    };
    const api = new JitsiMeetExternalAPI(domain, options);

    You can set the userInfo (e.g., email, display name) for the call using the following:

    var domain = "meet.jit.si";
    var options = {
    ...
    userInfo: {
    email: 'email@jitsiexamplemail.com',
    displayName: 'John Doe'
    }
    }
    var api = new JitsiMeetExternalAPI(domain, options);

    Configuring the tile view:

    You can configure the maximum number of columns in the tile view by overriding the TILE_VIEW_MAX_COLUMNS property from the interface_config.js file via the interfaceConfigOverwrite object:

    const options = {
    ...
    interfaceConfigOverwrite: { TILE_VIEW_MAX_COLUMNS: 2 },
    ...
    };
    const api = new JitsiMeetExternalAPI(domain, options);
    note

    TILE_VIEW_MAX_COLUMNS accepts values from 1 to 5. The default value is 5.

    Functions

    All functions are documented here now.

    Commands

    All commands are documented here now.

    Events

    All events are documented here now.

    - +

    IFrame API

    Embedding the Jitsi Meet API into your site or app enables you to host and provide secure video meetings with your colleagues, teams, and stakeholders. The Meet API provides a full complement of comprehensive meeting features.

    Your Jitsi meetings can be hosted and attended using any device while keeping your data and privacy protected. You can reach your meeting participants anywhere in the world eliminating the need for travel and the associated inconvenience.

    The IFrame API enables you to embed Jitsi Meet functionality into your meeting application so you can experience the full functionality of the globally distributed and highly available deployment available with meet.jit.si.

    You can also embed and integrate the globally distributed and highly available deployment on the meet.jit.si platform itself.

    NOTE

    JaaS customers, please make sure you also read this!

    tip

    If you use React in your web application you might want to use our React SDK instead.

    Integration

    To enable the Jitsi Meet API in your application you must use one of the following JavaScript (JS) Jitsi Meet API library scripts and integrate it into your application:

    For self-hosting in your domain:

    <script src='https://<your-domain>/external_api.js'></script>

    meet.jit.si:

    <script src='https://meet.jit.si/external_api.js'></script>

    Mobile support

    The iframe API works on mobile browsers the same way as it does on desktop browsers.

    Opening meetings in the Jitsi Meet app

    In order to open meetings with the Jitsi Meet app you can use our custom URL scheme as follows:

    (let's assume the meeting is https://meet.jit.si/test123)

    • Android: intent://meet.jit.si/test123#Intent;scheme=org.jitsi.meet;package=org.jitsi.meet;end
    • iOS: org.jitsi.meet://meet.jit.si/test123

    This works with custom servers too, just replace meet.jit.si with your custom server URL.

    Creating the Jitsi Meet API object

    After you have integrated the Meet API library, you must then create the Jitsi Meet API object.

    The Meet API object takes the following form:

    api = new JitsiMeetExternalAPI(domain, options)

    The API object constructor uses the following options:

    • domain: The domain used to build the conference URL (e.g., meet.jit.si).

    • options: The object with properties.

      Optional arguments include:

      • roomName: The name of the room to join.

      • width: The created IFrame width.

        The width argument has the following characteristics:

        • A numerical value indicates the width in pixel units.

        • If a string is specified the format is a number followed by px, em, pt, or %.

      • height: The height for the created IFrame.

        The height argument has the following characteristics:

        • A numerical value indicates the height in pixel units.

        • If a string is specified the format is a number followed by px, em, pt, or %.

      • parentNode: The HTML DOM Element where the IFrame is added as a child.

      • configOverwrite: The JS object with overrides for options defined in the config.js file.

      • interfaceConfigOverwrite: The JS object with overrides for options defined in the interface_config.js file.

      • jwt: The JWT token.

      • onload: The IFrame onload event handler.

      • invitees: Object arrays that contain information about participants invited to a call.

      • devices: Information map about the devices used in a call.

      • userInfo: The JS object that contains information about the participant starting or joining the meeting (e.g., email).

      • lang: The default meeting language.

        For example:

    const domain = 'meet.jit.si';
    const options = {
    roomName: 'JitsiMeetAPIExample',
    width: 700,
    height: 700,
    parentNode: document.querySelector('#meet'),
    lang: 'de'
    };
    const api = new JitsiMeetExternalAPI(domain, options);

    You can set the initial media devices for the call using the following:

    const domain = 'meet.jit.si';
    const options = {
    ...
    devices: {
    audioInput: '<deviceLabel>',
    audioOutput: '<deviceLabel>',
    videoInput: '<deviceLabel>'
    },
    ...
    };
    const api = new JitsiMeetExternalAPI(domain, options);

    You can override options set in the config.js file and the interface_config.js file using the configOverwrite and interfaceConfigOverwrite objects, respectively.

    For example:

    const options = {
    ...
    configOverwrite: { startWithAudioMuted: true },
    interfaceConfigOverwrite: { DISABLE_DOMINANT_SPEAKER_INDICATOR: true },
    ...
    };
    const api = new JitsiMeetExternalAPI(domain, options);

    To pass a JWT token to Jitsi Meet use the following:

    const options = {
    ...
    jwt: '<jwt_token>',
    ...
    };
    const api = new JitsiMeetExternalAPI(domain, options);

    You can set the userInfo (e.g., email, display name) for the call using the following:

    var domain = "meet.jit.si";
    var options = {
    ...
    userInfo: {
    email: 'email@jitsiexamplemail.com',
    displayName: 'John Doe'
    }
    }
    var api = new JitsiMeetExternalAPI(domain, options);

    Configuring the tile view:

    You can configure the maximum number of columns in the tile view by overriding the TILE_VIEW_MAX_COLUMNS property from the interface_config.js file via the interfaceConfigOverwrite object:

    const options = {
    ...
    interfaceConfigOverwrite: { TILE_VIEW_MAX_COLUMNS: 2 },
    ...
    };
    const api = new JitsiMeetExternalAPI(domain, options);
    note

    TILE_VIEW_MAX_COLUMNS accepts values from 1 to 5. The default value is 5.

    Functions

    All functions are documented here now.

    Commands

    All commands are documented here now.

    Events

    All events are documented here now.

    + \ No newline at end of file diff --git a/docs/dev-guide/dev-guide-ios-sdk/index.html b/docs/dev-guide/dev-guide-ios-sdk/index.html index dcc6973c7..c9329df03 100644 --- a/docs/dev-guide/dev-guide-ios-sdk/index.html +++ b/docs/dev-guide/dev-guide-ios-sdk/index.html @@ -4,7 +4,7 @@ iOS SDK | Jitsi Meet - + @@ -45,8 +45,8 @@ desired, apps need to implement non-native Picture-in-Picture themselves and resize JitsiMeetView.

    If delegate implements enterPictureInPicture:, the in-call toolbar will render a button to afford the user to request entering Picture-in-Picture.

    Dropbox integration

    To setup the Dropbox integration, follow these steps:

    1. Add the following to the app's Info.plist and change <APP_KEY> to your -Dropbox app key:
    <key>CFBundleURLTypes</key>
    <array>
    <dict>
    <key>CFBundleURLName</key>
    <string></string>
    <key>CFBundleURLSchemes</key>
    <array>
    <string>db-<APP_KEY></string>
    </array>
    </dict>
    </array>
    <key>LSApplicationQueriesSchemes</key>
    <array>
    <string>dbapi-2</string>
    <string>dbapi-8-emm</string>
    </array>
    1. Make sure your app calls the Jitsi Meet SDK universal / deep linking delegate methods.

    Screen Sharing integration

    The screen sharing functionality for iOS was added to Jitsi starting with JitsiMeetSDK version 3.3.0. It is available for applications running on iOS 14 or newer.

    For achieving this we are using the Broadcast Upload Extension for capturing the contents of the user's screen. Passing the frames to the RN WebRTC is done using Unix stream-oriented sockets communication, the extension acting as the client and the React Native WebRTC being the server.

    The following documentation covers the code provided in the sample app.

    Creating the Broadcast Upload Extension

    The Broadcast Upload Extension is one of the App Extensions types defined in iOS and is used for capturing the contents of the user's screen.

    For creating the extension you need to add a new target to your application, selecting the Broadcast Upload Extension template. Fill in the desired name, change the language to Swift, make sure Include UI Extension is not selected, as we don't need custom UI for our case, then press Finish (screenshot 1). You will see that a new folder with the extension's name was added to the project's tree, containing the SampleHandler.swift class. Also, make sure to update the Deployment Info, for the newly created extension, to iOS 14 or newer. To learn more about creating App Extensions check the official documentation.

    screenshot 1

    With the extension created the next steps are to set up the socket connection, add the functionality for handling the received frames, and send them to RN WebRTC for processing. We will be using the code provided with the sample project for this. Copy SampleUploader.swift, SocketConnection.swift, DarwinNotificationCenter.swift, and Atomic.swift files to your extension's folder and make sure they're added to the target.

    Setting up the socket connection

    Sending the recorded frames to RN WebRTC is done via Unix SOCK_STREAM sockets. The extension needs to be set up as the client endpoint for this.

    We will update SampleHandler.swift to initiate the socket connection with RN WebRTC, using the SocketConnection class. But before, we have to set up the file that the sockets will use for communication.

    Even though an app extension bundle is nested within its containing app’s bundle, the running app extension and containing app have no direct access to each other’s containers. We will address this by enabling data sharing. To enable data sharing, use Xcode or the Developer portal to enable app groups for the containing app and its contained app extensions. Next, register the app group in the portal and specify the app group to use in the containing app. To learn about working with app groups, see Adding an App to an App Group.

    Now, add a private var socketFilePath: String to your SampleHandler class and set it up with a shared file named rtc_SSFD, using the newly registered app group, like this:

    private enum Constants {
    static let appGroupIdentifier = "my.custom.app.group"
    }

    private var socketFilePath: String {
    let sharedContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: Constants.appGroupIdentifier)

    return sharedContainer?.appendingPathComponent("rtc_SSFD").path ?? ""
    }

    Next, we will configure the SocketConnection to use the shared file. Add a private var clientConnection: SocketConnection? to the SampleHandler class and override init to set it up, like this:

    override init() {
    super.init()
    if let connection = SocketConnection(filePath: socketFilePath) {
    clientConnection = connection
    }
    }

    In order for this to work, the RN WebRTC end needs to know about the app group identifier we have configured the app with. We are doing this by adding a new key named RTCAppGroupIdentifier to the app's Info.plist with the app group identifier as the value.

    Opening the socket connection

    For starting screen sharing JitsiMeet SDK provides the UI to present the RPSystemBroadcastPickerView to the user. By default, the picker will display a list of all the available broadcast providers. In order to limit the picker to our particular broadcast provider, we have to set preferredExtension to the bundle identifier of the broadcast extension. We are doing this by adding a new key named RTCScreenSharingExtension to the app's Info.plist and setting the broadcast extension bundle identifier as the value.

    Once screen recording has started ReplayKit invokes the methods to handle video buffers, as well as the methods to handle starting and stopping the broadcast, from the SampleHandler class. The broadcastStarted(withSetupInfo:) method is our entry point for opening the socket connection with the RN WebRTC server. To do this we have to post the broadcastStarted notification the server is listening for, in order to start the connection, and we are ready to connect. Add a new method openConnection() to the SampleHandler class which will repeatedly attempt connecting to the server, for cases when the server connection start is delayed:

    func openConnection() {
    let queue = DispatchQueue(label: "broadcast.connectTimer")
    let timer = DispatchSource.makeTimerSource(queue: queue)
    timer.schedule(deadline: .now(), repeating: .milliseconds(100), leeway: .milliseconds(500))
    timer.setEventHandler { [weak self] in
    guard self?.clientConnection?.open() == true else {
    return
    }

    timer.cancel()
    }

    timer.resume()
    }

    Next, update the broadcastStarted(withSetupInfo:) method to post the notification and connect:

    override func broadcastStarted(withSetupInfo setupInfo: [String: NSObject]?) {
    DarwinNotificationCenter.shared.postNotification(.broadcastStarted)
    openConnection()
    }

    DarwinNotificationCenter is a simple helper class for broadcasting system-wide notifications, instead of delivering only within a single program, as NSNotificationCenter does. This mechanism allows the app to register for notifications sent from the extension.

    Now we are ready to start sending video frames.

    Sending video frames

    RN WebRTC is designed to work with jpeg encoded images framed in a CFHTTPMessage object. The following header fields are required:

    • Content-Length - the size of the jpeg data
    • Buffer-Width - the width of the buffer, in pixels
    • Buffer-Height - the buffer height, in pixels
    • Buffer-Orientation - the value for the RPVideoSampleOrientationKey that describes the video orientation.

    We are going to prepare and send our video frames using the SampleUploader class. Add a new private var uploader: SampleUploader? to the SampleHandler class and update init() to initialize it:

    override init() {
    super.init()
    if let connection = SocketConnection(filePath: socketFilePath) {
    clientConnection = connection
    uploader = SampleUploader(connection: connection)
    }
    }

    Next, we are going to update the processSampleBuffer(_ sampleBuffer: CMSampleBuffer, with sampleBufferType: RPSampleBufferType) method to send our video frames. For performance reasons, we'll also implement a very simple mechanism for adjusting the frame rate by using every third frame. Add a new private var frameCount = 0 and update the above-mentioned method like this:

    override func processSampleBuffer(_ sampleBuffer: CMSampleBuffer, with sampleBufferType: RPSampleBufferType) {
    switch sampleBufferType {
    case .video:
    // very simple mechanism for adjusting frame rate by using every third frame
    frameCount += 1
    if frameCount % 3 == 0 {
    uploader?.send(sample: sampleBuffer)
    }
    default:
    break
    }
    }

    Also, update broadcastStarted(withSetupInfo setupInfo: [String: NSObject]?) to reset the frameCount every time screen sharing is started:

    override func broadcastStarted(withSetupInfo setupInfo: [String: NSObject]?) {
    frameCount = 0

    DarwinNotificationCenter.shared.postNotification(.broadcastStarted)
    openConnection()
    }

    With this, we've concluded sending the video frames and we can move to the last step, handling stop screen sharing.

    Handling stop screen sharing

    Besides the in-meeting UI (screenshot 2), ReplayKit integration with iOS provides support for stopping screen recording outside of the app's control, from the status bar (screenshot 3) or using the Control Center (screenshot 4).

    ios-screensharing ios-screensharing ios-screensharing

    Any of these actions will trigger broadcastFinished in our SampleHandler implementation. This is our entry point for closing the connection and cleaning up. We will update broadcastFinished to post a DarwinNotification.broadcastStopped system-wide notification and close the connection:

    override func broadcastFinished() {
    DarwinNotificationCenter.shared.postNotification(.broadcastStopped)
    clientConnection?.close()
    }

    Another scenario we need to take care of is when the server connection is dropped, like when leaving a meeting while screen sharing or an error is encountered. We will address this by handling clientConnection.didClose event. Add a new method setupConnection to the SampleHandler class and update init to call it:

    func setupConnection() {
    clientConnection?.didClose = { [weak self] error in
    if let error = error {
    self?.finishBroadcastWithError(error)
    } else {
    // the displayed failure message is more user friendly when using NSError instead of Error
    let JMScreenSharingStopped = 10001
    let customError = NSError(domain: RPRecordingErrorDomain, code: JMScreenSharingStopped, userInfo: [NSLocalizedDescriptionKey: "Screen sharing stopped"])
    self?.finishBroadcastWithError(customError)
    }
    }
    }

    override init() {
    super.init()
    if let connection = SocketConnection(filePath: socketFilePath) {
    clientConnection = connection
    setupConnection()

    uploader = SampleUploader(connection: connection)
    }
    }

    Now, that we are done writing the implementation, we just need to enable the functionality in Jitsi. We are doing this by configuring JitsiMeetConferenceOptionsBuilder with the ios.screensharing.enabled feature flag, like this:

    let options = JitsiMeetConferenceOptions.fromBuilder { [weak self] builder in
    ...
    builder.setFeatureFlag("ios.screensharing.enabled", withBoolean: true)
    }
    meetView.join(options)

    Finally, we are ready to test the implementation. Before doing so, make sure voip is added to UIBackgroundModes, in the app's Info.playlist, in order to work when the app is in the background.

    TL;DR

    • Add a Broadcast Upload Extension, without UI, to your app. Update deployment info to run in iOS 14 or newer.
    • Copy SampleUploader.swift, SocketConnection.swift, DarwinNotificationCenter.swift and Atomic.swift files from the sample project to your extension. Make sure they are added to the extension's target.
    • Add both the app and the extension to the same App Group. Next, add the app group id value to the app's Info.plist for the RTCAppGroupIdentifier key.
    • Add a new key RTCScreenSharingExtension to the app's Info.plist with the extension's Bundle Identifier as the value.
    • Update SampleHandler.swift with the code from the sample project. Update appGroupIdentifier constant with the App Group name your app and extension are both registered to.
    • Update JitsiMeetConferenceOptions to enable screen sharing using the ios.screensharing.enabled feature flag.
    • Make sure voip is added to UIBackgroundModes, in the app's Info.plist, in order to work when the app is in the background.
    - +Dropbox app key:
    <key>CFBundleURLTypes</key>
    <array>
    <dict>
    <key>CFBundleURLName</key>
    <string></string>
    <key>CFBundleURLSchemes</key>
    <array>
    <string>db-<APP_KEY></string>
    </array>
    </dict>
    </array>
    <key>LSApplicationQueriesSchemes</key>
    <array>
    <string>dbapi-2</string>
    <string>dbapi-8-emm</string>
    </array>
    1. Make sure your app calls the Jitsi Meet SDK universal / deep linking delegate methods.

    Screen Sharing integration

    The screen sharing functionality for iOS was added to Jitsi starting with JitsiMeetSDK version 3.3.0. It is available for applications running on iOS 14 or newer.

    For achieving this we are using the Broadcast Upload Extension for capturing the contents of the user's screen. Passing the frames to the RN WebRTC is done using Unix stream-oriented sockets communication, the extension acting as the client and the React Native WebRTC being the server.

    The following documentation covers the code provided in the sample app.

    Creating the Broadcast Upload Extension

    The Broadcast Upload Extension is one of the App Extensions types defined in iOS and is used for capturing the contents of the user's screen.

    For creating the extension you need to add a new target to your application, selecting the Broadcast Upload Extension template. Fill in the desired name, change the language to Swift, make sure Include UI Extension is not selected, as we don't need custom UI for our case, then press Finish (screenshot 1). You will see that a new folder with the extension's name was added to the project's tree, containing the SampleHandler.swift class. Also, make sure to update the Deployment Info, for the newly created extension, to iOS 14 or newer. To learn more about creating App Extensions check the official documentation.

    screenshot 1

    With the extension created the next steps are to set up the socket connection, add the functionality for handling the received frames, and send them to RN WebRTC for processing. We will be using the code provided with the sample project for this. Copy SampleUploader.swift, SocketConnection.swift, DarwinNotificationCenter.swift, and Atomic.swift files to your extension's folder and make sure they're added to the target.

    Setting up the socket connection

    Sending the recorded frames to RN WebRTC is done via Unix SOCK_STREAM sockets. The extension needs to be set up as the client endpoint for this.

    We will update SampleHandler.swift to initiate the socket connection with RN WebRTC, using the SocketConnection class. But before, we have to set up the file that the sockets will use for communication.

    Even though an app extension bundle is nested within its containing app’s bundle, the running app extension and containing app have no direct access to each other’s containers. We will address this by enabling data sharing. To enable data sharing, use Xcode or the Developer portal to enable app groups for the containing app and its contained app extensions. Next, register the app group in the portal and specify the app group to use in the containing app. To learn about working with app groups, see Adding an App to an App Group.

    Now, add a private var socketFilePath: String to your SampleHandler class and set it up with a shared file named rtc_SSFD, using the newly registered app group, like this:

    private enum Constants {
    static let appGroupIdentifier = "my.custom.app.group"
    }

    private var socketFilePath: String {
    let sharedContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: Constants.appGroupIdentifier)

    return sharedContainer?.appendingPathComponent("rtc_SSFD").path ?? ""
    }

    Next, we will configure the SocketConnection to use the shared file. Add a private var clientConnection: SocketConnection? to the SampleHandler class and override init to set it up, like this:

    override init() {
    super.init()
    if let connection = SocketConnection(filePath: socketFilePath) {
    clientConnection = connection
    }
    }

    In order for this to work, the RN WebRTC end needs to know about the app group identifier we have configured the app with. We are doing this by adding a new key named RTCAppGroupIdentifier to the app's Info.plist with the app group identifier as the value.

    Opening the socket connection

    For starting screen sharing JitsiMeet SDK provides the UI to present the RPSystemBroadcastPickerView to the user. By default, the picker will display a list of all the available broadcast providers. In order to limit the picker to our particular broadcast provider, we have to set preferredExtension to the bundle identifier of the broadcast extension. We are doing this by adding a new key named RTCScreenSharingExtension to the app's Info.plist and setting the broadcast extension bundle identifier as the value.

    Once screen recording has started ReplayKit invokes the methods to handle video buffers, as well as the methods to handle starting and stopping the broadcast, from the SampleHandler class. The broadcastStarted(withSetupInfo:) method is our entry point for opening the socket connection with the RN WebRTC server. To do this we have to post the broadcastStarted notification the server is listening for, in order to start the connection, and we are ready to connect. Add a new method openConnection() to the SampleHandler class which will repeatedly attempt connecting to the server, for cases when the server connection start is delayed:

    func openConnection() {
    let queue = DispatchQueue(label: "broadcast.connectTimer")
    let timer = DispatchSource.makeTimerSource(queue: queue)
    timer.schedule(deadline: .now(), repeating: .milliseconds(100), leeway: .milliseconds(500))
    timer.setEventHandler { [weak self] in
    guard self?.clientConnection?.open() == true else {
    return
    }

    timer.cancel()
    }

    timer.resume()
    }

    Next, update the broadcastStarted(withSetupInfo:) method to post the notification and connect:

    override func broadcastStarted(withSetupInfo setupInfo: [String: NSObject]?) {
    DarwinNotificationCenter.shared.postNotification(.broadcastStarted)
    openConnection()
    }

    DarwinNotificationCenter is a simple helper class for broadcasting system-wide notifications, instead of delivering only within a single program, as NSNotificationCenter does. This mechanism allows the app to register for notifications sent from the extension.

    Now we are ready to start sending video frames.

    Sending video frames

    RN WebRTC is designed to work with jpeg encoded images framed in a CFHTTPMessage object. The following header fields are required:

    • Content-Length - the size of the jpeg data
    • Buffer-Width - the width of the buffer, in pixels
    • Buffer-Height - the buffer height, in pixels
    • Buffer-Orientation - the value for the RPVideoSampleOrientationKey that describes the video orientation.

    We are going to prepare and send our video frames using the SampleUploader class. Add a new private var uploader: SampleUploader? to the SampleHandler class and update init() to initialize it:

    override init() {
    super.init()
    if let connection = SocketConnection(filePath: socketFilePath) {
    clientConnection = connection
    uploader = SampleUploader(connection: connection)
    }
    }

    Next, we are going to update the processSampleBuffer(_ sampleBuffer: CMSampleBuffer, with sampleBufferType: RPSampleBufferType) method to send our video frames. For performance reasons, we'll also implement a very simple mechanism for adjusting the frame rate by using every third frame. Add a new private var frameCount = 0 and update the above-mentioned method like this:

    override func processSampleBuffer(_ sampleBuffer: CMSampleBuffer, with sampleBufferType: RPSampleBufferType) {
    switch sampleBufferType {
    case .video:
    // very simple mechanism for adjusting frame rate by using every third frame
    frameCount += 1
    if frameCount % 3 == 0 {
    uploader?.send(sample: sampleBuffer)
    }
    default:
    break
    }
    }

    Also, update broadcastStarted(withSetupInfo setupInfo: [String: NSObject]?) to reset the frameCount every time screen sharing is started:

    override func broadcastStarted(withSetupInfo setupInfo: [String: NSObject]?) {
    frameCount = 0

    DarwinNotificationCenter.shared.postNotification(.broadcastStarted)
    openConnection()
    }

    With this, we've concluded sending the video frames and we can move to the last step, handling stop screen sharing.

    Handling stop screen sharing

    Besides the in-meeting UI (screenshot 2), ReplayKit integration with iOS provides support for stopping screen recording outside of the app's control, from the status bar (screenshot 3) or using the Control Center (screenshot 4).

    ios-screensharing ios-screensharing ios-screensharing

    Any of these actions will trigger broadcastFinished in our SampleHandler implementation. This is our entry point for closing the connection and cleaning up. We will update broadcastFinished to post a DarwinNotification.broadcastStopped system-wide notification and close the connection:

    override func broadcastFinished() {
    DarwinNotificationCenter.shared.postNotification(.broadcastStopped)
    clientConnection?.close()
    }

    Another scenario we need to take care of is when the server connection is dropped, like when leaving a meeting while screen sharing or an error is encountered. We will address this by handling clientConnection.didClose event. Add a new method setupConnection to the SampleHandler class and update init to call it:

    func setupConnection() {
    clientConnection?.didClose = { [weak self] error in
    if let error = error {
    self?.finishBroadcastWithError(error)
    } else {
    // the displayed failure message is more user friendly when using NSError instead of Error
    let JMScreenSharingStopped = 10001
    let customError = NSError(domain: RPRecordingErrorDomain, code: JMScreenSharingStopped, userInfo: [NSLocalizedDescriptionKey: "Screen sharing stopped"])
    self?.finishBroadcastWithError(customError)
    }
    }
    }

    override init() {
    super.init()
    if let connection = SocketConnection(filePath: socketFilePath) {
    clientConnection = connection
    setupConnection()

    uploader = SampleUploader(connection: connection)
    }
    }

    Now, that we are done writing the implementation, we just need to enable the functionality in Jitsi. We are doing this by configuring JitsiMeetConferenceOptionsBuilder with the ios.screensharing.enabled feature flag, like this:

    let options = JitsiMeetConferenceOptions.fromBuilder { [weak self] builder in
    ...
    builder.setFeatureFlag("ios.screensharing.enabled", withBoolean: true)
    }
    meetView.join(options)

    Finally, we are ready to test the implementation. Before doing so, make sure voip is added to UIBackgroundModes, in the app's Info.playlist, in order to work when the app is in the background.

    TL;DR

    • Add a Broadcast Upload Extension, without UI, to your app. Update deployment info to run in iOS 14 or newer.
    • Copy SampleUploader.swift, SocketConnection.swift, DarwinNotificationCenter.swift and Atomic.swift files from the sample project to your extension. Make sure they are added to the extension's target.
    • Add both the app and the extension to the same App Group. Next, add the app group id value to the app's Info.plist for the RTCAppGroupIdentifier key.
    • Add a new key RTCScreenSharingExtension to the app's Info.plist with the extension's Bundle Identifier as the value.
    • Update SampleHandler.swift with the code from the sample project. Update appGroupIdentifier constant with the App Group name your app and extension are both registered to.
    • Update JitsiMeetConferenceOptions to enable screen sharing using the ios.screensharing.enabled feature flag.
    • Make sure voip is added to UIBackgroundModes, in the app's Info.plist, in order to work when the app is in the background.
    + \ No newline at end of file diff --git a/docs/dev-guide/dev-guide-ljm-api/index.html b/docs/dev-guide/dev-guide-ljm-api/index.html index 77556aff4..3ae2a709c 100644 --- a/docs/dev-guide/dev-guide-ljm-api/index.html +++ b/docs/dev-guide/dev-guide-ljm-api/index.html @@ -4,7 +4,7 @@ lib-jitsi-meet API (low level) | Jitsi Meet - + @@ -23,8 +23,8 @@ Used for certain deployments where a stick table entry needs to be kept alive we use those GET requests.
  • websocketKeepAliveUrl - (optional) Specific Url to use for the websocket keepalive GET requests.
  • connect(options) - establish server connection

    • options - JS Object with id and password properties.
  • disconnect() - destroys the server connection

  • initJitsiConference(name, options) - creates new JitsiConference object.

    • name - the name of the conference

    • options - JS object with configuration options for the conference. You can change the following properties there:

      • audioQuality - Audio quality related settings.

        • stereo
        • opusMaxAverageBitrate
        • enableOpusDtx
      • bridgeChannel - Settings related to the bridge channel.

        • ignoreDomain - If the backend advertises multiple colibri websockets, this options allows to filter some of them out based on the domain name.
        • preferSctp - Enables the use of the SCTP data channel for bridge channel.
      • callStatsID - callstats credentials

      • callStatsSecret - callstats credentials

      • channelLastN

      • deploymentInfo

        • shard
        • userRegion
      • disableAudioLevels - boolean property. Enables/disables audio levels.

      • disableInitialGUM

      • disableRtx - boolean property (default to false). Enables/disable the use of RTX.

      • disableSimulcast - Enable / disable simulcast support.

      • e2eping

        • pingInterval
      • enableForcedReload

      • enableIceRestart

      • enableNoAudioDetection - boolean property.

      • enableOpusRed

      • enableTalkWhileMuted - boolean property.

      • enableNoisyMicDetection - boolean property.

      • enableRemb - boolean property. Enables/disables REMB support, enabled by default.

      • enableTcc - enables/disabled TCC for bandwidth estimation, enabled by default.

      • focusUserJid - The real JID of focus participant - can be overridden here

      • ignoreStartMuted - ignores start muted events coming from jicofo.

      • p2p - Peer to peer related options

        • enabled - enables or disable peer-to-peer connection, if disabled all media will be routed through the Jitsi Videobridge.
        • codecPreferenceOrder - Provides a way to set the codec preference on desktop based endpoints.
        • mobileCodecPreferenceOrder - Provides a way to set the codec preference on mobile devices, both on RN and mobile browser based endpoints.
        • stunServers - list of STUN servers e.g. { urls: 'stun:meet-jit-si-turnrelay.jitsi.net:443' }
        • backToP2PDelay - a delay given in seconds, before the conference switches back to P2P, after the 3rd participant has left the room.
      • recordingType - the type of recording to be used

      • rttMonitor

        • enabled
        • initialDelay
        • getStatsInterval
        • analyticsInterval
        • stunServers
      • startAudioOnly

      • startAudioMuted

      • startWithAudioMuted

      • startVideoMuted

      • startWithVideoMuted

      • startSilent - enables silent mode, will mark audio as inactive will not send/receive audio

      • videoQuality Video quality settings related to the bridge connection.

        • codecPreferenceOrder - Provides a way to set the codec preference on desktop based endpoints.
        • mobileCodecPreferenceOrder - Provides a way to set the codec preference on mobile devices, both on RN and mobile browser based endpoints.
        • maxBitratesVideo - Provides a way to specify the bitrates for different codecs.
      • testing

        NOTE: if 4 and 5 are set the library is going to send events to callstats. Otherwise the callstats integration will be disabled.

  • addEventListener(event, listener) - Subscribes the passed listener to the event.

    • event - one of the events from JitsiMeetJS.events.connection object.
    • listener - handler for the event.
  • removeEventListener(event, listener) - Removes event listener.

    • event - the event
    • listener - the listener that will be removed.
  • addFeature - Adds new feature to the list of supported features for the local participant

    • feature - string, the name of the feature
    • submit - boolean, default false, if true - the new list of features will be immediately submitted to the others.
  • removeFeature - Removes a feature from the list of supported features for the local participant

    • feature - string, the name of the feature
    • submit - boolean, default false, if true - the new list of features will be immediately submitted to the others.
  • JitsiConference

    The object represents a conference. We have the following methods to control the conference:

    1. join(password) - Joins the conference

      • password - string of the password. This parameter is not mandatory.
    2. leave() - leaves the conference. Returns Promise.

    3. myUserId() - get local user ID.

    4. getLocalTracks() - Returns array with JitsiTrack objects for the local streams.

    5. addEventListener(event, listener) - Subscribes the passed listener to the event.

      • event - one of the events from JitsiMeetJS.events.conference object.
      • listener - handler for the event.
    6. removeEventListener(event, listener) - Removes event listener.

      • event - the event
      • listener - the listener that will be removed.
    7. on(event, listener) - alias for addEventListener

    8. off(event, listener) - alias for removeEventListener

    9. sendTextMessage(text) - sends the given string to other participants in the conference.

    10. setDisplayName(name) - changes the display name of the local participant.

      • name - the new display name.
    11. sendCommand(name, values) - sends user defined system command to the other participants

      • name - the name of the command.
      • values - JS object. The object has the following structure:
                {


    value: the_value_of_the_command,


    attributes: {}, // map with keys the name of the attribute and values - the values of the attributes.


    children: [] // array with JS object with the same structure.
    }
    NOTE: When you use that method the passed object will be added in every system message that is sent to the other participants. It might be sent more than once.
    1. sendCommandOnce(name, values) - Sends only one time a user defined system command to the other participants
    1. removeCommand(name) - removes a command for the list of the commands that are sent to the ther participants

      • name - the name of the command
    2. addCommandListener(command, handler) - adds listener

      • command - string for the name of the command
      • handler(values) - the listener that will be called when a command is received from another participant.
    3. removeCommandListener(command) - removes the listeners for the specified command

      • command - the name of the command
    4. addTrack(track) - Adds JitsiLocalTrack object to the conference. Throws an error if adding second video stream of the same videoType. camera and desktop are considered as two separate video sources. Therefore, when adding a video source (camera or desktop) for the first time to the conference, addTack needs to be called and after that only replaceTrack needs to be used to replace the existing track with another track of the same video type or for removing it from the conference. Returns a promise.

      • track - the JitsiLocalTrack
    5. removeTrack(track) - Removes JitsiLocalTrack object to the conference. Returns Promise. This does not fire TRACK_REMOVED event anymore on the remote end. The same SSRC will be re-used when another track of the same kind is added back to the conference to keep signaling messages to a minimum.

      • track - the JitsiLocalTrack
    6. isDTMFSupported() - Check if at least one user supports DTMF.

    7. getRole() - returns string with the local user role ("moderator" or "none")

    8. isModerator() - checks if local user has "moderator" role

    9. lock(password) - set password for the conference; returns Promise

      • password - string password

      Note: available only for moderator

    10. unlock() - unset conference password; returns Promise

      Note: available only for moderator

    11. kickParticipant(id, reason) - Kick participant from the conference

      • id - string participant id
      • reason - (optional) string, default 'You have been kicked.' - reason of the participant to kick
    12. setStartMutedPolicy(policy) - make all new participants join with muted audio/video

      • policy - JS object with following properties
        • audio - boolean if audio stream should be muted
        • video - boolean if video stream should be muted

      Note: available only for moderator

    13. getStartMutedPolicy() - returns the current policy with JS object:

      • policy - JS object with following properties
        • audio - boolean if audio stream should be muted
        • video - boolean if video stream should be muted
    14. isStartAudioMuted() - check if audio is muted on join

    15. isStartVideoMuted() - check if video is muted on join

    16. sendFeedback(overallFeedback, detailedFeedback) - Sends the given feedback through CallStats if enabled.

      • overallFeedback - an integer between 1 and 5 indicating the user feedback
      • detailedFeedback - detailed feedback from the user. Not yet used
    17. setSubject(subject) - change subject of the conference

      • subject - string new subject

      Note: available only for moderator

    18. sendEndpointMessage(to, payload) - Sends message via the data channels.

      • to - the id of the endpoint that should receive the message. If "" the message will be sent to all participants.
      • payload - JSON object - the payload of the message.

    Throws NetworkError or InvalidStateError or Error if the operation fails.

    1. sendEndpointStatsMessage(payload) - Sends a EndpointStats Colibri message on the bridge channel. This should be used instead of broadcastEndpointMessage for relaying local stats to all the remote endpoints.
      • payload - JSON object - the payload of the message.

    Throws NetworkError, InvalidStateError or Error if the operation fails.

    1. broadcastEndpointMessage(payload) - Sends broadcast message via the datachannels.
      • payload - JSON object - the payload of the message.

    Throws NetworkError or InvalidStateError or Error if the operation fails.

    1. replaceTrack - replaces the track currently being used as the sender's source with a new MediaStreamTrack. The new track must be of the same media kind (audio, video, etc) and switching the track should not require negotiation. replaceTrack(oldTrack, newTrack)

    Throws NetworkError or InvalidStateError or Error if the operation fails.

    1. setReceiverConstraints - set the constraints for the video that is requested from the bridge. This single message should be used in lieu of setLastN, setReceiverVideoConstraint and selectParticipants methods. These constraints are applicable to bridge connection only. More information about the signaling message format and how the Jitsi Videobridge allocates bandwidth can be found here.

      • videoConstraints - Object that specifies the constraints in the following format.
      {
      'lastN': 20, // Number of videos requested from the bridge.
      'selectedSources': ['A', 'B', 'C'], // The source names of the video tracks that are prioritized first.
      'onStageSources': ['A'], // The source names of the video tracks that are prioritized up to a higher resolution.
      'defaultConstraints': { 'maxHeight': 180 }, // Default resolution requested for all endpoints.
      'constraints': { // Source specific resolution.
      'A': { 'maxHeight': 720 }
      }
      }
    2. setSenderVideoConstraint(resolution) - set the desired resolution to send to JVB or the peer (180, 360, 720).

    3. isHidden - checks if local user has joined as a "hidden" user. This is a specialized role used for integrations.

    4. setLocalParticipantProperty(propertyKey, propertyValue) - used to set a custom propery to the local participant("fullName": "Full Name", favoriteColor: "red", "userId": 234). Also this can be used to modify an already set custom property.

      • propertyKey - string - custom property name
      • propertyValue - string - custom property value
    5. getParticipants() - Retrieves an array of all participants in this conference.

    6. revokeOwner(participantId) - Revokes owner's rights to the participant. The particiapnt that invokes the function should have same or more rights than the targeted participant. This rights check is done at the XMPP server level.

    JitsiTrack

    The object represents single track - video or audio. They can be remote tracks ( from the other participants in the call) or local tracks (from the devices of the local participant). We have the following methods for controling the tracks:

    1. getType() - returns string with the type of the track( "video" for the video tracks and "audio" for the audio tracks)

    2. mute() - mutes the track. Returns Promise.

      Note: This method is implemented only for the local tracks.

    3. unmute() - unmutes the track. Returns Promise.

      Note: This method is implemented only for the local tracks.

    4. isMuted() - check if track is muted

    5. attach(container) - attaches the track to the given container.

    6. detach(container) - removes the track from the container.

    7. dispose() - disposes the track. If the track is added to a conference the track will be removed. Returns Promise.

      Note: This method is implemented only for the local tracks.

    8. getId() - returns unique string for the track.

    9. getParticipantId() - returns id(string) of the track owner

      Note: This method is implemented only for the remote tracks.

    10. getSourceName() - returns the source name of the track.

    11. setAudioOutput(audioOutputDeviceId) - sets new audio output device for track's DOM elements. Video tracks are ignored.

    12. getDeviceId() - returns device ID associated with track (for local tracks only)

    13. isEnded() - returns true if track is ended

    14. setEffect(effect) - Applies the effect by swapping out the existing MediaStream on the JitsiTrack with the new

      MediaStream which has the desired effect. "undefined" is passed to this function for removing the effect and for

      restoring the original MediaStream on the JitsiTrack.

      The following methods have to be defined for the effect instance.

      startEffect() - Starts the effect and returns a new MediaStream that is to be swapped with the existing one.

      stopEffect() - Stops the effect.

      isEnabled() - Checks if the local track supports the effect.

      Note: This method is implemented only for the local tracks.

    JitsiTrackError

    The object represents error that happened to a JitsiTrack. Is inherited from JavaScript base Error object, so "name", "message" and "stack" properties are available. For GUM-related errors, -exposes additional "gum" property, which is an object with following properties:

    • error - original GUM error
    • constraints - GUM constraints object used for the call
    • devices - array of devices requested in GUM call (possible values - "audio", "video", "screen", "desktop", "audiooutput")
    - +exposes additional "gum" property, which is an object with following properties:

    • error - original GUM error
    • constraints - GUM constraints object used for the call
    • devices - array of devices requested in GUM call (possible values - "audio", "video", "screen", "desktop", "audiooutput")
    + \ No newline at end of file diff --git a/docs/dev-guide/dev-guide-ljm/index.html b/docs/dev-guide/dev-guide-ljm/index.html index e230169ff..3212a003f 100644 --- a/docs/dev-guide/dev-guide-ljm/index.html +++ b/docs/dev-guide/dev-guide-ljm/index.html @@ -4,14 +4,14 @@ Modifying lib-jitsi-meet | Jitsi Meet - +

    Modifying lib-jitsi-meet

    By default the library is referenced as a prebuilt artifact in a GitHub release. Packages are NOT published to npm. The default dependency path in package.json is:

    "lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v<version>+<commit-hash>/lib-jitsi-meet.tgz)",

    To work with local copy you may change the path to:

    "lib-jitsi-meet": "file:///Users/name/local-lib-jitsi-meet-packed-copy.tgz",

    In order to create the packed file run npm pack in the lib-jitsi-meet project directory.

    To make the project you must force it to take the sources as 'npm update':

    npm install lib-jitsi-meet --force && make

    Or if you are making only changes to the library:

    npm install lib-jitsi-meet --force && make deploy-lib-jitsi-meet

    Alternative way is to use npm link. -It allows to link lib-jitsi-meet dependency to local source in few steps:

    cd lib-jitsi-meet

    #### create global symlink for lib-jitsi-meet package
    npm link

    cd ../jitsi-meet

    #### create symlink from the local node_modules folder to the global lib-jitsi-meet symlink
    npm link lib-jitsi-meet
    note

    Linking will not work when building the mobile applications.

    After changes in your local lib-jitsi-meet repository, you can rebuild it with npm run build and your jitsi-meet repository will use that modified library:

    cd node_modules/lib-jitsi-meet
    npm run build

    If you do not want to use local repository anymore you should run:

    cd jitsi-meet
    npm unlink lib-jitsi-meet
    npm install
    - +It allows to link lib-jitsi-meet dependency to local source in few steps:

    cd lib-jitsi-meet

    #### create global symlink for lib-jitsi-meet package
    npm link

    cd ../jitsi-meet

    #### create symlink from the local node_modules folder to the global lib-jitsi-meet symlink
    npm link lib-jitsi-meet
    note

    Linking will not work when building the mobile applications.

    After changes in your local lib-jitsi-meet repository, you can rebuild it with npm run build and your jitsi-meet repository will use that modified library:

    cd node_modules/lib-jitsi-meet
    npm run build

    If you do not want to use local repository anymore you should run:

    cd jitsi-meet
    npm unlink lib-jitsi-meet
    npm install
    + \ No newline at end of file diff --git a/docs/dev-guide/dev-guide-mobile-jitsi-meet/index.html b/docs/dev-guide/dev-guide-mobile-jitsi-meet/index.html index 8e796d377..c9f25ef81 100644 --- a/docs/dev-guide/dev-guide-mobile-jitsi-meet/index.html +++ b/docs/dev-guide/dev-guide-mobile-jitsi-meet/index.html @@ -4,7 +4,7 @@ Developer Guide for Jitsi Meet | Jitsi Meet - + @@ -22,8 +22,8 @@ preferred method for debugging.

    note

    When using Chrome Developer Tools for debugging the JavaScript source code is being interpreted by Chrome's V8 engine, instead of JSCore which React Native uses. It's important to keep this in mind due to potential differences in -supported JavaScript features.

    Enabling extra features

    - +supported JavaScript features.

    Enabling extra features

    + \ No newline at end of file diff --git a/docs/dev-guide/dev-guide-react-native-sdk/index.html b/docs/dev-guide/dev-guide-react-native-sdk/index.html index 4f1c3de2c..e2a13607c 100644 --- a/docs/dev-guide/dev-guide-react-native-sdk/index.html +++ b/docs/dev-guide/dev-guide-react-native-sdk/index.html @@ -4,7 +4,7 @@ React Native SDK | Jitsi Meet - + @@ -12,8 +12,10 @@

    React Native SDK

    The Jitsi React Native SDK provides the same user experience as the Jitsi Meet app, in a customizable way which you can embed in your React Native apps.

    Sample application using the React Native SDK

    If you want to see how easy integrating the Jitsi React Native SDK into a React Native application is, take a look at the
    sample applications repository.

    Usage

    While this is a published library, you can npm i @jitsi/react-native-sdk.
    -Also, some dependencies need will maybe need to be added and this will be done by running the following script node node_modules/@jitsi/react-native-sdk/update_dependencies.js. -This will sync all of our peer dependencies with your dependencies. Next you need to do npm install.

    Because our SDK uses SVG files, you will need to update your metro bundler configuration accordingly:

    metro.config
    const { getDefaultConfig } = require('metro-config');

    module.exports = (async () => {
    const {
    resolver: {
    sourceExts,
    assetExts
    }
    } = await getDefaultConfig();

    return {
    transformer: {
    babelTransformerPath: require.resolve('react-native-svg-transformer'),
    getTransformOptions: async () => ({
    transform: {
    experimentalImportSupport: false,
    inlineRequires: true,
    },
    }),
    },
    resolver: {
    assetExts: assetExts.filter(ext => ext !== 'svg'),
    sourceExts: [...sourceExts, 'svg']
    }
    }
    })();

    Android permissions

    • In android/app/src/debug/AndroidManifest.xml and android/app/src/main/AndroidManifest.xml, under the </application> tag, include
        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
      <uses-permission android:name="android.permission.BLUETOOTH" />
      <uses-permission android:name="android.permission.CAMERA" />
      <uses-permission android:name="android.permission.INTERNET" />
      <uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
      <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
      <uses-permission android:name="android.permission.RECORD_AUDIO" />
      <uses-permission android:name="android.permission.WAKE_LOCK" />
      <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
      <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

    iOS permissions

    • React Native SDK requests camera and microphone access, make sure to include the required entries for NSCameraUsageDescription and NSMicrophoneUsageDescriptionin your Info.plist file.
    • React Native SDK shows and hides the status bar based on the conference state, +Dependency conflicts may occur between RNSDK and your app.
      If that is the case, please run npm i @jitsi/react-native-sdk --force.
      +To check if some dependencies need to be added, please run the following script node node_modules/@jitsi/react-native-sdk/update_dependencies.js.
      +This will sync all of our peer dependencies with your dependencies.
      +Next you will need to do npm install.

      Because our SDK uses SVG files, you will need to update your metro bundler configuration accordingly:

      metro.config
      const { getDefaultConfig } = require('metro-config');

      module.exports = (async () => {
      const {
      resolver: {
      sourceExts,
      assetExts
      }
      } = await getDefaultConfig();

      return {
      transformer: {
      babelTransformerPath: require.resolve('react-native-svg-transformer'),
      getTransformOptions: async () => ({
      transform: {
      experimentalImportSupport: false,
      inlineRequires: true,
      },
      }),
      },
      resolver: {
      assetExts: assetExts.filter(ext => ext !== 'svg'),
      sourceExts: [...sourceExts, 'svg']
      }
      }
      })();

      Android permissions

      • In android/app/src/debug/AndroidManifest.xml and android/app/src/main/AndroidManifest.xml, under the </application> tag, include
          <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
        <uses-permission android:name="android.permission.BLUETOOTH" />
        <uses-permission android:name="android.permission.CAMERA" />
        <uses-permission android:name="android.permission.INTERNET" />
        <uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
        <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
        <uses-permission android:name="android.permission.RECORD_AUDIO" />
        <uses-permission android:name="android.permission.WAKE_LOCK" />
        <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
        <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

      iOS permissions

      • React Native SDK requests camera and microphone access, make sure to include the required entries for NSCameraUsageDescription and NSMicrophoneUsageDescriptionin your Info.plist file.
      • React Native SDK shows and hides the status bar based on the conference state, you may want to set UIViewControllerBasedStatusBarAppearance to NO in your Info.plist file.
      • For starting screen sharing React Native SDK provides the UI to present the RPSystemBroadcastPickerView to the user. By default, the picker will display a list of all the available broadcast providers. In order to limit the picker to our particular broadcast provider, we have to set preferredExtension to the bundle identifier of the broadcast extension. We are doing this by adding a new key named RTCScreenSharingExtension to the app's Info.plist and setting the broadcast extension bundle identifier as the value.
      • Make sure voip is added to UIBackgroundModes, in the app's Info.plist, in order to work when the app is in the background.

      JitsiMeeting props

      Our JitsiMeeting component renders the full meeting experience. This has some customizable properties:

      config

      Object - Updates configuration.

      flags

      Object - Add different feature flags that your meeting experience would like to have.

      • For example:
      <JitsiMeeting flags={{
      'call-integration.enabled': true,
      'fullscreen.enabled': false,
      'invite.enabled': true }} />

      eventListeners

      Object - Options that personalize your meeting experience:

      • onConferenceBlurred @@ -27,8 +29,8 @@ Function - Takes a function that gets triggered when READY_TO_CLOSE action is dispatched, more exactly when one exits a conference.

      room

      string - Name of the room where the conference takes place.

      serverURL

      string - Server where the conference should take place.

      style

      Object - CSS your meeting experience.

      token

      string - JWT token used for authentication.

      userInfo

      • avatarUrl string - Path to participant's avatar.

      • displayName string - Default participant name to be displayed.

      • email -string - Default email for participant.

    - +string - Default email for participant.

    + \ No newline at end of file diff --git a/docs/dev-guide/dev-guide-react-sdk/index.html b/docs/dev-guide/dev-guide-react-sdk/index.html index 1e6c16158..1e3e3437b 100644 --- a/docs/dev-guide/dev-guide-react-sdk/index.html +++ b/docs/dev-guide/dev-guide-react-sdk/index.html @@ -4,13 +4,13 @@ React SDK | Jitsi Meet - +
    -

    React SDK

    The Jitsi Meet React SDK provides the same user experience as the Jitsi Meet app, in a customizable way which you can embed in your apps.

    info

    React 16 or higher is required.

    Sample application using the SDK

    If you want to see how easy integrating the Jitsi Meet React SDK into a React application is, take a look at our example.

    Installation

    To access the React SDK modules in your application you need to install it as a dependency:

    npm install @jitsi/react-sdk

    Modules

    The SDK exposes two components with similar properties, intended for different use-cases.

    JitsiMeeting

    To be used with custom domains as-it-is in React projects:

    <JitsiMeeting
    domain = { YOUR_DOMAIN }
    roomName = "PleaseUseAGoodRoomName"
    configOverwrite = {{
    startWithAudioMuted: true,
    disableModeratorIndicator: true,
    startScreenSharing: true,
    enableEmailInStats: false
    }}
    interfaceConfigOverwrite = {{
    DISABLE_JOIN_LEAVE_NOTIFICATIONS: true
    }}
    userInfo = {{
    displayName: 'YOUR_USERNAME'
    }}
    onApiReady = { (externalApi) => {
    // here you can attach custom event listeners to the Jitsi Meet External API
    // you can also store it locally to execute commands
    } }
    getIFrameRef = { (iframeRef) => { iframeRef.style.height = '400px'; } }
    />

    Properties specific to the JitsiMeeting component

    • domain: Optional. Field used to retrieve the external_api.js file that initializes the IFrame. If omitted, defaults to meet.jit.si.

    JaaSMeeting

    To be used with the 8x8.vc domain as-it-is in React projects:

    <JaaSMeeting
    appId = { YOUR_APP_ID }
    roomName = "PleaseUseAGoodRoomName"
    jwt = { YOUR_VALID_JWT }
    configOverwrite = {{
    disableThirdPartyRequests: true,
    disableLocalVideoFlip: true,
    backgroundAlpha: 0.5
    }}
    interfaceConfigOverwrite = {{
    VIDEO_LAYOUT_FIT: 'nocrop',
    MOBILE_APP_PROMO: false,
    TILE_VIEW_MAX_COLUMNS: 4
    }}
    spinner = { SpinnerView }
    onApiReady = { (externalApi) => { ... } }
    />

    ...or with the stage.8x8.vc domain:

    <JaaSMeeting
    appId = { YOUR_APP_ID }
    roomName = "PleaseUseAGoodRoomName"
    ...
    useStaging = { true }
    />

    Properties specific to the JaaSMeeting component

    • appId: Required. Provides an isolated context and prefixes the room name.
    • useStaging: Optional. Tells whether to use the staging environment or not.

    Common properties

    The component modules support a similar kind of customization to the Jitsi Meet IFrame. The following properties can be passed down to your instances of JitsiMeeting or JaaSMeeting.

    • roomName: Required. The name of the room to join.

    • configOverwrite: Optional. The JS object with overrides for options defined in the config.js file.

    • interfaceConfigOverwrite: Optional. The JS object with overrides for options defined in the interface_config.js file.

    • jwt: Optional. The JWT token.

    • invitees: Optional. Object arrays that contain information about participants invited to a call.

    • devices: Optional. Information map about the devices used in a call.

    • userInfo: Optional. The JS object that contains information about the participant starting or joining the meeting (e.g., email).

    • release: Optional. Information regarding the stage.8x8.vc or 8x8.vc release version. Expects the following format: release-1234.

    • spinner: Optional. The custom spinner to be displayed while the IFrame is loading.

    • onApiReady: Optional. The external API reference for events and commands.

    • onReadyToClose: Optional. The callback for when the meeting is ready to be closed.

    • getIFrameRef: Optional. The parent node used by the IFrame.

    - +

    React SDK

    The Jitsi Meet React SDK provides the same user experience as the Jitsi Meet app, in a customizable way which you can embed in your apps.

    info

    React 16 or higher is required.

    Sample application using the SDK

    If you want to see how easy integrating the Jitsi Meet React SDK into a React application is, take a look at our example.

    Installation

    To access the React SDK modules in your application you need to install it as a dependency:

    npm install @jitsi/react-sdk

    Modules

    The SDK exposes two components with similar properties, intended for different use-cases.

    JitsiMeeting

    To be used with custom domains as-it-is in React projects:

    <JitsiMeeting
    domain = { YOUR_DOMAIN }
    roomName = "PleaseUseAGoodRoomName"
    configOverwrite = {{
    startWithAudioMuted: true,
    disableModeratorIndicator: true,
    startScreenSharing: true,
    enableEmailInStats: false
    }}
    interfaceConfigOverwrite = {{
    DISABLE_JOIN_LEAVE_NOTIFICATIONS: true
    }}
    userInfo = {{
    displayName: 'YOUR_USERNAME'
    }}
    onApiReady = { (externalApi) => {
    // here you can attach custom event listeners to the Jitsi Meet External API
    // you can also store it locally to execute commands
    } }
    getIFrameRef = { (iframeRef) => { iframeRef.style.height = '400px'; } }
    />

    Properties specific to the JitsiMeeting component

    • domain: Optional. Field used to retrieve the external_api.js file that initializes the IFrame. If omitted, defaults to meet.jit.si.

    JaaSMeeting

    To be used with the 8x8.vc domain as-it-is in React projects:

    <JaaSMeeting
    appId = { YOUR_APP_ID }
    roomName = "PleaseUseAGoodRoomName"
    jwt = { YOUR_VALID_JWT }
    configOverwrite = {{
    disableThirdPartyRequests: true,
    disableLocalVideoFlip: true,
    backgroundAlpha: 0.5
    }}
    interfaceConfigOverwrite = {{
    VIDEO_LAYOUT_FIT: 'nocrop',
    MOBILE_APP_PROMO: false,
    TILE_VIEW_MAX_COLUMNS: 4
    }}
    spinner = { SpinnerView }
    onApiReady = { (externalApi) => { ... } }
    />

    ...or with the stage.8x8.vc domain:

    <JaaSMeeting
    appId = { YOUR_APP_ID }
    roomName = "PleaseUseAGoodRoomName"
    ...
    useStaging = { true }
    />

    Properties specific to the JaaSMeeting component

    • appId: Required. Provides an isolated context and prefixes the room name.
    • useStaging: Optional. Tells whether to use the staging environment or not.

    Common properties

    The component modules support a similar kind of customization to the Jitsi Meet IFrame. The following properties can be passed down to your instances of JitsiMeeting or JaaSMeeting.

    • roomName: Required. The name of the room to join.

    • configOverwrite: Optional. The JS object with overrides for options defined in the config.js file.

    • interfaceConfigOverwrite: Optional. The JS object with overrides for options defined in the interface_config.js file.

    • jwt: Optional. The JWT token.

    • invitees: Optional. Object arrays that contain information about participants invited to a call.

    • devices: Optional. Information map about the devices used in a call.

    • userInfo: Optional. The JS object that contains information about the participant starting or joining the meeting (e.g., email).

    • release: Optional. Information regarding the stage.8x8.vc or 8x8.vc release version. Expects the following format: release-1234.

    • spinner: Optional. The custom spinner to be displayed while the IFrame is loading.

    • onApiReady: Optional. The external API reference for events and commands.

    • onReadyToClose: Optional. The callback for when the meeting is ready to be closed.

    • getIFrameRef: Optional. The parent node used by the IFrame.

    + \ No newline at end of file diff --git a/docs/dev-guide/dev-guide-web-integrations/index.html b/docs/dev-guide/dev-guide-web-integrations/index.html index bd37797c7..f5d4de618 100644 --- a/docs/dev-guide/dev-guide-web-integrations/index.html +++ b/docs/dev-guide/dev-guide-web-integrations/index.html @@ -4,13 +4,13 @@ Web integrations | Jitsi Meet - +
    -

    Web integrations

    Creating the Google API client for Google Calendar and YouTube integration

    1. Log into a Google admin account.
    2. Go to Google cloud platform dashboard. https://console.cloud.google.com/apis/dashboard
    3. In the Select a Project dropdown, click New Project.
    4. Give the project a name.
    5. Proceed to the Credentials settings of the new project.
    6. In the Credentials tab of the Credentials settings, click Create Credentials and select the type OAuth client ID.
    7. Proceed with creating a Web application and add the domains (origins) on which the application will be hosted. Local development environments (http://localhost:8000 for example) can be added here.
    8. While still in the Google cloud platform dashboard, click the Library settings for the calendar project.
    9. Search for the Google Calendar API (used for calendar accessing), click its result, and enable it.
    10. Do the same for YouTube Data API v3

    Creating the Microsoft app for Microsoft Outlook integration

    1. Go to https://apps.dev.microsoft.com/
    2. Proceed through the "Add an app" flow. Once created, a page with several Graph Permissions fields should display.
    3. Under "Platforms" add "Web"
    4. Add a redirect URL for the Microsoft auth flow to visit once a user has confirmed authentication. Target domain if available is just 'yourdomain.com' (the deployment address) and the redirect URL is https://yourdomain.com/static/msredirect.html.
    5. Add Microsoft Graph delegated permissions, if this option is available: Calendars.Read, Calendars.ReadWrite, Calendars.Read.Shared, Calendars.ReadWrite.Shared.
    6. Check Allow Implicit Flow (and Restrict token issuing to this app if available).
    7. Save the changes.

    Creating the Dropbox app for Dropbox recording integration

    1. You need a Dropbox account (If you don't already have one, you can sign up for a free account here.)
    2. Create new App as described in Getting Started Guide in App Console section.
    3. Choose
      1. 'Dropbox API - For apps that need to access files in Dropbox.'
      2. 'App folder– Access to a single folder created specifically for your app.'
      3. Fill in the name of your app
    4. You need only, the newly created App key, goes in /etc/jitsi/meet/yourdeployment.com-config.js in
          dropbox: {
      appKey: '__dropbox_app_key__',
      redirectURI: 'https://yourdeployment.com/static/oauth.html'
      }
    5. Add your Dropbox Redirect URIs in the Dropbox form https://yourdeployment.com/static/oauth.html
    6. Fill in Branding
    - +

    Web integrations

    Creating the Google API client for Google Calendar and YouTube integration

    1. Log into a Google admin account.
    2. Go to Google cloud platform dashboard. https://console.cloud.google.com/apis/dashboard
    3. In the Select a Project dropdown, click New Project.
    4. Give the project a name.
    5. Proceed to the Credentials settings of the new project.
    6. In the Credentials tab of the Credentials settings, click Create Credentials and select the type OAuth client ID.
    7. Proceed with creating a Web application and add the domains (origins) on which the application will be hosted. Local development environments (http://localhost:8000 for example) can be added here.
    8. While still in the Google cloud platform dashboard, click the Library settings for the calendar project.
    9. Search for the Google Calendar API (used for calendar accessing), click its result, and enable it.
    10. Do the same for YouTube Data API v3

    Creating the Microsoft app for Microsoft Outlook integration

    1. Go to https://apps.dev.microsoft.com/
    2. Proceed through the "Add an app" flow. Once created, a page with several Graph Permissions fields should display.
    3. Under "Platforms" add "Web"
    4. Add a redirect URL for the Microsoft auth flow to visit once a user has confirmed authentication. Target domain if available is just 'yourdomain.com' (the deployment address) and the redirect URL is https://yourdomain.com/static/msredirect.html.
    5. Add Microsoft Graph delegated permissions, if this option is available: Calendars.Read, Calendars.ReadWrite, Calendars.Read.Shared, Calendars.ReadWrite.Shared.
    6. Check Allow Implicit Flow (and Restrict token issuing to this app if available).
    7. Save the changes.

    Creating the Dropbox app for Dropbox recording integration

    1. You need a Dropbox account (If you don't already have one, you can sign up for a free account here.)
    2. Create new App as described in Getting Started Guide in App Console section.
    3. Choose
      1. 'Dropbox API - For apps that need to access files in Dropbox.'
      2. 'App folder– Access to a single folder created specifically for your app.'
      3. Fill in the name of your app
    4. You need only, the newly created App key, goes in /etc/jitsi/meet/yourdeployment.com-config.js in
          dropbox: {
      appKey: '__dropbox_app_key__',
      redirectURI: 'https://yourdeployment.com/static/oauth.html'
      }
    5. Add your Dropbox Redirect URIs in the Dropbox form https://yourdeployment.com/static/oauth.html
    6. Fill in Branding
    + \ No newline at end of file diff --git a/docs/dev-guide/dev-guide-web-jitsi-meet/index.html b/docs/dev-guide/dev-guide-web-jitsi-meet/index.html index 51d8b968a..41227235b 100644 --- a/docs/dev-guide/dev-guide-web-jitsi-meet/index.html +++ b/docs/dev-guide/dev-guide-web-jitsi-meet/index.html @@ -4,14 +4,14 @@ Developer Guide for Jitsi Meet | Jitsi Meet - +

    Developer Guide for Jitsi Meet

    This guide will help you setup a development environment to start working on the Jitsi Meet web app itself.

    Building the sources

    note

    Node.js >= 16 and npm >= 8 are required.

    caution

    Windows is not supported.

    On Debian/Ubuntu systems, the required packages can be installed with:

    Then go ahead:

    # Clone the repository
    git clone https://github.com/jitsi/jitsi-meet
    cd ./jitsi-meet

    npm install

    # To build the Jitsi Meet application, just type
    make
    danger

    DO NOT run npm update or use yarn or delete package-lock.json. Dependencies are pinned for a reason.

    Running with webpack-dev-server for development

    Use the following command in your terminal:

    make dev

    By default the backend deployment used is alpha.jitsi.net. You can point the Jitsi Meet app at a different backend by using a proxy server. To do this, set the WEBPACK_DEV_SERVER_PROXY_TARGET variable:

    export WEBPACK_DEV_SERVER_PROXY_TARGET=https://your-example-server.com
    make dev

    The app should be running at https://localhost:8080/

    Certificate Error

    Browsers may show a certificate error since the development certificate is self-signed. It's safe to disregard those -warning and continue to your site.

    Building .debs

    To make a deb you can easily deploy to a public test server, ensure you have the lib-jitsi-meet sources you wish, then:

    npm install
    make
    dpkg-buildpackage -A -rfakeroot -us -uc -tc

    You'll have a bunch of .deb files in the parent directory, and can push the updated source to your server and install it with the jitsi-meet-web deb file.

    Running from source on existing deployment

    Follow the document https://community.jitsi.org/t/how-to-how-to-build-jitsi-meet-from-source-a-developers-guide/75422

    - +warning and continue to your site.

    Building .debs

    To make a deb you can easily deploy to a public test server, ensure you have the lib-jitsi-meet sources you wish, then:

    npm install
    make
    dpkg-buildpackage -A -rfakeroot -us -uc -tc

    You'll have a bunch of .deb files in the parent directory, and can push the updated source to your server and install it with the jitsi-meet-web deb file.

    Running from source on existing deployment

    Follow the document https://community.jitsi.org/t/how-to-how-to-build-jitsi-meet-from-source-a-developers-guide/75422

    + \ No newline at end of file diff --git a/docs/dev-guide/mobile-dropbox/index.html b/docs/dev-guide/mobile-dropbox/index.html index 7ba0dc952..38a5fd7dc 100644 --- a/docs/dev-guide/mobile-dropbox/index.html +++ b/docs/dev-guide/mobile-dropbox/index.html @@ -4,7 +4,7 @@ Setting up Dropbox integration | Jitsi Meet - + @@ -12,8 +12,8 @@

    Setting up Dropbox integration

    1. Create a Dropbox app.
    2. Add the following to ios/app/src/Info.plist by replacing <APP_KEY> with your own Dropbox app key (which can be found in the App Console):
    <key>CFBundleURLTypes</key>
    <array>
    <dict>
    <key>CFBundleURLName</key>
    <string></string>
    <key>CFBundleURLSchemes</key>
    <array>
    <string>db-<APP_KEY></string>
    </array>
    </dict>
    </array>
    <key>LSApplicationQueriesSchemes</key>
    <array>
    <string>dbapi-2</string>
    <string>dbapi-8-emm</string>
    </array>

    NOTE: Both Android and iOS builds of the apps will parse the Dropbox app key -from ios/app/src/Info.plist.

    NOTE: See Dropbox developer guide for more information

    - +from ios/app/src/Info.plist.

    NOTE: See Dropbox developer guide for more information

    + \ No newline at end of file diff --git a/docs/dev-guide/mobile-feature-flags/index.html b/docs/dev-guide/mobile-feature-flags/index.html index 6ed05e638..106cabf03 100644 --- a/docs/dev-guide/mobile-feature-flags/index.html +++ b/docs/dev-guide/mobile-feature-flags/index.html @@ -4,14 +4,14 @@ Feature flags | Jitsi Meet - + - +UI aspects and behavior.

    All flags are defined here.

    + \ No newline at end of file diff --git a/docs/dev-guide/mobile-google-auth/index.html b/docs/dev-guide/mobile-google-auth/index.html index d62e82fee..8a1492b5c 100644 --- a/docs/dev-guide/mobile-google-auth/index.html +++ b/docs/dev-guide/mobile-google-auth/index.html @@ -4,7 +4,7 @@ Setting up Google sign-in integration | Jitsi Meet - + @@ -22,8 +22,8 @@ googleApiApplicationClientID in config.js.
  • Add your iOS client ID (the REVERSED_CLIENT_ID in the plist file) as an application URL schema into ios/app/src/Info.plist (replacing placeholder).
  • Enable YouTube API access on the developer console (see above) to enable live -streaming.
  • - +streaming. + \ No newline at end of file diff --git a/docs/devops-guide/cloud-api/index.html b/docs/devops-guide/cloud-api/index.html index 1eba294bb..6522fa6de 100644 --- a/docs/devops-guide/cloud-api/index.html +++ b/docs/devops-guide/cloud-api/index.html @@ -4,13 +4,13 @@ Cloud API | Jitsi Meet - +
    -
    - +
    + \ No newline at end of file diff --git a/docs/devops-guide/devops-guide-bsd/index.html b/docs/devops-guide/devops-guide-bsd/index.html index e2b887cde..ab4829385 100644 --- a/docs/devops-guide/devops-guide-bsd/index.html +++ b/docs/devops-guide/devops-guide-bsd/index.html @@ -4,13 +4,13 @@ Self-Hosting Guide - FreeBSD/NetBSD/OpenBSD | Jitsi Meet - +
    -

    Self-Hosting Guide - FreeBSD/NetBSD/OpenBSD

    This document is a reference point for pointing to the upstream packages provided by the FreeBSD, NetBSD and OpenBSD distributions. Jitsi only officially supports Linux, for any problems with the BSD packages you can contact their respective mailing lists.

    Note: Many of the installation steps require root access.

    FreeBSD

    FreeBSD provides ports for Jitsi along with documentation on how to configure it and the current limitations - https://wiki.freebsd.org/Jitsi.

    Jitsi can be installed using the meta port net-im/jitsi-meet-full which pulls in Jitsi Videobridge, Jicofo and Jitsi Meet Web UI, along with prosody, the Jitsi prosody plugins, nginx and other required dependencies. Instructions on how to build the port can be read on the FreeBSD Foundation site - https://freebsdfoundation.org/freebsd-project/resourcesold/installing-a-port-on-freebsd/.

    NetBSD

    NetBSD provides individual ports for Jitsi Videobridge, Jicofo, Jitsi prosody plugins and Jitsi Meet Web UI. They can be installed using the command pkg_add <pkg-name>.

    OpenBSD

    OpenBSD provides ports for Jitsi, along with a pkg-readme which details how to configure Jitsi for a single host install, located at /usr/local/share/docs/pkg-readme/jitsi.

    The meta port can be installed by the command pkg_add jitsi, which pulls in the individual ports, Jitsi Videobridge, Jicofo and Jitsi Meet Web UI, along with prosody, Jitsi prosody plugins and other required dependencies.

    Limitations

    • Jigasi and Jibri have not yet been ported to work with any BSD systems.
    - +

    Self-Hosting Guide - FreeBSD/NetBSD/OpenBSD

    This document is a reference point for pointing to the upstream packages provided by the FreeBSD, NetBSD and OpenBSD distributions. Jitsi only officially supports Linux, for any problems with the BSD packages you can contact their respective mailing lists.

    Note: Many of the installation steps require root access.

    FreeBSD

    FreeBSD provides ports for Jitsi along with documentation on how to configure it and the current limitations - https://wiki.freebsd.org/Jitsi.

    Jitsi can be installed using the meta port net-im/jitsi-meet-full which pulls in Jitsi Videobridge, Jicofo and Jitsi Meet Web UI, along with prosody, the Jitsi prosody plugins, nginx and other required dependencies. Instructions on how to build the port can be read on the FreeBSD Foundation site - https://freebsdfoundation.org/freebsd-project/resourcesold/installing-a-port-on-freebsd/.

    NetBSD

    NetBSD provides individual ports for Jitsi Videobridge, Jicofo, Jitsi prosody plugins and Jitsi Meet Web UI. They can be installed using the command pkg_add <pkg-name>.

    OpenBSD

    OpenBSD provides ports for Jitsi, along with a pkg-readme which details how to configure Jitsi for a single host install, located at /usr/local/share/docs/pkg-readme/jitsi.

    The meta port can be installed by the command pkg_add jitsi, which pulls in the individual ports, Jitsi Videobridge, Jicofo and Jitsi Meet Web UI, along with prosody, Jitsi prosody plugins and other required dependencies.

    Limitations

    • Jigasi and Jibri have not yet been ported to work with any BSD systems.
    + \ No newline at end of file diff --git a/docs/devops-guide/devops-guide-docker/index.html b/docs/devops-guide/devops-guide-docker/index.html index 9d2923f46..e979a54bd 100644 --- a/docs/devops-guide/devops-guide-docker/index.html +++ b/docs/devops-guide/devops-guide-docker/index.html @@ -4,7 +4,7 @@ Self-Hosting Guide - Docker | Jitsi Meet - + @@ -64,8 +64,8 @@ Docker Compose with the additional config file etherpad.yml.

    Here are the required options:

    VariableDescriptionExample
    ETHERPAD_URL_BASESet etherpad-lite URLhttp://etherpad.meet.jitsi:9001

    Transcription configuration

    If you want to enable the Transcribing function, these options are required:

    VariableDescriptionExample
    ENABLE_TRANSCRIPTIONSEnable Jigasi transcription in a conference1
    GC_PROJECT_IDproject_id from Google Cloud Credentials
    GC_PRIVATE_KEY_IDprivate_key_id from Google Cloud Credentials
    GC_PRIVATE_KEYprivate_key from Google Cloud Credentials
    GC_CLIENT_EMAILclient_email from Google Cloud Credentials
    GC_CLIENT_IDclient_id from Google Cloud Credentials
    GC_CLIENT_CERT_URLclient_x509_cert_url from Google Cloud Credentials
    JIGASI_TRANSCRIBER_RECORD_AUDIOJigasi will record audio when transcriber is ontrue
    JIGASI_TRANSCRIBER_SEND_TXTJigasi will send transcribed text to the chat when transcriber is ontrue
    JIGASI_TRANSCRIBER_ADVERTISE_URLJigasi will post an url to the chat with transcription filetrue

    For setting the Google Cloud Credentials please read https://cloud.google.com/text-to-speech/docs/quickstart-protocol section "Before you begin" paragraph 1 to 5.

    Sentry logging configuration

    VariableDescriptionDefault value
    JVB_SENTRY_DSNSentry Data Source Name (Endpoint for Sentry project)https://public:private@host:port/1
    JICOFO_SENTRY_DSNSentry Data Source Name (Endpoint for Sentry project)https://public:private@host:port/1
    JIGASI_SENTRY_DSNSentry Data Source Name (Endpoint for Sentry project)https://public:private@host:port/1
    SENTRY_ENVIRONMENTOptional environment info to filter eventsproduction
    SENTRY_RELEASEOptional release info to filter events1.0.0

    TURN server configuration

    Configure external TURN servers.

    VariableDescriptionDefault value
    TURN_CREDENTIALSCredentials for TURN servers
    TURN_HOSTTURN server hostnames as a comma separated list (UDP or TCP transport)
    TURN_PORTTURN server port (UDP or TCP transport)443
    TURN_TRANSPORTTURN server protocols as a comma separated list (UDP or TCP or both)tcp
    TURNS_HOSTTURN server hostnames as a comma separated list (TLS transport)
    TURNS_PORTTURN server port (TLS transport)443

    Advanced configuration

    These configuration options are already set and generally don't need to be changed.

    VariableDescriptionDefault value
    XMPP_DOMAINInternal XMPP domainmeet.jitsi
    XMPP_AUTH_DOMAINInternal XMPP domain for authenticated servicesauth.meet.jitsi
    XMPP_SERVERInternal XMPP server name xmpp.meet.jitsixmpp.meet.jitsi
    XMPP_BOSH_URL_BASEInternal XMPP server URL for BOSH modulehttp://xmpp.meet.jitsi:5280
    XMPP_MUC_DOMAINXMPP domain for the MUCmuc.meet.jitsi
    XMPP_INTERNAL_MUC_DOMAINXMPP domain for the internal MUCinternal-muc.meet.jitsi
    XMPP_GUEST_DOMAINXMPP domain for unauthenticated usersguest.meet.jitsi
    XMPP_RECORDER_DOMAINDomain for the jibri recorderrecorder.meet.jitsi
    XMPP_MODULESCustom Prosody modules for XMPP_DOMAIN (comma separated)info,alert
    XMPP_MUC_MODULESCustom Prosody modules for MUC component (comma separated)info,alert
    XMPP_INTERNAL_MUC_MODULESCustom Prosody modules for internal MUC component (comma separated)info,alert
    GLOBAL_MODULESCustom prosody modules to load in global configuration (comma separated)statistics,alert
    GLOBAL_CONFIGCustom configuration string with escaped newlinesfoo = bar;\nkey = val;
    RESTART_POLICYContainer restart policydefaults to unless-stopped
    DISABLE_HTTPSHandle TLS connections outside of this setup0
    ENABLE_HTTP_REDIRECTRedirect HTTP traffic to HTTPS0
    LOG_LEVELControls which logs are output from prosody and associated modulesinfo
    ENABLE_HSTSSend a strict-transport-security header to force browsers to use a secure and trusted connection. Recommended for production use.1
    ENABLE_IPV6Provides means to disable IPv6 in environments that don't support it1

    Advanced Prosody options

    VariableDescriptionDefault value
    PROSODY_RESERVATION_ENABLEDEnable Prosody's reservation REST APIfalse
    PROSODY_RESERVATION_REST_BASE_URLBase URL of Prosody's reservation REST API
    PROSODY_AUTH_TYPESelect authentication type for Prosody (internal, jwt or ldap)AUTH_TYPE

    Advanced Jicofo options

    VariableDescriptionDefault value
    JICOFO_COMPONENT_SECRETXMPP component password for Jicofos3cr37
    JICOFO_AUTH_USERXMPP user for Jicofo client connectionsfocus
    JICOFO_AUTH_PASSWORDXMPP password for Jicofo client connections<unset>
    JICOFO_ENABLE_AUTHEnable authentication in JicofoENABLE_AUTH
    JICOFO_AUTH_TYPESelect authentication type for Jicofo (internal, jwt or ldap)AUTH_TYPE
    JICOFO_AUTH_LIFETIMESelect session timeout value for an authenticated user24 hours
    JICOFO_ENABLE_HEALTH_CHECKSEnable health checks inside Jicofo, allowing the use of the REST api to check Jicofo's statusfalse

    Advanced JVB options

    VariableDescriptionDefault value
    JVB_AUTH_USERXMPP user for JVB MUC client connectionsjvb
    JVB_AUTH_PASSWORDXMPP password for JVB MUC client connections<unset>
    JVB_STUN_SERVERSSTUN servers used to discover the server's public IPstun.l.google.com:19302, stun1.l.google.com:19302, stun2.l.google.com:19302
    JVB_PORTUDP port for media used by Jitsi Videobridge10000
    JVB_COLIBRI_PORTCOLIBRI REST API port of JVB exposed to localhost8080
    JVB_BREWERY_MUCMUC name for the JVB pooljvbbrewery
    COLIBRI_REST_ENABLEDEnable the COLIBRI REST APItrue
    SHUTDOWN_REST_ENABLEDEnable the shutdown REST APItrue

    Advanced Jigasi options

    VariableDescriptionDefault value
    JIGASI_ENABLE_SDES_SRTPEnable SDES srtp0
    JIGASI_SIP_KEEP_ALIVE_METHODKeepalive methodOPTIONS
    JIGASI_HEALTH_CHECK_SIP_URIHealth-check extension
    JIGASI_HEALTH_CHECK_INTERVALHealth-check interval300000
    JIGASI_XMPP_USERXMPP user for Jigasi MUC client connectionsjigasi
    JIGASI_XMPP_PASSWORDXMPP password for Jigasi MUC client connections<unset>
    JIGASI_BREWERY_MUCMUC name for the Jigasi pooljigasibrewery
    JIGASI_PORT_MINMinimum port for media used by Jigasi20000
    JIGASI_PORT_MAXMaximum port for media used by Jigasi20050

    Running behind NAT or on a LAN environment

    When running running in a LAN environment, or on the public Internet via NAT, the JVB_ADVERTISE_IPS env variable should be set. This variable allows to control which IP addresses the JVB will advertise for WebRTC media traffic.

    note

    This variable used to be called DOCKER_HOST_ADDRESS but it got renamed for clarity and to support a list of IPs.

    If your users are coming in over the Internet (and not over LAN), this will likely be your public IP address. If this is not set up correctly, calls will crash when more than two users join a meeting.

    The public IP address is attempted to be discovered via STUN. STUN servers can be specified with the JVB_STUN_SERVERS option.

    note

    Due to a bug in the docker version currently in the Debian repos (20.10.5), Docker does not listen on IPv6 ports, so for that combination you will have to manually obtain the latest version.

    Split horizon

    If you are running in a split horizon environemt (LAN internal clients connect to a local IP and other clients connect to a public IP) you can specify -multiple advertised IPs by separating them with commas:

    JVB_ADVERTISE_IPS=192.168.1.1,1.2.3.4

    Offline / airgapped installation

    If your setup does not have access to the Internet you'll need to disable STUN on the JVB since discovering its own IP address will fail, but that is not necessary on that type of environment.

    JVB_DISABLE_STUN=true

    Accessing server logs

    The default bahavior of docker-jitsi-meet is to log to stdout.

    While the logs are sent to stdout, they are not lost: unless configured to drop all logs, Docker keeps them available for future retrieval and processing.

    If you need to access the container's logs you have multiple options. Here are the main ones:

    • run docker-compose logs -t -f <service_name> from command line, where <service_name> is one of web, prosody,jvb, jicofo. This command will output the logs for the selected service to stdout with timestamps.
    • use a standard docker logging driver to redirect the logs to the desired target (for instance syslog or splunk).
    • serach docker hub for a third party docker logging driver plugin
    • or write your own driver plugin if you have a very specific need.

    For instance, if you want to have all logs related to a <service_name> written to /var/log/jitsi/<service_name> as json output, you could use docker-file-log-driver and configure it by adding the following block in your docker-compose.yml file, at the same level as the image block of the selected <service_name>:

    services:
    <service_name>:
    image: ...
    ...
    logging:
    driver: file-log-driver
    options:
    fpath: "/jitsi/<service_name>.log"

    If you want to only display the message part of the log in json format, simply execute the following command (for instance if fpath was set to /jitsi/jvb.log) which uses jq to extract the relevant part of the logs:

    sudo cat /var/log/jitsi/jvb.log | jq -r '.msg' | jq -r '.message'

    Build Instructions

    Building your images allows you to edit the configuration files of each image individually, providing more customization for your deployment.

    The docker images can be built by running the make command in the main repository folder. If you need to overwrite existing images from the remote source, use FORCE_REBUILD=1 make.

    If you are on the unstable branch, build the images with FORCE_REBUILD=1 JITSI_RELEASE=unstable make.

    You are now able to run docker-compose up as usual.

    Running behind a reverse proxy

    By default this setup is using WebSocket connections for 2 core components:

    • Signalling (XMPP)
    • Bridge channel (colibri)

    Due to the hop-by-hop nature of WebSockets the reverse proxy must properly terminate and forward WebSocket connections. There 2 routes require such treatment:

    • /xmpp-websocket
    • /colibri-ws

    With nginx, these routes can be forwarded using the following config snippet:

    location /xmpp-websocket {
    proxy_pass https://localhost:8443;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    }
    location /colibri-ws {
    proxy_pass https://localhost:8443;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    }

    With apache, mod_proxy and mod_proxy_wstunnel need to be enabled and these routes can be forwarded using the following config snippet:

    <IfModule mod_proxy.c>
    <IfModule mod_proxy_wstunnel.c>
    ProxyTimeout 900
    <Location "/xmpp-websocket">
    ProxyPass "wss://localhost:8443/xmpp-websocket"
    </Location>
    <Location "/colibri-ws/">
    ProxyPass "wss://localhost:8443/colibri-ws/"
    </Location>
    </IfModule>
    </IfModule>

    where https://localhost:8443/ is the url of the web service's ingress.

    Disabling WebSocket connections

    note

    This is not the recommended setup.

    If using WebSockets is not an option, these environment variables can be set to fallback to HTTP polling and WebRTC datachannels:

    ENABLE_SCTP=1
    ENABLE_COLIBRI_WEBSOCKET=0
    ENABLE_XMPP_WEBSOCKET=0
    - +multiple advertised IPs by separating them with commas:

    JVB_ADVERTISE_IPS=192.168.1.1,1.2.3.4

    Offline / airgapped installation

    If your setup does not have access to the Internet you'll need to disable STUN on the JVB since discovering its own IP address will fail, but that is not necessary on that type of environment.

    JVB_DISABLE_STUN=true

    Accessing server logs

    The default bahavior of docker-jitsi-meet is to log to stdout.

    While the logs are sent to stdout, they are not lost: unless configured to drop all logs, Docker keeps them available for future retrieval and processing.

    If you need to access the container's logs you have multiple options. Here are the main ones:

    • run docker-compose logs -t -f <service_name> from command line, where <service_name> is one of web, prosody,jvb, jicofo. This command will output the logs for the selected service to stdout with timestamps.
    • use a standard docker logging driver to redirect the logs to the desired target (for instance syslog or splunk).
    • serach docker hub for a third party docker logging driver plugin
    • or write your own driver plugin if you have a very specific need.

    For instance, if you want to have all logs related to a <service_name> written to /var/log/jitsi/<service_name> as json output, you could use docker-file-log-driver and configure it by adding the following block in your docker-compose.yml file, at the same level as the image block of the selected <service_name>:

    services:
    <service_name>:
    image: ...
    ...
    logging:
    driver: file-log-driver
    options:
    fpath: "/jitsi/<service_name>.log"

    If you want to only display the message part of the log in json format, simply execute the following command (for instance if fpath was set to /jitsi/jvb.log) which uses jq to extract the relevant part of the logs:

    sudo cat /var/log/jitsi/jvb.log | jq -r '.msg' | jq -r '.message'

    Build Instructions

    Building your images allows you to edit the configuration files of each image individually, providing more customization for your deployment.

    The docker images can be built by running the make command in the main repository folder. If you need to overwrite existing images from the remote source, use FORCE_REBUILD=1 make.

    If you are on the unstable branch, build the images with FORCE_REBUILD=1 JITSI_RELEASE=unstable make.

    You are now able to run docker-compose up as usual.

    Running behind a reverse proxy

    By default this setup is using WebSocket connections for 2 core components:

    • Signalling (XMPP)
    • Bridge channel (colibri)

    Due to the hop-by-hop nature of WebSockets the reverse proxy must properly terminate and forward WebSocket connections. There 2 routes require such treatment:

    • /xmpp-websocket
    • /colibri-ws

    With nginx, these routes can be forwarded using the following config snippet:

    location /xmpp-websocket {
    proxy_pass https://localhost:8443;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    }
    location /colibri-ws {
    proxy_pass https://localhost:8443;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    }

    With apache, mod_proxy and mod_proxy_wstunnel need to be enabled and these routes can be forwarded using the following config snippet:

    <IfModule mod_proxy.c>
    <IfModule mod_proxy_wstunnel.c>
    ProxyTimeout 900
    <Location "/xmpp-websocket">
    ProxyPass "wss://localhost:8443/xmpp-websocket"
    </Location>
    <Location "/colibri-ws/">
    ProxyPass "wss://localhost:8443/colibri-ws/"
    </Location>
    </IfModule>
    </IfModule>

    where https://localhost:8443/ is the url of the web service's ingress.

    Disabling WebSocket connections

    note

    This is not the recommended setup.

    If using WebSockets is not an option, these environment variables can be set to fallback to HTTP polling and WebRTC datachannels:

    ENABLE_SCTP=1
    ENABLE_COLIBRI_WEBSOCKET=0
    ENABLE_XMPP_WEBSOCKET=0
    + \ No newline at end of file diff --git a/docs/devops-guide/devops-guide-manual/index.html b/docs/devops-guide/devops-guide-manual/index.html index fcd28586d..99051cecf 100644 --- a/docs/devops-guide/devops-guide-manual/index.html +++ b/docs/devops-guide/devops-guide-manual/index.html @@ -4,7 +4,7 @@ Self-Hosting Guide - Manual installation | Jitsi Meet - + @@ -12,8 +12,8 @@

    Self-Hosting Guide - Manual installation

    Manual installation is not recommended

    We recommend following the quick-install document. The current document describes the steps that are needed to install a working deployment, but steps are easy to mess up, and the debian packages are more up-to-date, where this document is sometimes not updated to reflect latest changes.

    This describes configuring a server jitsi.example.com on a Debian-based distribution.
    For other distributions you can adapt the steps (especially changing the dependencies package installations (e.g. for nginx) and paths accordingly) so that it matches your host's distribution.
    You will also need to generate some passwords for YOURSECRET1, YOURSECRET2 and YOURSECRET3.

    There are also some complete example config files available, mentioned in each section.

    There are additional configurations to be done for a scalable installation.

    Network description

    This is how the network looks:

                       +                           +
    | |
    | |
    v |
    443 |
    +-------+ |
    | | |
    | Nginx | |
    | | |
    +--+-+--+ |
    | | |
    +------------+ | | +--------------+ |
    | | | | | | |
    | Jitsi Meet +<---+ +--->+ prosody/xmpp | |
    | |files 5280 | | |
    +------------+ +--------------+ v
    5222 ^ ^ 5222 10000
    +--------+ | | +-------------+
    | | | | | |
    | jicofo +----^ ^----+ videobridge |
    | | | |
    +--------+ +-------------+

    Install prosody

    apt-get install prosody

    Configure prosody

    Add config file in /etc/prosody/conf.avail/jitsi.example.com.cfg.lua :

    • add your domain virtual host section:
    VirtualHost "jitsi.example.com"
    authentication = "anonymous"
    ssl = {
    key = "/var/lib/prosody/jitsi.example.com.key";
    certificate = "/var/lib/prosody/jitsi.example.com.crt";
    }
    modules_enabled = {
    "bosh";
    "pubsub";
    }
    c2s_require_encryption = false
    • add domain with authentication for conference focus user:
    VirtualHost "auth.jitsi.example.com"
    ssl = {
    key = "/var/lib/prosody/auth.jitsi.example.com.key";
    certificate = "/var/lib/prosody/auth.jitsi.example.com.crt";
    }
    authentication = "internal_hashed"
    • add focus user to server admins:
    admins = { "focus@auth.jitsi.example.com" }
    • and finally configure components:
    Component "conference.jitsi.example.com" "muc"
    Component "jitsi-videobridge.jitsi.example.com"
    component_secret = "YOURSECRET1"
    Component "focus.jitsi.example.com"
    component_secret = "YOURSECRET2"

    Add link for the added configuration

    ln -s /etc/prosody/conf.avail/jitsi.example.com.cfg.lua /etc/prosody/conf.d/jitsi.example.com.cfg.lua

    Generate certs for the domain:

    prosodyctl cert generate jitsi.example.com
    prosodyctl cert generate auth.jitsi.example.com

    Add auth.jitsi.example.com to the trusted certificates on the local machine:

    ln -sf /var/lib/prosody/auth.jitsi.example.com.crt /usr/local/share/ca-certificates/auth.jitsi.example.com.crt
    update-ca-certificates -f

    Note that the -f flag is necessary if there are symlinks left from a previous installation.

    If you are using a JDK package not provided by Debian, as the ones from adoptjdk, you should also make your JDK aware of the new debian certificate keystore replacing or linking the JDK cacerts. Example, if you use JDK from adoptjdk:

    cd /usr/lib/jvm/adoptopenjdk-8-hotspot-amd64/jre
    ln -sf /etc/ssl/certs/java/cacerts lib/security/cacerts

    Create conference focus user:

    prosodyctl register focus auth.jitsi.example.com YOURSECRET3

    Restart prosody XMPP server with the new config

    prosodyctl restart

    Install Nginx

    apt-get install nginx

    Add a new file jitsi.example.com in /etc/nginx/sites-available (see also the example config file):

    server_names_hash_bucket_size 64;

    server {
    listen 0.0.0.0:443 ssl http2;
    listen [::]:443 ssl http2;
    # tls configuration that is not covered in this guide
    # we recommend the use of https://certbot.eff.org/
    server_name jitsi.example.com;
    # set the root
    root /srv/jitsi-meet;
    index index.html;
    location ~ ^/([a-zA-Z0-9=\?]+)$ {
    rewrite ^/(.*)$ / break;
    }
    location / {
    ssi on;
    }
    # BOSH, Bidirectional-streams Over Synchronous HTTP
    # https://en.wikipedia.org/wiki/BOSH_(protocol)
    location /http-bind {
    proxy_pass http://localhost:5280/http-bind;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header Host $http_host;
    }
    # external_api.js must be accessible from the root of the
    # installation for the electron version of Jitsi Meet to work
    # https://github.com/jitsi/jitsi-meet-electron
    location /external_api.js {
    alias /srv/jitsi-meet/libs/external_api.min.js;
    }
    }

    Add link for the added configuration

    cd /etc/nginx/sites-enabled
    ln -s ../sites-available/jitsi.example.com jitsi.example.com

    Install Jitsi Videobridge

    danger

    This method is no longer supported. -You can either install the JVB from https://download.jitsi.org/stable/ and follow these Instructions or clone the repo and build it manually.

    Visit https://download.jitsi.org/jitsi-videobridge/linux to determine the current build number, download and unzip it:

    wget https://download.jitsi.org/jitsi-videobridge/linux/jitsi-videobridge-linux-{arch-buildnum}.zip
    unzip jitsi-videobridge-linux-{arch-buildnum}.zip

    Install JRE if missing:

    apt-get install openjdk-8-jre

    NOTE: When installing on older Debian releases keep in mind that you need JRE >= 1.7.

    Create ~/.sip-communicator/sip-communicator.properties in the home folder of the user that will be starting Jitsi Videobridge:

    mkdir -p ~/.sip-communicator
    cat > ~/.sip-communicator/sip-communicator.properties << EOF
    org.jitsi.impl.neomedia.transform.srtp.SRTPCryptoContext.checkReplay=false
    # The videobridge uses 443 by default with 4443 as a fallback, but since we're already
    # running nginx on 443 in this example doc, we specify 4443 manually to avoid a race condition
    org.jitsi.videobridge.TCP_HARVESTER_PORT=4443
    EOF

    Start the videobridge with:

    ./jvb.sh --host=localhost --domain=jitsi.example.com --secret=YOURSECRET1 &

    Or autostart it by adding the line in /etc/rc.local:

    /bin/bash /root/jitsi-videobridge-linux-{arch-buildnum}/jvb.sh --host=localhost --domain=jitsi.example.com --secret=YOURSECRET1 </dev/null >> /var/log/jvb.log 2>&1

    Install Jitsi Conference Focus (jicofo)

    Install JDK and Maven if missing:

    apt-get install openjdk-8-jdk maven

    NOTE: When installing on older Debian releases keep in mind that you need JDK >= 1.7.

    Clone source from Github repo:

    git clone https://github.com/jitsi/jicofo.git

    Build the package.

    cd jicofo
    mvn package -DskipTests -Dassembly.skipAssembly=false

    Run jicofo:

    =======
    unzip target/jicofo-1.1-SNAPSHOT-archive.zip
    cd jicofo-1.1-SNAPSHOT-archive'
    ./jicofo.sh --host=localhost --domain=jitsi.example.com --secret=YOURSECRET2 --user_domain=auth.jitsi.example.com --user_name=focus --user_password=YOURSECRET3

    Deploy Jitsi Meet

    Checkout and configure Jitsi Meet:

    cd /srv
    git clone https://github.com/jitsi/jitsi-meet.git
    cd jitsi-meet
    npm install
    make

    NOTE: When installing on older distributions keep in mind that you need Node.js >= 12 and npm >= 6.

    Edit host names in /srv/jitsi-meet/config.js (see also the example config file):

    var config = {
    hosts: {
    domain: 'jitsi.example.com',
    muc: 'conference.jitsi.example.com',
    bridge: 'jitsi-videobridge.jitsi.example.com',
    focus: 'focus.jitsi.example.com'
    },
    useNicks: false,
    bosh: '//jitsi.example.com/http-bind', // FIXME: use xep-0156 for that
    //chromeExtensionId: 'diibjkoicjeejcmhdnailmkgecihlobk', // Id of desktop streamer Chrome extension
    //minChromeExtVersion: '0.1' // Required version of Chrome extension
    };

    Verify that nginx config is valid and reload nginx:

    nginx -t && nginx -s reload

    Running behind NAT

    Jitsi Videobridge can run behind a NAT, provided that both required ports are routed (forwarded) to the machine that it runs on. By default these ports are TCP/4443 and UDP/10000.

    If you do not route these two ports, Jitsi Meet will only work with video for two people, breaking upon 3 or more people trying to show video.

    TCP/443 is required for the webserver which can be running on another machine than the Jitsi Videobrige is running on.

    The following extra lines need to be added to the file ~/.sip-communicator/sip-communicator.properties (in the home directory of the user running the videobridge):

    org.ice4j.ice.harvest.NAT_HARVESTER_LOCAL_ADDRESS=<Local.IP.Address>
    org.ice4j.ice.harvest.NAT_HARVESTER_PUBLIC_ADDRESS=<Public.IP.Address>

    Hold your first conference

    You are now all set and ready to have your first meet by going to http://jitsi.example.com

    Enabling recording

    Jibri is a set of tools for recording and/or streaming a Jitsi Meet conference.

    - +You can either install the JVB from https://download.jitsi.org/stable/ and follow these Instructions or clone the repo and build it manually.

    Visit https://download.jitsi.org/jitsi-videobridge/linux to determine the current build number, download and unzip it:

    wget https://download.jitsi.org/jitsi-videobridge/linux/jitsi-videobridge-linux-{arch-buildnum}.zip
    unzip jitsi-videobridge-linux-{arch-buildnum}.zip

    Install JRE if missing:

    apt-get install openjdk-8-jre

    NOTE: When installing on older Debian releases keep in mind that you need JRE >= 1.7.

    Create ~/.sip-communicator/sip-communicator.properties in the home folder of the user that will be starting Jitsi Videobridge:

    mkdir -p ~/.sip-communicator
    cat > ~/.sip-communicator/sip-communicator.properties << EOF
    org.jitsi.impl.neomedia.transform.srtp.SRTPCryptoContext.checkReplay=false
    # The videobridge uses 443 by default with 4443 as a fallback, but since we're already
    # running nginx on 443 in this example doc, we specify 4443 manually to avoid a race condition
    org.jitsi.videobridge.TCP_HARVESTER_PORT=4443
    EOF

    Start the videobridge with:

    ./jvb.sh --host=localhost --domain=jitsi.example.com --secret=YOURSECRET1 &

    Or autostart it by adding the line in /etc/rc.local:

    /bin/bash /root/jitsi-videobridge-linux-{arch-buildnum}/jvb.sh --host=localhost --domain=jitsi.example.com --secret=YOURSECRET1 </dev/null >> /var/log/jvb.log 2>&1

    Install Jitsi Conference Focus (jicofo)

    Install JDK and Maven if missing:

    apt-get install openjdk-8-jdk maven

    NOTE: When installing on older Debian releases keep in mind that you need JDK >= 1.7.

    Clone source from Github repo:

    git clone https://github.com/jitsi/jicofo.git

    Build the package.

    cd jicofo
    mvn package -DskipTests -Dassembly.skipAssembly=false

    Run jicofo:

    =======
    unzip target/jicofo-1.1-SNAPSHOT-archive.zip
    cd jicofo-1.1-SNAPSHOT-archive'
    ./jicofo.sh --host=localhost --domain=jitsi.example.com --secret=YOURSECRET2 --user_domain=auth.jitsi.example.com --user_name=focus --user_password=YOURSECRET3

    Deploy Jitsi Meet

    Checkout and configure Jitsi Meet:

    cd /srv
    git clone https://github.com/jitsi/jitsi-meet.git
    cd jitsi-meet
    npm install
    make

    NOTE: When installing on older distributions keep in mind that you need Node.js >= 12 and npm >= 6.

    Edit host names in /srv/jitsi-meet/config.js (see also the example config file):

    var config = {
    hosts: {
    domain: 'jitsi.example.com',
    muc: 'conference.jitsi.example.com',
    bridge: 'jitsi-videobridge.jitsi.example.com',
    focus: 'focus.jitsi.example.com'
    },
    useNicks: false,
    bosh: '//jitsi.example.com/http-bind', // FIXME: use xep-0156 for that
    //chromeExtensionId: 'diibjkoicjeejcmhdnailmkgecihlobk', // Id of desktop streamer Chrome extension
    //minChromeExtVersion: '0.1' // Required version of Chrome extension
    };

    Verify that nginx config is valid and reload nginx:

    nginx -t && nginx -s reload

    Running behind NAT

    Jitsi Videobridge can run behind a NAT, provided that both required ports are routed (forwarded) to the machine that it runs on. By default these ports are TCP/4443 and UDP/10000.

    If you do not route these two ports, Jitsi Meet will only work with video for two people, breaking upon 3 or more people trying to show video.

    TCP/443 is required for the webserver which can be running on another machine than the Jitsi Videobrige is running on.

    The following extra lines need to be added to the file ~/.sip-communicator/sip-communicator.properties (in the home directory of the user running the videobridge):

    org.ice4j.ice.harvest.NAT_HARVESTER_LOCAL_ADDRESS=<Local.IP.Address>
    org.ice4j.ice.harvest.NAT_HARVESTER_PUBLIC_ADDRESS=<Public.IP.Address>

    Hold your first conference

    You are now all set and ready to have your first meet by going to http://jitsi.example.com

    Enabling recording

    Jibri is a set of tools for recording and/or streaming a Jitsi Meet conference.

    + \ No newline at end of file diff --git a/docs/devops-guide/devops-guide-opensuse/index.html b/docs/devops-guide/devops-guide-opensuse/index.html index f2998570b..a3828bad1 100644 --- a/docs/devops-guide/devops-guide-opensuse/index.html +++ b/docs/devops-guide/devops-guide-opensuse/index.html @@ -4,7 +4,7 @@ Self-Hosting Guide - openSUSE | Jitsi Meet - + @@ -28,8 +28,8 @@ this on a dedicated host for Jitsi.
  • Updating Jitsi is crucial to get rid of bugs and updated dependencies with possible security fixes.
  • Although tempted through Chrome: Don't install a full X11 stack like KDE or Gnome for this.
  • Don't mix the rpms or debs with a source installation of the same component.
  • Securely backup your configuration, preferably in a VCS. This saves time and -pain when doing rollbacks or dealing with other problems.
  • - +pain when doing rollbacks or dealing with other problems. + \ No newline at end of file diff --git a/docs/devops-guide/devops-guide-quickstart/index.html b/docs/devops-guide/devops-guide-quickstart/index.html index 0a1081385..bb30de72a 100644 --- a/docs/devops-guide/devops-guide-quickstart/index.html +++ b/docs/devops-guide/devops-guide-quickstart/index.html @@ -4,7 +4,7 @@ Self-Hosting Guide - Debian/Ubuntu server | Jitsi Meet - + @@ -30,8 +30,8 @@ You can also visit https://webrtc.github.io/samples/src/content/getusermedia/gum to test your browser's WebRTC support.

  • Firewall: If participants cannot see or hear each other, double check your firewall / NAT rules.

  • Nginx/Apache: As we prefer the usage of Nginx as webserver, the installer checks first for the presence of Nginx and then for Apache. In case you desperately need to enforce the usage of apache, try pre-setting the variable jitsi-meet/enforce_apache for package jitsi-meet-web-config on debconf.

  • Log files: -Take a look at the various log files:

  • /var/log/jitsi/jvb.log
    /var/log/jitsi/jicofo.log
    /var/log/prosody/prosody.log

    Additional Functions

    Adding sip-gateway to Jitsi Meet

    Install Jigasi

    Jigasi is a server-side application acting as a gateway to Jitsi Meet conferences. It allows regular SIP clients to join meetings and provides transcription capabilities.

    sudo apt install jigasi

    During the installation, you will be asked to enter your SIP account and password. This account will be used to invite the other SIP participants.

    Reload Jitsi Meet

    Launch again a browser with the Jitsi Meet URL and you'll see a telephone icon on the right end of the toolbar. Use it to invite SIP accounts to join the current conference.

    Enjoy!

    - +Take a look at the various log files:

    /var/log/jitsi/jvb.log
    /var/log/jitsi/jicofo.log
    /var/log/prosody/prosody.log

    Additional Functions

    Adding sip-gateway to Jitsi Meet

    Install Jigasi

    Jigasi is a server-side application acting as a gateway to Jitsi Meet conferences. It allows regular SIP clients to join meetings and provides transcription capabilities.

    sudo apt install jigasi

    During the installation, you will be asked to enter your SIP account and password. This account will be used to invite the other SIP participants.

    Reload Jitsi Meet

    Launch again a browser with the Jitsi Meet URL and you'll see a telephone icon on the right end of the toolbar. Use it to invite SIP accounts to join the current conference.

    Enjoy!

    + \ No newline at end of file diff --git a/docs/devops-guide/devops-guide-requirements/index.html b/docs/devops-guide/devops-guide-requirements/index.html index 2666d3dad..3f0e86a0a 100644 --- a/docs/devops-guide/devops-guide-requirements/index.html +++ b/docs/devops-guide/devops-guide-requirements/index.html @@ -4,7 +4,7 @@ Requirements | Jitsi Meet - + @@ -30,8 +30,8 @@ If you are knowledgeable, you can setup Jibris in containers and use a big server to save a bit on resources but that's about it.

    Jibri RAM, CPU and hard disk needs are far higher than Jitsi Meet itself, as it does video encoding. For 1080x720 you currently need at least 8 GB RAM, for 1280x1024 12 GB (this is for recording a single meeting). For cloud storage you will need at least SSD drives. -If memory is not sufficient, CPU can't encode fast enough or hard disk is not fast enough, recordings will fail.

    While Jibri and Jitsi Meet can technically be hosted in a single server, it's not recommended because Jibri is a resource drain and it can harm Jitsi Meet performance, and can exhaust disk space and stop Jitsi Meet function altogether.

    - +If memory is not sufficient, CPU can't encode fast enough or hard disk is not fast enough, recordings will fail.

    While Jibri and Jitsi Meet can technically be hosted in a single server, it's not recommended because Jibri is a resource drain and it can harm Jitsi Meet performance, and can exhaust disk space and stop Jitsi Meet function altogether.

    + \ No newline at end of file diff --git a/docs/devops-guide/devops-guide-scalable/index.html b/docs/devops-guide/devops-guide-scalable/index.html index a7395e81c..99aada3a7 100644 --- a/docs/devops-guide/devops-guide-scalable/index.html +++ b/docs/devops-guide/devops-guide-scalable/index.html @@ -4,7 +4,7 @@ DevOps Guide (scalable setup) | Jitsi Meet - + @@ -28,8 +28,8 @@ (We are not installing the jitsi-meet package which would handle that for us)

    Install the debconf-utils package

    $ cat << EOF | sudo debconf-set-selections
    jitsi-videobridge jitsi-videobridge/jvb-hostname string meet.example.com
    jitsi-meet jitsi-meet/jvb-serve boolean false
    jitsi-meet-prosody jitsi-videobridge/jvb-hostname string meet.example.com
    jitsi-meet-web-config jitsi-meet/cert-choice select I want to use my own certificate
    jitsi-meet-web-config jitsi-meet/cert-path-crt string /etc/ssl/meet.example.com.crt
    jitsi-meet-web-config jitsi-meet/cert-path-key string /etc/ssl/meet.example.com.key
    jitsi-meet-web-config jitsi-meet/jaas-choice boolean false
    EOF

    To enable integration with Jitsi Meet Components for telephony support, set the jitsi-meet/jaas-choice option above to true.

    On the jitsi-meet server, install the following packages:

    • nginx
    • prosody
    • jicofo
    • jitsi-meet-web
    • jitsi-meet-prosody
    • jitsi-meet-web-config

    Installation of Videobridge(s)

    For simplicities sake, set the same debconf variables as above and install

    • jitsi-videobridge2

    Configuration of jitsi-meet

    Firewall

    Open the following ports:

    Open to world:

    • 80 TCP
    • 443 TCP

    Open to the videobridges only

    • 5222 TCP (for Prosody)

    NGINX

    Create the /etc/nginx/sites-available/meet.example.com.conf as usual

    Prosody

    Follow the steps in the manual install for setup tasks

    Jitsi-Meet

    Adapt /usr/share/jitsi-meet/config.js and /usr/share/jitsi-meet/interface-config.js to your specific needs

    Jicofo

    No changes necessary from the default install.

    Configuration of the Videobridge

    Firewall

    Open the following ports:

    Open to world:

    • 10000 UDP (for media)

    jitsi-videobridge2

    No changes necessary from the default setup.

    Testing

    After restarting all services (prosody, jicofo and all the jitsi-videobridge2) you can see in /var/log/prosody/prosody.log and -/var/log/jitsi/jicofo.log that the videobridges connect to Prososy and that Jicofo picks them up.

    When a new conference starts, Jicofo picks a videobridge and schedules the conference on it.

    - +/var/log/jitsi/jicofo.log that the videobridges connect to Prososy and that Jicofo picks them up.

    When a new conference starts, Jicofo picks a videobridge and schedules the conference on it.

    + \ No newline at end of file diff --git a/docs/devops-guide/devops-guide-videotutorials/index.html b/docs/devops-guide/devops-guide-videotutorials/index.html index 3bf833733..3d8b70b6c 100644 --- a/docs/devops-guide/devops-guide-videotutorials/index.html +++ b/docs/devops-guide/devops-guide-videotutorials/index.html @@ -4,13 +4,13 @@ Video Tutorials | Jitsi Meet - + - + + \ No newline at end of file diff --git a/docs/devops-guide/faq/index.html b/docs/devops-guide/faq/index.html index 463316cbf..ee86c91f1 100644 --- a/docs/devops-guide/faq/index.html +++ b/docs/devops-guide/faq/index.html @@ -4,7 +4,7 @@ FAQ | Jitsi Meet - + @@ -12,8 +12,8 @@

    FAQ

    How to migrate away from multiplexing and enable bridge websockets

    For a while, we were using nginx multiplexing to serve Jitsi Meet's content on https(port 443) and use the same port for running a turn server. This proved to be problematic(you cannot use websockets with this setup) and we moved away from it. Here is how to remove multiplexing and enable websockets in favor of WebRTC Data Channels.

    1. Dropping multiplexing in nginx
    • delete /etc/nginx/modules-enabled/60-jitsi-meet.conf
    • Then go to /etc/nginx/sites-available/your-conf and change your virtual host to listen on 443 instead of 4444.
    1. Edit turnserver config
    • make sure your turnserver is listening on standard port tls port 5349. Make sure in /etc/turnserver.conf you have the following: tls-listening-port=5349
    • In /etc/prosody/conf.avail/your-conf.cfg.lua prosody is instructed to announce turns turn server on port 5349 by having this line: -{ type = "turns", host = "your-domain", port = "5349", transport = "tcp" }. Make sure you replace your-domain with the DNS of your deployment.
    1. Add the bridge websocket location in your nginx config (add it after the location = /xmpp-websocket section):
        # colibri (JVB) websockets for jvb1
      location ~ ^/colibri-ws/default-id/(.*) {
      proxy_pass http://127.0.0.1:9090/colibri-ws/default-id/$1$is_args$args;
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
      tcp_nodelay on;
      }
    2. Enable the websockets in Jitsi Videobridge. Make sure in /etc/jitsi/videobridge/jvb.conf you have:
      videobridge {
      http-servers {
      public {
      port = 9090
      }
      }
      websockets {
      enabled = true
      domain = "your-domain:443"
      tls = true
      }
      }
      Make sure you replace your-domain with the DNS of your deployment.
    3. After restarting the bridge (systemctl restart jitsi-videobridge2) and nginx (systemctl restart nginx) you are good to go!
    - +{ type = "turns", host = "your-domain", port = "5349", transport = "tcp" }. Make sure you replace your-domain with the DNS of your deployment.
    1. Add the bridge websocket location in your nginx config (add it after the location = /xmpp-websocket section):
        # colibri (JVB) websockets for jvb1
      location ~ ^/colibri-ws/default-id/(.*) {
      proxy_pass http://127.0.0.1:9090/colibri-ws/default-id/$1$is_args$args;
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
      tcp_nodelay on;
      }
    2. Enable the websockets in Jitsi Videobridge. Make sure in /etc/jitsi/videobridge/jvb.conf you have:
      videobridge {
      http-servers {
      public {
      port = 9090
      }
      }
      websockets {
      enabled = true
      domain = "your-domain:443"
      tls = true
      }
      }
      Make sure you replace your-domain with the DNS of your deployment.
    3. After restarting the bridge (systemctl restart jitsi-videobridge2) and nginx (systemctl restart nginx) you are good to go!
    + \ No newline at end of file diff --git a/docs/devops-guide/index.html b/docs/devops-guide/index.html index 87a68cd48..477ad546d 100644 --- a/docs/devops-guide/index.html +++ b/docs/devops-guide/index.html @@ -4,14 +4,14 @@ Self-Hosting Guide - Overview | Jitsi Meet - +

    Self-Hosting Guide - Overview

    note

    These guides help you to host your own Jitsi-Meet server.
    -If you want to have a video conference without setting up any infrastructure, use https://meet.jit.si instead.

    First, a bit of general advice

    Jitsi Meet being based on WebRTC, an encrypted communication link (https) is necessary to get working multimedia, and the setup is not always trivial.

    The best option is an Internet server with a certificate for a domain registered in the DNS.

    While it's possible to setup a server on a private network and/or a self-signed certificate, it can be less straightforward and you can expect difficulties, first if you want access both from the private network and the public internet, and second when using phones as these clients often don't accept self-signed certificates.

    In case of trouble with clients using phones, check your certificate.


    - +If you want to have a video conference without setting up any infrastructure, use https://meet.jit.si instead.

    First, a bit of general advice

    Jitsi Meet being based on WebRTC, an encrypted communication link (https) is necessary to get working multimedia, and the setup is not always trivial.

    The best option is an Internet server with a certificate for a domain registered in the DNS.

    While it's possible to setup a server on a private network and/or a self-signed certificate, it can be less straightforward and you can expect difficulties, first if you want access both from the private network and the public internet, and second when using phones as these clients often don't accept self-signed certificates.

    In case of trouble with clients using phones, check your certificate.


    + \ No newline at end of file diff --git a/docs/devops-guide/ldap-authentication/index.html b/docs/devops-guide/ldap-authentication/index.html index 95987332f..f3a601364 100644 --- a/docs/devops-guide/ldap-authentication/index.html +++ b/docs/devops-guide/ldap-authentication/index.html @@ -4,7 +4,7 @@ LDAP authentication | Jitsi Meet - + @@ -35,8 +35,8 @@ /var/run/saslauthd/ to communicate with the authentication daemon. This folder only allows access to user root and group sasl while prosody runs as the system user/group prosody.

    The easiest solution is to add the sasl group to the prosody user and -restart the service.

    sudo adduser prosody sasl
    sudo service prosody restart
    - +restart the service.

    sudo adduser prosody sasl
    sudo service prosody restart
    + \ No newline at end of file diff --git a/docs/devops-guide/reservation/index.html b/docs/devops-guide/reservation/index.html index 79ffb7637..e66b0eedb 100644 --- a/docs/devops-guide/reservation/index.html +++ b/docs/devops-guide/reservation/index.html @@ -4,7 +4,7 @@ Reservation System setup | Jitsi Meet - + @@ -52,8 +52,8 @@ is exceeded. In the latter case Prosody will destroy the XMPP MUC room. After the MUC room is destroyed, Prosody sends an HTTP DELETE request to '/conference/{id}' endpoint where {id} is replaced with -conference identifier assigned by the reservation system.

    DELETE /conference/364758328 HTTP/1.1
    host: http://reservation.example.com
    ...

    Implementation diagram

    - +conference identifier assigned by the reservation system.

    DELETE /conference/364758328 HTTP/1.1
    host: http://reservation.example.com
    ...

    Implementation diagram

    + \ No newline at end of file diff --git a/docs/devops-guide/secure-domain/index.html b/docs/devops-guide/secure-domain/index.html index acffa57ec..5b4c79577 100644 --- a/docs/devops-guide/secure-domain/index.html +++ b/docs/devops-guide/secure-domain/index.html @@ -4,7 +4,7 @@ Secure Domain setup | Jitsi Meet - + @@ -14,8 +14,8 @@ a user name and password. After the room is created, others will be able to join from anonymous domain. Here's what has to be configured:

    Prosody configuration

    If you have installed Jitsi Meet from the Debian package, these changes should be made in /etc/prosody/conf.avail/[your-hostname].cfg.lua

    Enable authentication

    Inside the VirtualHost "[your-hostname]" block, replace anonymous authentication with hashed password authentication:

    VirtualHost "jitsi-meet.example.com"
    authentication = "internal_hashed"

    Replace jitsi-meet.example.com with your hostname.

    Enable anonymous login for guests

    Add this block after the previous VirtualHost to enable the anonymous login method for guests:

    VirtualHost "guest.jitsi-meet.example.com"
    authentication = "anonymous"
    c2s_require_encryption = false

    Note that guest.jitsi-meet.example.com is internal to Jitsi, and you do not need to (and should not) create a DNS record for it, or generate an SSL/TLS certificate, or do any web server configuration. While it is internal, you should still replace jitsi-meet.example.com with your hostname.

    Jitsi Meet configuration

    In config.js, the anonymousdomain options has to be set.

    If you have installed jitsi-meet from the Debian package, these changes should be made in /etc/jitsi/meet/[your-hostname]-config.js.

    var config = {
    hosts: {
    domain: 'jitsi-meet.example.com',
    anonymousdomain: 'guest.jitsi-meet.example.com',
    ...
    },
    ...
    }

    Jicofo configuration

    When running Jicofo, specify your main domain in an additional configuration property. Jicofo will accept conference allocation requests only from the -authenticated domain. This should go as a new 'authentication' section in /etc/jitsi/jicofo/jicofo.conf:

    jicofo {
    authentication: {
    enabled: true
    type: XMPP
    login-url: jitsi-meet.example.com
    }
    ...

    When using token based authentication, the type must use JWT as the scheme instead:

    jicofo {
    authentication: {
    enabled: true
    type: JWT
    login-url: jitsi-meet.example.com
    }
    ...

    Create users in Prosody (internal auth)

    Finally, run prosodyctl to create a user in Prosody:

    sudo prosodyctl register <username> jitsi-meet.example.com <password>

    and then restart prosody, jicofo and jitsi-videobridge2

    systemctl restart prosody
    systemctl restart jicofo
    systemctl restart jitsi-videobridge2
    note

    Docker users may require an alternate config path. Users of the official jitsi/prosody image should invoke prosodyctl as follows.

    prosodyctl --config /config/prosody.cfg.lua register <username> meet.jitsi <password>

    Full documentation for prosodyctl can be found on the official site.

    Optional: Jigasi configuration

    Enable Authentication

    If you are using Jigasi, set it to authenticate by editing the following lines in /etc/jitsi/jigasi/sip-communicator.properties:

    org.jitsi.jigasi.xmpp.acc.USER_ID=SOME_USER@SOME_DOMAIN
    org.jitsi.jigasi.xmpp.acc.PASS=SOME_PASS
    org.jitsi.jigasi.xmpp.acc.ANONYMOUS_AUTH=false

    Note that the password is the actual plaintext password, not a base64 encoding.

    Debugging

    If you experience problems with a certificate chain, you may need to uncomment the following line, also in sip-communicator.properties:

    net.java.sip.communicator.service.gui.ALWAYS_TRUST_MODE_ENABLED=true
    note

    This should only be used for testing/debugging purposes, or in controlled environments. If you confirm that this is the problem, you should then solve it in another way (e.g. get a signed certificate for Prosody, or add the particular certificate to Jigasi’s trust store).

    - +authenticated domain. This should go as a new 'authentication' section in /etc/jitsi/jicofo/jicofo.conf:

    jicofo {
    authentication: {
    enabled: true
    type: XMPP
    login-url: jitsi-meet.example.com
    }
    ...

    When using token based authentication, the type must use JWT as the scheme instead:

    jicofo {
    authentication: {
    enabled: true
    type: JWT
    login-url: jitsi-meet.example.com
    }
    ...

    Create users in Prosody (internal auth)

    Finally, run prosodyctl to create a user in Prosody:

    sudo prosodyctl register <username> jitsi-meet.example.com <password>

    and then restart prosody, jicofo and jitsi-videobridge2

    systemctl restart prosody
    systemctl restart jicofo
    systemctl restart jitsi-videobridge2
    note

    Docker users may require an alternate config path. Users of the official jitsi/prosody image should invoke prosodyctl as follows.

    prosodyctl --config /config/prosody.cfg.lua register <username> meet.jitsi <password>

    Full documentation for prosodyctl can be found on the official site.

    Optional: Jigasi configuration

    Enable Authentication

    If you are using Jigasi, set it to authenticate by editing the following lines in /etc/jitsi/jigasi/sip-communicator.properties:

    org.jitsi.jigasi.xmpp.acc.USER_ID=SOME_USER@SOME_DOMAIN
    org.jitsi.jigasi.xmpp.acc.PASS=SOME_PASS
    org.jitsi.jigasi.xmpp.acc.ANONYMOUS_AUTH=false

    Note that the password is the actual plaintext password, not a base64 encoding.

    Debugging

    If you experience problems with a certificate chain, you may need to uncomment the following line, also in sip-communicator.properties:

    net.java.sip.communicator.service.gui.ALWAYS_TRUST_MODE_ENABLED=true
    note

    This should only be used for testing/debugging purposes, or in controlled environments. If you confirm that this is the problem, you should then solve it in another way (e.g. get a signed certificate for Prosody, or add the particular certificate to Jigasi’s trust store).

    + \ No newline at end of file diff --git a/docs/devops-guide/speakerstats/index.html b/docs/devops-guide/speakerstats/index.html index 567704a5d..01da30434 100644 --- a/docs/devops-guide/speakerstats/index.html +++ b/docs/devops-guide/speakerstats/index.html @@ -4,7 +4,7 @@ Enabling Speaker Stats | Jitsi Meet - + @@ -13,8 +13,8 @@ virtual host, this is to enable the advertising the speaker stats component, which address needs to be specified in speakerstats_component option.

    We need to also enable the component with the address specified in speakerstats_component. The component needs also to have the option with the muc component address in -muc_component option.

    VirtualHost "jitsi.example.com"
    speakerstats_component = "speakerstats.jitsi.example.com"
    modules_enabled = {
    "speakerstats";
    }

    Component "speakerstats.jitsi.example.com" "speakerstats_component"
    muc_component = "conference.jitsi.example.com"

    Component "conference.jitsi.example.com" "muc"
    - +muc_component option.

    VirtualHost "jitsi.example.com"
    speakerstats_component = "speakerstats.jitsi.example.com"
    modules_enabled = {
    "speakerstats";
    }

    Component "speakerstats.jitsi.example.com" "speakerstats_component"
    muc_component = "conference.jitsi.example.com"

    Component "conference.jitsi.example.com" "muc"
    + \ No newline at end of file diff --git a/docs/devops-guide/turn/index.html b/docs/devops-guide/turn/index.html index 45d3c3443..5534999fc 100644 --- a/docs/devops-guide/turn/index.html +++ b/docs/devops-guide/turn/index.html @@ -4,7 +4,7 @@ Setting up TURN | Jitsi Meet - + @@ -16,8 +16,8 @@ For this you will need a second DNS record for your turn domain pointing to the same machine (as a reference below we will use turn-jitsi-meet.example.com).

    • You need to enable the multiplexing based on that new DNS record. You need to create a file in /etc/nginx/modules or /etc/nginx/modules-available. If you are placing the file in /etc/nginx/modules-available you need to add a symlink in /etc/nginx/modules-enabled. The file content should be:
    stream {
    map $ssl_preread_server_name $name {
    jitsi-meet.example.com web_backend;
    turn-jitsi-meet.example.com turn_backend;
    }

    upstream web_backend {
    server 127.0.0.1:4444;
    }

    upstream turn_backend {
    server __your_public_ip__:5349;
    }

    server {
    listen 443;
    listen [::]:443;

    # since 1.11.5
    ssl_preread on;

    proxy_pass $name;

    # Increase buffer to serve video
    proxy_buffer_size 10m;
    }
    }

    Make sure you edit the file and replace jitsi-meet.example.com with your domain of deployment, turn-jitsi-meet.example.com with the DNS name you will use for the TURN server and __your_public_ip__ with your public IP of the deployment. If you have more virtual hosts make sure you add them here and do the port change for them (the next step).

    • Then go to /etc/nginx/site-available/your-conf and change your virtual host to listen on port 4444 instead of 443.
    server {
    listen 4444 ssl;
    listen [::]:4444 ssl;
    server_name jitsi-meet.example.com;
    • Next you need to make sure Prosody is advertising the correct DNS name and port for the TURN server. You should edit the line using port 5349 in the file /etc/prosody/conf.d/jitsi-meet.example.com.cfg.lua and make it look like (change port and address):
    { type = "turns", host = "turn-jitsi-meet.example.com", port = "443", transport = "tcp" }
    • Now you need to make sure the TURN server (coturn) uses trusted certificates. -Here is how to request those from Let's Encrypt (make sure you set correct values for the domain and email):
    systemctl stop nginx
    DOMAIN="turn-jitsi-meet.example.com"
    apt install socat
    /opt/acmesh/.acme.sh/acme.sh -f --issue -d ${DOMAIN} --standalone --server letsencrypt
    /opt/acmesh/.acme.sh/acme.sh -f --install-cert \
    -d ${DOMAIN} \
    --key-file /etc/jitsi/meet/${DOMAIN}.key \
    --fullchain-file /etc/jitsi/meet/${DOMAIN}.crt \
    --reloadcmd "/usr/share/jitsi-meet/scripts/coturn-le-update.sh ${DOMAIN}"
    systemctl start nginx
    • After restarting prosody (systemctl restart prosody) you are good to go!
    - +Here is how to request those from Let's Encrypt (make sure you set correct values for the domain and email):
    systemctl stop nginx
    DOMAIN="turn-jitsi-meet.example.com"
    apt install socat
    /opt/acmesh/.acme.sh/acme.sh -f --issue -d ${DOMAIN} --standalone --server letsencrypt
    /opt/acmesh/.acme.sh/acme.sh -f --install-cert \
    -d ${DOMAIN} \
    --key-file /etc/jitsi/meet/${DOMAIN}.key \
    --fullchain-file /etc/jitsi/meet/${DOMAIN}.crt \
    --reloadcmd "/usr/share/jitsi-meet/scripts/coturn-le-update.sh ${DOMAIN}"
    systemctl start nginx
    • After restarting prosody (systemctl restart prosody) you are good to go!
    + \ No newline at end of file diff --git a/docs/devops-guide/videosipgw/index.html b/docs/devops-guide/videosipgw/index.html index 3a0b51d10..0896bd02d 100644 --- a/docs/devops-guide/videosipgw/index.html +++ b/docs/devops-guide/videosipgw/index.html @@ -4,15 +4,15 @@ Configuring a video SIP gateway | Jitsi Meet - +

    Configuring a video SIP gateway

    This document describes how you can configure jitsi-meet to use sipgw jibri and enable rooms in 'Add people dialog' You will need a working deployment of jibri configured to use a regular sip video device, for more info check out the jibri documentation.

    This feature is available for non-guests of the system, so this relies on setting in config.js enableUserRolesBasedOnToken: true and providing a jwt token when accessing the conference.

    • Jicofo configuration: -edit /etc/jitsi/jicofo/sip-communicator.properties (or similar), set the appropriate MUC to look for the Jibri Controllers. This should be the same MUC as is referenced in jibri's config.json file. Restart Jicofo after setting this property.
      org.jitsi.jicofo.jibri.SIP_BREWERY=TheSipBrewery@conference.yourdomain.com
    • Jitsi Meet configuration:
    • config.js: add
      enableUserRolesBasedOnToken: true,
    peopleSearchQueryTypes: ['conferenceRooms'],
    peopleSearchUrl: 'https://api.yourdomain.com/testpath/searchpeople',

    The combination of the above settings and providing a jwt token will enable a button under invite option which will show the dialog 'Add people'.

    People search service

    When searching in the dialog, a request for results is made to the peopleSearchUrl service.

    The request is in the following format:

    https://api.yourdomain.com/testpath/searchpeople?query=testroomname&queryTypes=[%22conferenceRooms%22]&jwt=somejwt

    The parameters are:

    • query - The text entered by the user.
    • queryTypes - What type of results we want people, rooms, conferenceRooms. This is the value from config.js peopleSearchQueryTypes
    • jwt - The token used by the user to access the conference.

    The response of the service is a json in the following format:

    [
    {
    "id": "address@sip.domain.com",
    "name": "Some room name",
    "type": "videosipgw"
    },
    {
    "id": "address2@sip.domain.com",
    "name": "Some room name2",
    "type": "videosipgw"
    }
    ]

    Type should be videosipgw, name is the name shown to the user and id is the sip address to be called by the sipgw jibri.

    - +edit /etc/jitsi/jicofo/sip-communicator.properties (or similar), set the appropriate MUC to look for the Jibri Controllers. This should be the same MUC as is referenced in jibri's config.json file. Restart Jicofo after setting this property.
      org.jitsi.jicofo.jibri.SIP_BREWERY=TheSipBrewery@conference.yourdomain.com
    • Jitsi Meet configuration:
    • config.js: add
      enableUserRolesBasedOnToken: true,
    peopleSearchQueryTypes: ['conferenceRooms'],
    peopleSearchUrl: 'https://api.yourdomain.com/testpath/searchpeople',

    The combination of the above settings and providing a jwt token will enable a button under invite option which will show the dialog 'Add people'.

    People search service

    When searching in the dialog, a request for results is made to the peopleSearchUrl service.

    The request is in the following format:

    https://api.yourdomain.com/testpath/searchpeople?query=testroomname&queryTypes=[%22conferenceRooms%22]&jwt=somejwt

    The parameters are:

    • query - The text entered by the user.
    • queryTypes - What type of results we want people, rooms, conferenceRooms. This is the value from config.js peopleSearchQueryTypes
    • jwt - The token used by the user to access the conference.

    The response of the service is a json in the following format:

    [
    {
    "id": "address@sip.domain.com",
    "name": "Some room name",
    "type": "videosipgw"
    },
    {
    "id": "address2@sip.domain.com",
    "name": "Some room name2",
    "type": "videosipgw"
    }
    ]

    Type should be videosipgw, name is the name shown to the user and id is the sip address to be called by the sipgw jibri.

    + \ No newline at end of file diff --git a/docs/faq/index.html b/docs/faq/index.html index 6e637be81..82d5b14e8 100644 --- a/docs/faq/index.html +++ b/docs/faq/index.html @@ -4,14 +4,14 @@ FAQ | Jitsi Meet - +

    FAQ

    How to tell if my server instance is behind NAT?

    In general, if the tool ifconfig (or ipconfig) shows the assigned IPv4 address to be a private / local address (10.x.x.x, or 172.16.x.x - 172.31.x.x, or 192.168.x.x) but you know that its public IPv4 address is different from that, the server is most probably behind NAT.

    If you are hosting your server on a VPS, and you are not sure, ask your VPS provider's support team.

    Clients could communicate well in room created at meet.jit.si. The same clients still could connect to my self-hosted instance but can neither hear nor see one another. What's wrong?

    Most probably, the server is behind NAT, but you haven't added the NAT-specific configuration. See this resolved question. You need to follow the steps detailed here.

    It works with two participants, but crashes or does not work properly when a third joins

    P2P mode is working, but it fails when you are trying to pass traffic via jitsi-videobridge2.

    Check you've got your firewall / NAT set up correctly — especially UDP 10000. For more information, see here.

    Can I mute and unmute other participants?

    If you are the moderator of a conference, you can mute everyone's microphone. You cannot unmute other people's microphones, and they can unmute their microphone at any time.

    You may want to set some "ground rules" for who can talk and when, just as with any physical meeting or classroom.

    If you would like to limit who can become a moderator, you need to set up your own instance of Jitsi and enable "secure domain". Please see here for more information.

    How can I protect my meetings with Jitsi?

    1. Create a "strong" room name.

    Use a strong room name, which no-one else is likely to be using. Use the name generator on the welcome page, or else generate your own "strong" name.

    For example, on macOS, in terminal, you can use uuidgen to generate a string of letters of numbers (e.g. B741B63E-C5E6-4D82-BAC4-048BE25D8CC7).

    Your room name would be meet.jit.si/B741B63E-C5E6-4D82-BAC4-048BE25D8CC7 on the hosted meet.jit.si platform.

    If you use "test" or "LucysMeeting" or "pilates" or similar, then it's highly likely that other users will have had the same idea.

    2. Use a different room name for each meeting / conference you have.

    If you are going to have multiple meetings, ideally use a different room name for each one.

    If that is not practical, at least use a different room name for each group of people.

    3. Add a password to the room.

    Once you have started your room, set a password for it. Only people who have the password can join from that point on, but it does not affect people who have already joined.

    You will need to tell everyone the password.

    If they give the password to others, those other people can also join.

    4. Enable "secure domain" if you are using your own instance of Jitsi.

    In addition to the tips above, consider enabling the "secure domain" configuration. This requires you (or someone else) to enter a username and password to open a room. It also allows you to become a moderator.

    It's working when I connect from a browser, but not from the iOS or Android apps

    This probably means that you are not serving the fullchain for your TLS certificate. You can check if your cert chain -is properly configured here.

    In nginx, if you are using Let's Encrypt, you should have a line like this:

    ssl_certificate /etc/letsencrypt/live/jitsi.example.com/fullchain.pem;

    Can I record and save the video?

    Yes. There are multiple methods (using external software or services):

    Note: If you want to use a privacy-friendly method, use method 1 or 2.

    1. OBS: Use OBS to record your Session (e.g. your browser window).

    2. RTMP-Server: For this you have to setup your own RTMP-Server and then use your RTMP URL + Stream key instead of the Youtube Stream key as described here. Self-installed Jitsi Meet deployments will need to setup Jibri to do this.

    3. Dropbox: Connect to Dropbox with Jitsi Meet and save the video in the Dropbox.

    4. Video Services/Websites: Stream your conference to YouTube or other sites (e.g. Twitch) and access the recording there (see howto). Self-installed Jitsi Meet deployments will need to setup Jibri to do this.

    More methods might be implemented in the future, but are not ready yet (e.g. Local Recording.

    I set the password in meeting but it is not working the next time

    Once the meeting ends it's password also gets removed, so you need to set the password again for next meeting.

    How to limit the number of participants?

    1. Use the command prosodyctl about to view the version of prosody and plug directory, similar to the output below.
    Prosody 0.11.6

    # Prosody directories

    Data directory: /var/lib/prosody

    Config directory: /etc/prosody

    Source directory: /usr/lib/prosody

    Plugin directories:

    /usr/share/jitsi-meet/prosody-plugins/

    /usr/lib/prosody/modules/
    1. Check if there is a mod_muc_max_occupants.lua file in your plugin directory.

    If not, please create a new file mod_muc_max_occupants.lua in the plugin directory And copy everything from here to paste.

    If it exists, please ignore this step.

    3.Edit your /etc/prosody/conf.avail/meet.example.com.cfg.lua file and add muc_max_occupants as a module_enabled in the conference.meet.example.com "muc" section.

    Then, add the options below that. You need both muc_max_occupants and muc_access_whitelist defined.

    Example:

    Component "conference.meet.example.com" "muc"
    storage = "memory"
    modules_enabled = {
    "muc_meeting_id";
    "muc_domain_mapper";
    "muc_max_occupants";
    }
    muc_max_occupants = "5"
    muc_access_whitelist = { "focus@auth.meet.example.com" }
    admins = { "focus@auth.meet.example.com" }
    muc_room_locking = false
    muc_room_default_public_jids = true

    Note: the relationship between storage = "" and your prosody version, and you need to modify all storage="" .

    • Prosody nightly747 storage = "null"
    • Prosody 0.10 storage = "none"
    • Prosody 0.11 storage = "memory"
    1. You need to use the command prosodyctl restart to see the effect.

    2. If you want to update to use prosody, you can check here.

    - +is properly configured here.

    In nginx, if you are using Let's Encrypt, you should have a line like this:

    ssl_certificate /etc/letsencrypt/live/jitsi.example.com/fullchain.pem;

    Can I record and save the video?

    Yes. There are multiple methods (using external software or services):

    Note: If you want to use a privacy-friendly method, use method 1 or 2.

    1. OBS: Use OBS to record your Session (e.g. your browser window).

    2. RTMP-Server: For this you have to setup your own RTMP-Server and then use your RTMP URL + Stream key instead of the Youtube Stream key as described here. Self-installed Jitsi Meet deployments will need to setup Jibri to do this.

    3. Dropbox: Connect to Dropbox with Jitsi Meet and save the video in the Dropbox.

    4. Video Services/Websites: Stream your conference to YouTube or other sites (e.g. Twitch) and access the recording there (see howto). Self-installed Jitsi Meet deployments will need to setup Jibri to do this.

    More methods might be implemented in the future, but are not ready yet (e.g. Local Recording.

    I set the password in meeting but it is not working the next time

    Once the meeting ends it's password also gets removed, so you need to set the password again for next meeting.

    How to limit the number of participants?

    1. Use the command prosodyctl about to view the version of prosody and plug directory, similar to the output below.
    Prosody 0.11.6

    # Prosody directories

    Data directory: /var/lib/prosody

    Config directory: /etc/prosody

    Source directory: /usr/lib/prosody

    Plugin directories:

    /usr/share/jitsi-meet/prosody-plugins/

    /usr/lib/prosody/modules/
    1. Check if there is a mod_muc_max_occupants.lua file in your plugin directory.

    If not, please create a new file mod_muc_max_occupants.lua in the plugin directory And copy everything from here to paste.

    If it exists, please ignore this step.

    3.Edit your /etc/prosody/conf.avail/meet.example.com.cfg.lua file and add muc_max_occupants as a module_enabled in the conference.meet.example.com "muc" section.

    Then, add the options below that. You need both muc_max_occupants and muc_access_whitelist defined.

    Example:

    Component "conference.meet.example.com" "muc"
    storage = "memory"
    modules_enabled = {
    "muc_meeting_id";
    "muc_domain_mapper";
    "muc_max_occupants";
    }
    muc_max_occupants = "5"
    muc_access_whitelist = { "focus@auth.meet.example.com" }
    admins = { "focus@auth.meet.example.com" }
    muc_room_locking = false
    muc_room_default_public_jids = true

    Note: the relationship between storage = "" and your prosody version, and you need to modify all storage="" .

    • Prosody nightly747 storage = "null"
    • Prosody 0.10 storage = "none"
    • Prosody 0.11 storage = "memory"
    1. You need to use the command prosodyctl restart to see the effect.

    2. If you want to update to use prosody, you can check here.

    + \ No newline at end of file diff --git a/docs/intro/index.html b/docs/intro/index.html index c9778b8c0..a45fa66fd 100644 --- a/docs/intro/index.html +++ b/docs/intro/index.html @@ -4,7 +4,7 @@ Introduction | Jitsi Meet - + @@ -14,8 +14,8 @@ understand all the available features and how to use them.

  • Developer guide: Designed to help developers who want to either integrate the Jitsi Meet API / SDK in their products or want to improve Jitsi Meet itself by developing new features or fixing bugs.

  • Self-Hosting guide: Designed for folks wanting to self-host, system administrators -or anyone who wishes to deploy and operate their own Jitsi Meet instance.

  • JaaS customers

    Are you a JaaS customer? If so, please start here.

    - +or anyone who wishes to deploy and operate their own Jitsi Meet instance.

    JaaS customers

    Are you a JaaS customer? If so, please start here.

    + \ No newline at end of file diff --git a/docs/releases/index.html b/docs/releases/index.html index b20dd9a79..a659220e4 100644 --- a/docs/releases/index.html +++ b/docs/releases/index.html @@ -4,14 +4,14 @@ Releases | Jitsi Meet - +

    Releases

    tip

    Release notes for Jitsi Meet are kept here.

    Mobile apps

    AndroidAndroid (F-Droid)iOS

    Beta versions

    If you are feeling adventurous and want to get an early scoop of the features as they are being -developed you can also sign up for our open beta testing here:

    AndroidiOS
    Play Store BetaTestFlight

    Desktop apps

    WindowsmacOSGNU/Linux (AppImage)GNU/Linux (Deb)
    DownloadDownloadDownloadDownload

    The desktop applications are based on Electron. For macOS it is also available as a brew cask which can be installed using brew install jitsi-meet.

    Mobile SDKs

    AndroidiOS
    Maven repositoryCocoaPods

    Docker images

    See the Docker image releases here.

    Debian/Ubuntu packages

    Web frontend

    Latest stable releaserelease

    Prebuilt source builds are also available.

    note

    Generally, you won't need to download this, as it's part of the Debian packages and Docker images already.

    - +developed you can also sign up for our open beta testing here:

    AndroidiOS
    Play Store BetaTestFlight

    Desktop apps

    WindowsmacOSGNU/Linux (AppImage)GNU/Linux (Deb)
    DownloadDownloadDownloadDownload

    The desktop applications are based on Electron. For macOS it is also available as a brew cask which can be installed using brew install jitsi-meet.

    Mobile SDKs

    AndroidiOS
    Maven repositoryCocoaPods

    Docker images

    See the Docker image releases here.

    Debian/Ubuntu packages

    Web frontend

    Latest stable releaserelease

    Prebuilt source builds are also available.

    note

    Generally, you won't need to download this, as it's part of the Debian packages and Docker images already.

    + \ No newline at end of file diff --git a/docs/security/index.html b/docs/security/index.html index ebd7ab259..d7214f15b 100644 --- a/docs/security/index.html +++ b/docs/security/index.html @@ -4,14 +4,14 @@ Security | Jitsi Meet - + - +more broadly here: https://jitsi.org/security

    + \ No newline at end of file diff --git a/docs/user-guide/client connection status indicators/index.html b/docs/user-guide/client connection status indicators/index.html index b3f0c699d..7178ef671 100644 --- a/docs/user-guide/client connection status indicators/index.html +++ b/docs/user-guide/client connection status indicators/index.html @@ -4,13 +4,13 @@ Client Connection Status Indicators | Jitsi Meet - +
    -

    Client Connection Status Indicators

    This document explains what the different connection quality indicators on the video thumbnails actually mean.

    GOOD

    • With video enabled, when the send bitrate for the video stream is at least 50% of the target bitrate expected for the stream. Please refer to the target bitrates table below.
    • With video disabled or screen sharing is in progress, when the downstream packet loss is less than 6%.

    NON-OPTIMAL

    • With video enabled, when the send bitrate for the video stream is at least 30% of the target bitrate expected for the stream. Please refer to the target bitrates table below.
    • With video disabled or screen sharing is in progress, when the downstream packet loss is between 6% and 8%.

    POOR

    • With video enabled, when the send bitrate for the video stream is at least 10% of the target bitrate expected for the stream. Please refer to the target bitrates table below.
    • With video disabled or screen sharing is in progress, when the downstream packet loss is between 8% and 12%.

    LOST

    • When the user stops receiving video for the remote endpoint even when the endpoint is not video muted and it is in LastN as indicated by the bridge’s LastNEndpointChangeEvent.
    • When the bridge sends an EndpointConnectivityStatusChangeEvent indicating that the remote endpoint is no longer active, i.e., when the bridge has not received media from the remote endpoint for more than 3 secs.

    GHOST/NINJA

    • When the user stops receiving video for the remote endpoint even when the endpoint is not video muted and it is not in LastN as indicated by the bridge’s LastNEndpointChangeEvent. This means that the bridge decided to suspend the video for this user. Bridge takes into consideration the available downlink bandwidth for the receiving endpoint and the number of video streams requested using the channelLast setting.

    Target bitrates expected for the video streams

    CodecType180p (in Kbps)360p (in Kbps)720p (in Kbps)
    VP82005001500
    VP91003001200
    - +

    Client Connection Status Indicators

    This document explains what the different connection quality indicators on the video thumbnails actually mean.

    GOOD

    • With video enabled, when the send bitrate for the video stream is at least 50% of the target bitrate expected for the stream. Please refer to the target bitrates table below.
    • With video disabled or screen sharing is in progress, when the downstream packet loss is less than 6%.

    NON-OPTIMAL

    • With video enabled, when the send bitrate for the video stream is at least 30% of the target bitrate expected for the stream. Please refer to the target bitrates table below.
    • With video disabled or screen sharing is in progress, when the downstream packet loss is between 6% and 8%.

    POOR

    • With video enabled, when the send bitrate for the video stream is at least 10% of the target bitrate expected for the stream. Please refer to the target bitrates table below.
    • With video disabled or screen sharing is in progress, when the downstream packet loss is between 8% and 12%.

    LOST

    • When the user stops receiving video for the remote endpoint even when the endpoint is not video muted and it is in LastN as indicated by the bridge’s LastNEndpointChangeEvent.
    • When the bridge sends an EndpointConnectivityStatusChangeEvent indicating that the remote endpoint is no longer active, i.e., when the bridge has not received media from the remote endpoint for more than 3 secs.

    GHOST/NINJA

    • When the user stops receiving video for the remote endpoint even when the endpoint is not video muted and it is not in LastN as indicated by the bridge’s LastNEndpointChangeEvent. This means that the bridge decided to suspend the video for this user. Bridge takes into consideration the available downlink bandwidth for the receiving endpoint and the number of video streams requested using the channelLast setting.

    Target bitrates expected for the video streams

    CodecType180p (in Kbps)360p (in Kbps)720p (in Kbps)
    VP82005001500
    VP91003001200
    + \ No newline at end of file diff --git a/docs/user-guide/keyboard-shortcuts/index.html b/docs/user-guide/keyboard-shortcuts/index.html index d91c85f92..865bb0ad6 100644 --- a/docs/user-guide/keyboard-shortcuts/index.html +++ b/docs/user-guide/keyboard-shortcuts/index.html @@ -4,13 +4,13 @@ Keyboard shortcuts | Jitsi Meet - +
    -

    Keyboard shortcuts

    • F - Show or hide video thumbnails
    • M - Mute or unmute your microphone
    • V - Start or stop your camera
    • A - Manage video quality
    • C - Open or close the chat
    • D - Switch between camera and screen sharing
    • P - Show or hide the participants pane
    • R - Raise or lower your hand
    • S - View or exit full screen
    • W - Toggle tile view
    • ? - Show or hide keyboard shortcuts
    • SPACE - Push to talk
    • T - Show speaker stats
    • 0 - Focus on your video
    • 1-9 - Focus on another person's video
    - +

    Keyboard shortcuts

    • F - Show or hide video thumbnails
    • M - Mute or unmute your microphone
    • V - Start or stop your camera
    • A - Manage video quality
    • C - Open or close the chat
    • D - Switch between camera and screen sharing
    • P - Show or hide the participants pane
    • R - Raise or lower your hand
    • S - View or exit full screen
    • W - Toggle tile view
    • ? - Show or hide keyboard shortcuts
    • SPACE - Push to talk
    • T - Show speaker stats
    • 0 - Focus on your video
    • 1-9 - Focus on another person's video
    + \ No newline at end of file diff --git a/docs/user-guide/supported-browsers/index.html b/docs/user-guide/supported-browsers/index.html index e3fa9b177..5fd823c59 100644 --- a/docs/user-guide/supported-browsers/index.html +++ b/docs/user-guide/supported-browsers/index.html @@ -4,7 +4,7 @@ Supported Browsers | Jitsi Meet - + @@ -12,8 +12,8 @@

    Supported Browsers

    Desktop browsers

    BrowserSupportVersionsNotes
    Chrome 1>= 72Best results with >= 96
    Firefox>= 68Best results with >= 101
    Safari>= 14Best results with >= 15, output device selection unsupported
    Edge>= 79Edge Legacy is unsupported
    Internet Explorer

    Mobile browsers

    Android

    BrowserSupportVersionsNotes
    Chrome 1Same support as the desktop version
    FirefoxSame support as the desktop version
    note

    For a better mobile experience (background support, Bluetooth support, etc.) we recommend using a native app instead. We provide a native Android SDK.

    iOS

    BrowserSupportVersionsNotes
    ChromeSame support as Safari as they share the engine
    FirefoxSame support as Safari as they share the engine
    Safari>= 14.3Best results with 15.4
    EdgeSame support as Safari as they share the engine
    note

    On iOS all browsers share the same engine, Safari. As such all features and limitations on all iOS browsers are those of Safari.

    For a better mobile experience (background support, CallKit integration, etc.) we recommend using a -native app instead. We provide a native iOS SDK.


    1. This also applies to all Chromium based browsers such as Brave, (current) Edge, Opera, Vivaldi and others.
    - +native app instead. We provide a native iOS SDK.


    1. This also applies to all Chromium based browsers such as Brave, (current) Edge, Opera, Vivaldi and others.
    + \ No newline at end of file diff --git a/docs/user-guide/user-guide-advanced/index.html b/docs/user-guide/user-guide-advanced/index.html index dc98845c9..845301c49 100644 --- a/docs/user-guide/user-guide-advanced/index.html +++ b/docs/user-guide/user-guide-advanced/index.html @@ -4,7 +4,7 @@ User Guide (advanced) | Jitsi Meet - + @@ -12,8 +12,8 @@

    User Guide (advanced)

    There are some options to tweak the invitation link to unlock more features in Jitsi. The following parameters apply to the web, iframe and mobile version.

    All keys listed here are prefixed with config.. You pick a key, combine it with its value using = and link parameters -with &, e.g. #config.defaultLanguage=en&config.minParticipants=3.

    Invitations

    These parameters affect how you can invite people either before or within a session.

    KeyValueEffect
    disableInviteFunctionstruedisable invite function of the app
    minParticipants2override the minimum number of participants before starting a call
    prejoinConfig.enabledtrueshow an intermediate page before joining to allow for adjustment of devices

    UI

    These parameters have an effect on the user interface.

    KeyValueEffect
    defaultLanguageenchange the UI default language
    disableThirdPartyRequeststruegenerate avatars locally and disable callstats integration
    enableDisplayNameInStatstruesend display names of participants to callstats
    enableEmailInStatstruesend email (if available) to callstats and other analytics
    enableInsecureRoomNameWarningtrueshow a warning label if the room name is deemed insecure

    Video

    Use these parameters to influence the video of a session.

    KeyValueEffect
    desktopSharingFrameRate.min5override the minimum framerate for desktop sharing
    desktopSharingFrameRate.max5override the maximum framerate for desktop sharing
    startWithVideoMutedtruedisable video when joining

    Audio

    Use these parameters to influence the audio of a session.

    KeyValueEffect
    disableAudioLevelstruedisable audio statistics polling (thereby perhaps improving performance)
    disableRemoteMutetruedisable all muting operations of remote participants
    startWithAudioMutedtrueturn off audio input when joining
    startSilenttruemute audio input and output
    - +with &, e.g. #config.defaultLanguage=en&config.minParticipants=3.

    Invitations

    These parameters affect how you can invite people either before or within a session.

    KeyValueEffect
    disableInviteFunctionstruedisable invite function of the app
    minParticipants2override the minimum number of participants before starting a call
    prejoinConfig.enabledtrueshow an intermediate page before joining to allow for adjustment of devices

    UI

    These parameters have an effect on the user interface.

    KeyValueEffect
    defaultLanguageenchange the UI default language
    disableThirdPartyRequeststruegenerate avatars locally and disable callstats integration
    enableDisplayNameInStatstruesend display names of participants to callstats
    enableEmailInStatstruesend email (if available) to callstats and other analytics
    enableInsecureRoomNameWarningtrueshow a warning label if the room name is deemed insecure

    Video

    Use these parameters to influence the video of a session.

    KeyValueEffect
    desktopSharingFrameRate.min5override the minimum framerate for desktop sharing
    desktopSharingFrameRate.max5override the maximum framerate for desktop sharing
    startWithVideoMutedtruedisable video when joining

    Audio

    Use these parameters to influence the audio of a session.

    KeyValueEffect
    disableAudioLevelstruedisable audio statistics polling (thereby perhaps improving performance)
    disableRemoteMutetruedisable all muting operations of remote participants
    startWithAudioMutedtrueturn off audio input when joining
    startSilenttruemute audio input and output
    + \ No newline at end of file diff --git a/docs/user-guide/user-guide-basic/index.html b/docs/user-guide/user-guide-basic/index.html index 25edc7f71..aed3e5cdd 100644 --- a/docs/user-guide/user-guide-basic/index.html +++ b/docs/user-guide/user-guide-basic/index.html @@ -4,13 +4,13 @@ User Guide (basic) | Jitsi Meet - + - + + \ No newline at end of file diff --git a/docs/user-guide/user-guide-jitsi-meet-for-google-calendar/index.html b/docs/user-guide/user-guide-jitsi-meet-for-google-calendar/index.html index 5ab8a1540..94366c67f 100644 --- a/docs/user-guide/user-guide-jitsi-meet-for-google-calendar/index.html +++ b/docs/user-guide/user-guide-jitsi-meet-for-google-calendar/index.html @@ -4,13 +4,13 @@ Jitsi Meet for Google Calendar | Jitsi Meet - + - + + \ No newline at end of file diff --git a/docs/user-guide/user-guide-jitsi-meet-on-mobile/index.html b/docs/user-guide/user-guide-jitsi-meet-on-mobile/index.html index 862c8f561..398fb747b 100644 --- a/docs/user-guide/user-guide-jitsi-meet-on-mobile/index.html +++ b/docs/user-guide/user-guide-jitsi-meet-on-mobile/index.html @@ -4,13 +4,13 @@ Use Jitsi Meet on Mobile | Jitsi Meet - +
    -

    Use Jitsi Meet on Mobile

    When you Join a Jitsi Meeting using a Mobile Phone, there are always options to join the meeting using the Jitsi Meet App or Continue on the web. The most convenient option is to Join using the Jitsi Meet App.

    You can download and Join / Create Meetings using the App as follows:

    • Download the Jitsi Meet App for Android / iOS / F-Droid
    • Open the app.
    • Join / Create a Meeting.
    - +

    Use Jitsi Meet on Mobile

    When you Join a Jitsi Meeting using a Mobile Phone, there are always options to join the meeting using the Jitsi Meet App or Continue on the web. The most convenient option is to Join using the Jitsi Meet App.

    You can download and Join / Create Meetings using the App as follows:

    • Download the Jitsi Meet App for Android / iOS / F-Droid
    • Open the app.
    • Join / Create a Meeting.
    + \ No newline at end of file diff --git a/docs/user-guide/user-guide-join-jitsi-meeting/index.html b/docs/user-guide/user-guide-join-jitsi-meeting/index.html index ce98bfe96..7664c0d52 100644 --- a/docs/user-guide/user-guide-join-jitsi-meeting/index.html +++ b/docs/user-guide/user-guide-join-jitsi-meeting/index.html @@ -4,7 +4,7 @@ Join a Jitsi Meeting | Jitsi Meet - + @@ -14,8 +14,8 @@ If you trust the person who invited you, confirm this access request. Please refer to the browser's documentation for details (e.g. Firefox, -Chrome).
  • If prompted, enter a name, which will be visible to other participants in the Jitsi Meeting room.
  • (Optional) Adjust the camera and/or microphone settings via the v dropdown menu items.
  • Click on Join meeting.
  • - +Chrome).
  • If prompted, enter a name, which will be visible to other participants in the Jitsi Meeting room.
  • (Optional) Adjust the camera and/or microphone settings via the v dropdown menu items.
  • Click on Join meeting.
  • + \ No newline at end of file diff --git a/docs/user-guide/user-guide-share-a-jitsi-meeting/index.html b/docs/user-guide/user-guide-share-a-jitsi-meeting/index.html index 8aab2aebe..76c7ad05f 100644 --- a/docs/user-guide/user-guide-share-a-jitsi-meeting/index.html +++ b/docs/user-guide/user-guide-share-a-jitsi-meeting/index.html @@ -4,13 +4,13 @@ Share a Jitsi Meeting | Jitsi Meet - + - +

    Share a Jitsi Meeting

    First, you need to Join / Start a Jitsi Meeting.

    Next:

    • Click on the More actions "..." Button.
    • Click on Invite people.
    • Select the desired option using which you want to share the meeting.
    + \ No newline at end of file diff --git a/docs/user-guide/user-guide-start-a-jitsi-meeting/index.html b/docs/user-guide/user-guide-start-a-jitsi-meeting/index.html index d7a912564..40ea822f1 100644 --- a/docs/user-guide/user-guide-start-a-jitsi-meeting/index.html +++ b/docs/user-guide/user-guide-start-a-jitsi-meeting/index.html @@ -4,7 +4,7 @@ Start a Jitsi Meeting | Jitsi Meet - + @@ -13,8 +13,8 @@ Note: Please do not use any special characters, spaces or umlauts, as this can lead to problems. Note: Jitsi offers a functionality that automatically suggests names for the conferences. These can be overwritten.
  • Click the blue Go button.
  • The following window opens:
  • screenshot 2

    1. It is possible that no picture of you will appear at first. To do this, the browser will ask you whether you want to allow camera access. Please confirm this by clicking on allow or permit. Sometimes you also have to click the camera button at the bottom of the screen first to activate the dialog for allowing camera access. Do the same with the microphone the first time you use Jitsi.
    2. Now enter your display name in the "enter your name" field.
    3. Click the blue Join meeting button.
    4. Have fun in your first conference.
    note

    If you do not see a video image of yourself, check the following points: The camera on your device is:

    • present (small lens at the top of the screen / an external webcam on the monitor),
    • activated (on some laptops you can actively switch the webcam on/off),
    • plugged in (only necessary for external webcams),
    • installed (some devices require the camera to be installed first).
    note

    If you cannot transmit sound, check the following points: -The microphone on your device is:

    • available (especially with desktop devices, a microphone is never actually integrated. Here you need an external microphone or headset, which you connect to the appropriate ports on your PC),
    • activated (on some laptops with an integrated microphone or headsets there is a switch to activate / deactivate the microphone),
    • plugged in (only necessary for external microphones),
    • installed (on some old computers the microphone must be installed).
    - +The microphone on your device is:

    • available (especially with desktop devices, a microphone is never actually integrated. Here you need an external microphone or headset, which you connect to the appropriate ports on your PC),
    • activated (on some laptops with an integrated microphone or headsets there is a switch to activate / deactivate the microphone),
    • plugged in (only necessary for external microphones),
    • installed (on some old computers the microphone must be installed).
    + \ No newline at end of file diff --git a/help/index.html b/help/index.html index 1534e47dc..df3609252 100644 --- a/help/index.html +++ b/help/index.html @@ -4,13 +4,13 @@ Getting help | Jitsi Meet - +

    Getting help

    Jitsi is maintained by a dedicated group of enthusiasts.

    If you need help with Jitsi our community is the best place to start.

    You can learn more about using Jitsi or developing applications with it by browsing our docs.

    - + \ No newline at end of file diff --git a/index.html b/index.html index 0f45fdd87..ae2925d9d 100644 --- a/index.html +++ b/index.html @@ -4,13 +4,13 @@ Jitsi Meet | Jitsi Meet - +

    Jitsi Meet

    State-of-the-art video conferencing you can self-host.

    What is Jitsi?

    [object Object]

    Hey there Fellow Jitster!

    Jitsi Meet is a set of Open Source projects which empower users to deploy secure, scalable and easy to use video conferencing platforms with state-of-the-art video quality and features.

    On This site you'll find documentation for all your Jitsi needs.

    Get started here.

    [object Object]

    Batteries included

    Jitsi Meet supports all common browsers and also mobile devices.

    Our APIs allow developers to easily integrate Jitsi Meet into existing applications, whether those are web based or native mobile apps.

    You can use our freely available instance at meet.jit.si or self-host it yourself using our readily available Debian packages or comprehensive Docker setup.

    [object Object]

    JaaS: Jitsi as a Service

    Looking for configuration flexibility without the complexity of self-hosting and scalability management?

    Look no further than JaaS. 8x8 Jitsi as a Service (JaaS) is an enterprise-ready video meeting platform that allows developers, organizations and businesses to easily build and deploy video solutions. With Jitsi as a Service we now give you all the power of Jitsi running on our global platform so you can focus on building secure and branded video experiences.

    Check JaaS out here.

    - + \ No newline at end of file diff --git a/search/index.html b/search/index.html index 6ac241b69..c4f2896ff 100644 --- a/search/index.html +++ b/search/index.html @@ -4,13 +4,13 @@ Search the documentation | Jitsi Meet - + - + \ No newline at end of file