Skip to content

Commit

Permalink
Merge branch 'main' into duan/external-audio-processing
Browse files Browse the repository at this point in the history
  • Loading branch information
hiroshihorie committed Feb 27, 2024
2 parents a1f2af3 + 88ab2ea commit e3373f7
Show file tree
Hide file tree
Showing 101 changed files with 1,287 additions and 910 deletions.
51 changes: 51 additions & 0 deletions .github/workflows/testing-matrix.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Testing Matrix

on:
workflow_dispatch:
push:
branches: [ main ]
pull_request:
branches: [ main ]

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
run_all_tests:
strategy:
fail-fast: false
matrix:
xcode-version: [14.2, 15.2]
destination: ['platform=iOS Simulator,OS=17.2,name=iPhone 14 Pro', 'platform=macOS', 'platform=macOS,variant=Mac Catalyst']

runs-on: macos-13

steps:
- uses: actions/checkout@v4

- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: ${{ matrix.xcode-version }}

- name: Xcode Version
run: xcodebuild -version

- name: Show SDKs
run: xcodebuild -showsdks

- name: Download iOS platforms
run: xcodebuild -downloadPlatform iOS

# TODO: Add step to install iOS 13
# - name: Install iOS 13
# run: xcversion simulators --install='iOS 13.0'

# - name: Download iOS platforms
# run: xcodebuild -downloadPlatform iOS

- name: Show Destinations
run: xcodebuild -scheme LiveKit -showdestinations

- name: Run All Tests
run: xcodebuild test -scheme LiveKit -destination '${{ matrix.destination }}'
4 changes: 2 additions & 2 deletions LiveKitClient.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Pod::Spec.new do |spec|

spec.name = 'LiveKitClient'
spec.version = '2.0.0'
spec.version = '2.0.4'
spec.summary = 'LiveKit Swift Client SDK. Easily build live audio or video experiences into your mobile app, game or website.'
spec.homepage = 'https://github.com/livekit/client-sdk-swift'
spec.license = { :type => 'Apache 2.0', :file => 'LICENSE' }
Expand All @@ -12,7 +12,7 @@ Pod::Spec.new do |spec|
spec.osx.deployment_target = '10.15'

spec.swift_versions = ['4.2', '5']
spec.source = { :git => 'https://github.com/livekit/client-sdk-swift.git', :tag => '2.0.0' }
spec.source = { :git => 'https://github.com/livekit/client-sdk-swift.git', :tag => '2.0.4' }

spec.source_files = 'Sources/**/*'

Expand Down
17 changes: 11 additions & 6 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.
// swift-tools-version:5.7
// (Xcode14.0+)

import PackageDescription

Expand All @@ -8,6 +8,7 @@ let package = Package(
platforms: [
.iOS(.v13),
.macOS(.v10_15),
.macCatalyst(.v14),
],
products: [
.library(
Expand All @@ -17,8 +18,8 @@ let package = Package(
],
dependencies: [
// LK-Prefixed Dynamic WebRTC XCFramework
.package(name: "WebRTC", url: "https://github.com/livekit/webrtc-xcframework.git", .exact("114.5735.10")),
.package(name: "SwiftProtobuf", url: "https://github.com/apple/swift-protobuf.git", .upToNextMajor(from: "1.25.2")),
.package(url: "https://github.com/livekit/webrtc-xcframework.git", exact: "114.5735.13"),
.package(url: "https://github.com/apple/swift-protobuf.git", .upToNextMajor(from: "1.25.2")),
.package(url: "https://github.com/apple/swift-log.git", .upToNextMajor(from: "1.5.3")),
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.3.0"),
],
Expand All @@ -28,8 +29,8 @@ let package = Package(
name: "LiveKit",
dependencies: [
.target(name: "CHeaders"),
"WebRTC",
"SwiftProtobuf",
.product(name: "LiveKitWebRTC", package: "webrtc-xcframework"),
.product(name: "SwiftProtobuf", package: "swift-protobuf"),
.product(name: "Logging", package: "swift-log"),
],
path: "Sources"
Expand All @@ -38,5 +39,9 @@ let package = Package(
name: "LiveKitTests",
dependencies: ["LiveKit"]
),
.testTarget(
name: "LiveKitTestsObjC",
dependencies: ["LiveKit"]
),
]
)
51 changes: 23 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ Use this SDK to add real-time video, audio and data features to your Swift app.

## Docs & Example app

> [!NOTE]
> Version 2 of the Swift SDK contains breaking changes from Version 1.
> Read the [migration guide](https://docs.livekit.io/guides/migrate-from-v1/) for a detailed overview of what has changed.
Docs and guides are at [https://docs.livekit.io](https://docs.livekit.io).

There is full source code of a [iOS/macOS Swift UI Example App](https://github.com/livekit/client-example-swift).
Expand All @@ -34,7 +38,7 @@ Add the dependency and also to your target
let package = Package(
...
dependencies: [
.package(name: "LiveKit", url: "https://github.com/livekit/client-sdk-swift.git", .upToNextMajor("1.0.0")),
.package(name: "LiveKit", url: "https://github.com/livekit/client-sdk-swift.git", .upToNextMajor("2.0.4")),
],
targets: [
.target(
Expand Down Expand Up @@ -66,51 +70,48 @@ class RoomViewController: UIViewController {
lazy var remoteVideoView: VideoView = {
let videoView = VideoView()
view.addSubview(videoView)
// additional initialization ...
// Additional initialization ...
return videoView
}()

lazy var localVideoView: VideoView = {
let videoView = VideoView()
view.addSubview(videoView)
// additional initialization ...
// Additional initialization ...
return videoView
}()

override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white

let url: String = "ws://your_host"
let token: String = "your_jwt_token"

room.connect(url, token).then { room in
let url = "ws://your_host"
let token = "your_jwt_token"

// Publish camera & mic
room.localParticipant?.setCamera(enabled: true)
room.localParticipant?.setMicrophone(enabled: true)
do {
try await room.connect(url: url, token: token)
// Connection successful...

}.catch { error in
// failed to connect
// Publishing camera & mic...
try await room.localParticipant.setCamera(enabled: true)
try await room.localParticipant.setMicrophone(enabled: true)
} catch let error in {
// Failed to connect
}
}
}

extension RoomViewController: RoomDelegate {

func room(_ room: Room, localParticipant: LocalParticipant, didPublish publication: LocalTrackPublication) {
guard let track = publication?.track as? VideoTrack else {
return
}
func room(_: Room, participant _: LocalParticipant, didPublishTrack publication: LocalTrackPublication) {
guard let track = publication?.track as? VideoTrack else { return }
DispatchQueue.main.async {
localVideoView.track = track
}
}

func room(_ room: Room, participant: RemoteParticipant, didSubscribe publication: RemoteTrackPublication, track: Track) {
guard let track = track as? VideoTrack else {
return
}
func room(_: Room, participant _: RemoteParticipant, didSubscribeTrack publication: RemoteTrackPublication) {
guard let track = publication?.track as? VideoTrack else { return }
DispatchQueue.main.async {
remoteVideoView.track = track
}
Expand All @@ -131,7 +132,7 @@ Since `VideoView` is a UI component, all operations (read/write properties etc)
Other core classes can be accessed from any thread.

Delegates will be called on the SDK's internal thread.
Make sure any access to the UI is within the main thread, for example by using `DispatchQueue.main.async`.
Make sure any access to your app's UI elements are from the main thread, for example by using `@MainActor` or `DispatchQueue.main.async`.

### Memory management

Expand All @@ -148,7 +149,6 @@ However, if you'd like to customize this behavior, you would override `AudioMana

### iOS Simulator limitations

- Currently, `VideoView` will use OpenGL for iOS Simulator.
- Publishing the camera track is not supported by iOS Simulator.

### ScrollView performance
Expand Down Expand Up @@ -214,15 +214,10 @@ For the full example, see 👉 [UIKit Minimal Example](https://github.com/liveki

# Frequently asked questions

### Mic privacy indicator (orange dot) remains on even after muting audio track

You will need to un-publish the LocalAudioTrack for the indicator to turn off.
More discussion here https://github.com/livekit/client-sdk-swift/issues/140

### How to publish camera in 60 FPS ?

- Create a `LocalVideoTrack` by calling `LocalVideoTrack.createCameraTrack(options: CameraCaptureOptions(fps: 60))`.
- Publish with `LocalParticipant.publishVideoTrack(track: track, publishOptions: VideoPublishOptions(encoding: VideoEncoding(maxFps: 60)))`.
- Publish with `LocalParticipant.publish(videoTrack: track, publishOptions: VideoPublishOptions(encoding: VideoEncoding(maxFps: 60)))`.

# Known issues

Expand Down
2 changes: 1 addition & 1 deletion Sources/LiveKit/Broadcast/BroadcastScreenCapturer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import UIKit
#endif

@_implementationOnly import WebRTC
@_implementationOnly import LiveKitWebRTC

class BroadcastScreenCapturer: BufferCapturer {
static let kRTCScreensharingSocketFD = "rtc_SSFD"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import CoreImage
import CoreVideo
import Foundation

@_implementationOnly import WebRTC
@_implementationOnly import LiveKitWebRTC

private class Message {
// Initializing a CIContext object is costly, so we use a singleton instead
Expand Down
2 changes: 1 addition & 1 deletion Sources/LiveKit/Core/DataChannelPairActor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import Foundation

@_implementationOnly import WebRTC
@_implementationOnly import LiveKitWebRTC

actor DataChannelPairActor: NSObject, Loggable {
// MARK: - Types
Expand Down
10 changes: 5 additions & 5 deletions Sources/LiveKit/Core/Engine+SignalClientDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@

import Foundation

@_implementationOnly import WebRTC
@_implementationOnly import LiveKitWebRTC

extension Engine: SignalClientDelegate {
func signalClient(_ signalClient: SignalClient, didUpdateConnectionState connectionState: ConnectionState, oldState: ConnectionState, disconnectError: LiveKitError?) async {
// connectionState did update
if connectionState != oldState,
// did disconnect
case .disconnected = connectionState,
// only attempt re-connect if disconnected(reason: network)
case .network = disconnectError?.type,
// Only attempt re-connect if not cancelled
let errorType = disconnectError?.type, errorType != .cancelled,
// engine is currently connected state
case .connected = _state.connectionState
{
Expand Down Expand Up @@ -69,7 +69,7 @@ extension Engine: SignalClientDelegate {
log("Received offer, creating & sending answer...")

guard let subscriber else {
log("failed to send answer, subscriber is nil", .error)
log("Failed to send answer, subscriber is nil", .error)
return
}

Expand Down Expand Up @@ -120,7 +120,7 @@ extension Engine: SignalClientDelegate {
await _room?.signalClient(signalClient, didUpdateConnectionQuality: quality)
}

func signalClient(_ signalClient: SignalClient, didUpdateRemoteMute trackSid: String, muted: Bool) async {
func signalClient(_ signalClient: SignalClient, didUpdateRemoteMute trackSid: Track.Sid, muted: Bool) async {
await _room?.signalClient(signalClient, didUpdateRemoteMute: trackSid, muted: muted)
}

Expand Down
32 changes: 25 additions & 7 deletions Sources/LiveKit/Core/Engine+TransportDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,43 @@

import Foundation

@_implementationOnly import WebRTC
@_implementationOnly import LiveKitWebRTC

extension RTCPeerConnectionState {
var isConnected: Bool {
self == .connected
}

var isDisconnected: Bool {
[.disconnected, .failed].contains(self)
}
}

extension Engine: TransportDelegate {
func transport(_ transport: Transport, didUpdateState pcState: RTCPeerConnectionState) async {
log("target: \(transport.target), state: \(pcState)")

// primary connected
if transport.isPrimary, case .connected = pcState {
primaryTransportConnectedCompleter.resume(returning: ())
if transport.isPrimary {
if pcState.isConnected {
primaryTransportConnectedCompleter.resume(returning: ())
} else if pcState.isDisconnected {
primaryTransportConnectedCompleter.reset()
}
}

// publisher connected
if case .publisher = transport.target, case .connected = pcState {
publisherTransportConnectedCompleter.resume(returning: ())
if case .publisher = transport.target {
if pcState.isConnected {
publisherTransportConnectedCompleter.resume(returning: ())
} else if pcState.isDisconnected {
publisherTransportConnectedCompleter.reset()
}
}

if _state.connectionState == .connected {
// Attempt re-connect if primary or publisher transport failed
if transport.isPrimary || (_state.hasPublished && transport.target == .publisher), [.disconnected, .failed].contains(pcState) {
if transport.isPrimary || (_state.hasPublished && transport.target == .publisher), pcState.isDisconnected {
do {
try await startReconnect(reason: .transport)
} catch {
Expand Down Expand Up @@ -66,7 +84,7 @@ extension Engine: TransportDelegate {
removeWhen: { state, _ in state.connectionState == .disconnected })
{ [weak self] in
guard let self else { return }
_delegate.notifyAsync { await $0.engine(self, didAddTrack: track, rtpReceiver: rtpReceiver, stream: streams.first!) }
self._delegate.notifyAsync { await $0.engine(self, didAddTrack: track, rtpReceiver: rtpReceiver, stream: streams.first!) }
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/LiveKit/Core/Engine+WebRTC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import Foundation

@_implementationOnly import WebRTC
@_implementationOnly import LiveKitWebRTC

private extension Array where Element: LKRTCVideoCodecInfo {
func rewriteCodecsIfNeeded() -> [LKRTCVideoCodecInfo] {
Expand Down
Loading

0 comments on commit e3373f7

Please sign in to comment.