• Stars
    star
    1,348
  • Rank 34,849 (Top 0.7 %)
  • Language
    Python
  • License
    MIT License
  • Created almost 4 years ago
  • Updated 3 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Real-time video and audio streams over the network, with Streamlit.

streamlit-webrtc

Handling and transmitting real-time video/audio streams over the network with Streamlit Open in Streamlit

Tests Frontend Tests

PyPI PyPI - Python Version PyPI - License PyPI - Downloads

Sister project: streamlit-fesion to execute video filters on browsers with Wasm.

Examples

⚡️Showcase including following examples and more: 🎈Online demo

  • Object detection
  • OpenCV filter
  • Uni-directional video streaming
  • Audio processing

⚡️Real-time Speech-to-Text: 🎈Online demo

It converts your voice into text in real time. This app is self-contained; it does not depend on any external API.

⚡️Real-time video style transfer: 🎈Online demo

It applies a wide variety of style transfer filters to real-time video streams.

⚡️Video chat

(Online demo not available)

You can create video chat apps with ~100 lines of Python code.

⚡️Tokyo 2020 Pictogram: 🎈Online demo

MediaPipe is used for pose estimation.

Install

$ pip install -U streamlit-webrtc

Quick tutorial

See also the sample pages, pages/*.py, which contain a wide variety of usage.

See also "Developing Web-Based Real-Time Video/Audio Processing Apps Quickly with Streamlit".


Create app.py with the content below.

from streamlit_webrtc import webrtc_streamer

webrtc_streamer(key="sample")

Unlike other Streamlit components, webrtc_streamer() requires the key argument as a unique identifier. Set an arbitrary string to it.

Then run it with Streamlit and open http://localhost:8501/.

$ streamlit run app.py

You see the app view, so click the "START" button.

Then, video and audio streaming starts. If asked for permissions to access the camera and microphone, allow it. Basic example of streamlit-webrtc

Next, edit app.py as below and run it again.

from streamlit_webrtc import webrtc_streamer
import av


def video_frame_callback(frame):
    img = frame.to_ndarray(format="bgr24")

    flipped = img[::-1,:,:]

    return av.VideoFrame.from_ndarray(flipped, format="bgr24")


webrtc_streamer(key="example", video_frame_callback=video_frame_callback)

Now the video is vertically flipped. Vertically flipping example

As 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). The input and output frames are the instance of av.VideoFrame (or av.AudioFrame when dealing with audio) of PyAV library.

You can inject any kinds of image (or audio) processing inside the callback. See examples above for more applications.

Pass parameters to the callback

You can also pass parameters to the callback.

In the example below, a boolean flip flag is used to turn on/off the image flipping.

import streamlit as st
from streamlit_webrtc import webrtc_streamer
import av


flip = st.checkbox("Flip")


def video_frame_callback(frame):
    img = frame.to_ndarray(format="bgr24")

    flipped = img[::-1,:,:] if flip else img

    return av.VideoFrame.from_ndarray(flipped, format="bgr24")


webrtc_streamer(key="example", video_frame_callback=video_frame_callback)

Pull values from the callback

Sometimes we want to read the values generated in the callback from the outer scope.

Note 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).

  • Thread-safety
    • Passing the values between inside and outside the callback must be thread-safe.
  • Using a loop to poll the values
    • 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.

The 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) is done on the image frames.

threading.Lock is one standard way to control variable accesses across threads. A 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.

import threading

import cv2
import streamlit as st
from matplotlib import pyplot as plt

from streamlit_webrtc import webrtc_streamer

lock = threading.Lock()
img_container = {"img": None}


def video_frame_callback(frame):
    img = frame.to_ndarray(format="bgr24")
    with lock:
        img_container["img"] = img

    return frame


ctx = webrtc_streamer(key="example", video_frame_callback=video_frame_callback)

fig_place = st.empty()
fig, ax = plt.subplots(1, 1)

while ctx.state.playing:
    with lock:
        img = img_container["img"]
    if img is None:
        continue
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    ax.cla()
    ax.hist(gray.ravel(), 256, [0, 256])
    fig_place.pyplot(fig)

Callback limitations

The callbacks are executed in forked threads different from the main one, so there are some limitations:

  • Streamlit methods (st.* such as st.write()) do not work inside the callbacks.
  • Variables inside the callbacks cannot be directly referred to from the outside.
  • The global keyword does not work expectedly in the callbacks.
  • 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.

Class-based callbacks

Until v0.37, the class-based callbacks were the standard. See the old version of the README about it.

Serving from remote host

When deploying apps to remote servers, there are some things you need to be aware of. In short,

  • HTTPS is required to access local media devices.
  • STUN/TURN servers are required to establish the media stream connection.

See the following sections.

HTTPS

streamlit-webrtc uses getUserMedia() API to access local media devices, and this method does not work in an insecure context.

This document says

A secure context is, in short, a page loaded using HTTPS or the file:/// URL scheme, or a page loaded from localhost.

So, when hosting your app on a remote server, it must be served via HTTPS if your app is using webcam or microphone. If not, you will encounter an error when starting using the device. For example, it's something like below on Chrome.

Error: navigator.mediaDevices is undefined. It seems the current document is not loaded securely.

Streamlit Community 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.

For the development purpose, sometimes suyashkumar/ssl-proxy is a convenient tool to serve your app via HTTPS.

$ streamlit run your_app.py  # Assume your app is running on http://localhost:8501
# Then, after downloading the binary from the GitHub page above to ./ssl-proxy,
$ ./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
# Then access https://localhost:8000

Configure the STUN server

To deploy the app to the cloud, we have to configure the STUN server via the rtc_configuration argument on webrtc_streamer() like below.

webrtc_streamer(
    # ...
    rtc_configuration={  # Add this config
        "iceServers": [{"urls": ["stun:stun.l.google.com:19302"]}]
    }
    # ...
)

This configuration is necessary to establish the media streaming connection when the server is on a remote host.

⚠️ You may need to set up a TURN server as well in some environments, including Streamlit Community Cloud. See also the next section.

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. As 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, 2, 3).

The example above is configured to use stun.l.google.com:19302, which is a free STUN server provided by Google.

You can also use any other STUN servers. For example, one user reported 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.

For those who know about the browser WebRTC API: The value of the rtc_configuration argument will be passed to the RTCPeerConnection constructor on the frontend.

Configure the TURN server if necessary

Even if the STUN server is properly configured, media streaming may not work in some network environments, either from the server or from the client. For 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 summarizes the possible situations.

In such environments, TURN server is required.

There are several options for setting up a TURN server:

  • Twilio Network Traversal Service (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. You can simply pass the ice_servers field of the Network Traversal Service Tokens API response to the iceServers field of the rtc_configuration argument of webrtc_streamer().
    ## This sample code is from https://www.twilio.com/docs/stun-turn/api
    # Download the helper library from https://www.twilio.com/docs/python/install
    import os
    from twilio.rest import Client
    
    # Find your Account SID and Auth Token at twilio.com/console
    # and set the environment variables. See http://twil.io/secure
    account_sid = os.environ['TWILIO_ACCOUNT_SID']
    auth_token = os.environ['TWILIO_AUTH_TOKEN']
    client = Client(account_sid, auth_token)
    
    token = client.tokens.create()
    
    # Then, pass the ICE server information to webrtc_streamer().
    webrtc_streamer(
      # ...
      rtc_configuration={
          "iceServers": token.ice_servers
      }
      # ...
    )
    The WebRTC sample app hosted on the Community Cloud uses this option. See how it retrieves the ICE server information from the Twilio API and how to use it in the app.
  • The Open Relay Project provides a free TURN server. However, it does not seem to be stable enough and is often down.
  • A self-hosted TURN server is also an option. See #335 (comment).

Logging

For logging, this library uses the standard logging module and follows the practice described in the official logging tutorial. Then the logger names are the same as the module names - streamlit_webrtc or streamlit_webrtc.*.

So you can get the logger instance with logging.getLogger("streamlit_webrtc") through which you can control the logs from this library.

For example, if you want to set the log level on this library's logger as WARNING, you can use the following code.

st_webrtc_logger = logging.getLogger("streamlit_webrtc")
st_webrtc_logger.setLevel(logging.WARNING)

In 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. You can do it in the same way as below.

aioice_logger = logging.getLogger("aioice")
aioice_logger.setLevel(logging.WARNING)

API changes

Currently there is no documentation about the interface. See the examples in ./pages/*.py for the usage. The API is not finalized yet and can be changed without backward compatibility in the future releases until v1.0.

For users since versions <0.20

VideoTransformerBase and its transform method have been marked as deprecated in v0.20.0. Please use VideoProcessorBase#recv() instead. Note 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.

Also, webrtc_streamer()'s video_transformer_factory and async_transform arguments are deprecated, so use video_processor_factory and async_processing respectively.

See the samples in app.py for their usage.

Resources

Support the project

ko-fi

Buy Me A Coffee

GitHub Sponsors

More Repositories

1

stlite

In-browser Streamlit 🎈🚀
TypeScript
1,138
star
2

vscode-emacs-mcx

Awesome Emacs Keymap - VSCode emacs keybinding with multi cursor support
TypeScript
367
star
3

streamlit-stt-app

Real time web based Speech-to-Text app with Streamlit
Python
213
star
4

streamlit-webrtc-example

Real time video and audio processing examples with Streamlit and streamlit-webrtc
Python
144
star
5

streamlit-server-state

A "server-wide" state object shared across sessions on a Streamlit server.
Python
129
star
6

lear-gist-python

A python wrapper for Lear's GIST implementation working with numpy
C
55
star
7

transformers.js.py

TypeScript
55
star
8

streamlit-component-template-react-hooks

Streamlit component lib with React hooks and template project using it
TypeScript
52
star
9

react-ymd-date-select

Hooks and components for Y-M-D dropdowns with React
TypeScript
45
star
10

streamlit-video-chat-example

Video chat apps with computer vision filters built on top of Streamlit
Python
42
star
11

stlite-image-processing-app

Serverless OpenCV image manipulation app with Streamlit
HTML
31
star
12

streamlit-fesion

TypeScript
18
star
13

streamlit-webrtc-article-tutorial-sample

Python
16
star
14

ReactCrossDeviceTodoExample

Sample app running on iOS / android / web (+ electron) using React, React native, Redux
JavaScript
15
star
15

stlite-desktop-example

Python
12
star
16

streamlit-theme-editor

Python
11
star
17

fastapi-typescript-openapi-example

TypeScript
10
star
18

tiny-streamlit-webrtc

Python
7
star
19

react-redux-pouch-example

Sample project using react, redux, react-router and pouchdb
JavaScript
7
star
20

quarto-stlite

Lua
5
star
21

randomstring-promise

Random string generator with promise interface for nodejs, browsers, and react-native
JavaScript
4
star
22

text-classification-app-example

Text classification server written in python (Flask, scikit-learn) and its interface using Vue.js
Jupyter Notebook
3
star
23

fuel-kana

日本語のフリガナをHTMLで表現するための<ruby>タグを出力するヘルパー
PHP
3
star
24

whitphx.info

TypeScript
2
star
25

risk-assessment-long-term-investment

Python
2
star
26

stlite-sample

Python
2
star
27

streamlit-appengine-samples

Dockerfile
2
star
28

europython2022-streamlit-webrtc-example

Python
2
star
29

europython2022-streamlit-webrtc

2
star
30

pycon2024-streamlit

1
star
31

stlite-desktop

1
star
32

streamlit-webrtc-hugging-face-transformer-example

Python
1
star
33

csv-to-md

CSV to Markdown converter https://tuttieee.github.io/csv-to-md/
JavaScript
1
star
34

kms7-decoder-bug-example

TypeScript
1
star
35

gstreamer-python-sample

Python
1
star
36

asha-nepal

CSS
1
star
37

europython2022-streamlit-webrtc-example-predeploy

Python
1
star