LE Audio: Broadcast Audio Streams

LE Audio: Broadcast Audio Streams

Chapter 8 — BIG, BIS, BASE, BASS, Commanders and how they all connect

BAP
Core Profile
BASS
Key Service
ISO
Socket API

What you will learn

BIG / BIS BASE Structure Broadcast Source Broadcast Sink BASS BIS_Sync Broadcast Assistant Scan Delegator Commander Broadcast_Code HCI LE_Create_BIG PAST Presentation Delay BlueZ ISO Socket

What Is Broadcast Audio in LE Audio?

Classic Bluetooth audio always works one-to-one. One phone, one set of headphones. They pair, connect, and audio flows between just those two devices. LE Audio changes this with Broadcast, which lets one device transmit audio to any number of receivers at the same time — with no pairing needed.

Think of FM radio. The station transmits. Anyone with a receiver in range can tune in without registering or connecting to the station. LE Audio Broadcast works the same way, except it is digital, uses LC3 audio compression, and supports features FM never could — like encrypted private streams and smart remote control via Commanders.

The broadcast topology originally came from hearing loop (telecoil) systems used in public venues. But LE Audio’s version is far more powerful: it handles multiple simultaneous streams, supports different languages in the same broadcast, encrypts audio for private use, and lets a phone or remote act as a smart controller for your earbuds.

The Three Things Broadcast Needs to Do

Broadcast in LE Audio is split into three separate functions. Different devices implement different subsets of these:

Broadcast Audio — Three Core Functions
1. Transmitting The Broadcast Source sends audio out over BIS channels grouped inside a BIG. It does not know or care who is listening. 2. Finding A Broadcast Assistant (part of a Commander) scans for available streams, filters them by what the receiver can handle, and hands the info to the receiver’s earbuds or hearing aids. 3. Receiving The Broadcast Sink (Acceptor) scans, locks on to the BIG, picks a BIS, decodes LC3, applies Presentation Delay, and renders audio. It can do this alone or with a Broadcast Assistant’s help.

In most real products all three happen together, but the specs keep them separate so each device only has to implement what it needs. A PA amplifier in an auditorium only needs the Transmit function. A hearing aid only needs the Receive function. A phone typically handles all three.

Setting Up a Broadcast Source

A Broadcast Source is the device that puts audio on air. A TV, a phone sharing audio, or a PA system all fit this role. Unlike a unicast Central device, the Broadcast Source does not need any receivers to be present. It just transmits. The Host application inside the device gives the Controller all the configuration it needs, and the Controller handles the rest.

State Machine — Three States

Broadcast Source State Machine
IDLE
CONFIGURED
STREAMING
← Release (back to Idle) ← Disable (back to Configured) Reconfiguration ↷ / Metadata Update ↷
Idle: Controller not set up. Nothing transmitting.  |  Configured: Advertising running, no audio yet.  |  Streaming: Audio packets flowing over BIS channels.

Transitions between states use HCI commands. Unlike unicast (where both sides negotiate), the Broadcast Source does all of this unilaterally — no handshakes with any receiver.

BAP defines six procedures for these transitions (configuration, establishment, disable, metadata update, release, reconfiguration). CAP simplifies them into five grouped procedures: Broadcast Audio Start, Update, Stop, Reception Start, and Reception Stop.

BASE — What the Receiver Reads to Know What Is Available

The BASE (Basic Audio Stream Endpoint) is a data structure packed into the Periodic Advertisement by the Broadcast Source. Every receiver reads the BASE to find out: how many audio streams are available, what codec is used, what quality, and what the content is (music, speech, which channel, which language, etc.).

BASE has three nested levels:

BASE — 3-Level Hierarchy
LEVEL 1 — BIG-wide parameters (applies to all streams)
Presentation Delay  |  Number of Subgroups
LEVEL 2 — Subgroup 0
Num_BIS  |  Codec_ID (LC3)  |  Codec Config  |  Metadata
e.g. Language: English, Context: Media
LEVEL 2 — Subgroup 1
Num_BIS  |  Codec_ID (LC3)  |  Codec Config  |  Metadata
e.g. Language: Spanish, Context: Media
LEVEL 3
BIS[0][0]
Index: 0x01
Left Channel
LEVEL 3
BIS[0][1]
Index: 0x02
Right Channel
LEVEL 3
BIS[1][0]
Index: 0x03
Left Channel
LEVEL 3
BIS[1][1]
Index: 0x04
Right Channel

The rule for which level to use:

  • Different languages or different parental ratings → separate subgroups (Level 2 entries). There is no other way to express this difference.
  • Different audio quality of the same content → separate BIS entries at Level 3 within the same subgroup (e.g., one BIS at 48 kHz, one at 24 kHz).
  • Typical stereo stream → one subgroup, two BISes (Left at index 1, Right at index 2). That is all most real products need.
Heads up: BIS_index starts from 1 (not 0). CIS_ID in unicast starts from 0. This is explicitly different and trips up many implementations.

BASE Parameters Table

Level Parameter Size What it means
1 (BIG) Basic Audio Announcement UUID 2 B Fixed value 0x1852 — marks this advertisement as a broadcast audio source
1 Presentation Delay 3 B Time in µs between packet arrival and audio output. Range 0 to 16.7 s.
1 Num_Subgroups 1 B How many subgroups exist in this BIG
2 (Subgroup) Num_BIS[i] 1 B Number of BIS channels in this subgroup
2 Codec_ID[i] 5 B Codec for this subgroup. 0x06 = LC3 (standard LE Audio codec).
2 Codec_Specific_Configuration[i] Varies Sampling rate, frame duration, bitrate, audio channel allocation
2 Metadata[i] Varies Language, parental rating, Streaming_Audio_Contexts (CAP requires this field)
3 (BIS) BIS_index[i[k]] 1 B Unique BIS number within the BIG. Starts at 1.
3 Codec_Specific_Configuration[i[k]] Varies Per-BIS codec override. Used when BISes in the same subgroup differ in quality.

Creating the BIG — LE_Create_BIG HCI Command

Once the BASE is assembled, the Host sends LE_Create_BIG to the Controller. This command schedules all the BIS channels and starts the isochronous broadcast. The key parameters:

Parameter Purpose
BIG_Handle Host-set identifier for this BIG
Advertising_Handle Links this BIG to the Extended Advertising set already running
Num_BIS Total number of BIS channels to create
SDU_Interval Time between audio packets in µs (e.g., 10000 = 10 ms per LC3 frame)
Max_SDU Maximum payload bytes per packet — must accommodate the largest BIS in the BIG
PHY 0 = 1 Mbps, 1 = 2 Mbps (preferred for audio), 2 = LE Coded
Packing 0 = Sequential, 1 = Interleaved. Controller treats this as a preference, not a mandate.
RTN Requested retransmission count. Controller uses this as a hint.
Encryption 0 = unencrypted, 1 = encrypt with Broadcast_Code
Broadcast_Code 16-byte encryption key. Set to all-zeros for unencrypted streams.
Key difference from CIG (unicast): Every BIS in a BIG shares the same QoS parameters. This is because BIGInfo — the structure receivers use to lock on — cannot carry per-BIS QoS values. If your BISes carry different quality levels, all of them must be sized to the largest one, which wastes some airtime.

How the Advertising Chain Works

A Broadcast Source runs three layers of advertising simultaneously. A receiver scans through all three layers to get everything it needs before locking on to the audio:

Broadcast Advertising Chain — From ADV to BIS
ADV_EXT_IND  —  Primary Advertisement Contains: Advertiser Address, SID (Advertising Set ID), AuxPtr pointing down to secondary
↓ AuxPtr
AUX_ADV_IND  —  Secondary Advertisement Contains: Broadcast Audio Announcement Service UUID, Broadcast_ID (3 bytes), SyncInfo for PA
↓ SyncInfo
AUX_SYNC_IND  —  Periodic Advertisement Contains: Basic Audio Announcement UUID, BASE (codec config, subgroups, BIS indices), BIGInfo in ACAD
↓ BIGInfo (hopping sequence, anchor points)
BIS Isochronous Data Channels Actual LC3-encoded audio PDUs transmitting at SDU_Interval (e.g., every 10 ms)
SID = static per power cycle  |  Broadcast_ID = static per BIG lifetime  |  Source_ID = assigned by Acceptor, local reference only

BlueZ Code: Creating a BIG Using ISO Sockets

Linux kernel 5.14+ supports Broadcast Isochronous Streams via ISO sockets in BlueZ. A connect() call on a bound ISO socket triggers LE_Create_BIG inside the kernel automatically:

/*
 * broadcast_source.c
 * Minimal Broadcast Source: creates a BIG with one BIS using BlueZ ISO socket API.
 * Build: gcc -o bcast_src broadcast_source.c -lbluetooth
 * Run as root: ./bcast_src
 */

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/iso.h>

/* LC3 at 16 kHz, 10 ms frame, 32 kbps = 40 bytes per SDU */
#define LC3_SDU_SIZE   40
#define SDU_INTERVAL   10000   /* 10 ms in microseconds */

int main(void)
{
    int sk, ret;
    struct sockaddr_iso src = {0}, dst = {0};
    struct bt_iso_qos    qos = {0};
    uint8_t              frame[LC3_SDU_SIZE] = {0};

    /* 1. Create an ISO socket for BIS (broadcast) */
    sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_ISO);
    if (sk < 0) { perror("socket"); return 1; }

    /* 2. Bind to default local adapter */
    src.iso_family      = AF_BLUETOOTH;
    src.iso_bdaddr_type = BDADDR_LE_PUBLIC;
    bacpy(&src.iso_bdaddr, BDADDR_ANY);

    ret = bind(sk, (struct sockaddr *)&src, sizeof(src));
    if (ret < 0) { perror("bind"); goto fail; }

    /* 3. Set BIG / BIS QoS parameters */
    qos.bcast.big           = BT_ISO_QOS_BIG_UNSET; /* kernel assigns */
    qos.bcast.bis           = BT_ISO_QOS_BIS_UNSET; /* kernel assigns */
    qos.bcast.packing       = 0x00;  /* Sequential */
    qos.bcast.framing       = 0x00;  /* Unframed */
    qos.bcast.encryption    = 0x00;  /* No encryption */
    memset(qos.bcast.bcode, 0, sizeof(qos.bcast.bcode));

    /* TX path parameters (LE Audio 16_2 preset) */
    qos.bcast.out.phy      = 0x02;        /* 2 Mbps */
    qos.bcast.out.sdu      = LC3_SDU_SIZE;
    qos.bcast.out.interval = SDU_INTERVAL;
    qos.bcast.out.latency  = 10;          /* ms */
    qos.bcast.out.rtn      = 2;           /* retransmissions */

    ret = setsockopt(sk, SOL_BLUETOOTH, BT_ISO_QOS, &qos, sizeof(qos));
    if (ret < 0) { perror("setsockopt BT_ISO_QOS"); goto fail; }

    /* 4. Connect: triggers LE_Create_BIG inside BlueZ kernel stack.
     *    iso_bdaddr_type = BDADDR_BREDR signals broadcast mode.
     *    iso_num_bis / iso_bis[] choose which BIS indices to create. */
    dst.iso_family      = AF_BLUETOOTH;
    dst.iso_bdaddr_type = BDADDR_BREDR;  /* broadcast flag */
    bacpy(&dst.iso_bdaddr, BDADDR_ANY);
    dst.iso_bc_sid      = 0x00;
    dst.iso_num_bis     = 1;
    dst.iso_bis[0]      = 0x01;          /* BIS index 1 (Left channel) */

    ret = connect(sk, (struct sockaddr *)&dst, sizeof(dst));
    if (ret < 0) { perror("connect (BIG create)"); goto fail; }

    printf("BIG created — sending audio frames\n");

    /* 5. Write LC3-encoded audio frames (here we send silence) */
    for (int i = 0; i < 500; i++) {        /* 500 frames = 5 seconds */
        /* In a real application: lc3_encode(pcm_buf, frame, LC3_SDU_SIZE) */
        ret = write(sk, frame, LC3_SDU_SIZE);
        if (ret < 0) { perror("write"); break; }
        usleep(SDU_INTERVAL);               /* pace to SDU interval */
    }

    printf("Done — releasing BIG\n");
    close(sk);
    return 0;

fail:
    close(sk);
    return 1;
}

Receiving a Broadcast Audio Stream

A Broadcast Sink scans for Extended Advertisements that carry the Broadcast Audio Announcement Service UUID. When it finds one it reads through the advertising chain, gets the BASE and BIGInfo from the Periodic Advertisement, picks the right BIS, and locks on.

The Four-Step Reception Flow

Broadcast Sink — Step-by-Step Reception
1 Scan for Extended Advertisements — filter on Broadcast Audio Announcement UUID and a valid Broadcast_ID. Also check profile-specific UUIDs (e.g., Public Broadcast Profile) to narrow down relevant streams.
2 Sync to Periodic Advertising (PA) — use SyncInfo from the AUX_ADV_IND to lock on to the AUX_SYNC_IND train. Read the BASE and BIGInfo from it. Now the Sink knows every stream’s codec, format, and channel layout.
3 Choose which BIS to receive — match codec config, Audio Location (Left/Right), Context Type, and language against what this device supports. The Acceptor makes this decision autonomously (or it is told by a Commander via BIS_Sync).
4 Sync to BIG and receive audio — use BIGInfo (hopping sequence + BIS Anchor Points) to lock on to the right BIS. Set up the audio data path. Apply the Presentation Delay so left and right earbuds render audio at the exact same moment.

BlueZ Code: Scanning and Syncing to a BIS

/*
 * broadcast_sink.c
 * Connects to a known Broadcast Source and receives BIS audio via ISO sockets.
 * BlueZ kernel ISO socket API (kernel 5.14+).
 * Build: gcc -o bcast_sink broadcast_sink.c -lbluetooth
 */

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/iso.h>

#define MAX_SDU 200

int main(void)
{
    int sk, ret;
    struct sockaddr_iso src = {0}, bsrc = {0};
    uint8_t  buf[MAX_SDU];
    ssize_t  n;

    /* 1. Open ISO socket */
    sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_ISO);
    if (sk < 0) { perror("socket"); return 1; }

    /* 2. Bind to local adapter */
    src.iso_family      = AF_BLUETOOTH;
    src.iso_bdaddr_type = BDADDR_LE_PUBLIC;
    bacpy(&src.iso_bdaddr, BDADDR_ANY);

    ret = bind(sk, (struct sockaddr *)&src, sizeof(src));
    if (ret < 0) { perror("bind"); goto fail; }

    /*
     * 3. Connect to the Broadcast Source.
     *    iso_bdaddr       = address of the Broadcast Source device
     *    iso_bdaddr_type  = BDADDR_LE_PUBLIC or BDADDR_LE_RANDOM
     *    iso_bc_sid       = SID from ADV_EXT_IND (Advertising Set ID)
     *    iso_num_bis      = how many BIS we want to receive
     *    iso_bis[]        = BIS index values from the BASE
     *
     * The kernel handles PA sync and BIG sync internally.
     * On success, the socket is ready to receive audio frames.
     */
    bsrc.iso_family      = AF_BLUETOOTH;
    bsrc.iso_bdaddr_type = BDADDR_LE_PUBLIC;
    str2ba("AA:BB:CC:DD:EE:FF", &bsrc.iso_bdaddr); /* source address */

    bsrc.iso_bc_sid    = 0x00;  /* SID from the primary advertisement */
    bsrc.iso_num_bis   = 2;     /* receive stereo: Left + Right */
    bsrc.iso_bis[0]    = 0x01;  /* BIS index 1 = Left channel */
    bsrc.iso_bis[1]    = 0x02;  /* BIS index 2 = Right channel */

    ret = connect(sk, (struct sockaddr *)&bsrc, sizeof(bsrc));
    if (ret < 0) { perror("connect (BIG sync)"); goto fail; }

    printf("Synced to BIG — receiving audio\n");

    /* 4. Read incoming LC3 frames and pass to decoder */
    while ((n = read(sk, buf, sizeof(buf))) > 0) {
        /*
         * Each read() returns one SDU (one LC3 frame per BIS).
         * Pass to LC3 decoder:
         *   lc3_decode(decoder, buf, n, pcm_out, stride);
         * Then write pcm_out to ALSA / audio pipeline.
         */
        printf("Rx %zd bytes | pass to LC3 decoder\n", n);
    }

    close(sk);
    return 0;

fail:
    close(sk);
    return 1;
}

BASS — Broadcast Audio Scan Service

A Broadcast Sink can find and receive streams on its own — but that is not a good user experience. A small earbud with no screen needs to independently pick from potentially dozens of broadcast streams nearby. That is impractical. BASS solves this.

BASS is a GATT service that lives inside the Broadcast Sink (the Acceptor). It exposes the Sink’s current state — what streams it knows about, which one it is receiving, whether it needs an encryption key — so a separate device (a phone, a remote, a smartwatch acting as a Commander) can read and control it over a regular ACL connection.

The Two BASS Characteristics

Characteristic Properties Count What it does
Broadcast Audio Scan Control Point Write, Write w/o response One Commander writes here to give the Acceptor instructions: add or remove a source, change BIS_Sync, provide an encryption key
Broadcast Receive State Read, Notify One or more Acceptor reports its current status — which BIG it is synced to, which BIS channels are active, encryption state, current metadata. One instance per BIG the Acceptor can receive simultaneously.

Control Point Operation Codes

Opcode Operation What happens
0x00 Remote Scan Stopped Assistant reports it has stopped scanning on the Acceptor’s behalf
0x01 Remote Scan Started Assistant reports it is now scanning. Acceptor may stop its own scanning to save power.
0x02 Add Source Commander tells Acceptor about a Broadcast Source (address, SID, Broadcast_ID) and which BIS channels to receive (BIS_Sync bitmap)
0x03 Modify Source Update an existing source entry. Setting BIS_Sync = 0 stops that stream; setting it = 1 starts it again.
0x04 Set Broadcast Code Deliver the 16-byte decryption key for an encrypted BIG along with its Source_ID
0x05 Remove Source Delete the source record entirely. Acceptor stops receiving that BIG.

BIS_Sync — The Most Important Field

BIS_Sync is a 4-byte (32-bit) bitmap sent from the Commander to the Acceptor in the Add Source or Modify Source operations. Each bit position corresponds to a BIS index:

BIS_Sync Bitmap — How Individual Streams Are Selected
Bit 0 Bit 1 Bit 2 Bit 3 Bit 4 Bit 31
BIS index 1
e.g. Left CH
BIS index 2
e.g. Right CH
BIS index 3 BIS index 4 BIS index 5 BIS index 32
Set a bit to 1 to receive that BIS. Set to 0 to skip it.
BIS_Sync is per-subgroup: only bits from one subgroup should be set at a time (you pick English or Spanish, not both).
0xFFFFFFFF = “no preference” — Acceptor decides which BIS to use.
Example: Left earbud (wants BIS index 1) → BIS_Sync[0] = 0x00000001  |  Right earbud (wants BIS index 2) → BIS_Sync[0] = 0x00000002

Broadcast Receive State — What the Acceptor Reports Back

Field Size Meaning
Source_ID 1 B Acceptor-assigned local ID for this source record. Not shared with the other earbud — both assign their own.
Source_Address + SID + Broadcast_ID 8 B + 1 B + 3 B Identifies the Broadcast Source this record refers to
PA_Sync_State 1 B 0x00=Not synced, 0x01=SyncInfo needed (request to Commander), 0x02=Synced, 0x03=Failed, 0x04=No PAST
BIG_Encryption 1 B 0x00=No encryption, 0x01=Code needed, 0x02=Decrypting, 0x03=Wrong key (Bad_Code)
BIS_Sync_State[i] 4 B Same bitmap as BIS_Sync, but showing current state — which BIS channels the Acceptor is actively receiving right now
Metadata[i] Varies Current Level 2 metadata (audio context, language, etc.) the Acceptor has for this stream

Commanders — The Smart Remote for Broadcast

Without a Commander, a user with two earbuds would have to manually navigate each earbud’s minimal interface to select a stream, and handling encrypted streams would be nearly impossible. A Commander solves this by acting as a central control point — like a smart TV remote that also knows how to find radio stations and quietly pass the decryption keys to your earbuds.

Commander and Acceptor — Architecture Block Diagram
COMMANDER (e.g., Phone App or Dedicated Remote) ACL Link ACCEPTOR (e.g., Earbud / Hearing Aid)
Broadcast Assistant
Scans for streams. Reads BASE and filters by Acceptor capabilities. Presents list to user. Writes Add Source to BASS.
Scan Delegator (BASS)
Solicits Broadcast Assistants. Hosts BASS characteristics. Receives source info and BIS_Sync commands from Commander.
CSIP Set Coordinator
Discovers all members of a Coordinated Set (e.g., left + right earbud) and sends commands to all of them.
CSIP Set Member
Identifies itself as part of a Coordinated Set. Commander uses this to find the matching earbud.
VCP Volume Controller
Controls volume on each Acceptor in the set.
VCP Volume Renderer + PACS
Renders volume. PACS exposes codec capabilities so Commander filters streams by what this device can decode.
Broadcast_Code Manager
Holds or fetches the decryption key. Sends it via opcode 0x04 (Set Broadcast Code) when Acceptor requests it.
Broadcast Sink
Does the actual BIG / BIS sync and LC3 decoding. Reports sync status and encryption state via Broadcast Receive State notifications.

Scan Delegator — How the Acceptor Solicits Help

The Scan Delegator role lives inside the Broadcast Sink. Its only job is to find Broadcast Assistants and hand them the scanning work. It does this by sending a Solicitation Request — an Extended Advertisement containing the BASS Service UUID. Any Broadcast Assistant that sees it can connect and take over.

Why offload scanning? Scanning is power-hungry. A hearing aid or earbud running on a tiny zinc-air battery should not scan continuously. A phone or dedicated remote has a much larger battery and a screen to show the results. Offloading scanning saves battery on the earbuds and dramatically improves the user experience.

Remote Broadcast Scanning — The Full Setup Flow

Broadcast Assistant Sets Up for a Coordinated Set of Acceptors
Broadcast Assistant (Phone) Action / Message Broadcast Sink (Earbud Pair)
← Solicitation (AUX_EXT_IND + BASS UUID) Earbud 1 sends solicitation request
Responds, initiates ACL connect ACL Connect →
Discovers Coordinated Set membership via CSIP Read CSIS characteristics → Earbud 1 reports it is in a set with Earbud 2
Finds and connects to Earbud 2 ACL Connect → Earbud 2
Reads PACS on both earbuds — now knows what codecs, locations, and context types they support Read PACS (both earbuds) →
Reads current BASS Receive States; subscribes to notifications Discover BASS, Read Receive States →
Starts scanning for Broadcast Sources that match both earbuds’ capabilities Write Scan Control Point: 0x01 (Scan Started) → Both earbuds may now stop their own scanning
User picks a stream from the list on the phone screen Write Scan Control Point: 0x02 (Add Source + BIS_Sync) → Both earbuds receive source identity and sync instruction
← Notify Receive State (PA_Sync=0x02, BIS_Sync active) Both earbuds notify success — audio flowing

A Broadcast Assistant can also use PAST (Periodic Advertising Sync Transfer) to hand the PA sync directly to each earbud, instead of making the earbud scan for the periodic advertisement itself. This is faster and more power-efficient — the process is called Scan Offloading.

Broadcast_Codes — Encrypted Private Streams

A Broadcast Source can encrypt its audio using a 16-byte Broadcast_Code. Without this key, a receiver can lock on to the BIG but cannot decode the audio — it hears nothing. This creates a software boundary around the broadcast: only people or devices with the code can listen.

Encryption lifetimes vary widely by use case:

Use Case Code Lifetime
Coffee shop background music Permanent — the code stays the same indefinitely
Hotel room TV Per stay — changes when the guest checks out
Personal TV at home Per power cycle — refreshes at each boot so visitors cannot keep the code
Phone audio sharing Per session — brand new code each time you share

How the Code Gets to the Acceptor

When an Acceptor locks on to an encrypted BIG, it cannot decrypt the audio. It sets BIG_Encryption = 0x01 in its Broadcast Receive State and sends a notification to all connected Broadcast Assistants — essentially asking “does anyone have the key?”

Any Assistant that has the code can respond by writing opcode 0x04 (Set Broadcast Code) to the Scan Control Point with the 16-byte key and the Source_ID. If the Acceptor previously had a wrong key, it sets BIG_Encryption = 0x03 and includes the bad key in the Bad_Code field so Assistants do not waste time re-sending the same wrong key.

Presentation Delay in Broadcast

In unicast, both sides negotiate the Presentation Delay. In broadcast, the Source picks a value and puts it in the BASE. Receivers must use it or fall back to a default. This matters because left and right earbuds need to render audio at exactly the same moment.

Profile / Rule Minimum Supported Delay Notes
BAP 40 ms All Broadcast Receivers must support at least 40 ms. Safe default for Sources.
TMAP / HAP 20 ms Acceptors qualifying to TMAP or HAP must support 20 ms. Better choice for live audio (lower latency).
Fallback rule 40 ms If a Source sets a delay that is too low or too high for the Acceptor’s buffers, the Acceptor should fall back to 40 ms to stay in sync with the other earbud.

Handover Between Unicast and Broadcast

CAP defines two procedures to switch the same audio content between unicast and broadcast without an audible gap. A real-world example: you are listening to TV over a unicast connection, then a friend arrives and you want to share the audio. The TV switches to broadcast mode. Both of you now receive over BIS channels.

Two important rules govern this handover:

  • The Streaming Context Types and CCID list from the original unicast stream carry over to the broadcast stream. The original listener keeps full playback control (pause, fast-forward, volume) because the ACL link stays up.
  • Friends who join the broadcast only get the audio. They do not inherit control because they never had the unicast connection.

Summary — Key Terms at a Glance

Chapter 8 Reference Table
Term One-line explanation
BIG Broadcast Isochronous Group — the top-level container for all BIS audio channels from one Source
BIS Broadcast Isochronous Stream — one audio channel inside a BIG (e.g., Left or Right)
BASE Data structure in the Periodic Advertisement: codec, subgroups, BIS indices, metadata — everything a receiver needs to pick a stream
BIGInfo Timing and hopping information in the PA’s ACAD field. Receiver uses this to find BIS Anchor Points.
Broadcast_ID 3-byte random ID for a BIG. Lives in AUX_ADV_IND. Static for the BIG’s lifetime.
SID Advertising Set ID. Lives in ADV_EXT_IND. Does not change between power cycles. Identifies the advertising set.
Source_ID Local ID assigned by the Acceptor. Not shared. Left and right earbuds have different Source_IDs for the same BIG.
BASS GATT service inside the Acceptor. Exposes stream state and receives instructions from Broadcast Assistants.
BIS_Sync 4-byte bitmap from Commander to Acceptor selecting which BIS channels to receive. One subgroup at a time only.
Broadcast Assistant Scans for Broadcast Sources on behalf of the Acceptor and writes source info to BASS
Scan Delegator Role in the Acceptor. Solicits Broadcast Assistants using Extended Advertising containing the BASS UUID.
Commander Device combining Broadcast Assistant + CSIP Set Coordinator + VCP Volume Controller. Acts as the user’s remote control.
PAST Periodic Advertising Sync Transfer. Lets the Broadcast Assistant give the Acceptor direct PA sync without re-scanning. Called Scan Offloading.
Broadcast_Code 16-byte key for encrypting a BIG. Delivered via BASS opcode 0x04 or out-of-band. Lifetime varies by application.

Continue the LE Audio Series

This post covered Chapter 8 — Broadcast Audio Streams. The series continues with Volume Control Profile, Hearing Access Profile, and Media Control.

View Full LE Audio Series Back to EmbeddedPathashala

Leave a Reply

Your email address will not be published. Required fields are marked *