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
78 changes: 77 additions & 1 deletion sdk/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ if (is_ios || is_mac) {
"objc/base/RTCEncodedImage.h",
"objc/base/RTCEncodedImage.m",
"objc/base/RTCI420Buffer.h",
"objc/base/RTCNV12Buffer.h",
"objc/base/RTCLogging.h",
"objc/base/RTCLogging.mm",
"objc/base/RTCMacros.h",
Expand Down Expand Up @@ -596,6 +597,16 @@ if (is_ios || is_mac) {
"objc/components/video_frame_buffer/RTCCVPixelBuffer.h",
"objc/components/video_frame_buffer/RTCCVPixelBuffer.mm",
]
# Stream-only: NV12 wrappers are built when the flag is enabled.
# Stream-only: enable gating macro for ObjC bridge policies.
# Stream-only: switch to NV12-enabled ObjC bridge implementation.
if (stream_enable_rendering_backend) {
sources += [
"objc/api/video_frame_buffer/RTCNativeNV12Buffer+Private.h",
"objc/api/video_frame_buffer/RTCNativeNV12Buffer.h",
"objc/api/video_frame_buffer/RTCNativeNV12Buffer.mm",
]
}
deps = [
":base_objc",
"../rtc_base:logging",
Expand Down Expand Up @@ -642,6 +653,8 @@ if (is_ios || is_mac) {
sources += [
"objc/components/renderer/metal/RTCMTLVideoView.h",
"objc/components/renderer/metal/RTCMTLVideoView.m",
"objc/components/renderer/metal/RTCVideoRenderingView.h",
"objc/components/renderer/metal/RTCVideoRenderingView.m",
]
}
if (is_mac) {
Expand All @@ -657,6 +670,42 @@ if (is_ios || is_mac) {
]
configs += [ "..:common_objc" ]
public_configs = [ ":common_config_objc" ]
# Stream-only: add shared Metal backend as a separate target.
if (stream_enable_rendering_backend) {
defines = [ "RTC_STREAM_RENDERING_BACKEND" ]
deps += [ ":stream_shared_metal_objc" ]
}
}

# Stream-only: iOS shared Metal implementation isolated here.
# XR (visionOS) is excluded until the shared backend is validated there.
if (stream_enable_rendering_backend && is_ios &&
!(target_environment == "xrsimulator" || target_environment == "xrdevice")) {
rtc_library("stream_shared_metal_objc") {
visibility = [ "*" ]
sources = [
"objc/components/renderer/metal/RTCSharedMetalRenderAdapter.h",
"objc/components/renderer/metal/RTCSharedMetalRenderAdapter.mm",
"objc/components/renderer/metal/RTCSharedMetalRenderingContext+Private.h",
"objc/components/renderer/metal/RTCSharedMetalRenderingContext.h",
"objc/components/renderer/metal/RTCSharedMetalRenderingContext.mm",
"objc/components/renderer/metal/RTCSharedMetalVideoView.h",
"objc/components/renderer/metal/RTCSharedMetalVideoView.mm",
]
frameworks = [
"CoreVideo.framework",
"Metal.framework",
"QuartzCore.framework",
"UIKit.framework",
]
deps = [
":base_objc",
":videoframebuffer_objc",
"../rtc_base:checks",
]
configs += [ "..:common_objc" ]
public_configs = [ ":common_config_objc" ]
}
}

# TODO(bugs.webrtc.org/9627): Remove this target.
Expand Down Expand Up @@ -1036,6 +1085,9 @@ if (is_ios || is_mac) {
"environment_construction",
]
configs += [ "..:no_global_constructors" ]
if (stream_enable_rendering_backend) {
defines = [ "RTC_STREAM_RENDERING_BACKEND" ]
}
sources = [
"objc/api/peerconnection/RTCAudioDeviceModule.h",
"objc/api/peerconnection/RTCAudioDeviceModule+Private.h",
Expand Down Expand Up @@ -1528,6 +1580,13 @@ if (is_ios || is_mac) {
"objc/components/audio/RTCAudioCustomProcessingDelegate.h",
"objc/components/audio/RTCAudioProcessingConfig.h",
]
# Stream-only: export NV12 headers when the flag is enabled.
if (stream_enable_rendering_backend) {
common_objc_headers += [
"objc/base/RTCNV12Buffer.h",
"objc/api/video_frame_buffer/RTCNativeNV12Buffer.h",
]
}

if (rtc_use_h265) {
common_objc_headers += [
Expand All @@ -1542,6 +1601,7 @@ if (is_ios || is_mac) {
common_objc_headers += [
"objc/helpers/RTCCameraPreviewView.h",
"objc/components/renderer/metal/RTCMTLVideoView.h",
"objc/components/renderer/metal/RTCVideoRenderingView.h",
]
}

Expand Down Expand Up @@ -1692,6 +1752,7 @@ if (is_ios || is_mac) {
"objc/components/capturer/RTCDesktopSource.h",
"objc/components/capturer/RTCDesktopMediaList.h",
"objc/components/renderer/metal/RTCMTLVideoView.h",
"objc/components/renderer/metal/RTCVideoRenderingView.h",
"objc/components/renderer/metal/RTCMTLNSVideoView.h",
"objc/components/renderer/opengl/RTCVideoViewShading.h",
"objc/components/video_codec/RTCCodecSpecificInfoH264.h",
Expand All @@ -1714,6 +1775,13 @@ if (is_ios || is_mac) {
"objc/components/audio/RTCAudioCustomProcessingDelegate.h",
"objc/components/audio/RTCAudioProcessingConfig.h",
]
# Stream-only: export NV12 headers when the flag is enabled.
if (stream_enable_rendering_backend) {
sources += [
"objc/base/RTCNV12Buffer.h",
"objc/api/video_frame_buffer/RTCNativeNV12Buffer.h",
]
}
if (!build_with_chromium) {
sources += [
"objc/api/logging/RTCCallbackLogger.h",
Expand Down Expand Up @@ -1865,7 +1933,6 @@ if (is_ios || is_mac) {
rtc_library("native_video") {
sources = [
"objc/native/src/objc_frame_buffer.h",
"objc/native/src/objc_frame_buffer.mm",
"objc/native/src/objc_video_decoder_factory.h",
"objc/native/src/objc_video_decoder_factory.mm",
"objc/native/src/objc_video_encoder_factory.h",
Expand All @@ -1877,6 +1944,15 @@ if (is_ios || is_mac) {
"objc/native/src/objc_video_track_source.h",
"objc/native/src/objc_video_track_source.mm",
]
if (stream_enable_rendering_backend) {
sources += [
"objc/native/src/objc_frame_buffer_stream.mm",
"objc/native/src/objc_nv12_conversion.h",
"objc/native/src/objc_nv12_conversion.mm",
]
} else {
sources += [ "objc/native/src/objc_frame_buffer.mm" ]
}

configs += [ "..:common_objc" ]

Expand Down
21 changes: 21 additions & 0 deletions sdk/objc/api/peerconnection/RTCPeerConnectionFactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ typedef NS_ENUM(NSInteger, RTC_OBJC_TYPE(RTCRtpMediaType));
@protocol RTC_OBJC_TYPE
(RTCAudioProcessingModule);

typedef NS_ENUM(NSInteger, RTC_OBJC_TYPE(RTCFrameBufferPolicy)) {
RTC_OBJC_TYPE(RTCFrameBufferPolicyNone) = 0,
RTC_OBJC_TYPE(RTCFrameBufferPolicyWrapOnlyExistingNV12),
RTC_OBJC_TYPE(RTCFrameBufferPolicyCopyToNV12),
RTC_OBJC_TYPE(RTCFrameBufferPolicyConvertWithPoolToNV12)
};

RTC_OBJC_EXPORT
@interface RTC_OBJC_TYPE (RTCPeerConnectionFactory) : NSObject

Expand Down Expand Up @@ -71,6 +78,20 @@ RTC_OBJC_EXPORT

@property(nonatomic, readonly) RTC_OBJC_TYPE(RTCAudioDeviceModule) *audioDeviceModule;

/**
* Controls how decoded frame buffers are bridged to Objective-C. Default is `none`.
* This can be toggled at runtime. It only takes effect when
* `stream_enable_rendering_backend=true` (RTC_STREAM_RENDERING_BACKEND).
*
* Note: the policy is evaluated per frame. Changing it mid-call can result in
* a mix of I420 and NV12 buffers for in-flight frames. For consistent format,
* set it before starting a call.
*
* Thread-safety: property access is not synchronized; set it from a single
* thread if you need strict consistency.
*/
@property(nonatomic, assign) RTC_OBJC_TYPE(RTCFrameBufferPolicy) frameBufferPolicy;

/**
* Valid kind values are kRTCMediaStreamTrackKindAudio and
* kRTCMediaStreamTrackKindVideo.
Expand Down
17 changes: 17 additions & 0 deletions sdk/objc/api/peerconnection/RTCPeerConnectionFactory.mm
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@
#include "sdk/objc/native/api/video_encoder_factory.h"
#include "sdk/objc/native/src/objc_video_decoder_factory.h"
#include "sdk/objc/native/src/objc_video_encoder_factory.h"
#if defined(RTC_STREAM_RENDERING_BACKEND)
#include "sdk/objc/native/src/objc_nv12_conversion.h"
#endif

#import "components/audio/RTCAudioProcessingModule.h"
#import "components/audio/RTCDefaultAudioProcessingModule+Private.h"
Expand All @@ -73,6 +76,7 @@ @implementation RTC_OBJC_TYPE (RTCPeerConnectionFactory) {
RTC_OBJC_TYPE(RTCDefaultAudioProcessingModule) *_defaultAudioProcessingModule;

BOOL _hasStartedAecDump;
RTC_OBJC_TYPE(RTCFrameBufferPolicy) _frameBufferPolicy;
}

@synthesize nativeFactory = _nativeFactory;
Expand Down Expand Up @@ -196,6 +200,19 @@ - (instancetype)initWithNativeDependencies:
return self;
}

- (RTC_OBJC_TYPE(RTCFrameBufferPolicy))frameBufferPolicy {
return _frameBufferPolicy;
}

- (void)setFrameBufferPolicy:(RTC_OBJC_TYPE(RTCFrameBufferPolicy))frameBufferPolicy {
_frameBufferPolicy = frameBufferPolicy;
#if defined(RTC_STREAM_RENDERING_BACKEND)
// Only available in Stream builds with the shared backend enabled.
webrtc::SetObjCFrameBufferPolicy(
static_cast<webrtc::ObjCFrameBufferPolicy>(frameBufferPolicy));
#endif
}

- (RTC_OBJC_TYPE(RTCRtpCapabilities) *)rtpSenderCapabilitiesFor:(RTC_OBJC_TYPE(RTCRtpMediaType))mediaType {

webrtc::RtpCapabilities capabilities = _nativeFactory->GetRtpSenderCapabilities([RTC_OBJC_TYPE(RTCRtpReceiver) nativeMediaTypeForMediaType: mediaType]);
Expand Down
29 changes: 29 additions & 0 deletions sdk/objc/api/video_frame_buffer/RTCNativeNV12Buffer+Private.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2026 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/

#import "RTCNativeNV12Buffer.h"

#include "api/video/video_frame_buffer.h"

NS_ASSUME_NONNULL_BEGIN

@interface RTC_OBJC_TYPE(RTCNV12Buffer) () {
@protected
webrtc::scoped_refptr<webrtc::NV12BufferInterface> _nv12Buffer;
}

/** Initialize an RTCNV12Buffer with its backing NV12BufferInterface. */
- (instancetype)initWithFrameBuffer:
(webrtc::scoped_refptr<webrtc::NV12BufferInterface>)nv12Buffer;
- (webrtc::scoped_refptr<webrtc::NV12BufferInterface>)nativeNV12Buffer;

@end

NS_ASSUME_NONNULL_END
24 changes: 24 additions & 0 deletions sdk/objc/api/video_frame_buffer/RTCNativeNV12Buffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright 2026 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/

#import <AVFoundation/AVFoundation.h>

#import "RTCNV12Buffer.h"
#import "sdk/objc/base/RTCMacros.h"

NS_ASSUME_NONNULL_BEGIN

/** RTCNV12Buffer implements the RTCNV12Buffer protocol */
RTC_OBJC_EXPORT
@interface RTC_OBJC_TYPE(RTCNV12Buffer)
: NSObject<RTC_OBJC_TYPE(RTCNV12Buffer)>
@end

NS_ASSUME_NONNULL_END
93 changes: 93 additions & 0 deletions sdk/objc/api/video_frame_buffer/RTCNativeNV12Buffer.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright 2026 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/

#import "RTCNativeNV12Buffer+Private.h"
#import "RTCNativeI420Buffer+Private.h"

#include "api/video/nv12_buffer.h"

@implementation RTC_OBJC_TYPE(RTCNV12Buffer)

- (instancetype)initWithWidth:(int)width height:(int)height {
self = [super init];
if (self) {
_nv12Buffer = webrtc::NV12Buffer::Create(width, height);
}
return self;
}

- (instancetype)initWithWidth:(int)width
height:(int)height
strideY:(int)strideY
strideUV:(int)strideUV {
self = [super init];
if (self) {
_nv12Buffer =
webrtc::NV12Buffer::Create(width, height, strideY, strideUV);
}
return self;
}

- (instancetype)initWithFrameBuffer:
(webrtc::scoped_refptr<webrtc::NV12BufferInterface>)nv12Buffer {
self = [super init];
if (self) {
_nv12Buffer = nv12Buffer;
}
return self;
}

- (int)width {
return _nv12Buffer->width();
}

- (int)height {
return _nv12Buffer->height();
}

- (int)strideY {
return _nv12Buffer->StrideY();
}

- (int)strideUV {
return _nv12Buffer->StrideUV();
}

- (int)chromaWidth {
return _nv12Buffer->ChromaWidth();
}

- (int)chromaHeight {
return _nv12Buffer->ChromaHeight();
}

- (const uint8_t *)dataY {
return _nv12Buffer->DataY();
}

- (const uint8_t *)dataUV {
return _nv12Buffer->DataUV();
}

- (id<RTC_OBJC_TYPE(RTCI420Buffer)>)toI420 {
webrtc::scoped_refptr<webrtc::I420BufferInterface> buffer =
_nv12Buffer->ToI420();
RTC_OBJC_TYPE(RTCI420Buffer) *result =
[[RTC_OBJC_TYPE(RTCI420Buffer) alloc] initWithFrameBuffer:buffer];
return result;
}

#pragma mark - Private

- (webrtc::scoped_refptr<webrtc::NV12BufferInterface>)nativeNV12Buffer {
return _nv12Buffer;
}

@end
Loading