Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make media objects optional #539

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
24 changes: 17 additions & 7 deletions Decimus/Publications/PublicationFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,27 @@ protocol PublicationFactory {
func create(publication: ManifestPublication, codecFactory: CodecFactory, endpointId: String, relayId: String) throws -> [(FullTrackName, QPublishTrackHandlerObjC)]
}

enum PubSubFactoryError: Error {
case cannotCreate(String)
}

class PublicationFactoryImpl: PublicationFactory {
private let opusWindowSize: OpusWindowSize
private let reliability: MediaReliability
private let granularMetrics: Bool
private let engine: DecimusAudioEngine
private let engine: DecimusAudioEngine?
private let metricsSubmitter: MetricsSubmitter?
private let captureManager: CaptureManager
private let captureManager: CaptureManager?
private let participantId: ParticipantId
private let keyFrameInterval: TimeInterval
private let logger = DecimusLogger(PublicationFactory.self)

init(opusWindowSize: OpusWindowSize,
reliability: MediaReliability,
engine: DecimusAudioEngine,
engine: DecimusAudioEngine?,
metricsSubmitter: MetricsSubmitter?,
granularMetrics: Bool,
captureManager: CaptureManager,
captureManager: CaptureManager?,
participantId: ParticipantId,
keyFrameInterval: TimeInterval) {
self.opusWindowSize = opusWindowSize
Expand All @@ -51,7 +55,7 @@ class PublicationFactoryImpl: PublicationFactory {
relayId: relayId)
publications.append((fullTrackName, publication))
} catch {
self.logger.error("[\(fullTrackName)] Failed to create publication: \(error.localizedDescription)")
self.logger.warning("[\(fullTrackName)] Failed to create publication: \(error.localizedDescription)", alert: true)
}
}
return publications
Expand All @@ -65,6 +69,9 @@ class PublicationFactoryImpl: PublicationFactory {
relayId: String) throws -> QPublishTrackHandlerObjC {
switch config.codec {
case .h264, .hevc:
guard let captureManager = self.captureManager else {
throw PubSubFactoryError.cannotCreate("No camera capability")
}
guard let config = config as? VideoCodecConfig else {
throw CodecError.invalidCodecConfig(type(of: config))
}
Expand Down Expand Up @@ -101,9 +108,12 @@ class PublicationFactoryImpl: PublicationFactory {
device: device,
endpointId: endpointId,
relayId: relayId)
try self.captureManager.addInput(publication)
try captureManager.addInput(publication)
return publication
case .opus:
guard let engine = self.engine else {
throw PubSubFactoryError.cannotCreate("No audio capability")
}
guard let config = config as? AudioCodecConfig else {
throw CodecError.invalidCodecConfig(type(of: config))
}
Expand All @@ -112,7 +122,7 @@ class PublicationFactoryImpl: PublicationFactory {
metricsSubmitter: metricsSubmitter,
opusWindowSize: opusWindowSize,
reliable: reliability.audio.publication,
engine: self.engine,
engine: engine,
granularMetrics: self.granularMetrics,
config: config,
endpointId: endpointId,
Expand Down
14 changes: 10 additions & 4 deletions Decimus/Subscriptions/SubscriptionFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -140,15 +140,15 @@ class SubscriptionFactoryImpl: SubscriptionFactory {
private let metricsSubmitter: MetricsSubmitter?
private let subscriptionConfig: SubscriptionConfig
private let granularMetrics: Bool
private let engine: DecimusAudioEngine
private let engine: DecimusAudioEngine?
private let participantId: ParticipantId?
var activeSpeakerNotifier: ActiveSpeakerNotifierSubscriptionSet?

init(videoParticipants: VideoParticipants,
metricsSubmitter: MetricsSubmitter?,
subscriptionConfig: SubscriptionConfig,
granularMetrics: Bool,
engine: DecimusAudioEngine,
engine: DecimusAudioEngine?,
participantId: ParticipantId?) {
self.videoParticipants = videoParticipants
self.metricsSubmitter = metricsSubmitter
Expand All @@ -160,9 +160,12 @@ class SubscriptionFactoryImpl: SubscriptionFactory {

func create(subscription: ManifestSubscription, codecFactory: CodecFactory, endpointId: String, relayId: String) throws -> any SubscriptionSet {
if subscription.mediaType == ManifestMediaTypes.audio.rawValue && subscription.profileSet.type == "switched" {
guard let engine = self.engine else {
throw PubSubFactoryError.cannotCreate("No audio capability")
}
// This a switched / active speaker subscription type.
return ActiveSpeakerSubscriptionSet(subscription: subscription,
engine: self.engine,
engine: engine,
jitterDepth: self.subscriptionConfig.jitterDepthTime,
jitterMax: self.subscriptionConfig.jitterMaxTime,
opusWindowSize: self.subscriptionConfig.opusWindowSize,
Expand Down Expand Up @@ -205,8 +208,11 @@ class SubscriptionFactoryImpl: SubscriptionFactory {
}

if found.isSubset(of: opusCodecs) {
guard let engine = self.engine else {
throw PubSubFactoryError.cannotCreate("No audio capability")
}
return try OpusSubscription(subscription: subscription,
engine: self.engine,
engine: engine,
submitter: self.metricsSubmitter,
jitterDepth: self.subscriptionConfig.jitterDepthTime,
jitterMax: self.subscriptionConfig.jitterMaxTime,
Expand Down
86 changes: 46 additions & 40 deletions Decimus/Views/InCallView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -342,19 +342,13 @@ extension InCallView {
}
self.currentManifest = manifest

// TODO: Doesn't need to be this defensive.
guard let captureManager = self.captureManager,
let engine = self.engine else {
return false
}

// Create the factories now that we have the participant ID.
let publicationFactory = PublicationFactoryImpl(opusWindowSize: self.subscriptionConfig.value.opusWindowSize,
reliability: self.subscriptionConfig.value.mediaReliability,
engine: engine,
engine: self.engine,
metricsSubmitter: self.submitter,
granularMetrics: self.influxConfig.value.granular,
captureManager: captureManager,
captureManager: self.captureManager,
participantId: manifest.participantId,
keyFrameInterval: self.subscriptionConfig.value.keyFrameInterval)
let playtime = self.playtimeConfig.value
Expand All @@ -363,7 +357,7 @@ extension InCallView {
metricsSubmitter: self.submitter,
subscriptionConfig: self.subscriptionConfig.value,
granularMetrics: self.influxConfig.value.granular,
engine: engine,
engine: self.engine,
participantId: ourParticipantId)
self.publicationFactory = publicationFactory
self.subscriptionFactory = subscriptionFactory
Expand All @@ -388,55 +382,67 @@ extension InCallView {
}

// Inject the manifest in order to create publications & subscriptions.
do {
// Publish.
for publication in manifest.publications {

// Publish.
for publication in manifest.publications {
do {
try controller.publish(details: publication, factory: publicationFactory, codecFactory: CodecFactoryImpl())
} catch {
Self.logger.warning("[\(publication.sourceID)] Couldn't create publication: \(error.localizedDescription)")
}
}

// Subscribe.
for subscription in manifest.subscriptions {
// Subscribe.
for subscription in manifest.subscriptions {
do {
_ = try controller.subscribeToSet(details: subscription, factory: subscriptionFactory, subscribe: true)
} catch {
Self.logger.warning("[\(subscription.sourceID)] Couldn't create subscription: \(error.localizedDescription)")
}
}

// Active speaker handling.
let notifier: ActiveSpeakerNotifier?
if playtime.playtime && playtime.manualActiveSpeaker {
let manual = ManualActiveSpeaker()
self.manualActiveSpeaker = manual
notifier = manual
} else if let real = subscriptionFactory.activeSpeakerNotifier {
notifier = real
} else {
notifier = nil
}
if let notifier = notifier {
let videoSubscriptions = manifest.subscriptions.filter { $0.mediaType == ManifestMediaTypes.video.rawValue }
// Active speaker handling.
let notifier: ActiveSpeakerNotifier?
if playtime.playtime && playtime.manualActiveSpeaker {
let manual = ManualActiveSpeaker()
self.manualActiveSpeaker = manual
notifier = manual
} else if let real = subscriptionFactory.activeSpeakerNotifier {
notifier = real
} else {
notifier = nil
}
if let notifier = notifier {
let videoSubscriptions = manifest.subscriptions.filter { $0.mediaType == ManifestMediaTypes.video.rawValue }
do {
self.activeSpeaker = try .init(notifier: notifier,
controller: controller,
videoSubscriptions: videoSubscriptions,
factory: subscriptionFactory,
participantId: manifest.participantId)
} catch {
Self.logger.error("Failed to create active speaker controller: \(error.localizedDescription)")
}
} catch {
Self.logger.error("Failed to set manifest: \(error.localizedDescription)")
return false
}

// Start audio media.
do {
try engine.start()
self.audioCapture = true
} catch {
Self.logger.warning("Audio failure. Apple requires us to have an aggregate input AND output device", alert: true)
if let engine = self.engine {
do {
try engine.start()
self.audioCapture = true
} catch {
Self.logger.warning("Audio failure. Apple requires us to have an aggregate input AND output device", alert: true)
}
}

// Start video media.
do {
try captureManager.startCapturing()
self.videoCapture = true
} catch {
Self.logger.warning("Camera failure", alert: true)
if let captureManager = self.captureManager {
do {
try captureManager.startCapturing()
self.videoCapture = true
} catch {
Self.logger.warning("Camera failure", alert: true)
}
}
return true
}
Expand Down