Sunday, 27 December 2015

Streaming from Banana Pi using gstreamer

Banana Pi M1+ is the upgraded version of the the trusty Banana Pi with a 40 pin header with some very useful interfaces for audio; namely the i2s-0 interface and spdif output pins routed into the  upper end pins.


I coupled my DAB module - which is a i2s master device - hence set Banana Pi as i2s slave (in fex file).  The connection can be tested with alsa record / play utils  ..
  arecord  -D hw:2 -f S16_LE -c 2  -r 48000 | aplay -D hw:3  -
where hw2: is the i2s input alsa device (the DAB input); hw:3 is the usb sound card Once the i2s input is proved working, its time to stream this input to my laptop at 192.168.99.130, udp port 5000. The gstreamer pipeline at Banana Pi end is:
   gst-launch-0.10 alsasrc  device=hw:2 ! 'audio/x-raw-int, endianness=(int)1234, signed=(boolean)true, width=(int)16, depth=(int)16, rate=(int)48000,channels=(int)2'! udpsink host=192.168.99.130 port=5000
and at my laptop:
 gst-launch udpsrc port=5000 ! 'audio/x-raw-int, endianness=(int)1234, signed=(boolean)true, width=(int)16, depth=(int)16, rate=(int)48000,channels=(int)2' ! pulsesink
and to play in vlc :
 vlc --demux=rawaud --rawaud-channels=2 --rawaud-samplerate=48000 udp://@:5000
The raw audio datarate for 48k sample rate, S16LE stereo is 1536 Kbits/s. This can easily be handled in a wifi connection, and I had no issues streaming raw over udp. The aggregate data rate will be around with 1.6Mbits/s . If this needs to be carried out in a leaner channel (i.e bluetooth) then opus encoder / decoder is an excellent choice.The opus encoded pipeline is:
 Server : (Banana Pi)
 gst-launch-0.10 alsasrc  device=hw:2 ! 'audio/x-raw-int, endianness=(int)1234, signed=(boolean)true, width=(int)16, depth=(int)16, rate=(int)48000,channels=(int)2' ! opusenc !  udpsink host=192.168.99.130 port=5000
client:
 gst-launch udpsrc port=5000 ! opusdec ! pulsesink
The processor usage for the raw pipeline is about 6~7% and for the opus encoded pipeline is ~25% - excellent !!

Sunday, 4 January 2015

Using Imagemagick to generate a c array for images

Imagemagick is a really versatile tool for manipulating images from the command line. It also is a vary handy tool for generating image bitmaps for embedded systems.
This example is for generating a data array for SSD1306 based OLED displays. These are available  for a few pounds from china / ebay and comes in two forms of interfaces:   i2c or spi interface.  For simplicity I picked up an i2c one.
The data is written as column major. i.e each byte represents 8 vertical pixels. The following image is from the SSD1306 datasheet, describing display ram writing sequence:
click to enlarge
It takes 128x64/8 = 1024 bytes for a full image for these displays. So lets start with a simple 350x174 png image :

now use imagemagick to convert this to an .xbm

  convert -resize 128x64\! 1.png 1.xbm  
The 1.xbm file looks like :
 #define 1_width 128  
 #define 1_height 64  
 static char 1_bits[] = {  
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0,   
  0x03, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   
  0x00, 0x00, 0x00, 0x60, 0x87, 0xCF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0xFC, 0x81, 0x01, 0x00,   
  0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,   
 ........ abbreviated ...       
  0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,   
  0x00, 0xC0, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00,   
  0x00, 0x00, 0x00, 0x00, };  
Now we have a c array with 1024 bytes where eight pixels in each byte. as mentioned before, this data is row major, and needs to be transposed to column major array of 1024 bytes. The transpose looks something like :
      __u8 xfdata[1024] = {0x00,};   
      int row, col , offset , pos;  
      pos =0;   
      for (row=0; row <1024; row +=128)  
      {  
           for (col=0; col <128 ; col++)  
           {  
                pos = (0x07 & col);  
                offset = (col >> 3);  
                if ((1<<pos) & data[row+offset+0]) xfdata[row+col] +=1;  
                if ((1<<pos) & data[row +offset+16]) xfdata[row+col] +=2;  
                if ((1<<pos) & data[row +offset +32]) xfdata[row+col] +=4;  
                if ((1<<pos) & data[row +offset +48]) xfdata[row+col] +=8;  
                if ((1<<pos) & data[row +offset + 64]) xfdata[row+col] +=16;  
                if ((1<<pos) & data[row +offset +80]) xfdata[row+col] +=32;  
                if ((1<<pos) & data[row +offset +96]) xfdata[row+col] +=64;  
                if ((1<<pos) & data[row +offset +112]) xfdata[row+col] +=128;  
           }  
      }  

the xfdata[] can now be dumped into SSD1306 ...
note: this display is a dual colour display, where top 16rows are yellow, hence the yellow hats !