Skip to content
Merged
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
74 changes: 74 additions & 0 deletions include/livekit/external_video_decoder.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright 2026 LiveKit
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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,
* 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.
*/

#pragma once

#include <cstddef>
#include <cstdint>

#include "livekit/visibility.h"

// Application-provided video decoder, e.g. a GPU decoder whose frames stay on
// the application's device. WebRTC keeps its own decoder as the fallback when
// create() returns null or decode() asks for it. Layout must match
// webrtc-sys/include/livekit/external_video_decoder.h.
extern "C" {

#define LK_EXTERNAL_VIDEO_DECODER_ABI 1

enum LkExternalVideoCodec : std::int32_t {
LK_EXTERNAL_VIDEO_CODEC_H264 = 0,
};

enum LkExternalDecodeResult : std::int32_t {
LK_EXTERNAL_DECODE_FRAME = 0, ///< `out->frame` holds a new reference

Check failure on line 37 in include/livekit/external_video_decoder.h

View workflow job for this annotation

GitHub Actions / C++ Tools / clang-format

clang-format

code should be clang-formatted
LK_EXTERNAL_DECODE_NO_FRAME = 1, ///< accepted, nothing to show yet

Check failure on line 38 in include/livekit/external_video_decoder.h

View workflow job for this annotation

GitHub Actions / C++ Tools / clang-format

clang-format

code should be clang-formatted
LK_EXTERNAL_DECODE_ERROR = 2, ///< undecodable: WebRTC requests a keyframe

Check failure on line 39 in include/livekit/external_video_decoder.h

View workflow job for this annotation

GitHub Actions / C++ Tools / clang-format

clang-format

code should be clang-formatted
LK_EXTERNAL_DECODE_FALLBACK = 3, ///< unusable: switch to the built-in decoder

Check failure on line 40 in include/livekit/external_video_decoder.h

View workflow job for this annotation

GitHub Actions / C++ Tools / clang-format

clang-format

code should be clang-formatted
};

struct LkExternalVideoFrame {
void* frame; ///< opaque, released with release_frame

Check failure on line 44 in include/livekit/external_video_decoder.h

View workflow job for this annotation

GitHub Actions / C++ Tools / clang-format

clang-format

code should be clang-formatted
std::int32_t width;
std::int32_t height;
};

/// Every function may run on any WebRTC decoder thread; one decoder instance
/// is never called concurrently. The table must outlive the SDK.
struct LkExternalVideoDecoder {
std::uint32_t abi_version;
void* user;
void* (*create)(void* user, std::int32_t codec);
void (*destroy)(void* decoder);
std::int32_t (*decode)(void* decoder, const std::uint8_t* data, std::size_t size,

Check failure on line 56 in include/livekit/external_video_decoder.h

View workflow job for this annotation

GitHub Actions / C++ Tools / clang-format

clang-format

code should be clang-formatted
std::int32_t keyframe, LkExternalVideoFrame* out);

Check failure on line 57 in include/livekit/external_video_decoder.h

View workflow job for this annotation

GitHub Actions / C++ Tools / clang-format

clang-format

code should be clang-formatted
void (*release_frame)(void* frame);
/// CPU copy for streams that did not request external frames; 0 on success.
std::int32_t (*read_i420)(void* frame, std::uint8_t* y, std::int32_t stride_y,

Check failure on line 60 in include/livekit/external_video_decoder.h

View workflow job for this annotation

GitHub Actions / C++ Tools / clang-format

clang-format

code should be clang-formatted
std::uint8_t* u, std::int32_t stride_u, std::uint8_t* v,

Check failure on line 61 in include/livekit/external_video_decoder.h

View workflow job for this annotation

GitHub Actions / C++ Tools / clang-format

clang-format

code should be clang-formatted

Check failure on line 61 in include/livekit/external_video_decoder.h

View workflow job for this annotation

GitHub Actions / C++ Tools / clang-format

clang-format

code should be clang-formatted
std::int32_t stride_v);
};

} // extern "C"

namespace livekit {

/// Installs (or with null removes) the decoder for video tracks subscribed
/// afterwards. Video streams opened with `external_frames` receive its frames
/// as VideoBufferType::External without a CPU copy.
LIVEKIT_API void setExternalVideoDecoder(const LkExternalVideoDecoder* decoder);

} // namespace livekit
1 change: 1 addition & 0 deletions include/livekit/livekit.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "livekit/audio_stream.h"
#include "livekit/build.h"
#include "livekit/e2ee.h"
#include "livekit/external_video_decoder.h"
#include "livekit/local_audio_track.h"
#include "livekit/local_participant.h"
#include "livekit/local_track_publication.h"
Expand Down
12 changes: 10 additions & 2 deletions include/livekit/video_frame.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,17 @@

#include <cstddef>
#include <cstdint>
#include <memory>
#include <optional>
#include <vector>

#include "livekit/visibility.h"

namespace livekit {

/// Mirror of WebRTC video buffer type
enum class VideoBufferType { RGBA = 0, ABGR, ARGB, BGRA, RGB24, I420, I420A, I422, I444, I010, NV12 };
/// Mirror of WebRTC video buffer type. External frames come from an
/// application decoder (see setExternalVideoDecoder) and carry no pixels.
enum class VideoBufferType { RGBA = 0, ABGR, ARGB, BGRA, RGB24, I420, I420A, I422, I444, I010, NV12, External };

/// @brief Plane layout metadata for a VideoFrame buffer.
struct VideoPlaneInfo {
Expand Down Expand Up @@ -69,6 +71,10 @@ class LIVEKIT_API VideoFrame {
const std::uint8_t* data() const noexcept { return data_.data(); }
std::size_t dataSize() const noexcept { return data_.size(); }

/// The decoder's frame for VideoBufferType::External, valid while this
/// VideoFrame lives; null otherwise.
void* externalFrame() const noexcept { return external_frame_; }

/// Compute plane layout for this frame (Y/U/V, UV, etc.), in terms of
/// pointers & sizes relative to this frame's backing buffer.
///
Expand Down Expand Up @@ -111,6 +117,8 @@ class LIVEKIT_API VideoFrame {
int height_;
VideoBufferType type_;
std::vector<std::uint8_t> data_;
void* external_frame_ = nullptr;
std::shared_ptr<void> external_owner_;
};

} // namespace livekit
4 changes: 4 additions & 0 deletions include/livekit/video_stream.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ class LIVEKIT_API VideoStream {
/// Preferred pixel format for frames delivered by read(). The FFI layer
/// converts into this format if supported (e.g., RGBA, BGRA, I420, ...).
VideoBufferType format{VideoBufferType::RGBA};

/// Deliver frames of the external decoder (setExternalVideoDecoder) as
/// VideoBufferType::External instead of converting them to `format`.
bool external_frames{false};
};

/// Factory: create a VideoStream bound to a specific Track
Expand Down
Loading
Loading