{"_id":"@airtame/webrtc","_rev":"2-ffadd1f1c3c33a3333e14471c0dfe695","name":"@airtame/webrtc","dist-tags":{"latest":"4.0.0"},"versions":{"3.0.0":{"name":"@airtame/webrtc","version":"3.0.0","description":"WebRTC React library.","main":"dist/index.js","scripts":{"start":"webpack-dev-server --config ./webpack.config.js","build":"npm run clean && NODE_ENV=production webpack --config ./webpack.config.js --mode=production && npm run declaration","clean":"rimraf ./dist && rimraf ./public","watch":"npm run start","declaration":"tsc --declaration --declarationDir ./dist && cp -rf ./dist/src/* ./dist && rimraf dist/src && rimraf dist/example"},"dependencies":{"debug":"^4.2.0"},"peerDependencies":{"react":">=16.13.1","react-dom":">=16.13.1"},"devDependencies":{"@airtame/ui":"^6.9.3","@types/debug":"^4.1.5","@types/react":"^16.9.53","@types/react-dom":"^16.9.8","@typescript-eslint/eslint-plugin":"^4.5.0","@typescript-eslint/parser":"^4.5.0","classnames":"^2.3.1","copy-webpack-plugin":"^6.2.1","css-loader":"^5.0.0","eslint":"^7.12.0","eslint-config-prettier":"^6.14.0","eslint-import-resolver-webpack":"^0.13.0","eslint-loader":"^4.0.2","eslint-plugin-import":"^2.22.1","eslint-plugin-jsx-a11y":"^6.3.1","eslint-plugin-prettier":"^3.1.4","eslint-plugin-react":"^7.21.5","fork-ts-checker-webpack-plugin":"^5.2.0","html-webpack-plugin":"^4.5.0","prettier":"^2.1.2","prettier-eslint":"^11.0.0","react":"^16.13.1","react-dom":"^16.13.1","rimraf":"^3.0.2","sass":"^1.27.0","sass-loader":"^10.0.3","style-loader":"^2.0.0","terser-webpack-plugin":"^5.0.1","ts-loader":"^8.0.7","typescript":"^4.0.3","webpack":"^5.90.3","webpack-cli":"^5.1.4","webpack-dev-server":"^5.0.4","write-file-webpack-plugin":"^4.5.1"},"_id":"@airtame/webrtc@3.0.0","gitHead":"d375dd3274524df57ac42761510236d05b6b18a5","types":"./dist/index.d.ts","_nodeVersion":"20.9.0","_npmVersion":"10.1.0","dist":{"integrity":"sha512-3c632GE3lSOWjM/RfOMHnOYlw7xpsNLPsR1iu2WyFwaSaVKfs2IXIyaGRGJcHDGiQM7Zg28Od45XmDMZkW0WBQ==","shasum":"c6b0c25580387bf841e081af7670d112cf7fed1a","tarball":"https://registry.npmjs.org/@airtame/webrtc/-/webrtc-3.0.0.tgz","fileCount":8,"unpackedSize":112548,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDRd4tbd7YvWTZe6j2/zirIPmbziz0MUrRNj30LBNxQbQIgJOOdPYBIzPB7bsn/m6RM6+Q3hoDB+lPP3Ax5jdF37/0="}]},"_npmUser":{"name":"airtame-ops","email":"devops@airtame.com"},"directories":{},"maintainers":[{"name":"rene-airtame","email":"rene.hansen@airtame.com"},{"name":"airtame-ops","email":"devops@airtame.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/webrtc_3.0.0_1711375511433_0.2390885250001309"},"_hasShrinkwrap":false},"3.0.1":{"name":"@airtame/webrtc","version":"3.0.1","description":"WebRTC React library.","main":"dist/index.js","scripts":{"start":"webpack-dev-server --config ./webpack.config.js","build":"npm run clean && NODE_ENV=production webpack --config ./webpack.config.js --mode=production && npm run declaration","clean":"rimraf ./dist && rimraf ./public","watch":"npm run start","declaration":"tsc --declaration --declarationDir ./dist && cp -rf ./dist/src/* ./dist && rimraf dist/src && rimraf dist/example"},"dependencies":{"debug":"^4.2.0"},"peerDependencies":{"react":">=16.13.1","react-dom":">=16.13.1"},"devDependencies":{"@airtame/ui":"^6.9.3","@types/debug":"^4.1.5","@types/react":"^16.9.53","@types/react-dom":"^16.9.8","@typescript-eslint/eslint-plugin":"^4.5.0","@typescript-eslint/parser":"^4.5.0","classnames":"^2.3.1","copy-webpack-plugin":"^6.2.1","css-loader":"^5.0.0","eslint":"^7.12.0","eslint-config-prettier":"^6.14.0","eslint-import-resolver-webpack":"^0.13.0","eslint-loader":"^4.0.2","eslint-plugin-import":"^2.22.1","eslint-plugin-jsx-a11y":"^6.3.1","eslint-plugin-prettier":"^3.1.4","eslint-plugin-react":"^7.21.5","fork-ts-checker-webpack-plugin":"^5.2.0","html-webpack-plugin":"^4.5.0","prettier":"^2.1.2","prettier-eslint":"^11.0.0","react":"^16.13.1","react-dom":"^16.13.1","rimraf":"^3.0.2","sass":"^1.27.0","sass-loader":"^10.0.3","style-loader":"^2.0.0","terser-webpack-plugin":"^5.0.1","ts-loader":"^8.0.7","typescript":"^4.0.3","webpack":"^5.90.3","webpack-cli":"^5.1.4","webpack-dev-server":"^5.0.4","write-file-webpack-plugin":"^4.5.1"},"_id":"@airtame/webrtc@3.0.1","gitHead":"7647f70d9c2d68f4e64962520a803c357eda3bc0","types":"./dist/index.d.ts","_nodeVersion":"20.9.0","_npmVersion":"10.1.0","dist":{"integrity":"sha512-M0wRp6gl/PEZ/70VZVe7/uOZPKVORX08KX4jmsy6qUL7o+sJNHFcXKrOJb/gOpvDemjJmIYKcD9P6xd678TcmA==","shasum":"29a51ebc5fe81e6fd8ba73449a05e6e4ac65f82c","tarball":"https://registry.npmjs.org/@airtame/webrtc/-/webrtc-3.0.1.tgz","fileCount":8,"unpackedSize":111852,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCp0J6bIZJQv3sZwPG7RU8UjvmAxGv8GFaF7CRSznRA9gIgITbqlvS6bk0n3WpaonRO0bmkS4Ts6RlU68FDkHeGnc0="}]},"_npmUser":{"name":"airtame-ops","email":"devops@airtame.com"},"directories":{},"maintainers":[{"name":"rene-airtame","email":"rene.hansen@airtame.com"},{"name":"airtame-ops","email":"devops@airtame.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/webrtc_3.0.1_1711376216562_0.14439247372614572"},"_hasShrinkwrap":false},"4.0.0":{"name":"@airtame/webrtc","version":"4.0.0","description":"WebRTC React library.","main":"dist/index.js","scripts":{"start":"webpack-dev-server --config ./webpack.config.js","build":"npm run clean && NODE_ENV=production webpack --config ./webpack.config.js --mode=production && npm run declaration","clean":"rimraf ./dist && rimraf ./public","watch":"npm run start","declaration":"tsc --declaration --declarationDir ./dist && cp -rf ./dist/src/* ./dist && rimraf dist/src && rimraf dist/example"},"dependencies":{"debug":"^4.2.0"},"peerDependencies":{"react":">=16.13.1","react-dom":">=16.13.1"},"devDependencies":{"@airtame/ui":"^6.9.3","@types/debug":"^4.1.5","@types/react":"^16.9.53","@types/react-dom":"^16.9.8","@typescript-eslint/eslint-plugin":"^4.5.0","@typescript-eslint/parser":"^4.5.0","classnames":"^2.3.1","copy-webpack-plugin":"^6.2.1","css-loader":"^5.0.0","eslint":"^7.12.0","eslint-config-prettier":"^6.14.0","eslint-import-resolver-webpack":"^0.13.0","eslint-loader":"^4.0.2","eslint-plugin-import":"^2.22.1","eslint-plugin-jsx-a11y":"^6.3.1","eslint-plugin-prettier":"^3.1.4","eslint-plugin-react":"^7.21.5","fork-ts-checker-webpack-plugin":"^5.2.0","html-webpack-plugin":"^4.5.0","prettier":"^2.1.2","prettier-eslint":"^11.0.0","react":"^16.13.1","react-dom":"^16.13.1","rimraf":"^3.0.2","sass":"^1.27.0","sass-loader":"^10.0.3","style-loader":"^2.0.0","terser-webpack-plugin":"^5.0.1","ts-loader":"^8.0.7","typescript":"^4.0.3","webpack":"^5.90.3","webpack-cli":"^5.1.4","webpack-dev-server":"^5.0.4","write-file-webpack-plugin":"^4.5.1"},"_id":"@airtame/webrtc@4.0.0","gitHead":"e00aa820659d4c800de4b149d8d9e39826ea73fb","types":"./dist/index.d.ts","_nodeVersion":"20.9.0","_npmVersion":"10.1.0","dist":{"integrity":"sha512-tpg9lRLPSEjBc2D4z3fvxo8gnaK1PNtS7W8VqvKwp8Lv8tRKy5KX2MjQA6HTp/MNzMQ9k+M7Mg+JSQuIORytNA==","shasum":"ac0ac32b8c57e4a1320e9c9025d62a53879656cd","tarball":"https://registry.npmjs.org/@airtame/webrtc/-/webrtc-4.0.0.tgz","fileCount":8,"unpackedSize":111996,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGX7jAZ4frq1EOO6f3T7AK/Uu7yeWZmL2KFsvcA4Sb/dAiEA2lsAFp6+QYr2PB/3FiWDPSR98WScPEd19CqdoUH9aUs="}]},"_npmUser":{"name":"airtame-ops","email":"devops@airtame.com"},"directories":{},"maintainers":[{"name":"rene-airtame","email":"rene.hansen@airtame.com"},{"name":"airtame-ops","email":"devops@airtame.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/webrtc_4.0.0_1712672664718_0.748865244316758"},"_hasShrinkwrap":false}},"time":{"created":"2024-03-25T14:05:11.316Z","3.0.0":"2024-03-25T14:05:11.614Z","modified":"2024-04-09T14:24:25.173Z","3.0.1":"2024-03-25T14:16:56.765Z","4.0.0":"2024-04-09T14:24:24.923Z"},"maintainers":[{"name":"rene-airtame","email":"rene.hansen@airtame.com"},{"name":"airtame-ops","email":"devops@airtame.com"}],"description":"WebRTC React library.","readme":"# Airtame WebRTC abstraction library\n\n## Introduction\n\nThis library is intended to be used for Airtame use-cases. Namely standalone streaming and\nstreaming during a video call.\n\nIt supports, 1-1, 1-N, N-1, N-N types calls. The library is very opinionated and should only be\nused as intended.\n\nIn the library 2 things have to be understood:\n\n- The streamer connection is always the one to create the data channel and to initiate the media\n  connection. Additionally, it will always be the \"impolite\" peer (see Perfect Negotiation Pattern).\n- The library is not tested to support bi-directional media meaning that we haven't tested if\n  a receiver can easily send data and a streamer receive some.\n\n## Usage\n\n### Build\n\nTo build the library:\n\n```bash\n# Install the dependencies\nnpm install\n\n# Run the build script\nnpm run build\n```\n\nAfter that you have the artifacts in the `dist/` folder.\n\n### Run the examples\n\n```bash\n# Install the dependencies\nnpm install\n\n# Start the dev server\nnpm run start\n```\n\nYou can now go to `http://localhost:8080/public/example/streamer` for the streamer and\n`http://localhost:8080/public/example/receiver` for the receiver.\n\n### To implement\n\nThere are only two raw object in this library: `WebRTCApplication` and `WebRTCConnection`. For\nconveniance we also provide a `React` wrapper: `WebRTCApplicationProvider`.\n\nHere are the outline of both main object:\n\n`WebRTCApplication`:\n\n```typescript\nclass WebRTCApplication {\n  /*\n   * Create a connection that will be used by a receiver.\n   *\n   * @returns A tuple containing the newly created connection and the answer to the offer.\n   */\n  public acceptConnection(id: string, offer: string): Promise<[Connection, string]>;\n\n  /*\n   * Create a connection that will be used by a streamer.\n   *\n   * @returns A tuple containing the newly created connection and the offer.\n   */\n  public connect(id: string): Promise<[Connection, string]>;\n}\n```\n\n`WebRTCConnection`:\n\n```typescript\nclass WebRTCConnection extends EventEmitter {\n  /*\n   * Returns the main RTCPeerConnection used for data communication.\n   */\n  get mainPeerConnection(): RTCPeerConnection;\n\n  /*\n   * Returns the RTCPeerConnection used to transport media. Can be undefined is no streaming\n   * session is started.\n   */\n  get mediaPeerConnection(): RTCPeerConnection | undefined;\n  /*\n   * Returns the current state of the main connection - data channel (see below for the state).\n   */\n  get connectionState(): ConnectionState;\n\n  /*\n   * Returns the current state of the media connection (see below for the state).\n   */\n  get mediaConnectionState(): ConnectionState;\n\n  /*\n   * Initialize the connection to be used as a receiving connection. That is, to be\n   * used in a receiver.\n   *\n   * @returns The answer to the offer provided in argument.\n   */\n  public initReceiver(offer: string): Promise<string>;\n\n  /*\n   * Initialize the connection to be used as a sending connection. That is, to be\n   * used in a streamer.\n   *\n   * @returns The offer.\n   */\n  public initSender(): Promise<string>;\n\n  /*\n   * Disconnect the full application, media stream included. Will emit the `CLOSED` event on\n   * the CONNECTION_STATE and the MEDIA_STATE.\n   * The connection is not usable from that point, and you need to call `connect` once again to\n   * create a new connection.\n   */\n  public disconnect(): void;\n\n  /*\n   * Start the streaming of media data to the receiver.\n   *\n   * *This is only used in a streamer otherwise it will throw an error*.\n   *\n   * NOTE:\n   * -----\n   *   This is the responsibility of the caller to stop the mediaStream tracks when\n   *   needed. The WebRTC API is not responsible for it when calling stopStream.\n   */\n  public startStream(ms: MediaStream, codecFilter?: CodecFilterPredicate): void;\n\n  /*\n   * Stop the stream connection. Stop all the tracks on the media stream and close the connection.\n   * The state closed will be emitted.\n   */\n  public stopStream(): void;\n\n  /*\n   * Set the initial answer provided by a receiver to a streamer.\n   *\n   * *This is only used in a streamer otherwise it will throw an error*.\n   */\n  public setAnswer(answer: string): Promise<void>;\n\n  /*\n   * Send a message to the peer connection. This is used in both the receiver and streamer.\n   *\n   * Notes: The kind is used to identify the message being sent and the message is the content.\n   * Notes: The kind shouldn't start with `__`. This is reserved to the internal usage of the\n   *        library.\n   */\n  public sendMessage(kind: string, message: string): void;\n}\n```\n\nAdditionally, we have a connection state `enum`:\n\n```typescript\nenum ConnectionState {\n  // When nothing started, for example:\n  //   * No receiver nor streamer are event initialized\n  //   * For media state, it would mean that no streaming session is started\n  NOT_INITIALIZED = 'not-initialized',\n  // When somethig is initialized like the streamer or receiver, or stream is called\n  INITIALIZED = 'initialized',\n  // When the connections are starting to their initialization and connection\n  LOADING = 'loading',\n  // When a session is started (main or media)\n  OK = 'ok',\n  // When a connection looses contact with the peer, but still trying to connect\n  DISCONNECTED = 'disconnected',\n  // When the disconnection passes a timeout, it turns out to this state\n  FAILED = 'failed',\n  // When a connection closed properly\n  CLOSED = 'closed',\n  // Other more generic errors\n  ERROR = 'error',\n}\n```\n\nFor a complete usage example (using the `React` wrapper), please refer to the examples.\n\n### Filtering codecs\n\nIn some specific use cases, you might want to limit the codec used by the streamer. For example\nthis is what happens with our Zoom SIP application.\n\nHere is an example:\n\n```typescript\nconnection.startStream(mediaStream, (codecs: RTCRtpCodecCapability[]) =>\n  codecs.filter(\n    (codec) =>\n      (codec.mimeType.toLowerCase() === 'video/h264' &&\n        codec.sdpFmtpLine?.match(/42e01f/) &&\n        codec.sdpFmtpLine.match(/packetization-mode=0/)) ||\n      codec.mimeType.toLowerCase() === 'video/rtx'\n  )\n);\n```\n\n## Events\n\nAn enum Events is available to be used to subscribe to events:\n\n```typescript\n// See below for specific explanation of how to use those events and when.\nexport enum Events {\n  MESSAGE = 'message',\n  STREAM = 'stream',\n  CONNECTION_STATE_CHANGE = 'connectionStateChange',\n  MEDIA_STATE_CHANGE = 'mediaStateChange',\n}\n```\n\nThe Connection object described above implements `EventEmitter` and has some built in events to be\naware of:\n\n- `message`: Used for the text communication between 2 peers. This is generated by both kind\n  of connections.\n\n  ```typescript\n  connection.on(Events.MESSAGE, (data) => {\n    const message = JSON.parse(data);\n    if (message.kind === 'my_great_message') {\n      doSomething(message.message);\n    }\n  });\n  ```\n\n- `stream`: Used by the receiver when it receives media data from the streamer.\n\n  ```typescript\n  connection.on(Events.STREAM, (stream) => {\n    if (video && video.current) {\n      video.current.srcObject = stream;\n    }\n  });\n  ```\n\n- `connectionStateChange`: Emitted when the main RTC session state changed. The emitter callback\n  will have the signature `(state: ConnectionState, message?: string): void`.\n\n  ```typescript\n  connection.on(Events.CONNECTION_STATE_CHANGE, (state, message) => {\n    if (state === ConnectionState.OK) {\n      console.log('Connected successfully');\n    } else if (state === ConnectionState.ERROR) {\n      console.log(`Error: ${message}`);\n    }\n  });\n  ```\n\n- `mediaStateChange`: Emitted when the media streaming session state changed. The emitter callback\n  will have the signature `(state: ConnectionState, message?: string): void`.\n\n  ```typescript\n  connection.on(Events.MEDIA_STATE_CHANGE, (state, message) => {\n    if (state === ConnectionState.OK) {\n      console.log('Connected successfully');\n    } else if (state === ConnectionState.ERROR) {\n      console.log(`Error: ${message}`);\n    }\n  });\n  ```\n","readmeFilename":"README.md"}