(because Hackster.io change how the sourcecode is displayed I recommend to check it here : https://remoteme.org/youtube-srteaming/ )
IntroductionI've used platform RemoteMe (I'm the author of this platform ) it's hosting your files for free, and helps to communicate with ESP. So first please create an account at : https://app.remoteMe.org.
In this project I will show how to stream an video from a USB camera connected to Raspberry Pi to YouTube. Streaming will not be continuous – the camera will be turned on and streamed to YouTube only when someone opens a page with a YouTube window.
What will be neededRaspberry Pi – I use RPI4 with 4GB Ramu, I also tested on RPI3 (below screen with Rpi4 htop
when streaming video with a resolution of 1280x720
as you can see processor cores are used in 36% at one core)
- Heatsink for Raspberry Pi CPU – streaming can make Raspberry Pi hot, therefore heatsink is needed, I use Flirc Raspberry Pi 4 Case (Silver)
- USB camera – I use the A4Tech PK-910H model – before buying the camera, please check if it will work with Rasbian
- it is an aluminum box that also acts as a heat sink. When using it, the processor temperature is around 45C at an outside temperature of 24C. More about this box on the Mr. Andreas Spiess channel, the only caveat I have for this box is that there is no output for the belt from the camera connected directly to the RPI board, and to output PINS with a 40-pin belt you need to saw the sides of the tape plug
To start stream the following command will be run:
ffmpeg -threads 0 -f v4l2 -i /dev/video0 -ar 44100 -ac 2 -acodec pcm_s16le -f s16le -ac 2 -i /dev/zero -acodec aac -ab 128k -strict experimental -s 1280x720 -b 6000000 -aspect 16:9 -vcodec h264_omx -vb 820k -pix_fmt yuv420p -g 60 -r 30 -f flv rtmp://a.rtmp.youtube.com/live2/SECRET_KEY
of course you must install ffmpeg
before if you don’t have one(sudo apt-get install ffmpeg )
the script will be run when our website with a YouTube frame will be displayed by at least one person, when the page is closed and not re-opened within 10 seconds, the script will be ‘killed’ the webcam will turn off and will not stream to YouTube ‘ anymore.
Because we will actually create a new stream each time, iframe with the stream address must point not to a specific stream but to the active stream of the given user, i.e.
https://www.youtube.com/embed/live_stream?channel=CHANNEL_ID&autoplay=1
Because of that our streams has to be public
Let’s do this :)in fact, it takes a few clicks to start a project – because this project is already in the quick projects
tab(
https://app.remoteme.org/en/#/app/quickstart
)
click “Read More or Build It” on the project:
then in Build It
tab:
we filled YouTube stream name / key, which we download from YouTube at
it will have a form similar to: 7awp-w65c-zbas-61c9 -
however, you can enter anything and then complete it with the correct secret key in the file youtuberun.sh
When we have not streamed anything before, we need to activate the service – activation takes up to 24h only then we will be able to start streaming
Channel id is taken from the address https://www.youtube.com/account_advanced
in my case it’s ZCPuP7AKof_poZY664N4yJCA
after filled
then click Next step – choose or add Raspberry Pi (in case of problems please refer to the article about Raspberry Pi in RemoteMe I python devcies in RemoteMe ), then next step
and the green “build the project
” button and after successfully creating the project we close the window.
A page has been created, a python script has been added:
we can open the page to see if everything works:
after opening we will see the page where after a while the image from streaming will appear.
How exactly it works (and what to do if the image is not displayed)Let’s start by discussing the files on your device python script
:
1) youtuberun.sh – after open we will see ffmpeg command:
ffmpeg -threads 0 -f v4l2 -i /dev/video0 -ar 44100 -ac 2 -acodec pcm_s16le -f s16le -ac 2 -i /dev/zero -acodec aac -ab 128k -strict experimental -s 1280x720 -b 6000000 -aspect 16:9 -vcodec h264_omx -vb 820k -pix_fmt yuv420p -g 60 -r 30 -f flv rtmp://a.rtmp.youtube.com/live2/SECRET_KEY
if the image is not streaming to YouTube, I recommend run this command directly in the console and check the logs, it may turn out that we do not have ffmpeg
installed (then install by calling sudo apt-get install ffmpeg
) or our camera is at a different address than / dev/video0
(then edit the file and save), in this file we can also change the parameters of our stream sent to YouTube – a preview of the stream can be found at: https://www.youtube.com/live_dashboard
2) python.py is more complicated – to understand how it works, remember that our website has id 2:
and that the value of the youtube_state
variable (read about variables in RemoteMe) keeps the current state of YouTube stream and it is:
-1:Youtube FAIL
0:Youtube OFF
1: Youtube ON
2:Youtube STARTING
3:Youtube CLOSING
I will now discuss individual parts of the file code python.py
def onDeviceConnectionChange(deviceId,state):
global receiveDeviceConnected
if deviceId == 2:
if state:
logger.info("device receive is now ON ")
else:
logger.info("device receive is now OFF transmission will end in 10s ")
receiveDeviceConnected = state
# in initialization
remoteMe.subscribeDeviceConnectionChangeEvent(onDeviceConnectionChange)
because we want to enable streaming when our website with deviceId
= 2 connects – we set the function onDeviceConnectionChange
will be called as any device changes the connection status with the RemoteMe platform – and in the function itself we only filter status changes for the device with id 2 (because it is the deviceId of our website), then the current state is saved to the receiveDeviceConnected
global variable
variable state readout takes place in a separate thread whose code looks like this:
def youtubeThreadFunction():
global receiveDeviceConnected
global youtubeState
currentOn = False
wait=0
while True:
logger.info('receiveDeviceConnected {} currentOn {} wait {} youtube state : {}'.format(receiveDeviceConnected,currentOn,wait,youtubeState))
if not currentOn and receiveDeviceConnected:
currentOn=True
startYoutube()
wait=0
elif currentOn and not receiveDeviceConnected:
if wait > 10:
wait= 0
currentOn = False
endYoutube()
else:
wait=wait+1
elif currentOn and youtubeState == -1:
currentOn = False
endYoutube()
wait= 0
time.sleep(1)
the function has an infinite loop that checks if the device is connected and, depending on what was the previous status, starts streaming or closes streaming 10 seconds after disconnecting the website.
Streaming enables the function
def startYoutube():
global thread1,process
#thread1.stop()
onYoutube(2)
process = subprocess.Popen(['bash', 'youtuberun.sh'], stdout=subprocess.PIPE,stderr=subprocess.PIPE)
thread1 = threading.Thread(target = checkingThread)
thread1.start()
logger.info("process ID {}".format(process.pid))
which runs the youtuberun.sh
script in a separate thread, and the function:
def endYoutube():
global process
for x in range(0, 3):
subprocess.run(["pkill","-TERM","-P",format(process.pid)])
logger.info("pkill -TERM -P {}".format(process.pid))
time.sleep(0.5)
onYoutube(0)
calls the pkill
command on our thread – to end it with its “children” threads (then the webcam turns off and we don’t stream anything anymore)
in addition, both functions set youtube_state
variables by calling the function:
def onYoutube(b):
global youtubeState
youtubeState = b
remoteMe.getVariables().setInteger("youtube_state", b)
if b==1:
logger.info('Youtube ON')
elif b==0:
logger.info('Youtube OFF')
elif b==-1:
logger.info('Youtube FAIL')
elif b==2:
logger.info('Youtube STARTING')
elif b==3:
logger.info('Youtube CLOSING')
else:
logger.info('Youtube undefined')
the value of the youtube_state
variable is read by the website (discussed below) and displays the appropriate message to the user and injects the iframe code with YouTube preview.
a function worth discussing is also the function:
def checkingThread():
global process
i=0
while True:
time.sleep(0.2)
line = process.stderr.read(200)
i=i+1
if line and 'speed=' in line.decode("utf-8"):
onYoutube(1)
return
if i>100:
onYoutube(-1)
return
it checks by scanning the script output youtuberun.sh
whether the message “speed =
” appeared within 40s if so it means that our ffmpeg
is streaming correctly, if it doesn’t mean that something went wrong and the appropriate value of the youtube_state
variable is set
In addition, python.py
checks the temperature of our raspberry and sets the variable cpu_temp
which is displayed on the webpage.
here you will find a detailed description about websites is in RemoteMe, and here about RemoteMe components
and now let’s discussed script.js files because only it requires explanations:
remoteme.remoteMeConfig.deviceConnectionChange.push((deviceId, connected) => {
if (deviceId == 1) {//rpi device
clearTimeout(notConnected);
if (!connected) {
showInfoModal("Raspberry Pi Not Connected - You cannot interact ", "fas fa-unlink", "#FF0000");
} else {
showInfoModal("Raspberry Pi connected", "fab fa-raspberry-pi", "#00c900", 2);
connectYoutubeVariable();
}
}});
we check if our RPI is connected, if so we call the function
function connectYoutubeVariable() {
remoteme = RemoteMe.getInstance();
remoteme.getVariables().observeInteger("youtube_state", (state) => {
let htmlYoutubeState = "";
if (state == 0) {
htmlYoutubeState=`<i class="fab fa-youtube" style='color:black'></i> not connected`;
} else if (state == 1) {
htmlYoutubeState=`<i class="fab fa-youtube" style='color:#00c900'></i>connected`;
if (previousYoutube != 1) {
if (previousYoutube != undefined) {
showInfoModal("Youtube actived. Page will be reload in 10s", "fab fa-youtube", "#00c900");
setTimeout(() => {
location.reload(true);
}, 10000);
} else {
$("#youtubeContainer").html(`<iframe width="100%" height="100%" src="https://www.youtube.com/embed/live_stream?channel=CHANELID&autoplay=1" frameborder="0" allowfullscreen></iframe>`);
showInfoModal("Youtube actived.", "fab fa-youtube", "#00c900", 2);
}
}
} else if (state == -1) {
showInfoModal("Youtube connect fail", "fab fa-youtube", "#00c900");
htmlYoutubeState=`<i class="fab fa-youtube" style='color:#00c900'></i>connected`;
} else if (state == 2) {
showInfoModal("Youtube starting", "fab fa-youtube", "#0000FF");
htmlYoutubeState=`<i class="fab fa-youtube" style='color:#0000FF'></i>connecting`;
} else if (state == 3) {
showInfoModal("Youtube closing", "fab fa-youtube", "orange");
htmlYoutubeState=`<i class="fab fa-youtube" style='color:orange'></i>closing`;
}
previousYoutube = state;
$('#youtubeStatediv').html(htmlYoutubeState);
});
}
this feature notifies the user of notifications, and when YouTube is actively enabled it injects a YouTube frame into html:
$("#youtubeContainer").html(<iframe width="100%" height="100%" src="https://www.youtube.com/embed/live_stream?channel=CHANELID&autoplay=1" frameborder="0" allowfullscreen></iframe>
);
however, this only happens if the active transmission status is the first received – otherwise we refresh the page after 10 seconds (for unknown reasons this solution turns out to be better than simply injecting the YouTube frame) – then the first received state is the active state (set through the page before refreshing, and because Python waits 10 seconds after disconnecting the page will not be able to turn off the transmission) and we inject the frame.
Problems I noticedSometimes (I can’t determine the exact scenario) the YouTube frame shows the recorded previous broadcast – then it helps refreshing the page, or you need to delete the recorded broadcasts directly on YouTube. In the Python stopYoutube
function, the pkill
command is invoked three times – sometimes the first call does not kill the thread.
I hope that the project will prove useful, and thanks to it you will give your cams and internet connections a break :). If you have an idea to improve scripts, write on the project’s Facebook page. I remind you that there is documentation of remoteMe libraries where you will find a description of the JavaScript, Python and ESP functions that I use in my examples
Comments