Pull to refresh
0

Streaming multiple RTSP IP cameras on YouTube and/or Facebook

Reading time 8 min
Views 7.4K

As you know, YouTube doesn't have a feature for capturing an RTSP stream. Perhaps this isn't done by a chance, but on the basis of the true pragmatics. This way, people don't stream static video surveillance of their doorways on YouTube and don't ruin its channels, which, as it turned out in the pandemic, are not limitless at all. Just a reminder: there were some cases when the quality of streaming was deteriorated and restricted to 240p. By the way, there is another assumption: streams from IP cameras are the true evil for YouTube because they have a little more than zero viewers, so they can't give the platform a million ad views. Anyway, the feature doesn't exist but we would like to change this and help YouTube to make their viewers happy.

Let's say we want to take an ordinary street IP camera that sends an H.264 stream over RTSP and redirect it to YouTube. To do this, we will need to take the RTSP stream and convert it to the RTMPS stream that YouTube accepts. Why RTMPS, not RTMP? As you know, non-security protocols are dying out. HTTP is brutally persecuted, and its fate has befallen other protocols that don't have the S letter in the end - which means Secure. Facebook refused RTMP stream but fortunately left RTMPS alone. So, to convert RTSP to RTMPS, we do this in a headless way (without using the UI), i.e. on the server.

For one RTSP stream we need one YouTube account that will accept the stream but what should we do if we have not one but a lot of cameras?

Of course we can manually create several YouTube accounts, for example, to cover the infield with video surveillance. However, this is very likely to violate the Terms of Service. And what if there are 50 cameras overall, not 10? Should we create 50 accounts? And what's next? How can it be watched? In this case, the mixer can save the occasion and consolidate the cameras into one stream. Let's see how it works with 2 RTPS cameras. The resulting stream is mixer1 = rtsp1 + rtsp2. We send the mixer1 stream to YouTube. Everything works - both cameras are in the same stream. It's worth noting here that mixing is a quite intensive operation using CPU.

At the same time, since we already have an RTSP stream on the server side, we can redirect this stream to other RTMP endpoints without incurring additional CPU and memory costs. We simply withdraw traffic from the RTSP stream and replicate it on Facebook, Twitch, anywhere we want without additional RTSP capture and de-packetizing.

As a DevOps I would feel guilty if I don't script what I could script. Automation is facilitated by the presence of a REST API for controlling video capture from the camera and retransmission.

For example, by using a query:

/rtsp/startup

you can capture a video stream from an IP camera.

A query:

/rtsp/find_all

allows you to get a list of streams captured by the RTSP server.

A query to end the RTSP session is as follows:

/rtsp/terminate

You can control capture and relay of video streams either by using a simple browser and any convenient REST client, or by using the minimum number of code lines to embed the server management functionality in your web project.

Let's take a closer look at how this can be done.

A small manual on how to organize a live broadcast on YouTube and Facebook using a minimum code

For the server side, we use demo.flashphoner.com. To quickly deploy your WCS server, use this instruction or run one of the virtual instances on Amazon, DigitalOcean or in Docker.

It is assumed that you have a verified YouTube account and you have already created a broadcast in YouTube Studio, as well as a live video broadcast in your Facebook account.

For live broadcasts to work on YouTube and Facebook, you need to specify the following lines in the WCS settings file flashphoner.properties:

rtmp_transponder_stream_name_prefix= – Removes all prefixes for the relayed stream.

rtmp_transponder_full_url=true – With "true", ignores "streamName" and uses the RTMP address to relay the stream in the form in which the user specified it.

rtmp_flash_ver_subscriber=LNX 76.219.189.0 - to negotiate RTMP client versions between WCS and YouTube.

Now that all the preparatory steps have been completed, let's move on to programming. Place the minimum necessary elements in the HTML file:

Connect the scripts of the main API and JS for the live broadcast, which we will create a little later:

 <script type="text/javascript" src="../../../../flashphoner.js"></script>
 <script type="text/javascript" src="rtsp-to-rtmp-min.js"></script>

Initialize API to load the web page:

<body onload="init_page()">

Add the necessary elements and buttons - fields for entering unique stream codes for YouTube and Facebook, a button for publishing the RTSP stream, a div element for displaying the current status of the program, and a button for stopping the publication:

<input id="streamKeyYT" type="text" placeholder="YouTube Stream key"/>
<input id="streamKeyFB" type="text" placeholder="FaceBook Stream key"/>
<button id="repubBtn">Start republish</button>
<div id="republishStatus"></div>
<br>
<button id="stopBtn">Stop republish</button>

Then move on to creating JS for RTSP republication. The script is a mini REST client.

Create constants:

Constant "url" to write an address for queries REST API . Replace "demo.flashphoner.com" with your WCS address.

Constant "rtspStream" — specify the RTSP address of the stream from the IP camera. As an example, we use an RTSP stream from a virtual camera.

var url = "https://demo.flashphoner.com:8444/rest-api";
var rtspStream = "rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov"

The function "init_page()" initializes the main API when a web page is loaded. Also in this function, we assign the buttons to the called functions and call the "getStream" function, which captures the RTSP video stream from the IP camera:

function init_page() {
    Flashphoner.init({});    
    repubBtn.onclick = streamToYouTube;
    stopBtn.onclick = stopStream;
    getStream();
}

The function "getStream()" sends to WCS REST a query "/rtsp/startup" to capture the RTSP video stream whose address was written to the constant "rtspStream"

function getStream() {
    fetchUrl = url + "/rtsp/startup";
    const options = {
        method: "POST",
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify({
            "uri": rtspStream
        }),
    }
    fetch(fetchUrl, options);
    console.log("Stream Captured");
}

The function "streamToYouTube()" republishes the captured video stream to the live broadcast on YouTube:

function streamToYouTube() {
    fetchUrl = url + "/push/startup";
    const options = {
        method: "POST",
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify({
            "streamName": rtspStream,
            "rtmpUrl": "rtmp://a.rtmp.youtube.com/live2/"+document.getElementById("streamKeyYT").value
        }),
    }
    fetch(fetchUrl, options);
    streamToFB()
}

This function sends to WCS REST a call "/push/startup" with the following values in the parameters:

"streamName" - name of the stream captured from the IP camera. The stream name corresponds to its RTSP address written to the constant "rtspStream"

"rtmpUrl" - server URL + unique stream code. This data is given when creating a live broadcast in YouTube Studio. In our example, we rigidly fixed the URL in the code, you can add another field for it to your web page. The unique stream code is indicated in the field "streamKeyYT" on our web page.

The function "streamToFB" republishes the captured video stream to the live broadcast on Facebook:

function streamToFB() {
    fetchUrl = url + "/push/startup";
    const options = {
        method: "POST",
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify({
            "streamName": rtspStream,
            "rtmpUrl": "rtmps://live-api-s.facebook.com:443/rtmp/"+document.getElementById("streamKeyFB").value
        }),
    }
    fetch(fetchUrl, options);
    document.getElementById("republishStatus").textContent = "Stream republished";
}

This function also sends to WCS REST a call "/push/startup" with the following values in the parameters:

"streamName" - name of the stream captured from the IP camera. The stream name corresponds to its RTSP address written to the constant "rtspStream"

"rtmpUrl" - server URL + unique stream code. This data can be found on the Facebook Live Stream page in the Live API section. The server URL in this function is specified in the code, as well as for the publishing function on YouTube. For this time, take a unique stream code from the field "streamKeyFB" on the web page.

The function "stopStream()" sends to RTSP a query "/rtsp/terminate" which stops capturing the stream from the IP camera to WCS and accordingly stops publishing on YouTube and Facebook:

function stopStream() {
    fetchUrl = url + "/rtsp/terminate";
    const options = {
        method: "POST",
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify({
            "uri": document.getElementById("rtspLink").value
        }),
    }
    fetch(fetchUrl, options);
    document.getElementById("captureStatus").textContent = null;
    document.getElementById("republishStatus").textContent = null;
    document.getElementById("stopStatus").textContent = "Stream stopped";
}

Full HTML and JS file codes will be discussed below.

So. Save the files and try to run.

Procedure for testing

Create a live stream in YouTube Studio. Copy the unique video stream code:

Open the previously created HTML page. In the first field, enter the unique video stream code copied to YouTube:

Create a live stream in your account on Facebook. Copy the unique video stream code.

Go back to our web page, paste the copied code into the second field and click "Start republish

Now check our republication. Again, go to YouTube Studio and Facebook, wait a few seconds and get a stream preview.

To stop republication, click "Stop"

Now, as we promised, full source codes of the example:

Listing of the HTML file "rtsp-to-rtmp-min.html"

<!DOCTYPE html>
<html lang="en">
    <head>
        <script type="text/javascript" src="../../../../flashphoner.js"></script>
        <script type="text/javascript" src="rtsp-to-rtmp-min.js"></script>
    </head>
    <body onload="init_page()">
        <input id="streamKeyYT" type="text" placeholder="YouTube Stream key" /> <input id="streamKeyFB" type="text" placeholder="Facebook Stream key" /> <button id="repubBtn">Start republish</button>
        <div id="republishStatus"></div>
        <br />
        <button id="stopBtn">Stop republish</button>
    </body>
</html>

Listing of the JS file "rtsp-to-rtmp-min.js":

var url = "https://demo.flashphoner.com:8444/rest-api";
var rtspStream = "rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov"​
function init_page() {
    Flashphoner.init({});
    repubBtn.onclick = streamToYouTube;
    stopBtn.onclick = stopStream;
    getStream();
}

function getStream() {
    fetchUrl = url + "/rtsp/startup";
    const options = {
        method: "POST",
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify({
            "uri": rtspStream
        }),
    }
    fetch(fetchUrl, options);
    console.log("Stream Captured");
}

function streamToYouTube() {
    fetchUrl = url + "/push/startup";
    const options = {
        method: "POST",
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify({
            "streamName": rtspStream,
            "rtmpUrl": "rtmp://a.rtmp.youtube.com/live2/" + document.getElementById("streamKeyYT").value
        }),
    }
    fetch(fetchUrl, options);
    streamToFB()
}

function streamToFB() {
    fetchUrl = url + "/push/startup";
    const options = {
        method: "POST",
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify({
            "streamName": rtspStream,
            "rtmpUrl": "rtmps://live-api-s.facebook.com:443/rtmp/" + document.getElementById("streamKeyFB").value
        }),
    }
    fetch(fetchUrl, options);
    document.getElementById("republishStatus").textContent = "Stream republished";
}

function stopStream() {
    fetchUrl = url + "/rtsp/terminate";
    const options = {
        method: "POST",
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify({
            "uri": rtspStream
        }),
    }
    fetch(fetchUrl, options);
    document.getElementById("republishStatus").textContent = "Stream stopped";
}

Just a little code is required for the minimum implementation. Of course, for the final implementation of features, some adjustments are still needed – adding styles to a web page and various checks for data validity to the JS code. But it really works.

Have a great streaming!

Links

Demo

WCS on Amazon EC2

WCS on DigitalOcean

WCS in Docker

WebRTC video stream broadcast with conversion to RTMP --- Server functions for converting WebRTC audio video stream to RTMP

Streaming from an RTMP Live Encoder --- Server functions for converting video streams from Live Encoder to RTMP

HTML5-RTSP player for IP cams --- Server functions for playing RTSP video streams

Tags:
Hubs:
+7
Comments 4
Comments Comments 4

Articles

Information

Website
flashphoner.com
Registered
Founded
2010
Employees
2–10 employees
Location
Россия