As mentioned in my post on building a Broadcast Audio Source the Broadcast Audio URI (BAU) spec allows sharing information about broadcast sources through QR codes, NFC tags and more.
In this post, I'll show how you can make a web application that can read and parse the Broadcast Audio URI QR codes.
BarcodeDetector API
Reading barcodes directly in the browser is made possible with the introduction of the BarcodeDetector API and in this post, I will show an example of how it can be used to read the Broadcast Audio URI QR code with a few lines of JavaScript.
NOTE: The BarcodeDetector API is not yet available in all browsers. For this reason, a polyfill will be used where support is still missing.
Reading barcodes with this API is extremely simple. It only requires passing an image source to the detect()
function, which returns a Promise that fulfills with an array of DetectedBarcode
objects.
Even though we are only focused on QR codes in this post, the API supports many commonly used formats.
Lights, Camera, Action!
First, the camera needs to be switched on and streaming a video feed to an HTMLVideoElement
:
...
<video id="camera"
muted
autoplay="autoplay"
playsinline="playsinline">
</video>
...
and
...
const camera = document.querySelector('#camera');
const constraints = {video: true, audio: false};
let stream = await navigator.mediaDevices.getUserMedia(constraints);
camera.srcObject = stream;
...
We also need to make a barcode detector object:
const barcodeDetector = new window.BarcodeDetector();
In order for the detection to work as expected, the detect
function needs to be called at regular intervals to scan images captured with the camera:
let lastCode = "";
async function decodeQr(){
const barcodes = await barcodeDetector.detect(camera);
if (barcodes?.length) {
for (const barcode of barcodes) {
// Try to parse the URI string
const decoded = parseBroadcastURI(barcode.rawValue);
if (decoded?.length) {
// Avoid repainting if the data is already shown
if (lastCode == barcode.rawValue) {
break;
}
lastCode = barcode.rawValue;
// Display the parsed info
showBroadcastInfo(decoded);
break; // Only use the first code found
}
}
} else {
lastCode = "";
}
// Try again in 100ms
setTimeout(this.decodeQr, 100);
}
Parsing the Broadcast Audio URI
The scanned code should contain a string, starting with BLUETOOTH:
, followed by a number of fields as listed in the Broadcast Audio URI spec.
A QR code example from the spec:
, containing the following data:
BLUETOOTH:UUID:184F;BN:SG9ja2V5;SQ:1;AT:0;AD:AABBCC001122;AS:1;BI:DE51E9;PI:F
FFF;NS:1;BS:1;;
roughly translates to:
"Related to the 0x184F UUID (Broadcast Audio Scan Service), there is a Standard Quality mono channel broadcast at addresss AA:BB:CC:00:11:22 with Broadcast ID 0x0E51E9 and the Broadcast name 'Hockey'"
For this PoC, I created a very simple function to parse the most common fields:
const BROADCAST_AUDIO_URI_SCHEME = 'BLUETOOTH:';
const parseBroadcastURI = (str) => {
if (!str.startsWith(BROADCAST_AUDIO_URI_SCHEME)) {
return [];
}
const result = [];
// split sections (;)
const sections = str.substring(BROADCAST_AUDIO_URI_SCHEME.length).split(';');
sections.forEach(section => {
const [key, value] = section.split(':');
switch (key) {
case 'UUID':
result.push({
type: key,
name: 'UUID',
value: `0x${value}`
});
break;
case 'BI': // Broadcast ID
result.push({
type: key,
name: 'Broadcast ID',
value: `0x${value.padStart(6,0)}`
});
case 'BN': // Broadcast name
result.push({
type: key,
name: 'Broadcast Name',
value: new TextDecoder().decode(base64ToBytes(value))
});
break;
// ... (more fields in full scouce)
}
});
return result;
}
QR Scanner component
In order to make it possible for others to more easily embed the BAU QR scanner in a web application, I decided to create a web component that encapsulates the logic to control the video feed and display the information found in an overlay info box.
In order to use it in a page, just add the following:
<bau-scanner></bau-scanner>
<script type="module" src="./bau-scanner.js"></script>
The element has functions to start and stop the scanning (and camera feed), which can be hooked up to button events, e.g.:
...
<button id="start_scanner">Start scanner</button>
<button id='stop_scanner'>Stop scanner</button>
...
and
...
const startScan = document.querySelector('#start_scanner');
const stopScan = document.querySelector('#stop_scanner');
scanner = document.querySelector('bau-scanner');
startScan.addEventListener('click', scanner.startCamera);
stopScan.addEventListener('click', scanner.stopCamera);
...
An example index.html
together with the bau-scanner.js
component can be found here: https://github.com/larsgk/bau-source
See it in action
For demonstration purposes, I decided to use two QR codes from the Broadcast Audio URI spec plus a dynamic one, generated with the Broadcast Audio Source sample covered in another post.
Here is the scanner in action, running on a mobile phone:
You can try it yourself by opening this link, where the demo page is hosted: https://larsgk.github.io/bau-source/
Enjoy ;)
Top comments (0)