diff --git a/README.md b/README.md index 4f6c7e0..24a0560 100644 --- a/README.md +++ b/README.md @@ -1,72 +1,86 @@ # RPi2RTPMserver -Some bash script to create a streaming radio to broadcast some music to YouTube or other RTMP server with a looping video and the song's cover. - -Those files use some tools to make that possible. You have the list with all the tools and their websites in the **Prerequisites** section. - -This project need a lot of optimizations and rework to make it easier to configure. Also, there are a lot of features we can add to the project. -# Prerequisites -- From repositories - - [Git](https://git-scm.com/) - - [FFmpeg](https://www.ffmpeg.org/) - - [Mplayer](http://www.mplayerhq.hu/) - - [VLC](https://www.videolan.org/) - - [Exiftool](https://exiftool.org/) - - [ImageMagik](https://imagemagick.org/index.php) - - [jq](https://stedolan.github.io/jq/) - - [nkf](http://nkf.osdn.jp/) - - [PulseAudio](https://gitlab.freedesktop.org/pulseaudio/pulseaudio) -- Python - - [sacad](https://github.com/desbma/sacad) -- GitHub - - [tweet.sh](https://github.com/piroor/tweet.sh) -## Installing prerequisites from repositories -``` -sudo apt update && sudo apt install git ffmpeg mplayer vlc python3-pip libimage-exiftool-perl jq nkf pavucontrol -y -``` -## Installing sacad -``` -sudo pip3 install sacad -``` -## Cloning tweet.sh -``` -git clone https://github.com/piroor/tweet.sh -``` -After cloning that repository, you have to copy **tweet.sh** and **tweet.client.key** to the same folder of **play_audio.sh**. -You can get instructions about how to configure tweet.sh in its [GitHub](https://github.com/piroor/tweet.sh). -## Configuring PulseAudio -After You Install everything, You have to configure PulseAudio to work properly in Raspbian, so FFmpeg can get the audio from the sound card. To improve performance You have to add a parameter in **/etc/pulse/default.pa** as following. First You have to open the file with a text editor, like **nano** +Some bash script to create a streaming radio to broadcast some music to YouTube or other RTMP server with a looping video and, song's cover and information. + +Those files use some tools to make that possible. You have the list with all the tools and their websites in the **prerequisites** section. + +- [RPi2RTPMserver](#rpi2rtpmserver) + - [Prerequisites (Step 1)](#prerequisites-step-1) + - [Configuring PulseAudio](#configuring-pulseaudio) + - [Playing songs (Step 2)](#playing-songs-step-2) + - [Running video streamer (Step 3)](#running-video-streamer-step-3) + +## Prerequisites (Step 1) + +- **ffmpeg**: process the whole stream +- **vlc**: VLC media player +- **mplayer**: plays audio files +- **python3-pip**: needed to install sacad +- **sacad**: gets album cover +- **exiftool**: gets song information +- **jq**: tranforms json files +- **nkf**: network kanji filter +- **Pulse Audio**: needed to get a better performance +- **imagemagick**: converts and resizes album covers + +To install all dependencies just execute `install_deps.sh` as super user. + +```bash +sudo chmod 755 *.sh +sudo ./install_deps.sh ``` + +### Configuring PulseAudio + +After You Install everything, You have to configure PulseAudio to work properly in Raspbian, so ffmpeg can get the audio from the sound card. To improve performance You have to add a parameter in **/etc/pulse/default.pa** as following. First You have to open the file with a text editor, like **nano** + +```bash sudo nano /etc/pulse/default.pa ``` + Then You have to find the following lines, You can use **Ctrl+W** to search those lines: -``` + +```bash ### Automatically load driver modules depending on the hardware available .ifexists module-udev-detect.so load-module module-udev-detect ``` + And add **tsched=0** in the third line, having this as result: -``` + +```bash ### Automatically load driver modules depending on the hardware available .ifexists module-udev-detect.so load-module module-udev-detect tsched=0 ``` + After that, You should reboot your Raspberry Pi to make sure everything works as expected. -# Playing songs + +## Playing songs (Step 2) + Once You reboot your Raspberry Pi, You can use **play_audio.sh** to start playing songs in the Raspberry Pi. You can define where the songs are in the variable **FOLDER**. The script will look for **mp3** files on that folder and play them with **Mplayer** using a normalizer filter to get always the same volume between files. -# Running video streamer -Once You have the player working, It's time to start the video streamer and send the video to your RTMP server. By default, It's configured to send that video to YouTube, but You can change the server URL to stream to other services like Facebook Live, Twitch or Mixer. - -First of all, check all the configuration variables defined in the file **stream.sh** and then, run that using the command **./stream.sh**. You can modify the file **marquee.txt** to change the marquee text in the stream. -# ToDo things -- [ ] Set cover position depending on video dimensions -- [ ] Better file randomizer -# YouTube ended LiveStream test + +```bash +sudo ./play_audio.sh +``` + +Yo can also change the cover size specified on **COVER_SIZE**. In addition, if you do not want to download the cover from the internet you just have to configure **INTERNET_COVER** as ``0``, otherwise ``1``. + +## Running video streamer (Step 3) + +Once You have the player working, It's time to start the video streamer and send the video to your RTMP server especified on **RTMP_URL**. By default, It's configured to send that video to YouTube, but You can change the server URL to stream to other services like Facebook Live or Twitch. + +Do not forget to change the **KEY** with your own information. There are other variables you should know defined in the file **stream.sh** and then, run that using the command **./stream.sh**. You can modify the file **marquee.txt** to change the marquee text in the stream. + +```bash +sudo ./stream.sh +``` + + diff --git a/default.png b/default.png index 12e6851..ab24977 100644 Binary files a/default.png and b/default.png differ diff --git a/install_deps.sh b/install_deps.sh new file mode 100644 index 0000000..c884388 --- /dev/null +++ b/install_deps.sh @@ -0,0 +1,25 @@ +#! /bin/bash + +# This script installs all dependencies for the project +# It is run by the install script +# It is run as superuser +# Works on debian based systems + +echo "Installing dependencies..." +sudo apt-get update +sudo apt update && \ +sudo apt install \ +ffmpeg \ +mplayer \ +vlc \ +python3-pip \ +libimage-exiftool-perl \ +jq \ +nkf \ +pavucontrol \ +imagemagick -y + +echo "Installing pip packages..." +sudo pip3 install sacad + +echo "Done!" diff --git a/marquee.txt b/marquee.txt index bedb376..008f82d 100644 --- a/marquee.txt +++ b/marquee.txt @@ -1 +1 @@ -Streaming with RPi2RTMPserver +Enter your stream description... \ No newline at end of file diff --git a/mp3/Jon Worthy and the Bends - You Afraid.mp3 b/mp3/Jon Worthy and the Bends - You Afraid.mp3 deleted file mode 100644 index c03053c..0000000 Binary files a/mp3/Jon Worthy and the Bends - You Afraid.mp3 and /dev/null differ diff --git a/mp3/Mild Wild - Line Spacing.mp3 b/mp3/Mild Wild - Line Spacing.mp3 deleted file mode 100644 index 2b6f6d8..0000000 Binary files a/mp3/Mild Wild - Line Spacing.mp3 and /dev/null differ diff --git a/mp3/Robert John - Slinky.mp3 b/mp3/Robert John - Slinky.mp3 deleted file mode 100644 index 92d2358..0000000 Binary files a/mp3/Robert John - Slinky.mp3 and /dev/null differ diff --git a/mp3/purrple-cat-smores.mp3 b/mp3/purrple-cat-smores.mp3 new file mode 100644 index 0000000..b0247ed Binary files /dev/null and b/mp3/purrple-cat-smores.mp3 differ diff --git a/play_audio.sh b/play_audio.sh index 91dbd3f..ba398da 100644 --- a/play_audio.sh +++ b/play_audio.sh @@ -1,92 +1,58 @@ #!/bin/bash -# Configuration -FOLDER=mp3 # Folder with songs +FOLDER=mp3 # Folder with songs +COVER_SIZE="200" # Song cover size +INTERNET_COVER=0 # If cover doesn't exist download it form the internet [1|0] -# Choosing first song - - # Deleting old cover - rm cover.png - - # Looking for song in the folder - mapfile -t my_array < <(find $FOLDER -iname "*.mp3") - - # Choosing song - SONG=${my_array[ RANDOM % ${#my_array[@]}]} - - # Saving sond data to print later on the txt - ARTIST=$(exiftool "$SONG" | grep "Artist " | cut -f2 -d':') - ALBUM=$(exiftool "$SONG" | grep "Album " | cut -f2 -d':') - TITLE=$(exiftool "$SONG" | grep "Title " | cut -f2 -d':') - - # Extracting the cover from the mp3 - ffmpeg -i "$SONG" cover.jpg - if [ -f "cover.jpg" ]; then - convert cover.jpg -resize 120x120\> cover.png - rm cover.jpg - fi - - # If the mp3 doesn't have a cover we look for it on the internet - if [ ! -f "cover.png" ]; then - sacad "$ARTIST" "$ALBUM" 120 cover.png - fi - -# Infinite loop to play songs -echo "Starting next song" -while [ 1 ] +while : do - - # Saving song cover in a file which will be read with ffmpeg - # default.png will be a file to use if we dont have any cover downloaded - if [ ! -f "cover.png" ]; then - cp default.png image.png - else - cp cover.png image.png - fi - - # Changing song title in the video - echo $ARTIST > livetext.txt - echo $ALBUM >> livetext.txt - echo $TITLE >> livetext.txt - - # Tweeting what we're playing - ./tweet.sh post "NOW PLAYING\n$TITLE\nARTIST: $ARTIST\nALBUM: $ALBUM" & - - # Playing file - echo "Playing $SONG" - # ffplay -nodisp -autoexit -af dynaudnorm $SONG & - # cvlc --play-and-exit $SONG & - mplayer -af volnorm=2:0.05 "$SONG" & - - # Deleting old cover - rm "cover.png" - - # Lookig for next song in the folder - mapfile -t my_array < <(find $FOLDER -iname "*.mp3") - - # Choosing next song - SONG=${my_array[ RANDOM % ${#my_array[@]}]} - - # Saving next song data in variables for later use - ARTIST=$(exiftool "$SONG" | grep "Artist " | cut -f2 -d':') - ALBUM=$(exiftool "$SONG" | grep "Album " | cut -f2 -d':') - TITLE=$(exiftool "$SONG" | grep "Title " | cut -f2 -d':') - - # Taking next song's cover from file - ffmpeg -i "$SONG" cover.jpg - if [ -f "cover.jpg" ]; then - convert cover.jpg -resize 120x120\> cover.png - rm cover.jpg - fi - - # If next song don't have a cover we will look for it on the internet - if [ ! -f "cover.png" ]; then - sacad "$ARTIST" "$ALBUM" 120 cover.png - fi - - # We wait until the song ends - wait - echo "\n$SONG finished playing" - -done - + echo "Deleting old cover "; rm "cover.png" + + echo "Lookig for next song in the folder" + mapfile -t my_array < <(find $FOLDER -iname "*.mp3") + + echo "Choosing next song randomly" + SONG=${my_array[ RANDOM % ${#my_array[@]}]} + + # Saving next song data in variables for later use + ARTIST=$(exiftool "$SONG" | grep "Artist " | cut -f2 -d':') + ALBUM=$(exiftool "$SONG" | grep "Album " | cut -f2 -d':') + TITLE=$(exiftool "$SONG" | grep "Title " | cut -f2 -d':') + + # Changing song info in the video + echo $ARTIST > livetext.txt + echo $ALBUM >> livetext.txt + echo $TITLE >> livetext.txt + + echo "Playing $SONG" + # ffplay -nodisp -autoexit -af dynaudnorm $SONG & + # cvlc --play-and-exit $SONG & + + # If you are using ALSA: -ao alsa:device=hw=1 + mplayer -af volnorm=2:0.05 "$SONG" & + + # Taking next song's cover from file + # Remove -hide_banner -loglevel error if you want the whole ffmpeg feedback + ffmpeg -hide_banner -loglevel error -i "$SONG" cover.jpg + if [ -f "cover.jpg" ]; then + convert cover.jpg -resize "$COVER_SIZEx$COVER_SIZE"\> cover.png + rm cover.jpg + fi + + # If next song don't have a cover we will look for it on the internet + if [[ ! -f "cover.png" ]] && [[ $INTERNET_COVER -eq 1 ]]; then + sacad "$ARTIST" "$ALBUM" "$COVER_SIZE" cover.png + fi + + # default.png will be a file to use if we dont have any cover downloaded + if [ ! -f "cover.png" ]; then + cp default.png image.png + else + cp cover.png image.png + fi + + # We wait until the song ends + wait + echo "\n$SONG finished" + +done diff --git a/stream.sh b/stream.sh index b1a76cd..d4f7551 100644 --- a/stream.sh +++ b/stream.sh @@ -1,44 +1,61 @@ #! /bin/bash # Configuration of ffmpeg -VBR="3000k" # Video bitrate -FPS="30" # Video FPS +VBR="1000k" # Video bitrate (480p -> 1000k, 720p -> 3000k) +FPS="24" # Video FPS up to 30 QUAL="ultrafast" # FFMPEG preset quality -RESOLUTION="848x480" # Video resolution -YOUTUBE_URL="rtmp://a.rtmp.youtube.com/live2" # RTMP server URL +RESOLUTION="848x480" # Video resolution (848x480, 1280x720) + +RTMP_URL="rtmp://a.rtmp.youtube.com/live2" # RTMP server URL +KEY="xxxx-xxxx-xxxx-xxxx" # Stream key + VIDEO="video.mp4" # Video path -IMAGE="image.png" # Cover path -TEXT="livetext.txt" # Path to the TXT where the song data is -MARQUEE="marquee.txt" # Path to the TXT with a marquee text -FONT="FreeSerif.ttf" # Path to the font -FONT_SIZE="20" # Font size -THREADS="4" # Number of threadsusing to encode video and audio -THREAD_QUEUE="2048" # Thread queue size for audio -CRF="20" # Constant rate factor of the video -KEY="xxxx-xxxx-xxxx-xxxx" # Stream key - -# infinite loop + +# Use parentheses to use Structural SImilarity Metric +# more info at https://ffmpeg.org/ffmpeg-all.html#ssim +# ex_image: y=(100) +# ex_text: x=(250) + +IMAGE="image.png" # Cover path +IMAGE_X="50" # X position of cover +IMAGE_Y="(100)" # Y position of cover + +TEXT="livetext.txt" # Path to the TXT where the song data is +TEXT_X="(350)" # X position of the song data +TEXT_Y="200" # Y position of the song data + +MARQUEE="marquee.txt" # Path to the TXT with a marquee text +MARQUEE_Y="0.1" # Y position of the marquee text (0.01 - 0.9) + +FONT="FreeSerif.ttf" # Path to the font +FONT_SIZE="20" # Font size + +THREADS="4" # Number of threads used to encode video and audio +THREAD_QUEUE="2048" # Thread queue size for audio +CRF="20" # Constant rate factor of the video + +# Infinite loop while [ 1 ] do -ffmpeg \ - -stream_loop -1 -i $VIDEO \ - -thread_queue_size $THREAD_QUEUE -f pulse -i default \ - -f lavfi -re -i anullsrc \ - -f image2 -stream_loop -1 -r 2 -i $IMAGE \ - -filter_complex \ + # change pulse for alsa is you are using it (Not recommended) + ffmpeg \ + -stream_loop -1 -i $VIDEO \ + -thread_queue_size $THREAD_QUEUE -f pulse -i default \ + -f lavfi -re -i anullsrc \ + -f image2 -stream_loop -1 -r 2 -i $IMAGE \ + -filter_complex \ "[1:a][2:a] amix=inputs=2,volume=2.5 [audiooutput]; \ - [0:v] overlay=x=25:y=(250),fps=fps=$FPS,scale=$RESOLUTION [inputvideo]; \ + [0:v] overlay=x=$IMAGE_X:y=$IMAGE_Y,fps=fps=$FPS,scale=$RESOLUTION [inputvideo]; \ [inputvideo] \ - drawtext=textfile=$MARQUEE:fontfile=$FONT:fontsize=(w * 0.03333333333333333):bordercolor=#000000:borderw=1:fontcolor=#FFFFFF:reload=1:y=(h * 0.05):x=w-mod(max(t\, 0) * (w + tw) / 20\, (w + tw)), \ - drawtext=textfile=$TEXT:fontfile=$FONT:fontsize=(w * 0.03333333333333333):bordercolor=#000000:borderw=1:fontcolor=#FFFFFF:reload=1:y=(h-text_h-25):x=(170) \ - [videooutput]" \ - -acodec copy -acodec copy \ - -map [videooutput] -map [audiooutput] \ - -r $FPS -g $(($FPS*2)) \ - -pix_fmt yuv420p -x264-params keyint=$(($FPS*2)):min-keyint=$(($FPS*2)):scenecut=-1 \ - -s $RESOLUTION -b:v $VBR -b:a 128k -ar 44100 -acodec aac \ - -vcodec libx264 -preset $QUAL -bufsize $VBR -crf $CRF -threads $THREADS \ - -f flv "$YOUTUBE_URL/$KEY" - + drawtext=textfile=$MARQUEE:fontfile=$FONT:fontsize=(w * 0.03333333333333333):bordercolor=#000000:borderw=1:fontcolor=#FFFFFF:reload=1:y=(h * $MARQUEE_Y):x=w-mod(max(t\, 0) * (w + tw) / 20\, (w + tw)), \ + drawtext=textfile=$TEXT:fontfile=$FONT:fontsize=(w * 0.03333333333333333):bordercolor=#000000:borderw=1:fontcolor=#FFFFFF:reload=1:y=(h-text_h-$TEXT_Y):x=$TEXT_X \ + [videooutput]" \ + -acodec copy -acodec copy \ + -map [videooutput] -map [audiooutput] \ + -r $FPS -g $(($FPS*2)) \ + -pix_fmt yuv420p -x264-params keyint=$(($FPS*2)):min-keyint=$(($FPS*2)):scenecut=-1 \ + -s $RESOLUTION -b:v $VBR -b:a 128k -ar 44100 -acodec aac \ + -vcodec libx264 -preset $QUAL -bufsize $VBR -crf $CRF -threads $THREADS \ + -f flv "$RTMP_URL/$KEY" + done -