BirdDog Encoders – Firmware Analysis and Hardware Peak

I wrote recently about some of my experiences in trying to use BirdDog NDI encoders with OBS, specifically syncing issues. As part of that I took a deeper look into the devices themselves. I love hacking stuff, unfortunately I have to balance that out with warranties haha.

Let’s jump right into it, this is what the backside of a BirdDog Mini looks like inside:

And the other side of the board:

It’s a beautiful board. Black, copper and lots of classy white labels. I’d love to gain access to the serial console, but the selection of headers is a bit slim. There is a lonely header (J2) which appears to be 3.3V and GND. There are various test pins and also a ribbon cable connector labelled “BOT CT” but there’s no indication on the board that any of those pins/pads are the serial console.

BirdDog supplies firmware images for their devices and I was curious what might be inside those files. Looking at the log during a firmware update it looked suspiciously like the BirdDog encoders may be running Linux, which is a common approach to an embedded device like this. Enter binwalk.

We’re working with BirdDog_Flex_In_1.6.2.fw, available in an archive from their support site. I can’t tell you how much this excites me.

We’re going to ask binwalk to recursively scan and extract all archives in the fw update file:

yes, my home folder is aquarat and my desktop is called deskrat, I didn’t say I was creative 😉

binwalk will output a lot of info about what it finds and it will decompress many files that won’t be of interest, but these are the interesting bits:

0.tar  birddog-platform-files  external

birddog-platform-files is a linux rootfs, specifically the product of a Board Support Package, or BSP. The distribution is PetaLinux, which is largely a Xilinx thing.

aquarat@deskrat:~/Downloads/BirdDog teardown/_BirdDog_Flex_In_1.6.2.fw.extracted/_0.extracted$ ls birddog-platform-files/
bin  boot  etc  lib  srv  usr

I probably can’t paste the contents of files here without incurring some sort of copyright theft, but in short, these are the files of interest:

  • Makes a note of the hardware ID, writes the QSPI, which I assume is the bootloader, applies the update package. When you update the firmware and see the log in the browser window, it’s this script’s output that you’re seeing.
  • external/ : Filled with alsa and audio related .deb archives. Alsa seems to be used by the comms system.
aquarat@deskrat:~/Downloads/BirdDog teardown/_BirdDog_Flex_In_1.6.2.fw.extracted/_0.extracted/birddog-platform-files/bin$ ls
bdg_headset_mic  bdg_headset_speaker  birddog-check-gpio  birddog-runner  BirdDogSrvr  birddog-web-ui  _birddog-web-ui.extracted  ndi_src_find_app  tiny_4k_enc_app  ubuntu_ti3104_app
  • most of these files are binary executable (bdg_headset_mic: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/, for GNU/Linux 2.6.32, BuildID[sha1]=8d883e48f7e4789d5fdec8db5e9be466cd843a24, with debug_info, not stripped)
  • birddog-check-gpio: seems to check on startup if the reset button is being pressed. If it is it waits 5 seconds, checks again, resets the network config and the web ui password.
  • birddog-web-ui is interestingly a Golang executable, which relies on template HTML files:
aquarat@deskrat:~/Downloads/BirdDog teardown/_BirdDog_Flex_In_1.6.2.fw.extracted/_0.extracted/birddog-platform-files/bin$ ls ../srv/birddog-web-ui/
birddog_flex_in.png             birddog-mc-enable  decoder-src.html  login.html  net-dhclient.tmpl    network.html                  ptz.html     settings.html     static                trailer.html  update-progress.html  video-param.html
_birddog_flex_in.png.extracted  dashboard.html     header.html       link.html    ndi.html    net-interfaces.tmpl  operation-configuration.html  reboot.html  soft-reboot.html  system-settings.html  update.html   video.html            videoset.html

BirdDogSrvr is a folder containing Node files (JS), which drive the API portion of the system. It’s an express application with a couple of routes. There’s nothing particularly undocumented of interest.

aquarat@deskrat:~/Downloads/BirdDog teardown/_BirdDog_Flex_In_1.6.2.fw.extracted/_0.extracted/birddog-platform-files/bin/BirdDogSrvr$ ls
AboutMe.json  BashScript  BirdDogCam.js  BirdDog.js  connect.json  node_modules  resetPreset.json

I’ve sifted through these files and I haven’t found any way of turning on the installed telnet server or executing arbitrary commands :(. Good job to BirdDog for securing their devices, but meh for me.

aquarat@deskrat:~/Downloads/BirdDog teardown/_BirdDog_Flex_In_1.6.2.fw.extracted/_0.extracted/birddog-platform-files$ cat boot/uEnv.txt 
bootargs=console=ttyPS0,115200 root=/dev/mmcblk0p2 rw rootwait earlyprintk net.ifnames=0 mem=960M uio_pdrv_genirq.of_id=generic-uio
load_script=source ${script_address}
boot_script=load mmc 0:1 ${script_address} ${script_name} && source ${script_address} 
uenvcmd=run boot_script

What are the chances there’s an SD card in there? haha

aquarat@deskrat:~/Downloads/BirdDog teardown/_BirdDog_Flex_In_1.6.2.fw.extracted/_0.extracted/birddog-platform-files$ ls usr/bin
node-v10.13.0-linux-armv7l.tar.xz  _node-v10.13.0-linux-armv7l.tar.xz.extracted

rc.local appears to program the FPGA module and interacts with it through /dev/xdevcfg and /sys/devices/soc0/amba/f8007000.devcfg/prog_done.

rc.local also loads audio modules, extracts Node, sets some execute permissions on files and starts a BirdDog Central Server.

That was the 4K firmware, now let’s have a look at the Mini’s Firmware:

The Mini’s firmware is largely the same structure as the 4K Flex, but with a little more legacy stuff going on. I’m going to concentrate on what’s different and interesting:

  • is different but largely does the same stuff. It’s set up for several older products.
  • external/: is still alsa and sound related deb packages but there’s also another rootfs:
aquarat@deskrat:~/Downloads/BirdDog teardown/_BirdDog_Mini_NDI_4_V_3.6.3.fw.extracted/_0.extracted/external/_bdstum05r4cam7d27feb2020.bin.extracted/_petalinux-user-image-plnx_arm-20191217225041.rootfs.cpio.extracted/cpio-root$ ls
bin  boot  dev  etc  home  init  lib  media  mnt  proc  run  sbin  sys  tmp  usr  var
aquarat@deskrat:~/Downloads/BirdDog teardown/_BirdDog_Mini_NDI_4_V_3.6.3.fw.extracted/_0.extracted/external/_bdstum05r4cam7d27feb2020.bin.extracted/_petalinux-user-image-plnx_arm-20191217225041.rootfs.cpio.extracted/cpio-root$ ls etc
busybox.links.nosuid  filesystems  group-     hostname    init.d    issue    login.defs            mtab           passwd     profile    rc1.d  rc4.d  rcS.d  rpm-postinsts  shadow   skel                 syslog-startup.conf          udev
busybox.links.suid    fstab        gshadow    hosts       inittab    limits        logrotate-dmesg.conf  network        passwd-    protocols  rc2.d  rc5.d  rpc    securetty      shadow-  syslog.conf          syslog-startup.conf.busybox  udhcpc.d
default               group        host.conf  inetd.conf  iproute2  login.access  motd                  nsswitch.conf  petalinux  rc0.d      rc3.d  rc6.d  rpm    services       shells   syslog.conf.busybox  timestamp                    version
aquarat@deskrat:~/Downloads/BirdDog teardown/_BirdDog_Mini_NDI_4_V_3.6.3.fw.extracted/_0.extracted/external/_bdstum05r4cam7d27feb2020.bin.extracted/_petalinux-user-image-plnx_arm-20191217225041.rootfs.cpio.extracted/cpio-root$ cat etc/issue
PetaLinux 2017.3 \n \l
  • There is no ssh server in this rootfs, but there is a telnetd reference (via BusyBox).
  • telnetd is not configured to run in any startup scripts/runlevels.
  • etc/shadow contains a password for root, which is root (thanks John).
  • a getty runs on ttyPS0, which is not the VISCA interface on the minijack port (that’s ttyPS1).


  • bin: most files/utilities are the same, except for ndi_enc_dec_app, mini_dec and mini_enc.
  • slightly different uEnv.txt:
aquarat@deskrat:~/Downloads/BirdDog teardown/_BirdDog_Mini_NDI_4_V_3.6.3.fw.extracted/_0.extracted/birddog-platform-files$ cat boot/uEnv.txt
bootargs=console=ttyPS0,115200 root=/dev/mmcblk0p2 rw rootwait earlyprintk net.ifnames=0 mem=960M uio_pdrv_genirq.of_id=generic-uio
load_script=source ${script_address}
boot_script=load mmc 0:1 ${script_address} ${script_name} && source ${script_address} 
uenvcmd=run boot_script

Leave a Reply