i provide streaming audio and video solutions and hosting to a small community of like minded individuals. a single shoutcast server or more advanced broadcasting solutions utilizing custom scripting and tools for seamless automation and instant syndication of your media (live or archived) to sites such as youtube, justin.tv, twitch.tv, ustream.tv, soundcloud, amazon, and more. use soundcloud, youtube, or mixcloud tracks and playlists as sources for streams.

#!/bin/bash

echo "
######################################################################################
##                                   ffviz v1.0                                     ##
######################################################################################
## usage: Configure the options below, save and execute                             ##
##                                                                                  ##
## $ ./ffviz.sh input output                                                        ##
##                                                                                  ##
## input should be the audio to analyze for visual reactions                        ##
## output should be a path to a local file, or a remote livestream server           ##
##                                                                                  ##
## populate playlist.txt with a list of video files to randomy choose from          ##
## use the full path                                                                ##
##                                                                                  ##
## here is an example using shoutcast/icecast as the audio source (remote)          ##
## and streaming the video out via rtmp to a livestream provider                    ##
##                                                                                  ##
## $ ./ffviz.sh http://127.0.0.1:8000/path rtmp://live.youtube.com/app/streamkey.   ##
##                                                                                  ##
## here is an example using local input and output files                            ##
##                                                                                  ##
## $ ./ffviz.sh /full/path/to/ffviz/audioinput.mp3 /full/path/to/ffviz/output.mp4   ##
##                                                                                  ##
########### script pre-requisits: ffmpeg, mediainfo, randomize-lines, bc  ############
##                                                                                  ##
##        examples: https://www.dsfm.tv | https://www.youtube.com/dubstepfm.        ##
##                                                                                  ##
##                   https://www.github.com/dlnetworks/ffviz                        ##
######################################################################################
";

#########################
## START CONFIGURATION ##
#########################

# path to video playlist.txt (selects randomly)
playlist="/full/path/to/ffviz/playlist.txt";

# output frame size
outsize="1920x1080";

# frames per second
fps="60";

# video bitrate
bv="20M";

# audio bitrate
ba="256k";

# video codec
cv="h264";

# audio codec
ca="libfdk_aac";

# output container format
fmt="flv";

# base frequency (Hz)
bfreq="20";

# end frequency (Hz)
efreq="1420";

#######################
## END CONFIGURATION ##
#######################

randfile="$(cat "$playlist" | rl | head -1)";
duration="$(mediainfo --Inform="Video;%Duration/String3%" $randfile)";
viz="volume=2,showcqt=s=1920x144:text=0:r=$fps:axis=0:basefreq=$bfreq:endfreq=$efreq:count=15:sono_g=4:bar_g=4:bar_v=35:sono_h=144:sono_v=bar_v*a_weighting(f):tc=0.1,rotate=1200*sin(200*PI/200*t):ow=24:oh=24:c=none,scale=$outsize,setsar=1/1[viz];[1:v]scale=$outsize,setsar=1/1[vid1];[viz][vid1]blend=all_mode=heat:shortest=1:repeatlast=0,hue="H="2*PI*t/420""";
enc="-s $outsize -c:a $ca -b:a $ba -c:v $cv -preset ultrafast -b:v $bv -profile:v high -level 4.2 -g "$(bc <<< $fps*2)" -bf 2 -x264opts keyint="$(bc <<< $fps*2)":min-keyint="$(bc <<< $fps*2)":8x8dct=1 -pix_fmt yuv420p -r $fps";
ffmpeg -hide_banner -i "$1" -r $fps -i "$randfile" -filter_complex $viz $enc -t $duration -shortest -f $fmt "$2";

https://github.com/dlnetworks/multi-server-shoutcast-listener-stats-tcl-for-eggdrop

tcl script for eggdrop irc bots with multi server totals, bandwidth totals, and record/peak tracking.

triggers: .stat .record

output: bitrate – current/max/bandwidth

Radio Name Stats: (24 Kbps – 0/100/0.00 Mbps) (64 Kbps – 2/100/0.12 Mbps) (128 Kbps – 8/200/1.00 Mbps) (256 Kbps – 52/400/13.00 Mbps) (Total – 62/800/14.12 Mbps)

## original script by Niels Roosen (niels at okkernoot dot net)
## modified for dnas v2 and other fixes by dlnetworks.net

## Set some configuration options

## Shoutcast configuration
  ## Stream servers, format: { host_or_ip port stream_id }
  set shoutcast_relays {

  { 127.0.0.1 80 1 }
  { host.domain.com 8000 1 }
  { 127.0.0.1 8030 1 }
  { 127.0.0.1 8050 1 }
  { host.domain.com 80 2 }
  { 127.0.0.1 8000 2 }
  { host.domain.com 8030 2 }
  { 127.0.0.1 8050 2 }
}
 
  ## Channel to show shoutcast stats on
  set shoutcast_channels { "#channel" }

  ## Interval for showing stats
  set shoutcast_show_interval 60

  ## Name of the radio station
  set shoutcast_station_name "Radio Station"

## End of shoutcast configuration
  
package require http
package require tdom 

bind pub - ".record" show_listener_record
bind msg - ".stat" msg_show_stats
bind pub - ".stat" pub_show_stats
bind msg - ".stats" msg_show_stats
bind pub - ".stats" pub_show_stats
bind time - "?? * * * *" timer_show_stats

### CODE STARTS HERE ###

# Set show_stats semaphore
set sem_show_shoutstats 0

# Set show_stats counter
set counter_show_shoutstats $shoutcast_show_interval

proc test_stats {n m h c a} {
  after 0 [timer_show_stats 0 0 0 0 0]
}

proc pub_show_stats {nick mask hand channel args} {
  after 0 [show_shoutstats $channel "requested"]
}

proc msg_show_stats {nick mask hand channel args} {
  after 0 [show_shoutstats $nick "requested"]
}

proc timer_show_stats {mi ho da mo ye} {
  after 0 [show_shoutstats "#" "timer"]
}

proc show_listener_record {nick hand channel args} {
  after 0 [show_shoutstats $channel "record"]
}

set shoutcast_station_name2 "$shoutcast_station_name"

proc show_shoutstats {channel mode} {
  global shoutcast_relays sem_show_shoutstats shoutcast_show_interval \
    shoutcast_station_name2 shoutcast_station_name counter_show_shoutstats shoutcast_channels \
    homedir
  set run_allowed 0
  
  ## First wait for any other show functions to complete w/ some test-and-set instruction
  while { $run_allowed == 0 } {
    if { ( $sem_show_shoutstats == 0 ) && ( [set sem_show_shoutstats 1] ) && ( [set run_allowed 1] ) } {
      ## Continue the function
    } else {
      vwait $sem_show_shoutstats
      putserv "PRIVMSG $channel: I was delayed for execution"
    }
  }
  
  # Initialize the total stats
  # Per relay: { quality { current max bandwidth }}
  set total_stats {}

  # Totals current max bandwidth
  set t_unique 0
  set t_maxlst 0
  set t_bw 0.0

  # Extract data per relay
  foreach relay $shoutcast_relays {
    
    # Get attributes
    set server [lindex $relay 0]
    set port [lindex $relay 1]
    set sid [lindex $relay 2]
    set mark [lindex $relay 3]

    # Get the actual data
    if { [catch {::http::geturl "http://$server:$port/7.html?sid=$sid" \
      -timeout 5000 -headers "User-Agent: Mozilla (The King Kong of Lawn Care)"} stats_token] } {
      continue
    } else {
      # DO NOTHING
      set status [::http::status $stats_token]
      if { $status != "ok" } {
        continue
      }
    }
    set stats_data [::http::data $stats_token]

    # Get the stats from the html body
    set begin [expr [string first "<body>" $stats_data] + 6]
    set end [expr [string first "</body>" $stats_data] - 1]
    set relay_rawstats [string range $stats_data $begin $end]

    # Now extract the max-allowed and unique listener stats from the string
    set relay_liststats [split $relay_rawstats ","]

    set relay_unique [lindex $relay_liststats 1]
    set relay_maxlst [lindex $relay_liststats 3]
    set quality [lindex $relay_liststats 5]
    set relay_bw [expr ($quality * $relay_unique) / 1024 ]

    # Accumulate this to the totals
    # First check if this quality already appears in the totals list
    # And eventually search for the index where it should be inserted then
    set length [llength $total_stats]
    if { $length == 0 } {
      # The list is yet empty
      set q_totals [list $quality [list $relay_unique $relay_maxlst $relay_bw]]
      set total_stats [concat $total_stats $q_totals]
    } else {
      # Search for the right quality in the index
      # First try to find it in the list
      for { set q_index 0 } { $q_index <= $length } { set q_index [expr $q_index + 2] } {
        if { [lindex $total_stats $q_index] == $quality } {
          break
        }
      }
      
      if { $q_index > $length } {
        # It doesnt exist yet
        # Now we have to insert it in the list
        for { set q_index 0 } { ($quality > [lindex $total_stats $q_index]) \
          && ($q_index < $length) } { set q_index [expr $q_index + 2] } {
        }
        if { $q_index > $length } {
          # We have to append it to the list
          set q_totals [list $quality [list $relay_unique $relay_maxlst $relay_bw]]
          set total_stats [concat $total_stats $q_totals]
        } else {
          # We have to insert it in the list
          set q_totals [list $quality [list $relay_unique $relay_maxlst $relay_bw]]
          # First put it behind the first part of the list
          set total_stats_first [lrange $total_stats 0 [expr $q_index - 1]]
          set total_stats_last [lrange $total_stats $q_index end]
          set total_stats [concat $total_stats_first $q_totals]
          set total_stats [concat $total_stats $total_stats_last]
        }
      } else {
        # The stats for this quality already exist, add it to them
        # First get the current stats
        set cq_totals [lindex $total_stats [expr $q_index + 1]]

        # Add them together
        set q_unique [expr $relay_unique + [lindex $cq_totals 0]]
        set q_maxlst [expr $relay_maxlst + [lindex $cq_totals 1]]
        set q_bw [expr $relay_bw + [lindex $cq_totals 2]]
        
        # Replace the qurrent values in the totals
        set q_totals [list $q_unique $q_maxlst $q_bw]
        set total_stats [lreplace $total_stats [expr $q_index + 1] [expr $q_index + 1] $q_totals]
      }
    }

    # And accumulate this to the absolute totals
    set t_unique [expr $t_unique + $relay_unique]
    set t_maxlst [expr $t_maxlst + $relay_maxlst]
    set t_bw [expr $t_bw + $relay_bw]
    
    ::http::cleanup $stats_token
  }

  # Reset the show_stats_now var
  set show_stats_now 0

  # Truncate the bandwidth
  set t_bw [format "%.2f" $t_bw]

  # Now, before we display anything, check if the record is broken
  # If so, we dont display the stats but display a new record notice instead
  # Format of the file:
  #
  # Date\tListeners\tBw
  
  # First try to open the file
  if { [file exists "./$shoutcast_station_name.record"] == 1 } {
    set statfile [open "./$shoutcast_station_name.record" r]
    set record [read $statfile]
    close $statfile
  
    set frecord [split $record "\t"]

    # Now check if there was already something in the file
    if { [llength $frecord] != 4 } {
      set record_broken 1
    } elseif { [lindex $frecord 2] < $t_unique } {
      set record_broken 1
    } else {
      set record_broken 0
    }
  } else {
    # The file didnt exist
    set record_broken 1
  }
  
  # Now, check if we are gonna show the stats or the new record
  if { $record_broken == 1 } {
    # Re-open the statfile
    set statfile [open "./$shoutcast_station_name.record" w]

    # Insert the new data in the file
    set current_time [clock seconds]
    set ctime [clock format $current_time -format "%A %m-%d-%Y %H:%M"]
    
    set current_song [lindex $shoutcast_now_playing 0]
    puts -nonewline $statfile "$ctime\t$t_unique\t$t_bw"
  
    ## Be sure to close the file
    close $statfile

    set outputs "$shoutcast_station_name2 new record - Listener record broken on $ctime with $t_unique listeners."
    # Now print the record to the chat
    if { $mode == "timer" } {
      # For each shoutcast channel
      foreach chan $shoutcast_channels {
        putquick "PRIVMSG $chan :$outputs"
      }
    } else {
      # For the specified channel
      putquick "PRIVMSG $channel :$outputs"
    }
  } else {
    # Perform the command requested (timer, requested or record)
    if { $mode == "record" } {
      set rsong [lindex $frecord 0]
      set rtime [lindex $frecord 1]
      set rlst [lindex $frecord 2]
      set rbw [lindex $frecord 3]
      set outputs "$shoutcast_station_name2 record - Current record was set on $rtime with $rlst listeners."
      putquick "PRIVMSG $channel :$outputs"
    } else {
      # Format all stats in one line
      set outputs "$shoutcast_station_name2 Stats:"
    
      foreach {q s} $total_stats {
        set current [lindex $s 0]
        set max [lindex $s 1]
        set bw [format "%.2f" [lindex $s 2]]
        set outputs "$outputs ($q Kbps - $current/$max/$bw Mbps)"
      }
      set outputs "$outputs (Total - $t_unique/$t_maxlst/$t_bw Mbps)"
  
      if { $mode == "requested" } {
        # It seems we have a normal channel
        putquick "PRIVMSG $channel :$outputs"
      } else {
        # Just assume this is a timer thing
        set counter_show_shoutstats [expr $counter_show_shoutstats - 1]
        # putserv "PRIVMSG $channel :Decreasing counter to $counter_show_shoutstats"
        if { $counter_show_shoutstats <= 0 } {
          set counter_show_shoutstats $shoutcast_show_interval

          # Now display those stats
          foreach chan $shoutcast_channels {
            putquick "PRIVMSG $chan :$outputs"
          }
        }
      }
    }
  }

  ## Free the semaphore
  set sem_show_shoutstats 0
}

###############################
# Execute the show_stats
show_shoutstats "#" "timer"

putlog "multi server shoutcast listener stats tcl for eggdrop loaded..."

use ffmpeg to encode/re-encode any local or remote media to mp3 and stream to shoutcast.

prerequisites:

ffmpeg with libmp3lame and openssl enabled
youtube-dl
shout-perl
libshout3
libmp3lame

usage:

$ ffmpeg -d -re -i "/path/to/local/media.wav" -vn -c:a mp3 -b:a 256k -f mp3 - | ./stream_stdin.pl

streaming to shoutcast is handled by piping audio data to a perl script (shout-perl)

here is the perl script that reads audio data from pipe or STDIN

shout_stdin.pl

#!/usr/bin/perl -w

use strict;
use Shout;
use bytes;

###############################################################################
###  C O N F I G U R A T I O N
###############################################################################

# shoutcast DNAS version 1 or 2
my $version = 1; 

# shoutcast v2 stream id
my $sid = 1; 

# hostname or ip address without http://
my $host = '123.123.123.123';

# port
my $port = 8000;

# password
my $password = 'password';

# stream name
my $name = 'ffmpeg';

# stream url
my $url = 'http://www.radioname.com';

# stream genre
my $genre = 'genre';

# 1 for public, 0 for private
my $public = 0;

# bitrate
my $bitrate = 256;

# samplerate
my $samplerate = 44100;

###############################################################################
###  M A I N   P R O G R A M
###############################################################################

my $streamer = new Shout
  host          => $host,
  port          => $port,
  password      => $password,
  name          => $name,
  url           => $url,
  bitrate  => $bitrate,
  genre         => $genre,
  format        => SHOUT_FORMAT_MP3,
  protocol      => SHOUT_PROTOCOL_ICY,
  public        => $public;

$streamer->set_audio_info(SHOUT_AI_BITRATE => $bitrate, SHOUT_AI_SAMPLERATE => $samplerate);

if ($streamer->open) {
  print "connected\n";
  print "host: $host\n";
  print "port: $port\n";
  print "password: $password\n";
  print "name: $name\n";
  print "url: $url\n";
  print "bitrate: $bitrate\n";
  print "genre: $genre\n";
  print "format: SHOUT_FORMAT_MP3\n";
  print "public: $public\n";
  print "Streaming from STDIN...\n";
  
  $streamer->set_metadata("song" => "Streaming from STDIN");
  
  my ($buff, $len);
    while (($len = sysread(STDIN, $buff, 4096)) > 0) {
  unless ($streamer->send($buff)) {
      print "Error while sending: " . $streamer->get_error . "\n";
      last;
  }
  $streamer->sync;
    }
    $streamer->close;
} else {
    print "failed... "  . $streamer->get_error . "\n";
}

remote sources are handled using youtube-dl by using backticks in the ffmpeg command. this will enable you to stream any media resource thats supported by youtube-dl and ffmpeg, such as soundcloud, mixcloud, youtube, vimeo, twitch.tv, etc.

usage:

$ ffmpeg -d -re -i "`youtube-dl -f bestaudio -g https://soundcloud.com/dubstepfm/dubstep-fm-archive-2014-11-09-all-widdler-vol-7-mixed-by-dopelabs`" -vn -c:a mp3 -b:a 256k -f mp3 - | ./shout_perl.pl

shout_perl.pl expects raw mp3 audio data via pip or STDIN. this means you can simply:

$ cat song.mp3 | ./shout_perl.pl

and it will stream a single mp3. a little bit of crafty scripting and you can easily create your own auto dj.

https://github.com/Wavestreaming/jquery-shoutcast/wiki/Easy-Setup

this will display shoutcast dnas v2 status/info, auto updating every 5 seconds without page reload.

Prerequisites

Your DNAS version must have JSON support, if it does not then you will not be able to use this plugin.

Setup

Download the plugin and store jquery.shoutcast.easy.min.js somewhere on your server.

https://github.com/Wavestreaming/jquery-shoutcast.git

Configuration

In the second script tag you need to edit example.com to be your host and also edit the port if necessary, this is the minimum configration that is required by the plugin, there are more options which can be found here

Usage

Once you have configured the plugin you will need to add various elements to your page.

Showing Stats

To show any statistics from your stream you simply need to add an element with adata-shoutcast-value=”” attribute, for example, if I wanted to show the currently playing song this is what you would have:

<p data-shoutcast-value="songtitle"></p>
Or current listeners:

<span data-shoutcast-value="currentlisteners"></span>
All of the available values can be found here

Showing played tracks

To show played tracks you need to an an ul element with an id of played to your page:

<ul id="played"></ul>
With the above element on the page the names of the recently played tracks will be displayed in order.

Example HTML

<body>
      <h1 data-shoutcast-value="servertitle"></h1>

      <p>Now Playing: <span data-shoutcast-value="songtitle"></span></p>
      <p>Listeners: <span data-shoutcast-value="currentlisteners"></span>/<span data-shoutcast-value="maxlisteners"></span></p>

      <h2>You just missed</h2>
      <ul id="played"></ul>

      <script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
      <script src="jquery.shoutcast.easy.min.js?host=example.com&port=8000&interval=5000"></script>
</body>

The above HTML will display the server title, the current song, the current listeners, the maximum listeners and a list of all of the recently played tracks.

install liquidsoap

the following commands were tested on a fresh/default install of ubuntu server 14.04 lts, and is expected you already have an ice/shoutcast server running, and know your way around the linux command line.

you can find a list of the available liquidsoap packages for install by searching the apt list:

$ sudo apt-cache search liquidsoap

i take the easy route and just go ahead and also install all the supported streaming plugins as well.

$ sudo apt-get install liquidsoap liquidsoap-plugin-all

once installed you are ready to write a script.

this example will have the following functions/capabilities:

the script runs in the background (deamon mode)
plays local mp3’s listed in a playlist file (txt)
immediately switch to live dj (and back) on connect/disconnect
play a local backup file of any of the above fails for any reason
manually set title via url GET request (http://127.0.0.1:8080/setmeta?title=SongTitleGoesHere)
output the stream to multiple instances of shoutcast at different bit rates and formats (transcoding)

to execute simply
$ ./whateveryouwant.liq

create a file containing the following and name it whateveryouwant.liq and edit values as needed

#!/usr/bin/liquidsoap

# run this script in the background
set("init.daemon",true)

# do not create a pid file
set("init.daemon.pidfile",false)

# set the path and permissions for the logfile
set("log.file.path","/home/user/log/liquidsoap.log")
set("log.file.perms",755)
set("log.unix_timestamps",true)

# local ip address to bind and listen for input (dj or metadata)
set("harbor.bind_addr","127.0.0.1")

# port and pass for live djs to connect to (shoutcast protocol)
live = input.harbor(icy=true,"/",port=8888,password="PASS")

# path to playlist file which contains a list of local mp3's (/home/user/mp3/song.mp3)
playlist = playlist("/home/user/mp3/playlist.txt")

# path to failover song if all above fails
emergency = single("/home/user/mp3/backup.mp3")

# do not monitor for silence and specify the fallback/priority order
radio = fallback(track_sensitive=false,[live,playlist,emergency])

# function to manually change song title
title = insert_metadata(radio)
insert = fst(title)
radio = snd(title)

def set_meta(~protocol,~data,~headers,uri) =
title = url.split(uri)
meta = metadata.export(snd(title))
ret = if meta != [] then insert(meta) "OK!" else "No metadata to add!" end
http_response(protocol=protocol,code=200,headers=[("Content-Type","text/html")],data="#{ret}") end

# port to register metadata updates via http
harbor.http.register(port=8080,method="GET","/setmeta",set_meta)

# shoutcast servers to broadcast to
output.shoutcast(%mp3(bitrate=256,samplerate=44100,stereo=true),name="RADIO NAME",genre="GENRE",host="127.0.0.1",port = 8000,password = "PASS",radio)
output.shoutcast(%mp3(bitrate=128,samplerate=44100,stereo=true),name="RADIO NAME",genre="GENRE",host="127.0.0.1",port = 8002,password = "PASS",radio)
output.shoutcast(%mp3(bitrate=64,samplerate=44100,stereo=true),name="RADIO NAME",genre="GENRE",host="127.0.0.1",port = 8004,password = "PASS",radio)
output.shoutcast(%mp3(bitrate=24,samplerate=22050,stereo=true),name="RADIO NAME",genre="GENRE",host="127.0.0.1",port = 8006,password = "PASS",radio)
output.shoutcast(%aac(bitrate=256),name="RADIO NAME",genre="GENRE",host="127.0.0.1",port = 9000,password = "PASS",radio)
output.shoutcast(%aac(bitrate=128),name="RADIO NAME",genre="GENRE",host="127.0.0.1",port = 9002,password = "PASS",radio)
output.shoutcast(%aac(bitrate=64),name="RADIO NAME",genre="GENRE",host="127.0.0.1",port = 9004,password = "PASS",radio)
output.shoutcast(%aac(bitrate=24),name="RADIO NAME",genre="GENRE",host="127.0.0.1",port = 9006,password = "PASS",radio)

another example, relay a shoutcast stream

relay.liq

#!/usr/bin/liquidsoap

# set the path and permissions for the logfile
set("log.file.path","/home/user/log/liquidsoap.log")
set("log.file.perms",755)
set("log.unix_timestamps",true)

# stream url to relay (source)
url = "http://127.0.0.1:9000"
relay = mksafe(input.http(url))
radio = fallback(track_sensitive=false,[relay])

# shoutcast server to broadcast to
output.shoutcast(%mp3(bitrate=256,samplerate=44100,stereo=true),name="RADIO NAME",genre="GENRE",host="127.0.0.1",port = 8000,password = "PASS",radio)

other samples can be found on the liquidsoap website http://liquidsoap.fm/doc-svn/scripts

DL’s streaming toolbox: a post category for audio/video streaming, music management, automation, podcast generation, encoding, recording, transcoding, stats generation, etc, etc.

http://www.dlnetworks.net/tools

the quick list

scserv dnas v1/2
liquid soap
perl
streamtranscoder
shoutV2 js
curl
php
ffmpeg
apache/mod rewrite
eyed3
python
html/5
jwplayer
youtube-dl
soundcloud api
youtube api
bash/shell scripting
crontab

i will explain the capacity in which the technology is used, how to install, configure, and run.

first post ‘how to liquidsoap’ coming soon!

create a file such as itunes_title.sh with the following contents

#!/bin/bash

while true; do
echo "$(osascript -e 'tell app "iTunes" to artist of current track & " - " & name of current track') - $(($(osascript -e 'tell app "iTunes" to finish of current track' | cut -d '.' -f1) - $(osascript -e 'tell app "iTunes" to player position' | cut -d '.' -f1)))";
sleep 1;
done

dopelabs$ ffprobe -i `youtube-dl -g https://www.youtube.com/watch?v=holhZM1OpTc`
ffprobe version 2.4.3 Copyright (c) 2007-2014 the FFmpeg developers
  built on Nov 15 2014 21:12:17 with Apple LLVM version 6.0 (clang-600.0.54) (based on LLVM 3.5svn)
  configuration: --prefix=/opt/local --enable-swscale --enable-avfilter --enable-avresample --enable-libmp3lame --enable-libvorbis --enable-libopus --enable-libtheora --enable-libschroedinger --enable-libopenjpeg --enable-libmodplug --enable-libvpx --enable-libspeex --enable-libass --enable-libbluray --enable-lzma --enable-gnutls --enable-fontconfig --enable-libfreetype --enable-libfribidi --disable-indev=jack --disable-outdev=xv --mandir=/opt/local/share/man --enable-shared --enable-pthreads --cc=/usr/bin/clang --enable-vda --arch=x86_64 --enable-yasm --enable-gpl --enable-postproc --enable-libx264 --enable-libxvid
  libavutil      54.  7.100 / 54.  7.100
  libavcodec     56.  1.100 / 56.  1.100
  libavformat    56.  4.101 / 56.  4.101
  libavdevice    56.  0.100 / 56.  0.100
  libavfilter     5.  1.100 /  5.  1.100
  libavresample   2.  1.  0 /  2.  1.  0
  libswscale      3.  0.100 /  3.  0.100
  libswresample   1.  1.100 /  1.  1.100
  libpostproc    53.  0.100 / 53.  0.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'https://r8---sn-o097znel.googlevideo.com/videoplayback?id=o-APWfGROGgBVCWuaOIpEz4FFP4-KNU5ZI7ltSYG_cFEQO&dur=4689.432&ms=au&mt=1417430940&mv=m&ip=50.184.214.146&mm=31&itag=22&sver=3&ipbits=0&requiressl=yes&sparams=dur%2Cgcr%2Cid%2Cinitcwndbps%2Cip%2Cipbits%2Citag%2Cmm%2Cms%2Cmv%2Crequiressl%2Csource%2Cupn%2Cexpire&fexp=905639%2C907259%2C912146%2C916644%2C927622%2C932404%2C939100%2C942622%2C942702%2C942901%2C943909%2C947209%2C948124%2C952302%2C952605%2C952901%2C953803%2C953912%2C957103%2C957105%2C957201&signature=7E806AB05886ED86B919F62D169D9C853FB2A4D0.E5D29795257B3584E85369FF9238A79622F398AA&gcr=us&initcwndbps=2238750&source=youtube&key=yt5&upn=jpJQYp_jRH8&expire=1417452589&ratebypass=yes':
  Metadata:
    major_brand     : mp42
    minor_version   : 0
    compatible_brands: isommp42
    creation_time   : 2014-11-30 21:25:22
  Duration: 01:18:09.39, start: 0.000000, bitrate: 2915 kb/s
    Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 2721 kb/s, 29.97 fps, 29.97 tbr, 30k tbn, 59.94 tbc (default)
    Metadata:
      handler_name    : VideoHandler
    Stream #0:1(und): Audio: aac (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 192 kb/s (default)
    Metadata:
      creation_time   : 2014-11-30 21:26:39
      handler_name    : IsoMedia File Produced by Google, 5-11-2011

<?php

$station_name = "Radio Station";

$timeout = "5"; // timeout

$ip[1] = "127.0.0.1"; 
$port[1] = "80";
$sid[1] = "1";

$ip[2] = "127.0.0.1"; 
$port[2] = "8000";
$sid[2] = "1";

$ip[3] = "127.0.0.1";
$port[3] = "8030";
$sid[3] = "1";

$ip[4] = "127.0.0.1";
$port[4] = "8050";
$sid[4] = "1";

/* ----- End config ----- */

$servers = count($ip);

$i = "1";
while($i<=$servers)
  {
  $fp = @fsockopen($ip[$i],$port[$i],$errno,$errstr,$timeout);
  if (!$fp) 
    { 
    $listeners[$i] = "0";
    $msg[$i] = "ERROR [Connection refused / Server down]";
    $error[$i] = "1";
    } 
  else
    { 
    fputs($fp, "GET /7.html?sid=$sid[$i] HTTP/1.0\r\nUser-Agent: Mozilla (The King Kong of Lawn Care)\r\n\r\n");
    while (!feof($fp)) 
      {
      $info = fgets($fp);
      }

    $stats = explode(',', $info);
    if (empty($stats[1]) )
      {
      $listeners[$i] = "0";
      $msg[$i] = "ERROR [There is no source connected]";
      $error[$i] = "1";
      }
    else
      {
      if ($stats[1] == "1")
        {
        $listeners[$i] = $stats[4];
        }
      else
        {
        $listeners[$i] = "0";
        $msg[$i] = "ERROR [Cannot get info from server]";
        $error[$i] = "1";
        }
      }
    }
  $i++;
  }
$total_listeners = array_sum($listeners);
print "$total_listeners";
?>

works with shoutcast v1 and v2. php config requires sockets.

https://github.com/dlnetworks/php-multiserver-shoutcast-stats

connects to a remote shoutcast server and transcodes to aac and mp3 and rebroadcasts to multiple shoutcast servers
shoutast based sources (djs) connect, switching from the remote source to the live source immediately, and back on disconnect
if the remote source fails, play a local file

put the following code into a file.liq and run



#!/usr/bin/liquidsoap

# set full path to logfile
set("log.file.path","/var/log/liquidsoap/radiostation.log")

# enable daemon mode
set("init.daemon",true)

# change to run under a custom user/group
set("init.daemon.change_user",true)
set("init.daemon.change_user.group","username")
set("init.daemon.change_user.user","groupname")

# do not create a pid file
set("init.daemon.pidfile",false)

# ip to listen on for djs and sources
set("harbor.bind_addr","127.0.0.1")

# remote source to be transcoded
url = "http://www.remotehost.com:8000"
relay = mksafe(input.http(url))

# port and pass for djs
live = input.harbor("/",port=8000,password="djpassword")

# local file
emergency = single("/path/to/some/lastresort.mp3")

# fallback function
radio = fallback(track_sensitive=false,[live,relay,emergency])

# output to shoutcast servers
output.shoutcast(%mp3(bitrate=256,samplerate=44100,stereo=true),name="Radio Station",genre="Genre",host="127.0.0.1",port = 8000,password = "changeme",radio)
output.shoutcast(%mp3(bitrate=128,samplerate=44100,stereo=true),name="Radio Station",genre="Genre",host="127.0.0.1",port = 8010,password = "changeme",radio)
output.shoutcast(%mp3(bitrate=64,samplerate=44100,stereo=true),name="Radio Station",genre="Genre",host="127.0.0.1",port = 8020,password = "changeme",radio)
output.shoutcast(%mp3(bitrate=24,samplerate=22050,stereo=false),name="Radio Station",genre="Genre",host="127.0.0.1",port = 8030,password = "changeme",radio)
output.shoutcast(%aac(bitrate=256),name="Radio Station",genre="Genre",host="127.0.0.1",port = 8000,password = "changeme",radio)
output.shoutcast(%aac(bitrate=128),name="Radio Station",genre="Genre",host="127.0.0.1",port = 8010,password = "changeme",radio)
output.shoutcast(%aac(bitrate=64),name="Radio Station",genre="Genre",host="127.0.0.1",port = 8020,password = "changeme",radio)
output.shoutcast(%aac(bitrate=24),name="Radio Station",genre="Genre",host="127.0.0.1",port = 8030,password = "changeme",radio)

php jquery-mobile podcast parser

Use php to curl an rss/xml url and create a filterable, formatted list view for jquery-mobile

requires
php-curl
simple-xml

features
1. unlimited items! (does NOT proxy via google)
2. displays: pubdate, itunes duration, image, title, subtitle, size (MB)
3. audio icons for audio, video icons for videos

usage
1. edit the url to your rss/xml podcast feed
2. open in browser or include

demo
http://www.dlnetworks.net/php-jqm-podcast

download
https://github.com/dlnetworks/php-jquery-mobile-podcast

all web servers log requests to a file. each request is a single line in the logfile that looks something like this

127.0.0.1 - - [04/Jun/2014:21:00:48 -0700] "GET /ARCHIVE_-_0000-00-00_-_Show_Name_With_DJ_Name_From_Location.mp3 HTTP/1.1" 200 140398275 "-" "iTunes/11.2.1 (Macintosh; OS X 10.9.3) AppleWebKit/537.75.14"

there is a log entry for every whole or partial content request. every time you visit a website, for every image you see, for every js, css, etc file that loads is an entry. on busy sites this log file can become huge. the log file i used for this demonstration has 22,577 lines. each line (see above) can tell you a few things:

the ip address of the person making the request
time stamp
request type and request
http response code
bytes transferred
user agent

and now, with a wonderful tool called logstalgia combined with ffmpeg and some command line-fu

cat /var/log/apache2/podcast.access.log | logstalgia -1920x1080 -g Archives,ARCHIVE,99 --paddle-mode pid --update-rate 1 --output-framerate 60 --output-ppm-stream - - | ffmpeg -f image2pipe -r 60 -c:v ppm -s 1920x1080 -pix_fmt yuv420p -i - -crf 1 -c:v h264 -pix_fmt yuv420p -f mp4 outfile.mp4

and if you want real time…

tail -F -q /var/log/apache2/podcast.access.log | logstalgia -1920x1080 -g Archives,ARCHIVE,99 --paddle-mode pid --update-rate 1 --output-framerate 60 --output-ppm-stream - - | ffmpeg -f image2pipe -r 60 -c:v ppm -s 1920x1080 -pix_fmt yuv420p -i - -crf 1 -c:v h264 -pix_fmt yuv420p -f flv rtmp://videoservice.com/live/secretkey

and.. the timescale is non-linear! (because i like to fiddle)

Date | Hits | Bandwidth
6/04 | 20,034 | 2365.35 GB (2.3TB)
6/05 | 12,702 | 1528.78 GB (1.5TB)

T: 2 | 32,736 | 3894.13 GB (3.8TB)

and heres one using logs from a more well established instance of apache.

the goal:

use ffmpeg to stream audio from shoutcast and a video loop to ustream.tv, justin.tv, twitch.tv, youtube, wowza, flash media server, etc

because ffmpeg -loop 1 seems to only support images, export your loop video file as an image sequence (jpeg,png,etc)

so lets get started. in this example lets output to wowza (mpegts)

ffmpeg -i http://shoutcast.domain.com:port -re -loop 1 -pattern_type glob -i 'live/*.jpg' -c:v libx264 -b:v 3000k -pix_fmt yuvj420p -bsf h264_mp4toannexb -profile:v main -level 41 -g 20 -c:a libfdk_aac -b:a 192k -f mpegts udp://IP:10000?pkt_size=1316

breakitdown

-re will read the image sequence at native/default fps
-loop 1 loops a single image or image sequence
-pattern_type glob allows usage of *
-i '/path/to/loop00*.jpg' path to your image(s) sequence
-i http://shoutcast.domain.com:port host and port of shoutcast server
-f mpegts use mpegts for output
udp://IP:10000?pkt_size=1316 stream to udp port

console output

ffmpeg version 2.1.3 Copyright (c) 2000-2013 the FFmpeg developers
  built on Jan 21 2014 17:05:51 with gcc 4.6 (Ubuntu/Linaro 4.6.3-1ubuntu5)
  configuration: --prefix=/usr/local --extra-ldflags=-L/usr/local/lib --enable-openssl --enable-openal --enable-libxvid --enable-libx264 --enable-libwavpack --enable-libvorbis --enable-libvo-aacenc --enable-libtheora --enable-libssh --enable-librtmp --enable-libopenjpeg --enable-libopencv --enable-libmp3lame --enable-avisynth --enable-libaacplus --enable-libbluray --enable-libfaac --enable-libfdk-aac --enable-avresample --enable-gray --enable-nonfree --enable-version3 --enable-gpl
  libavutil      52. 48.101 / 52. 48.101
  libavcodec     55. 39.101 / 55. 39.101
  libavformat    55. 19.104 / 55. 19.104
  libavdevice    55.  5.100 / 55.  5.100
  libavfilter     3. 90.100 /  3. 90.100
  libavresample   1.  1.  0 /  1.  1.  0
  libswscale      2.  5.101 /  2.  5.101
  libswresample   0. 17.104 /  0. 17.104
  libpostproc    52.  3.100 / 52.  3.100
Input #0, mp3, from 'http://relay1.dubstep.fm:80':
  Duration: N/A, start: 0.000000, bitrate: 192 kb/s
    Stream #0:0: Audio: mp3, 44100 Hz, stereo, s16p, 192 kb/s
Input #1, image2, from 'live/*.jpg':
  Duration: 00:00:24.00, start: 0.000000, bitrate: N/A
    Stream #1:0: Video: mjpeg, yuvj420p(pc), 1280x720 [SAR 72:72 DAR 16:9], 25 fps, 25 tbr, 25 tbn, 25 tbc
[libx264 @ 0xa0b2960] using SAR=1/1
[libx264 @ 0xa0b2960] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX
[libx264 @ 0xa0b2960] profile Main, level 4.1
Output #0, mpegts, to 'udp://IP:10000?pkt_size=1316':
  Metadata:
    encoder         : Lavf55.19.104
    Stream #0:0: Video: h264 (libx264), yuvj420p, 1280x720 [SAR 1:1 DAR 16:9], q=-1--1, 3000 kb/s, 90k tbn, 25 tbc
    Stream #0:1: Audio: aac (libfdk_aac), 44100 Hz, stereo, s16, 192 kb/s
Stream mapping:
  Stream #1:0 -> #0:0 (mjpeg -> libx264)
  Stream #0:0 -> #0:1 (mp3 -> libfdk_aac)
Press [q] to stop, [?] for help
frame= 7482 fps= 25 q=12.0 size=  127133kB time=00:05:10.12 bitrate=3358.2kbits/s

to stream to ustream.tv just change to -f flv rtmp://ustream.tv/url/key and so on for the other services.

you may use any encoder you like as long as its supported by ustream.tv, justin.tv, twitch.tv, youtube, wowza, flash media server, etc

and for a little fun

when -loop 1 is set ffmpeg seems to read the file on every loop cycle, which means once ffmpeg is running and looping your image sequence, you can edit any of the individual image files and ffmpeg will stream the updated frames/images on the next loop cycle. this means you can edit/change individual frames in real time. (fight club lol)

if you want to use a single image file, for example -loop 1 -i image.png, you can then open with photoshop and make changes to layers, opacity, etc. just save the file and ffmpeg will stream it out!

note that if your using glob *.jpg for an image sequence, you can only make changes to files that were present at the time ffmpeg was executed. so just dropping another 100 images in the same directory and hoping that *.jpg will pick them up wont work, sorry :/

from what i can tell ffmpeg really hasnt been utilized in this particular way, but is good for radio station owners that want to take advantage of all available delivery methods.

(edit 04.19.14 – i have since bagged the still image, or looping video for something a bit sexier)

http://www.dsfm.tv

cheers =]

DL