Featured image of post Console App to Automate Stream with AltaCast, VLC, VB Virtual Cable

Console App to Automate Stream with AltaCast, VLC, VB Virtual Cable

Making a console app to automate online radio with AltaCast, VLC, and VB Virtual Audio Cable




02/22 Updated Code to Fix Timer

What happens when radio station’s go offline - or after their broadcast hours end? Dead air. Well, I am not sure if it is also the term being used in 24/7 audio servers.

This “dead air” not only affects the usability of streaming apps but also prevents users who differ in time zones. These users will be deprived of any content thus affecting possible audiences for the station.

I recently brought up to the team by letting our stations utilize the audio servers 24/7 by adding program replays during off-air hours. The servers, which use CentovaOS, allow uploading audio files to be played during “off-stream” called AutoDJ. The stations can also let their PCs run 24/7 but the cost, manpower, and effort to implement such system seem to be a struggle as operations differ from each station.

Since I have been letting my work PC “on” all of the time, I proceeded to make it as a client to stream filler programs at times all stations are off-the-air.

# Multi-port Stream with AltaCast

We’ve been using butt (broadcast using this tool) and/or WinAmp Shoutcast DSP to upload a single stream to the servers (each station) - and running 12 instances of it is highly unrecommended.

My internet search led me to a program called AltaCast - which supports multiple streams. The standalone app initially supports OGG encoded streams but downloading MP3Lame encoders now supports MP3 (duh).

I started off streaming one port, and it works well. Then 3, then all 12. But the app crashes. Further troubleshooting let me conclude that it might be limited to 6 simultaneous streams.

Now with that limitation, this thing would be impossible. Then I tried to “copy” the standalone app on another location and reconfigured the remaining 6 streams, and it worked!

The only problem is that there is no way to rename the configurations as to which is which.

AltaCast

# Virtual Cable

There should be nothing wrong to use the desktop audio to stream, but to avoid any unwanted noise/audio, I installed VB Virtual Audio Cable which installed a virtual audio I/O on Windows to re-route certain apps’ audio.

I used the VB Cable Output as the input for two instances of AltaCast.

# Media Playback

I will be using VLC as the media player since it supports network streams. Right now, I will be “hooking-up” to DWIZ 882 (one of our affiliate station) programs upto 12 midnight. Then use CNN Philippines programs 12-3am.

# Programming a Console App

This app will be a timer to check set times:

  • 10PM - Start Client Stream
  • 12MN - Change Media Source
  • 3AM - Stop/Quit Stream

UPDATE

  • Fixed various bugs (time check)
  • Changed every second tick to every minute
  • Adjusted Trim Extension to remove seconds

Below is the full code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
using System;
using System.Diagnostics;
using System.Threading;

public class Program
{
    private static Timer? _timer;
    private static bool isActive = false;

    public static void Main()
    {
        Console.WriteLine();
        Console.WriteLine("Welcome to Stream Control!");
        Console.WriteLine("--------------------------");
        Console.WriteLine("App Version: 0.1-beta");
        Console.WriteLine();

        Console.WriteLine("Starting timer...");
        // Changed timer tick to 60_000 ms | 60 secs
        _timer = new Timer(TimerCallback, null, 0, 60_000);

        Console.ReadLine();
    }

    private static void TimerCallback(Object o)
    {
        TimeSpan start = TimeSpan.Parse("22:10");
        TimeSpan shift = TimeSpan.Parse("23:59");
        TimeSpan stop = TimeSpan.Parse("02:55");
        TimeSpan now = DateTime.Now.TimeOfDay;
        now = now.StripSeconds();

        Console.WriteLine($"Now: {now} | Start: {start} | Shift: {shift} | Stop: {stop}");

        if (now == start && !isActive)
        {
            StartLocalStream();
            isActive = true;
        }
        else if (now == shift && isActive)
        {
            // Change Media Playback
            ChangeMediaPlayback();
        }
        else if (now == stop && isActive)
        {
            // Stop apps
            KillProcesses("vlc");
            KillProcesses("altacastStandalone");
            isActive = false;
        }
    }   

    private static void ChangeMediaPlayback()
    {
        // Get media playing for max 3 hours
        // Kill any initial VLC
        var vlc = @"C:\Program Files\VideoLAN\VLC\vlc.exe";

        KillProcesses("vlc");

        CreateAndRunProcess(directory: @"C:\Program Files\VideoLan\VLC", fileName: vlc, args: "https://streaming.cnnphilippines.com/live/myStream/playlist.m3u8");
    }

    private static void StartLocalStream()
    {
        var vlc = @"C:\Program Files\VideoLAN\VLC\vlc.exe";
        var altaCast1 = @"D:\altacast\altacastStandalone.exe";
        var altaCast2 = @"D:\altacast2\altacastStandalone.exe";

        CreateAndRunProcess(directory: @"C:\Program Files\VideoLan\VLC", fileName: vlc, args: "http://149.56.147.197:9079/stream");
        CreateAndRunProcess(directory: @"D:\altacast\", fileName: altaCast1, isShell: true, verb: "runas");
        CreateAndRunProcess(directory: @"D:\altacast2\", fileName: altaCast2, isShell: true, verb: "runas");
    }

    private static void CreateAndRunProcess(string directory, string fileName, string args = null, bool isShell = false, string verb = null)
    {
        ProcessStartInfo procInfo = new ProcessStartInfo
        {
            WorkingDirectory = directory,
            FileName = fileName,
            UseShellExecute = isShell,
            Verb = verb,
            Arguments = args
        };
        Process.Start(procInfo);
    }

    private static void KillProcesses(string processName)
    {
        foreach (var process in Process.GetProcessesByName(processName))
        {
            process.Kill();
        }
    }  
}

 

1
2
3
4
5
6
7
8
// https://stackoverflow.com/a/35750677
public static class TimeExtensions
{
    public static TimeSpan StripMilliseconds(this TimeSpan time)
    {
        return new TimeSpan(time.Days, time.Hours, time.Minutes, time.Seconds);
    }
}

Console App

I’ve only tested it for a day and made some fixes to continue the app running.

You can tune in at https://tunein.rpnradio.com/ and we’ll debug any errors. 😀

# Future Plans

As of now, stations’ broadcast hours differ (sign on and sign off) and I want the app to be dynamic. Some plans for the app:

  • Detect actual “dead air” time
  • Detect actual “on air” time
  • Custom playlist in VLC (not relying on “hook ups”)

Thanks for reading the blog post.

Share this: