What is LC3?
LC3 stands for Low Complexity Communication Codec. It is the default and mandatory codec for BLE Audio. LC3 encodes audio at much lower bitrates than SBC while sounding better. At 32 kbps, LC3 sounds comparable to SBC at 128 kbps.
One important detail: LC3 is a single-channel codec. If you want stereo audio, you run two independent LC3 encoders — one per channel — and multiplex the two codec frames together into one packet called a Media Packet.
LC3 does not define its own transport format. BAP defines a packet format (the LC3 Media Packet) for carrying LC3-encoded data over isochronous streams.
Key Terms
LTV Structures — How Codec Parameters Are Encoded
All codec parameters in BAP are carried in LTV (Length-Type-Value) structures. This is a compact binary format:
| Length 1 byte — counts Type + Value bytes |
Type 1 byte — identifies which parameter |
Value (Length − 1) bytes — the actual data |
For example, the Sampling_Frequency LTV structure for 16 kHz looks like this in raw bytes:
/* LTV for Sampling_Frequency = 16000 Hz */ 0x02 /* Length = 2 (1 byte Type + 1 byte Value) */ 0x01 /* Type = Sampling_Frequency */ 0x03 /* Value = 0x03 = 16000 Hz (see Bluetooth Assigned Numbers) */ /* LTV for Frame_Duration = 10 ms */ 0x02 /* Length = 2 */ 0x02 /* Type = Frame_Duration */ 0x01 /* Value = 0x01 = 10 ms */ /* LTV for Octets_Per_Codec_Frame = 40 octets */ 0x03 /* Length = 3 (1 byte Type + 2 bytes Value) */ 0x04 /* Type = Octets_Per_Codec_Frame */ 0x28 0x00 /* Value = 0x0028 = 40 (little-endian) */
These LTV structures are packed one after another in the Codec_Specific_Configuration field. This field appears inside PAC records (for capabilities) and in ASE characteristics (for active configuration).
Decoding the Codec Setting Name — What Does “16_2_1” Mean?
BAP uses a standardized naming convention for codec and QoS settings. Once you understand it, you can read any setting name at a glance.
in kHz
→ 16,000 Hz
1 = 7.5 ms 2 = 10 ms
→ 10 ms frames
1 = Low latency
2 = High reliability
Codec Capability Settings at a Glance
The table below shows the main codec settings defined by BAP. 16_2 and 24_2 are the mandatory settings. Everything else is optional.
| Setting | Freq (kHz) | Frame (ms) | Octets/Frame | Bitrate | Mandatory? |
|---|---|---|---|---|---|
| 8_1 | 8 | 7.5 | 26 | 27.7 kbps | Optional |
| 8_2 | 8 | 10 | 30 | 24 kbps | Optional |
| 16_1 | 16 | 7.5 | 30 | 32 kbps | Optional |
| 16_2 | 16 | 10 | 40 | 32 kbps | ✓ Mandatory |
| 24_1 | 24 | 7.5 | 45 | 48 kbps | Optional |
| 24_2 | 24 | 10 | 60 | 48 kbps | ✓ Mandatory (Sink only) |
| 48_1 | 48 | 7.5 | 75 | 80 kbps | Optional |
| 48_2 | 48 | 10 | 100 | 80 kbps | Optional |
| 48_4 | 48 | 10 | 120 | 96 kbps | Optional |
| 48_6 | 48 | 10 | 155 | 124 kbps | Optional |
QoS Parameters — What They Mean
Once codec settings are agreed upon, the Unicast Client submits QoS parameters to the Unicast Server. These parameters control how the isochronous channel behaves in the Link Layer.
How often a new audio packet (SDU) is sent. Matches the LC3 frame duration. For 10 ms frames this is 10,000 µs.
The maximum time (ms) the controller can take to deliver an SDU from transmitter to receiver, including retransmissions.
How many times the controller retransmits each packet. Higher = more reliable but more bandwidth and latency.
Time (µs) between the SDU sync reference and when the speaker actually plays the audio. Used to synchronize left/right earbuds.
QoS Set 1 vs Set 2 — Low Latency vs High Reliability
| Retransmit # | 2 |
| Max Transport Latency | 10 ms (for 16_2) |
| Use case | Voice calls, gaming |
| Retransmit # | 13 |
| Max Transport Latency | 95 ms (for 16_2) |
| Use case | Media streaming, hearing aids |
QoS Settings Reference (selected configurations)
| QoS Set | Codec | SDU Interval | Max SDU (octets) | Retransmit # | Max Latency (ms) | Presentation Delay |
|---|---|---|---|---|---|---|
| 16_2_1 | 16_2 | 10,000 µs | 40 | 2 | 10 | 40,000 µs |
| 16_2_2 | 16_2 | 10,000 µs | 40 | 13 | 95 | 40,000 µs |
| 24_2_1 | 24_2 | 10,000 µs | 60 | 2 | 10 | 40,000 µs |
| 24_2_2 | 24_2 | 10,000 µs | 60 | 13 | 95 | 40,000 µs |
| 48_2_1 | 48_2 | 10,000 µs | 100 | 5 | 20 | 40,000 µs |
| 48_2_2 | 48_2 | 10,000 µs | 100 | 13 | 95 | 40,000 µs |
Presentation Delay — Synchronizing Left and Right Earbuds
Presentation Delay is one of the most important concepts in BLE Audio. It solves the problem of left and right earbuds playing audio at slightly different times.
| Phone transmits SDU |
Transport
Latency ⟶
|
Controller receives SDU |
Presentation
Delay ⟶
|
Earbud plays audio |
Also at same
moment ↓ |
Other earbud plays audio |
The Unicast Client picks a single Presentation_Delay value that falls within the supported range of all Unicast Servers in the same CIG. Both left and right earbuds wait until the same point in time before rendering audio — achieving synchronization without a wired connection.
How Stereo Audio Works — Multi-channel LC3
Because LC3 is mono, stereo is handled by running two LC3 encoders in parallel. BAP defines an Audio_Channel_Allocation LTV to assign each channel to a spatial location (Front Left, Front Right, etc.).
|
Front Left (FL)
Front Right (FR)
|
⟶ |
LC3 encoder
LC3 encoder
|
⟶ |
Media Packet (one SDU)
CF[FL]
CF[FR]
Codec frames ordered lowest to highest
Audio_Channel_Allocation bit index |
For a stereo earbud scenario, the phone sends one SDU with two codec frames inside it. The Audio_Channel_Allocation LTV tells the receiver which frame is FL and which is FR. Codec_Frame_Blocks_Per_SDU can be set to >1 if you want to bundle multiple time-periods into one SDU (useful for reducing packet overhead).
BlueZ Code — Setting Up a CIS with QoS 16_2_1
In BlueZ, isochronous streams are set up using ISO sockets introduced in Linux kernel 5.10+. Below is how you configure a CIG with the 16_2_1 settings in C using the BlueZ socket API.
#include <stdio.h> #include <string.h> #include <sys/socket.h> #include <bluetooth/bluetooth.h> #include <bluetooth/hci.h> #include <bluetooth/iso.h> /* BlueZ ISO socket header */ int create_cis_socket_16_2_1(void) { int fd; struct sockaddr_iso src_addr = { .sa_family = AF_BLUETOOTH, .v.cis.dev_id = 0, /* hci0 */ }; /* BT_ISO_QOS for QoS configuration set 16_2_1 */ struct bt_iso_qos qos = { .cig = BT_ISO_QOS_CIG_UNSET, .cis = BT_ISO_QOS_CIS_UNSET, .in = { .interval = 10000, /* SDU interval: 10 ms in µs */ .latency = 10, /* Max transport latency: 10 ms */ .sdu = 40, /* Max SDU size: 40 octets (16_2 codec) */ .phy = BT_ISO_PHY_2M, .rtn = 2, /* Retransmission number: 2 (low latency set _1) */ }, .out = { .interval = 10000, .latency = 10, .sdu = 40, .phy = BT_ISO_PHY_2M, .rtn = 2, }, }; /* Create ISO socket (AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_ISO) */ fd = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_ISO); if (fd < 0) { perror("socket"); return -1; } /* Set QoS options before bind/connect */ if (setsockopt(fd, SOL_BLUETOOTH, BT_ISO_QOS, &qos, sizeof(qos)) < 0) { perror("setsockopt BT_ISO_QOS"); return -1; } /* Bind to local adapter */ if (bind(fd, (struct sockaddr *)&src_addr, sizeof(src_addr)) < 0) { perror("bind"); return -1; } return fd; /* ready to connect() to the remote device */ }
bt_iso_qos structure and BT_ISO_QOS socket option are part of the BlueZ kernel socket API. You need kernel 5.10+ and BlueZ 5.56+ for CIS support. For BIS (broadcast), a different structure bt_iso_qos.bcast is used.Next: Unicast and Broadcast Streaming Procedures
In Part 3 we walk through the complete step-by-step streaming procedures — the ASE state machine for unicast audio, and the BASE structure and state machine for broadcast audio — with BlueZ examples throughout.
