{"id":13405582,"url":"https://github.com/whitphx/streamlit-webrtc","last_synced_at":"2026-05-19T01:03:48.428Z","repository":{"id":37820167,"uuid":"326705246","full_name":"whitphx/streamlit-webrtc","owner":"whitphx","description":"Real-time video and audio processing on Streamlit","archived":false,"fork":false,"pushed_at":"2026-05-15T19:29:45.000Z","size":39667,"stargazers_count":1701,"open_issues_count":87,"forks_count":222,"subscribers_count":15,"default_branch":"main","last_synced_at":"2026-05-15T19:58:00.047Z","etag":null,"topics":["computer-vision","hacktoberfest","pypi","python","streamlit","streamlit-webrtc","video-processing","video-streaming","webrtc"],"latest_commit_sha":null,"homepage":"https://whitphx.github.io/streamlit-webrtc/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/whitphx.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null},"funding":{"github":["whitphx"],"patreon":"whitphx","open_collective":null,"ko_fi":"whitphx","tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":["https://www.buymeacoffee.com/whitphx"]}},"created_at":"2021-01-04T14:13:24.000Z","updated_at":"2026-05-15T19:29:23.000Z","dependencies_parsed_at":"2025-12-06T15:04:51.880Z","dependency_job_id":null,"html_url":"https://github.com/whitphx/streamlit-webrtc","commit_stats":{"total_commits":1135,"total_committers":5,"mean_commits":227.0,"dds":"0.48810572687224674","last_synced_commit":"c871978d5b5c6141d96b808ec465adc784cf2ec0"},"previous_names":[],"tags_count":166,"template":false,"template_full_name":null,"purl":"pkg:github/whitphx/streamlit-webrtc","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/whitphx%2Fstreamlit-webrtc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/whitphx%2Fstreamlit-webrtc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/whitphx%2Fstreamlit-webrtc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/whitphx%2Fstreamlit-webrtc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/whitphx","download_url":"https://codeload.github.com/whitphx/streamlit-webrtc/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/whitphx%2Fstreamlit-webrtc/sbom","scorecard":{"id":1237858,"data":{"date":"2025-08-18","repo":{"name":"github.com/whitphx/streamlit-webrtc","commit":"ec3e81b94df6cd939d7b9d9eff8e0f91729372f9"},"scorecard":{"version":"v5.2.1-41-g40576783","commit":"40576783fda6698350fcbbeaea760ff827433034"},"score":6.5,"checks":[{"name":"Maintained","score":10,"reason":"30 commit(s) and 4 issue activity found in the last 90 days -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#maintained"}},{"name":"Code-Review","score":0,"reason":"Found 0/17 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":10,"reason":"GitHub workflow tokens follow principle of least privilege","details":["Warn: jobLevel 'contents' permission set to 'write': .github/workflows/main.yml:268","Info: found token with 'none' permissions: .github/workflows/main.yml:1"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#cii-best-practices"}},{"name":"Pinned-Dependencies","score":1,"reason":"dependency not pinned by hash detected -- score normalized to 1","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:57: update your workflow using https://app.stepsecurity.io/secureworkflow/whitphx/streamlit-webrtc/main.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/main.yml:62: update your workflow using https://app.stepsecurity.io/secureworkflow/whitphx/streamlit-webrtc/main.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:65: update your workflow using https://app.stepsecurity.io/secureworkflow/whitphx/streamlit-webrtc/main.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:105: update your workflow using https://app.stepsecurity.io/secureworkflow/whitphx/streamlit-webrtc/main.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/main.yml:109: update your workflow using https://app.stepsecurity.io/secureworkflow/whitphx/streamlit-webrtc/main.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:116: update your workflow using https://app.stepsecurity.io/secureworkflow/whitphx/streamlit-webrtc/main.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:137: update your workflow using https://app.stepsecurity.io/secureworkflow/whitphx/streamlit-webrtc/main.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/main.yml:143: update your workflow using https://app.stepsecurity.io/secureworkflow/whitphx/streamlit-webrtc/main.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:146: update your workflow using https://app.stepsecurity.io/secureworkflow/whitphx/streamlit-webrtc/main.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/main.yml:154: update your workflow using https://app.stepsecurity.io/secureworkflow/whitphx/streamlit-webrtc/main.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:161: update your workflow using https://app.stepsecurity.io/secureworkflow/whitphx/streamlit-webrtc/main.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:175: update your workflow using https://app.stepsecurity.io/secureworkflow/whitphx/streamlit-webrtc/main.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:193: update your workflow using https://app.stepsecurity.io/secureworkflow/whitphx/streamlit-webrtc/main.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:208: update your workflow using https://app.stepsecurity.io/secureworkflow/whitphx/streamlit-webrtc/main.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:252: update your workflow using https://app.stepsecurity.io/secureworkflow/whitphx/streamlit-webrtc/main.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/main.yml:257: update your workflow using https://app.stepsecurity.io/secureworkflow/whitphx/streamlit-webrtc/main.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:273: update your workflow using https://app.stepsecurity.io/secureworkflow/whitphx/streamlit-webrtc/main.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/main.yml:278: update your workflow using https://app.stepsecurity.io/secureworkflow/whitphx/streamlit-webrtc/main.yml/main?enable=pin","Info:   0 out of  12 GitHub-owned GitHubAction dependencies pinned","Info:   1 out of   7 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#pinned-dependencies"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#license"}},{"name":"Branch-Protection","score":3,"reason":"branch protection is not maximal on development and all release branches","details":["Info: 'allow deletion' disabled on branch 'main'","Info: 'force pushes' disabled on branch 'main'","Warn: 'branch protection settings apply to administrators' is disabled on branch 'main'","Warn: 'stale review dismissal' is disabled on branch 'main'","Warn: branch 'main' does not require approvers","Warn: codeowners review is not required on branch 'main'","Warn: 'last push approval' is disabled on branch 'main'","Warn: no status checks found to merge onto branch 'main'","Info: PRs are required in order to make changes on branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#branch-protection"}},{"name":"Signed-Releases","score":8,"reason":"5 out of the last 5 releases have a total of 5 signed artifacts.","details":["Info: signed release artifact: streamlit_webrtc-0.63.4-py3-none-any.whl.sigstore.json: https://github.com/whitphx/streamlit-webrtc/releases/tag/v0.63.4","Info: signed release artifact: streamlit_webrtc-0.63.3-py3-none-any.whl.sigstore.json: https://github.com/whitphx/streamlit-webrtc/releases/tag/v0.63.3","Info: signed release artifact: streamlit_webrtc-0.63.2-py3-none-any.whl.sigstore.json: https://github.com/whitphx/streamlit-webrtc/releases/tag/v0.63.2","Info: signed release artifact: streamlit_webrtc-0.63.1-py3-none-any.whl.sigstore.json: https://github.com/whitphx/streamlit-webrtc/releases/tag/v0.63.1","Info: signed release artifact: streamlit_webrtc-0.63.0-py3-none-any.whl.sigstore.json: https://github.com/whitphx/streamlit-webrtc/releases/tag/v0.63.0","Warn: release artifact v0.63.4 does not have provenance: https://api.github.com/repos/whitphx/streamlit-webrtc/releases/237908082","Warn: release artifact v0.63.3 does not have provenance: https://api.github.com/repos/whitphx/streamlit-webrtc/releases/228499288","Warn: release artifact v0.63.2 does not have provenance: https://api.github.com/repos/whitphx/streamlit-webrtc/releases/228277435","Warn: release artifact v0.63.1 does not have provenance: https://api.github.com/repos/whitphx/streamlit-webrtc/releases/227252731","Warn: release artifact v0.63.0 does not have provenance: https://api.github.com/repos/whitphx/streamlit-webrtc/releases/226824136"],"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#signed-releases"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#security-policy"}},{"name":"SAST","score":10,"reason":"SAST tool is run on all commits","details":["Info: all commits (19) are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#sast"}},{"name":"Packaging","score":10,"reason":"packaging workflow detected","details":["Info: Project packages its releases by way of GitHub Actions.: .github/workflows/main.yml:239"],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#packaging"}},{"name":"Vulnerabilities","score":8,"reason":"2 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-xffm-g5w8-qvg7","Warn: Project is vulnerable to: GHSA-jgmv-j7ww-jx2x"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-09-18T13:29:38.865Z","repository_id":37820167,"created_at":"2025-09-18T13:29:38.865Z","updated_at":"2025-09-18T13:29:38.865Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33197520,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-18T09:27:30.708Z","status":"ssl_error","status_checked_at":"2026-05-18T09:27:28.300Z","response_time":71,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["computer-vision","hacktoberfest","pypi","python","streamlit","streamlit-webrtc","video-processing","video-streaming","webrtc"],"created_at":"2024-07-30T19:02:05.929Z","updated_at":"2026-05-19T01:03:48.417Z","avatar_url":"https://github.com/whitphx.png","language":"Python","funding_links":["https://github.com/sponsors/whitphx","https://patreon.com/whitphx","https://ko-fi.com/whitphx","https://www.buymeacoffee.com/whitphx","https://ko-fi.com/D1D2ERWFG"],"categories":["Python","webrtc","Players \u0026 Clients"],"sub_categories":["Mobile \u0026 Web Players"],"readme":"# streamlit-webrtc\n**Handling and transmitting real-time video/audio streams over the network with Streamlit** [![Open in Streamlit](https://static.streamlit.io/badges/streamlit_badge_black_white.svg)](https://share.streamlit.io/whitphx/streamlit-webrtc-example/main/app.py)\n\n[![Test and Build](https://github.com/whitphx/streamlit-webrtc/actions/workflows/test-build.yml/badge.svg)](https://github.com/whitphx/streamlit-webrtc/actions/workflows/test-build.yml)\n[![Post-build](https://github.com/whitphx/streamlit-webrtc/actions/workflows/post-build.yml/badge.svg)](https://github.com/whitphx/streamlit-webrtc/actions/workflows/post-build.yml)\n\n[![PyPI](https://img.shields.io/pypi/v/streamlit-webrtc)](https://pypi.org/project/streamlit-webrtc/)\n[![PyPI - License](https://img.shields.io/pypi/l/streamlit-webrtc)](https://pypi.org/project/streamlit-webrtc/)\n[![PyPI - Downloads](https://img.shields.io/pypi/dm/streamlit-webrtc)](https://pypi.org/project/streamlit-webrtc/)\n\n[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd width=\"48%\"\u003e\n  \u003ca href=\"https://share.streamlit.io/whitphx/streamlit-webrtc-example/main/app.py\"\u003e\n    \u003cimg src=\"https://global.discourse-cdn.com/streamlit/original/2X/a/af111a7393c77cb69d7712ac8e71ca862feaeb24.gif\" /\u003e\n  \u003c/a\u003e\n\u003c/td\u003e\n\u003ctd width=\"48%\"\u003e\n  \u003ca href=\"https://share.streamlit.io/whitphx/style-transfer-web-app/main/app.py\"\u003e\n    \u003cimg src=\"https://global.discourse-cdn.com/streamlit/original/2X/b/b3cb8aa60eb746366e06726a9137720583c02c3a.gif\" /\u003e\n  \u003c/a\u003e\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\nSister project: [`streamlit-fesion`](https://github.com/whitphx/streamlit-fesion) to execute video filters on browsers with Wasm.\n\n## Examples\n### [⚡️Showcase including following examples and more](https://github.com/whitphx/streamlit-webrtc-example): [🎈Online demo](https://share.streamlit.io/whitphx/streamlit-webrtc-example/main/app.py)\n\n* Object detection\n* OpenCV filter\n* Uni-directional video streaming\n* Audio processing\n\n### [⚡️Real-time Speech-to-Text](https://github.com/whitphx/streamlit-stt-app): [🎈Online demo](https://share.streamlit.io/whitphx/streamlit-stt-app/main/app_deepspeech.py)\n\nIt converts your voice into text in real time.\nThis app is self-contained; it does not depend on any external API.\n\n### [⚡️Real-time video style transfer](https://github.com/whitphx/style-transfer-web-app): [🎈Online demo](https://share.streamlit.io/whitphx/style-transfer-web-app/main/app.py)\nIt applies a wide variety of style transfer filters to real-time video streams.\n\n### [⚡️Video chat](https://github.com/whitphx/streamlit-video-chat-example)\n(Online demo not available)\n\nYou can create video chat apps with ~100 lines of Python code.\n\n### [⚡️Tokyo 2020 Pictogram](https://github.com/whitphx/Tokyo2020-Pictogram-using-MediaPipe): [🎈Online demo](https://share.streamlit.io/whitphx/tokyo2020-pictogram-using-mediapipe/streamlit-app)\n[MediaPipe](https://google.github.io/mediapipe/) is used for pose estimation.\n\n## Install\n```shell\n$ pip install -U streamlit-webrtc\n```\n\n## Quick tutorial\n\nSee also [the sample pages, `pages/*.py`](https://github.com/whitphx/streamlit-webrtc/tree/main/pages), which contain a wide variety of usage.\n\nSee also [\"Developing Web-Based Real-Time Video/Audio Processing Apps Quickly with Streamlit\"](https://towardsdatascience.com/developing-web-based-real-time-video-audio-processing-apps-quickly-with-streamlit-7c7bcd0bc5a8).\n\n---\n\nCreate `app.py` with the content below.\n```py\nfrom streamlit_webrtc import webrtc_streamer\n\nwebrtc_streamer(key=\"sample\")\n```\nUnlike other Streamlit components, `webrtc_streamer()` requires the `key` argument as a unique identifier. Set an arbitrary string to it.\n\nThen run it with Streamlit and open http://localhost:8501/.\n```shell\n$ streamlit run app.py\n```\n\nYou see the app view, so click the \"START\" button.\n\nThen, video and audio streaming starts. If asked for permissions to access the camera and microphone, allow it.\n![Basic example of streamlit-webrtc](./docs/images/streamlit_webrtc_basic.gif)\n\nNext, edit `app.py` as below and run it again.\n```py\nfrom streamlit_webrtc import webrtc_streamer\nimport av\n\n\ndef video_frame_callback(frame):\n    img = frame.to_ndarray(format=\"bgr24\")\n\n    flipped = img[::-1,:,:]\n\n    return av.VideoFrame.from_ndarray(flipped, format=\"bgr24\")\n\n\nwebrtc_streamer(key=\"example\", video_frame_callback=video_frame_callback)\n```\n\nNow the video is vertically flipped.\n![Vertically flipping example](./docs/images/streamlit_webrtc_flipped.gif)\n\nAs an example above, you can edit the video frames by defining a callback that receives and returns a frame and passing it to the `video_frame_callback` argument (or `audio_frame_callback` for audio manipulation).\nThe input and output frames are the instance of [`av.VideoFrame`](https://pyav.org/docs/develop/api/video.html#av.video.frame.VideoFrame) (or [`av.AudioFrame`](https://pyav.org/docs/develop/api/audio.html#av.audio.frame.AudioFrame) when dealing with audio) of [`PyAV` library](https://pyav.org/).\n\nYou can inject any kinds of image (or audio) processing inside the callback.\nSee examples above for more applications.\n\n### Pass parameters to the callback\n\nYou can also pass parameters to the callback.\n\nIn the example below, a boolean `flip` flag is used to turn on/off the image flipping.\n```python\nimport streamlit as st\nfrom streamlit_webrtc import webrtc_streamer\nimport av\n\n\nflip = st.checkbox(\"Flip\")\n\n\ndef video_frame_callback(frame):\n    img = frame.to_ndarray(format=\"bgr24\")\n\n    flipped = img[::-1,:,:] if flip else img\n\n    return av.VideoFrame.from_ndarray(flipped, format=\"bgr24\")\n\n\nwebrtc_streamer(key=\"example\", video_frame_callback=video_frame_callback)\n```\n\n### Pull values from the callback\n\nSometimes we want to read the values generated in the callback from the outer scope.\n\nNote that the callback is executed in a forked thread running independently of the main script, so we have to take care of the following points and need some tricks for implementation like the example below (See also the section below for some limitations in the callback due to multi-threading).\n\n* Thread-safety\n  * Passing the values between inside and outside the callback must be thread-safe.\n* Using a loop to poll the values\n  * During media streaming, while the callback continues to be called, the main script execution stops at the bottom as usual. So we need to use a loop to keep the main script running and get the values from the callback in the outer scope.\n\nThe following example is to pass the image frames from the callback to the outer scope and continuously process it in the loop. In this example, a simple image analysis (calculating the histogram like [this OpenCV tutorial](https://docs.opencv.org/4.x/d1/db7/tutorial_py_histogram_begins.html)) is done on the image frames.\n\n[`threading.Lock`](https://docs.python.org/3/library/threading.html#lock-objects) is one standard way to control variable accesses across threads.\nA dict object `img_container` here is a mutable container shared by the callback and the outer scope and the `lock` object is used at assigning and reading the values to/from the container for thread-safety.\n\n```python\nimport threading\n\nimport cv2\nimport streamlit as st\nfrom matplotlib import pyplot as plt\n\nfrom streamlit_webrtc import webrtc_streamer\n\nlock = threading.Lock()\nimg_container = {\"img\": None}\n\n\ndef video_frame_callback(frame):\n    img = frame.to_ndarray(format=\"bgr24\")\n    with lock:\n        img_container[\"img\"] = img\n\n    return frame\n\n\nctx = webrtc_streamer(key=\"example\", video_frame_callback=video_frame_callback)\n\nfig_place = st.empty()\nfig, ax = plt.subplots(1, 1)\n\nwhile ctx.state.playing:\n    with lock:\n        img = img_container[\"img\"]\n    if img is None:\n        continue\n    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n    ax.cla()\n    ax.hist(gray.ravel(), 256, [0, 256])\n    fig_place.pyplot(fig)\n```\n\n## Callback limitations\nThe callbacks are executed in forked threads different from the main one, so there are some limitations:\n* Streamlit methods (`st.*` such as `st.write()`) do not work inside the callbacks.\n* Variables inside the callbacks cannot be directly referred to from the outside.\n* The `global` keyword does not work expectedly in the callbacks.\n* You have to care about thread-safety when accessing the same objects both from outside and inside the callbacks as stated in the section above.\n\n## Class-based callbacks\nUntil v0.37, the class-based callbacks were the standard.\nSee the [old version of the README](https://github.com/whitphx/streamlit-webrtc/blob/v0.37.0/README.md#quick-tutorial) about it.\n\n## Serving from remote host\nWhen deploying apps to remote servers, there are some things you need to be aware of.\nIn short,\n* HTTPS is required to access local media devices.\n* STUN/TURN servers are required to establish the media stream connection.\n\nSee the following sections.\n\n### HTTPS\n`streamlit-webrtc` uses [`getUserMedia()`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia) API to access local media devices, and this method does not work in an insecure context.\n\n[This document](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia#privacy_and_security) says [^1]\n\u003e A secure context is, in short, a page loaded using HTTPS or the file:/// URL scheme, or a page loaded from localhost.\n\n[^1]: For more details about [This document](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts) explains the concept of secure context in detail.\n\nSo, when hosting your app on a remote server, it must be served via HTTPS if your app is using webcam or microphone.\nIf not, you will encounter an error when starting using the device. For example, it's something like below on Chrome.\n\u003e Error: navigator.mediaDevices is undefined. It seems the current document is not loaded securely.\n\n[Streamlit Community Cloud](https://streamlit.io/cloud) is a recommended way for HTTPS serving. You can easily deploy Streamlit apps with it, and most importantly for this topic, it serves the apps via HTTPS automatically by default.\n\nFor the development purpose, sometimes [`suyashkumar/ssl-proxy`](https://github.com/suyashkumar/ssl-proxy) is a convenient tool to serve your app via HTTPS.\n```shell\n$ streamlit run your_app.py  # Assume your app is running on http://localhost:8501\n# Then, after downloading the binary from the GitHub page above to ./ssl-proxy,\n$ ./ssl-proxy -from 0.0.0.0:8000 -to 127.0.0.1:8501  # Proxy the HTTP page from port 8501 to port 8000 via HTTPS\n# Then access https://localhost:8000\n```\n\n### Configure the STUN server\nTo deploy the app to the cloud, we have to configure the *STUN* server via the `rtc_configuration` argument on `webrtc_streamer()` like below.\n\n```python\nwebrtc_streamer(\n    # ...\n    rtc_configuration={  # Add this config\n        \"iceServers\": [{\"urls\": [\"stun:stun.l.google.com:19302\"]}]\n    }\n    # ...\n)\n```\n\nThis configuration is necessary to establish the media streaming connection when the server is on a remote host.\n\n:warning: You may need to set up a TURN server as well in some environments, **including Streamlit Community Cloud**. See also the next section.\n\n`streamlit_webrtc` uses WebRTC for its video and audio streaming. It has to access a \"STUN server\" in the global network for the remote peers (precisely, peers over the NATs) to establish WebRTC connections.\nAs we don't see the details about STUN servers here, please google it if interested with keywords such as STUN, TURN, or NAT traversal, or read these articles ([1](https://towardsdatascience.com/developing-web-based-real-time-video-audio-processing-apps-quickly-with-streamlit-7c7bcd0bc5a8#1cec), [2](https://dev.to/whitphx/python-webrtc-basics-with-aiortc-48id), [3](https://www.3cx.com/pbx/what-is-a-stun-server/)).\n\nThe example above is configured to use `stun.l.google.com:19302`, which is a free STUN server provided by Google.\n\nYou can also use any other STUN servers.\nFor example, [one user reported](https://github.com/whitphx/streamlit-webrtc/issues/283#issuecomment-889753789) that the Google's STUN server had a huge delay when using from China network, and the problem was solved by changing the STUN server.\n\nFor those who know about the browser WebRTC API: The value of the rtc_configuration argument will be passed to the [`RTCPeerConnection`](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/RTCPeerConnection) constructor on the frontend.\n\n### Configure the TURN server if necessary\nEven if the STUN server is properly configured, media streaming may not work in some network environments, either from the server or from the client.\nFor example, if the server is hosted behind a proxy, or if the client is on an office network behind a firewall, the WebRTC packets may be blocked (**Streamlit Community Cloud is the case**). [This article](https://blog.addpipe.com/troubleshooting-webrtc-connection-issues/#steptwodiscoverystunandturn) summarizes the possible situations.\n\nIn such environments, [TURN server](https://webrtc.org/getting-started/turn-server) is required.\n\nThere are several options for setting up a TURN server:\n* [Twilio Network Traversal Service](https://www.twilio.com/docs/stun-turn) (_recommended_) is a stable and easy-to-use solution. It's a paid service, but you can start with a free trial with a certain amount of credit.\n  You can simply pass the `ice_servers` field of the [Network Traversal Service Tokens API](https://www.twilio.com/docs/api/2010-04-01/rest/token) response to the `iceServers` field of the `rtc_configuration` argument of `webrtc_streamer()`.\n  ```python\n  ## This sample code is from https://www.twilio.com/docs/stun-turn/api\n  # Download the helper library from https://www.twilio.com/docs/python/install\n  import os\n  from twilio.rest import Client\n\n  # Find your Account SID and Auth Token at twilio.com/console\n  # and set the environment variables. See http://twil.io/secure\n  account_sid = os.environ['TWILIO_ACCOUNT_SID']\n  auth_token = os.environ['TWILIO_AUTH_TOKEN']\n  client = Client(account_sid, auth_token)\n\n  token = client.tokens.create()\n\n  # Then, pass the ICE server information to webrtc_streamer().\n  webrtc_streamer(\n    # ...\n    rtc_configuration={\n        \"iceServers\": token.ice_servers\n    }\n    # ...\n  )\n  ```\n  The [WebRTC sample app hosted on the Community Cloud](https://webrtc.streamlit.app/) uses this option. See [how it retrieves the ICE server information from the Twilio API](https://github.com/whitphx/streamlit-webrtc-example/blob/79ac65994a8c7f91475647d65e63b5040ea35863/sample_utils/turn.py) and [how to use it in the app](https://github.com/whitphx/streamlit-webrtc-example/blob/79ac65994a8c7f91475647d65e63b5040ea35863/pages/1_object_detection.py#L141).\n* The [Open Relay Project](https://www.metered.ca/tools/openrelay/) provides a free TURN server. However, it does not seem to be stable enough and is often down.\n* A self-hosted TURN server is also an option. See https://github.com/whitphx/streamlit-webrtc/issues/335#issuecomment-897326755.\n\n## Logging\nFor logging, this library uses the standard `logging` module and follows the practice described in [the official logging tutorial](https://docs.python.org/3/howto/logging.html#advanced-logging-tutorial). Then the logger names are the same as the module names - `streamlit_webrtc` or `streamlit_webrtc.*`.\n\nSo you can get the logger instance with `logging.getLogger(\"streamlit_webrtc\")` through which you can control the logs from this library.\n\nFor example, if you want to set the log level on this library's logger as WARNING, you can use the following code.\n```python\nst_webrtc_logger = logging.getLogger(\"streamlit_webrtc\")\nst_webrtc_logger.setLevel(logging.WARNING)\n```\n\nIn practice, `aiortc`, a third-party package this library is internally using, also emits many INFO level logs and you may want to control its logs too.\nYou can do it in the same way as below.\n```python\naioice_logger = logging.getLogger(\"aioice\")\naioice_logger.setLevel(logging.WARNING)\n```\n\n## API changes\nCurrently there is no documentation about the interface. See the examples in [./pages/*.py](./pages) for the usage.\nThe API is not finalized yet and can be changed without backward compatibility in the future releases until v1.0.\n\n### For users since versions `\u003c0.20`\n`VideoTransformerBase` and its `transform` method have been marked as deprecated in v0.20.0. Please use `VideoProcessorBase#recv()` instead.\nNote that the signature of the `recv` method is different from the `transform` in that the `recv` has to return an instance of `av.VideoFrame` or `av.AudioFrame`.\n\nAlso, `webrtc_streamer()`'s `video_transformer_factory` and `async_transform` arguments are deprecated, so use `video_processor_factory` and `async_processing` respectively.\n\nSee the samples in [app.py](./app.py) for their usage.\n\n## Resources\n* [📖 Developing web-based real-time video/audio processing apps quickly with Streamlit](https://www.whitphx.info/posts/20211231-streamlit-webrtc-video-app-tutorial/)\n  * A tutorial for real-time video app development using `streamlit-webrtc`.\n  * Crosspost on dev.to: https://dev.to/whitphx/developing-web-based-real-time-videoaudio-processing-apps-quickly-with-streamlit-4k89\n* [📖 New Component: streamlit-webrtc, a new way to deal with real-time media streams (Streamlit Community)](https://discuss.streamlit.io/t/new-component-streamlit-webrtc-a-new-way-to-deal-with-real-time-media-streams/8669)\n  * This is a forum topic where `streamlit-webrtc` has been introduced and discussed about.\n* [🎥 Real-time browser-ready computer vision apps with Streamlit, EuroPython 2022](https://www.youtube.com/watch?v=D4F6GKaoLns)\n  * A talk at EuroPython 2022 about `streamlit-webrtc` demonstrating quick development of a real-time computer vision app.\n\n## Support the project\n[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/D1D2ERWFG)\n\n\u003ca href=\"https://www.buymeacoffee.com/whitphx\" target=\"_blank\" rel=\"noreferrer\"\u003e\u003cimg src=\"https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png\" alt=\"Buy Me A Coffee\" width=\"180\" height=\"50\" \u003e\u003c/a\u003e\n\n[![GitHub Sponsors](https://img.shields.io/github/sponsors/whitphx?label=Sponsor%20me%20on%20GitHub%20Sponsors\u0026style=social)](https://github.com/sponsors/whitphx)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwhitphx%2Fstreamlit-webrtc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwhitphx%2Fstreamlit-webrtc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwhitphx%2Fstreamlit-webrtc/lists"}