Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,105 @@ with the same library loaded elsewhere in the host process.
- Use `LK_LOG_WARN` for non-fatal unexpected conditions.
- Use `Result<T, E>` for operations that can fail with typed errors (e.g., data track publish/subscribe).

### Public API Documentation (Doxygen)

The public API (`include/livekit/*.h`) is what consumers read first and is also
published as a Doxygen site (`docs/Doxyfile`, `.github/workflows/publish-docs.yml`).
Every public class, struct, free function, type alias, enum, enum value, and
method in `include/livekit/` must follow the rules below. **PRs adding or
modifying public symbols are gated on these rules during code review.**

#### Comment style

- Use triple-slash `///` Doxygen comments. Do **not** use `/** ... */` Javadoc
blocks for new code, and prefer migrating existing ones when you touch them.
- Apache license headers stay as `/* ... */` block comments — they are not
documentation.
- Implementation comments inside `.cpp` files (non-Doxygen) may use `//` freely.
- Keep the first line a short one-sentence brief. Follow with a blank `///`
line and a longer description if needed. Example:

```cpp
/// Publish a local track to the room.
///
/// Blocks until the FFI publish response arrives.
///
/// @param track Track to publish. Must be non-null.
/// @param options Publish options (codec, simulcast, etc.).
/// @throws std::runtime_error if the FFI reports an error.
///
/// @note Thread-safety: Not thread-safe. Must be externally synchronized.
void publishTrack(const std::shared_ptr<Track>& track, const TrackPublishOptions& options);
```

#### Parameter documentation

- Every parameter on every public function and method must be documented with
`@param name Description.`
- Document `@return` for any non-void return value. Omit `@return` for `void`.
- Document `@throws ExceptionType When/why it's thrown.` for anything that
can throw a typed exception. Operations that can fail without throwing
should return `Result<T, E>` (see Error Handling above) and the variants
should be documented in the doc block.
- Trivial inline accessors (e.g. `int sample_rate() const noexcept`) only need
a one-line `///` brief and may omit `@param`/`@return` when they take no
arguments and the brief already describes the returned value.
- Use `@param[out]` or `@param[in,out]` when a non-const reference or pointer
is used as an output or in-out parameter.

#### Thread-safety annotations

Every public class and every public method must declare its thread-safety in a
consistent, grep-able form. Two labels are used:

- **`Thread-safe.`** — Safe to call concurrently from multiple threads. If the
safety comes from internal synchronization (mutex, queue, atomic) rather
than statelessness, briefly say so. Methods whose callers are *serialized
internally* (e.g. `TextStreamWriter::write` via an internal `write_mutex_`)
are documented as thread-safe with the prose noting the serialization.
- **`Not thread-safe.`** — Must be externally synchronized; concurrent calls
from multiple threads are undefined behavior. Briefly say what the caller
must ensure (e.g. "must be called from a single capture thread").

Use the `@note` Doxygen tag so it renders as a styled callout and is easy to
search for:

```cpp
/// @note Thread-safety: Thread-safe. Internal `std::mutex` protects all
/// mutable state.
```

```cpp
/// @note Thread-safety: Not thread-safe. Concurrent `captureFrame` calls
/// from multiple threads are undefined behavior.
```

Placement:
- For classes where all (or all non-trivially) methods share the same
thread-safety guarantee, put the `@note Thread-safety:` line on the class
doc block. Individual methods only re-document thread-safety when they
*deviate* from the class-level guarantee.
- For classes where guarantees differ per-method (`Room`, `LocalParticipant`,
stream writers/readers, etc.), document `@note Thread-safety:` on every
public method.

The authoritative thread-safety table for the SDK as a whole lives in the
[Threading Model](#threading-model) section above; per-symbol annotations
must agree with it. If you add a new public type, also add an entry there.

#### Review checklist for new/modified public APIs

Before approving a PR that touches `include/livekit/*.h`, confirm:

- [ ] Every new/changed public symbol has a `///` doc block (no `/** */`).
- [ ] Every parameter is covered by `@param`.
- [ ] `@return` documents non-void returns; `@throws` documents thrown
exceptions; `Result<T, E>` variants are described.
- [ ] `@note Thread-safety: ...` is present, either at the class level or on
each affected method, using the two-tier vocabulary above.
- [ ] The Threading Model table at the top of this file is updated if a new
public type was introduced.

### Integer Types

- Prefer fixed-width integer types from `<cstdint>` (`std::int32_t`, `std::uint64_t`, etc.) over raw primitive integer types when size or signedness matters.
Expand Down
88 changes: 52 additions & 36 deletions include/livekit/audio_frame.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS,
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
Expand All @@ -29,65 +29,81 @@ class AudioFrameBufferInfo;
class OwnedAudioFrameBuffer;
} // namespace proto

/**
* @brief Represents a raw PCM audio frame with interleaved int16 samples.
*
* AudioFrame holds decoded audio data along with metadata such as sample rate,
* number of channels, and samples per channel. It is used for capturing and
* processing audio in the LiveKit SDK.
*/
/// A raw PCM audio frame with interleaved `int16` samples.
///
/// `AudioFrame` holds decoded audio data along with metadata such as
/// sample rate, number of channels, and samples per channel. It is used
/// for capturing and processing audio in the LiveKit SDK.
///
/// @note Thread-safety: Not thread-safe. A single `AudioFrame` instance
/// must not be mutated concurrently from multiple threads; concurrent
/// const-access is safe.
class LIVEKIT_API AudioFrame {
public:
/**
* Construct an AudioFrame from raw PCM samples.
*
* @param data Interleaved PCM samples (int16).
* @param sample_rate Sample rate (Hz).
* @param num_channels Number of channels.
* @param samples_per_channel Number of samples per channel.
*
* Throws std::invalid_argument if the data size is inconsistent with
* num_channels * samples_per_channel.
*/
/// Construct an `AudioFrame` from raw PCM samples.
///
/// @param data Interleaved PCM samples (`int16`).
/// @param sample_rate Sample rate (Hz).
/// @param num_channels Number of channels.
/// @param samples_per_channel Number of samples per channel.
///
/// @throws std::invalid_argument if `data.size()` is inconsistent with
/// `num_channels * samples_per_channel`.
AudioFrame(std::vector<std::int16_t> data, int sample_rate, int num_channels, int samples_per_channel);
AudioFrame(); // Default constructor

/// Construct an empty `AudioFrame` (all fields zero-initialized).
AudioFrame();

virtual ~AudioFrame() = default;

/**
* Create a new zero-initialized AudioFrame instance.
*/
/// Create a new zero-initialized `AudioFrame` instance.
///
/// @param sample_rate Sample rate (Hz).
/// @param num_channels Number of channels.
/// @param samples_per_channel Number of samples per channel.
/// @return A frame whose buffer is sized to
/// `num_channels * samples_per_channel`
/// and zero-filled.
///
/// @note Thread-safety: Thread-safe. Pure factory function.
static AudioFrame create(int sample_rate, int num_channels, int samples_per_channel);

/**
* Construct an AudioFrame by copying data out of an OwnedAudioFrameBuffer.
*/
/// Construct an `AudioFrame` by copying data out of an FFI-owned
/// audio buffer.
///
/// @param owned Buffer received from the FFI (data is copied).
/// @return A new `AudioFrame` owning a copy of the samples.
///
/// @note Thread-safety: Thread-safe. Pure transformation; no global
/// state.
static AudioFrame fromOwnedInfo(const proto::OwnedAudioFrameBuffer& owned);

// ---- Accessors ----

/// @return Mutable reference to the underlying sample buffer.
const std::vector<std::int16_t>& data() const noexcept { return data_; }

/// @return Mutable reference to the underlying sample buffer.
std::vector<std::int16_t>& data() noexcept { return data_; }

/// Number of samples in the buffer (per all channels).
/// @return Total number of samples in the buffer across all channels.
std::size_t total_samples() const noexcept { return data_.size(); }

/// Sample rate in Hz.
/// @return Sample rate in Hz.
int sample_rate() const noexcept { return sample_rate_; }

/// Number of channels.
/// @return Number of channels.
int num_channels() const noexcept { return num_channels_; }

/// Samples per channel.
/// @return Samples per channel.
int samples_per_channel() const noexcept { return samples_per_channel_; }

/// Duration in seconds (samples_per_channel / sample_rate).
/// @return Duration in seconds (`samples_per_channel / sample_rate`).
double duration() const noexcept;

/// A human-readable description.
/// @return Human-readable description (sample rate, channels, size).
std::string to_string() const;

protected:
// Build a proto AudioFrameBufferInfo pointing at this frames data.
// Build a proto AudioFrameBufferInfo pointing at this frame's data.
// Used internally by AudioSource.
proto::AudioFrameBufferInfo toProto() const;
friend class AudioSource;
Expand All @@ -99,4 +115,4 @@ class LIVEKIT_API AudioFrame {
int samples_per_channel_;
};

} // namespace livekit
} // namespace livekit
Loading
Loading