I’ve been experimenting with streaming on Twitch.tv with the goal of making connections, and sharing what I’ve learned. I have an issue with my home internet bandwidth which makes this tricky. There’s no way I can stream 360p@30fps on this connection, and use the internet for researching.
I want to stream even though I have bad internet, so I created a workaround. Instead of streaming video from my home computer, I stream video from a Virtual Private Server (VPS) in a datacenter.
I have done a few streams using this setup. It is imperfect, but it works!
Most budget VPS don’t have a dedicated graphics card, so I did what I could do to reduce the CPU intensive work that the VPS must perform. Here is a rundown of the setup.
Cloud Streaming Workspace Software
Ubuntu GNU/Linux
Ubuntu is the basis for the cloud workspace. It is familiar to me and I don’t have to worry about license payments. I am running a minimal window manager and desktop environment, openbox and lxqt.
x2go
I use x2go to remotely control my Cloud Streaming Workspace as if I am there. I can see the VPS’s virtual screen, and send mouse and keyboard clicks.
Mumble
I use a mumble server on my cloud workspace, and a mumble client on my home computer to pipe my voice into the cloud workspace. I run another mumble client in the cloud workspace so my voice is captured by FFmpeg.
FFmpeg
FFmpeg streams audio and video straight to Twitch, using the audio and video that is being heard/displayed on the cloud workspace.
Provisioning
I don’t steam much, and I’m broke. Having a VPS running 24/7 which serves a purpose only every now and then doesn’t make much sense for me. Instead of running it non-stop, I instead opted for a workspace solution which I can create and destroy on-demand. That way, I’m paying the hourly rate for only the time I need it.
Here is a look at my Ansible playbook which provisions the VPS to be a cloud workspace.
--- - name: Provision VPS to be a cloud streaming box hosts: seedbox vars_files: - vars/secret.yml gather_facts: true handlers: - name: restart mumble systemd: state: restarted name: mumble-server.service tasks: - name: create user account user: state: present name: crispy password: "{{ secret_crispy_password }}" shell: /bin/bash groups: sudo append: yes - name: create home folder file: state: directory path: /home/crispy mode: '755' - name: create .ssh folder file: state: directory path: /home/crispy/.ssh owner: crispy group: crispy mode: '700' - name: copy SSH keys to workspace copy: src: "{{ item.src }}" dest: "{{ item.dest }}" owner: crispy group: crispy mode: "{{ item.mode }}" remote_src: no with_items: - { src: /home/chris/.ssh/kaito.pub, dest: /home/crispy/.ssh/authorized_keys, mode: '0644' } - { src: files/tkey.pub, dest: /home/crispy/.ssh/tkey.pub, mode: '0644' } - { src: files/tkey, dest: /home/crispy/.ssh/tkey, mode: '0600' } # - name: set up ssh-agent - name: add x2go ppa apt_repository: repo: 'ppa:x2go/stable' state: present - name: install programs apt: state: present pkg: - apt-transport-https - ca-certificates - curl - gnupg-agent - software-properties-common - ffmpeg - mumble-server - mumble - x2goserver - x2goserver-xsession - python3 - python-is-python3 - xterm - git - mg - xorg - openbox - lxqt - xz-utils - wormhole - name: install nvm shell: curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.0/install.sh | bash args: creates: "{{ ansible_env.HOME }}/.nvm/nvm.sh" - name: configure git tags: - git copy: src: "{{ item.src }}" dest: "{{ item.dest }}" owner: crispy group: crispy mode: '644' with_items: - { src: files/.gitconfig, dest: /home/crispy/.gitconfig } - { src: files/.gitignore, dest: /home/crispy/.gitignore } - name: install atom editor apt: state: present deb: https://atom.io/download/deb - name: Install atom wordcount package tags: - atom command: cmd: /usr/bin/apm install wordcount creates: /home/crispy/.atom/packages/wordcount - name: download dotfiles tags: dotfiles git: repo: https://github.com/insanity54/dotfiles dest: /home/crispy/dotfiles - name: configure screen tags: dotfiles copy: src: /home/crispy/dotfiles/.screenrc dest: /home/crispy/.screenrc remote_src: yes - name: configure mumble server copy: src: files/mumble-server.ini dest: /etc/mumble-server.ini owner: root group: mumble-server mode: '640' notify: restart mumble - name: upload secrets file tags: - dostream copy: src: files/.secrets dest: /home/crispy/.secrets owner: crispy group: crispy mode: '600' - name: create ~/.local/bin dir file: state: directory path: /home/crispy/.local/bin mode: '755' - name: add ~/.local/bin to path for interactive non-login shells lineinfile: dest: /home/crispy/.bashrc state: present line: "export PATH=$PATH:/home/crispy/.local/bin" - name: add ~/.local/bin to path for interactive login shells lineinfile: dest: /home/crispy/.profile state: present line: "export PATH=$PATH:/home/crispy/.local/bin" - name: upload start-stream script copy: src: files/start-stream dest: /home/crispy/.local/bin/start-stream owner: crispy group: crispy mode: '755' - name: upload open-mumble script copy: src: files/open-mumble dest: /home/crispy/.local/bin/open-mumble owner: crispy group: crispy mode: '755'
This playbook…
- Creates a non-root user account
- Copies my SSH keys from my home computer to the workspace
- Installs dependency software
- Installs Node.js
- Installs my preferred code editors
- Installs my preferred Bash and Screen preferences
- Installs Mumble server as a system daemon & configures it
- Installs a script which handles the FFmpeg incantation for streaming to Twitch
Pretty cool, huh?
After this playbook runs, all I have to do to is…
- Start Mumble client on my home computer & connect to the Mumble server on my cloud workstation.
- Open x2go on my home computer, and connect it to my Cloud Workstation (via SSH)
- Remotely control the cloud workstation, and execute the FFMpeg script.
After that, I’m streaming from the high-speed cloud via my low quality home internet!