mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
WIP
This commit is contained in:
parent
518480564c
commit
ec6a8877f5
@ -251,9 +251,9 @@ official_bundle_ids = [
|
|||||||
"org.telegram.Telegram-iOS",
|
"org.telegram.Telegram-iOS",
|
||||||
]
|
]
|
||||||
|
|
||||||
apple_pay_merchants = []#official_apple_pay_merchants if telegram_bundle_id == "ph.telegra.Telegraph" else ""
|
apple_pay_merchants = official_apple_pay_merchants if telegram_bundle_id == "ph.telegra.Telegraph" else []
|
||||||
|
|
||||||
apple_pay_merchants_fragment = "" if apple_pay_merchants == "" else """
|
apple_pay_merchants_fragment = "" if apple_pay_merchants == [] else """
|
||||||
<key>com.apple.developer.in-app-payments</key>
|
<key>com.apple.developer.in-app-payments</key>
|
||||||
<array>
|
<array>
|
||||||
""" + "\n".join([
|
""" + "\n".join([
|
||||||
@ -1528,13 +1528,13 @@ ios_application(
|
|||||||
":AppStringResources",
|
":AppStringResources",
|
||||||
],
|
],
|
||||||
extensions = [
|
extensions = [
|
||||||
#":ShareExtension",
|
":ShareExtension",
|
||||||
#":NotificationContentExtension",
|
":NotificationContentExtension",
|
||||||
#":NotificationServiceExtension",
|
":NotificationServiceExtension",
|
||||||
#":IntentsExtension",
|
":IntentsExtension",
|
||||||
#":WidgetExtension",
|
":WidgetExtension",
|
||||||
],
|
],
|
||||||
#watch_application = ":TelegramWatchApp",
|
watch_application = ":TelegramWatchApp",
|
||||||
deps = [
|
deps = [
|
||||||
":Main",
|
":Main",
|
||||||
":Lib",
|
":Lib",
|
||||||
|
@ -1 +1 @@
|
|||||||
12.0.1
|
12.1
|
||||||
|
@ -21,6 +21,9 @@ public final class GroupCallController: ViewController {
|
|||||||
private var isMutedDisposable: Disposable?
|
private var isMutedDisposable: Disposable?
|
||||||
private let audioSessionActive = Promise<Bool>(false)
|
private let audioSessionActive = Promise<Bool>(false)
|
||||||
|
|
||||||
|
private var incomingVideoStreamList: [String] = []
|
||||||
|
private var incomingVideoStreamListDisposable: Disposable?
|
||||||
|
|
||||||
private var memberCount: Int = 0
|
private var memberCount: Int = 0
|
||||||
private let memberCountNode: ImmediateTextNode
|
private let memberCountNode: ImmediateTextNode
|
||||||
|
|
||||||
@ -28,6 +31,8 @@ public final class GroupCallController: ViewController {
|
|||||||
private let isMutedNode: ImmediateTextNode
|
private let isMutedNode: ImmediateTextNode
|
||||||
private let muteButton: HighlightableButtonNode
|
private let muteButton: HighlightableButtonNode
|
||||||
|
|
||||||
|
private var videoViews: [OngoingCallContextPresentationCallVideoView] = []
|
||||||
|
|
||||||
private var validLayout: ContainerViewLayout?
|
private var validLayout: ContainerViewLayout?
|
||||||
|
|
||||||
init(context: AccountContext) {
|
init(context: AccountContext) {
|
||||||
@ -77,6 +82,34 @@ public final class GroupCallController: ViewController {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
self.incomingVideoStreamListDisposable = (callContext.videoStreamList
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var addedStreamIds: [String] = []
|
||||||
|
for id in value {
|
||||||
|
if !strongSelf.incomingVideoStreamList.contains(id) {
|
||||||
|
addedStreamIds.append(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strongSelf.incomingVideoStreamList = value
|
||||||
|
|
||||||
|
for id in addedStreamIds {
|
||||||
|
callContext.makeIncomingVideoView(id: id, completion: { videoView in
|
||||||
|
guard let strongSelf = self, let videoView = videoView else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.videoViews.append(videoView)
|
||||||
|
videoView.view.backgroundColor = .black
|
||||||
|
strongSelf.view.addSubview(videoView.view)
|
||||||
|
if let layout = strongSelf.validLayout {
|
||||||
|
strongSelf.containerLayoutUpdated(layout, transition: .immediate)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
self.isMutedDisposable = (callContext.isMuted
|
self.isMutedDisposable = (callContext.isMuted
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
@ -94,6 +127,7 @@ public final class GroupCallController: ViewController {
|
|||||||
deinit {
|
deinit {
|
||||||
self.callDisposable?.dispose()
|
self.callDisposable?.dispose()
|
||||||
self.memberCountDisposable?.dispose()
|
self.memberCountDisposable?.dispose()
|
||||||
|
self.incomingVideoStreamListDisposable?.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func muteButtonPressed() {
|
@objc private func muteButtonPressed() {
|
||||||
@ -116,6 +150,17 @@ public final class GroupCallController: ViewController {
|
|||||||
let isMutedFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - isMutedSize.width) / 2.0), y: textFrame.maxY + 12.0), size: isMutedSize)
|
let isMutedFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - isMutedSize.width) / 2.0), y: textFrame.maxY + 12.0), size: isMutedSize)
|
||||||
transition.updateFrame(node: self.muteButton, frame: isMutedFrame)
|
transition.updateFrame(node: self.muteButton, frame: isMutedFrame)
|
||||||
self.isMutedNode.frame = CGRect(origin: CGPoint(), size: isMutedFrame.size)
|
self.isMutedNode.frame = CGRect(origin: CGPoint(), size: isMutedFrame.size)
|
||||||
|
|
||||||
|
let videoSize = CGSize(width: 200.0, height: 360.0)
|
||||||
|
var nextVideoOrigin = CGPoint()
|
||||||
|
for videoView in self.videoViews {
|
||||||
|
videoView.view.frame = CGRect(origin: nextVideoOrigin, size: videoSize)
|
||||||
|
nextVideoOrigin.x += videoSize.width
|
||||||
|
if nextVideoOrigin.x + videoSize.width > layout.size.width {
|
||||||
|
nextVideoOrigin.x = 0.0
|
||||||
|
nextVideoOrigin.y += videoSize.height
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -501,9 +501,15 @@ private extension ConferenceDescription.Content.Channel.PayloadType {
|
|||||||
[
|
[
|
||||||
"type": "transport-cc"
|
"type": "transport-cc"
|
||||||
] as [String: Any],
|
] as [String: Any],
|
||||||
/*[
|
[
|
||||||
|
"type": "ccm fir"
|
||||||
|
] as [String: Any],
|
||||||
|
[
|
||||||
"type": "nack"
|
"type": "nack"
|
||||||
] as [String: Any]*/
|
] as [String: Any],
|
||||||
|
[
|
||||||
|
"type": "nack pli"
|
||||||
|
] as [String: Any],
|
||||||
] as [Any]
|
] as [Any]
|
||||||
if let parameters = self.parameters {
|
if let parameters = self.parameters {
|
||||||
result["parameters"] = parameters
|
result["parameters"] = parameters
|
||||||
@ -782,7 +788,7 @@ private extension ConferenceDescription {
|
|||||||
appendSdp("a=ssrc:\(stream.audioSsrc) label:audio\(stream.audioSsrc)")
|
appendSdp("a=ssrc:\(stream.audioSsrc) label:audio\(stream.audioSsrc)")
|
||||||
}
|
}
|
||||||
|
|
||||||
appendSdp("m=video \(stream.isMain ? "1" : "0") RTP/SAVPF 100")
|
appendSdp("m=video 0 RTP/SAVPF 100")
|
||||||
appendSdp("a=mid:video\(stream.videoSsrc)")
|
appendSdp("a=mid:video\(stream.videoSsrc)")
|
||||||
if stream.isRemoved {
|
if stream.isRemoved {
|
||||||
appendSdp("a=inactive")
|
appendSdp("a=inactive")
|
||||||
@ -809,6 +815,11 @@ private extension ConferenceDescription {
|
|||||||
appendSdp("a=rtcp-fb:100 nack")
|
appendSdp("a=rtcp-fb:100 nack")
|
||||||
appendSdp("a=rtcp-fb:100 nack pli")
|
appendSdp("a=rtcp-fb:100 nack pli")
|
||||||
|
|
||||||
|
if stream.isMain {
|
||||||
|
appendSdp("a=sendrecv")
|
||||||
|
} else {
|
||||||
|
appendSdp("a=sendonly")
|
||||||
|
}
|
||||||
appendSdp("a=bundle-only")
|
appendSdp("a=bundle-only")
|
||||||
|
|
||||||
appendSdp("a=ssrc-group:FID \(stream.videoSsrc)")
|
appendSdp("a=ssrc-group:FID \(stream.videoSsrc)")
|
||||||
@ -1026,9 +1037,6 @@ private extension ConferenceDescription {
|
|||||||
[
|
[
|
||||||
"type": "transport-cc"
|
"type": "transport-cc"
|
||||||
] as [String: Any],
|
] as [String: Any],
|
||||||
/*[
|
|
||||||
"type": "nack"
|
|
||||||
] as [String: Any]*/
|
|
||||||
] as [Any]
|
] as [Any]
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
@ -1075,9 +1083,15 @@ private extension ConferenceDescription {
|
|||||||
[
|
[
|
||||||
"type": "transport-cc"
|
"type": "transport-cc"
|
||||||
] as [String: Any],
|
] as [String: Any],
|
||||||
|
[
|
||||||
|
"type": "ccm fir"
|
||||||
|
] as [String: Any],
|
||||||
[
|
[
|
||||||
"type": "nack"
|
"type": "nack"
|
||||||
] as [String: Any]
|
] as [String: Any],
|
||||||
|
[
|
||||||
|
"type": "nack pli"
|
||||||
|
] as [String: Any],
|
||||||
] as [Any]
|
] as [Any]
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@ -1263,6 +1277,7 @@ public final class GroupCallContext {
|
|||||||
private var localTransport: ConferenceDescription.Transport?
|
private var localTransport: ConferenceDescription.Transport?
|
||||||
|
|
||||||
let memberCount = ValuePromise<Int>(0, ignoreRepeated: true)
|
let memberCount = ValuePromise<Int>(0, ignoreRepeated: true)
|
||||||
|
let videoStreamList = ValuePromise<[String]>([], ignoreRepeated: true)
|
||||||
|
|
||||||
private var isMutedValue: Bool = false
|
private var isMutedValue: Bool = false
|
||||||
let isMuted = ValuePromise<Bool>(false, ignoreRepeated: true)
|
let isMuted = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||||
@ -1271,14 +1286,20 @@ public final class GroupCallContext {
|
|||||||
self.queue = queue
|
self.queue = queue
|
||||||
|
|
||||||
self.sessionId = UInt32.random(in: 0 ..< UInt32(Int32.max))
|
self.sessionId = UInt32.random(in: 0 ..< UInt32(Int32.max))
|
||||||
self.colibriHost = "192.168.8.118"
|
self.colibriHost = "192.168.93.24"
|
||||||
|
|
||||||
var relaySdpAnswerImpl: ((String) -> Void)?
|
var relaySdpAnswerImpl: ((String) -> Void)?
|
||||||
|
|
||||||
|
let videoStreamList = self.videoStreamList
|
||||||
|
|
||||||
self.context = GroupCallThreadLocalContext(queue: ContextQueueImpl(queue: queue), relaySdpAnswer: { sdpAnswer in
|
self.context = GroupCallThreadLocalContext(queue: ContextQueueImpl(queue: queue), relaySdpAnswer: { sdpAnswer in
|
||||||
queue.async {
|
queue.async {
|
||||||
relaySdpAnswerImpl?(sdpAnswer)
|
relaySdpAnswerImpl?(sdpAnswer)
|
||||||
}
|
}
|
||||||
|
}, incomingVideoStreamListUpdated: { streamList in
|
||||||
|
queue.async {
|
||||||
|
videoStreamList.set(streamList)
|
||||||
|
}
|
||||||
}, videoCapturer: video?.impl)
|
}, videoCapturer: video?.impl)
|
||||||
|
|
||||||
relaySdpAnswerImpl = { [weak self] sdpAnswer in
|
relaySdpAnswerImpl = { [weak self] sdpAnswer in
|
||||||
@ -1595,6 +1616,45 @@ public final class GroupCallContext {
|
|||||||
self.isMuted.set(self.isMutedValue)
|
self.isMuted.set(self.isMutedValue)
|
||||||
self.context.setIsMuted(self.isMutedValue)
|
self.context.setIsMuted(self.isMutedValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeIncomingVideoView(id: String, completion: @escaping (OngoingCallContextPresentationCallVideoView?) -> Void) {
|
||||||
|
self.context.makeIncomingVideoView(withStreamId: id, completion: { view in
|
||||||
|
if let view = view {
|
||||||
|
completion(OngoingCallContextPresentationCallVideoView(
|
||||||
|
view: view,
|
||||||
|
setOnFirstFrameReceived: { [weak view] f in
|
||||||
|
view?.setOnFirstFrameReceived(f)
|
||||||
|
},
|
||||||
|
getOrientation: { [weak view] in
|
||||||
|
if let view = view {
|
||||||
|
return OngoingCallVideoOrientation(view.orientation)
|
||||||
|
} else {
|
||||||
|
return .rotation0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getAspect: { [weak view] in
|
||||||
|
if let view = view {
|
||||||
|
return view.aspect
|
||||||
|
} else {
|
||||||
|
return 0.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setOnOrientationUpdated: { [weak view] f in
|
||||||
|
view?.setOnOrientationUpdated { value, aspect in
|
||||||
|
f?(OngoingCallVideoOrientation(value), aspect)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setOnIsMirroredUpdated: { [weak view] f in
|
||||||
|
view?.setOnIsMirroredUpdated { value in
|
||||||
|
f?(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
completion(nil)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private let queue = Queue()
|
private let queue = Queue()
|
||||||
@ -1619,6 +1679,18 @@ public final class GroupCallContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var videoStreamList: Signal<[String], NoError> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
let disposable = MetaDisposable()
|
||||||
|
self.impl.with { impl in
|
||||||
|
disposable.set(impl.videoStreamList.get().start(next: { value in
|
||||||
|
subscriber.putNext(value)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
return disposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public var isMuted: Signal<Bool, NoError> {
|
public var isMuted: Signal<Bool, NoError> {
|
||||||
return Signal { subscriber in
|
return Signal { subscriber in
|
||||||
let disposable = MetaDisposable()
|
let disposable = MetaDisposable()
|
||||||
@ -1636,5 +1708,11 @@ public final class GroupCallContext {
|
|||||||
impl.toggleIsMuted()
|
impl.toggleIsMuted()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func makeIncomingVideoView(id: String, completion: @escaping (OngoingCallContextPresentationCallVideoView?) -> Void) {
|
||||||
|
self.impl.with { impl in
|
||||||
|
impl.makeIncomingVideoView(id: id, completion: completion)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -472,7 +472,7 @@ public enum OngoingCallVideoOrientation {
|
|||||||
case rotation270
|
case rotation270
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension OngoingCallVideoOrientation {
|
extension OngoingCallVideoOrientation {
|
||||||
init(_ orientation: OngoingCallVideoOrientationWebrtc) {
|
init(_ orientation: OngoingCallVideoOrientationWebrtc) {
|
||||||
switch orientation {
|
switch orientation {
|
||||||
case .orientation0:
|
case .orientation0:
|
||||||
|
@ -154,11 +154,12 @@ typedef NS_ENUM(int32_t, OngoingCallDataSavingWebrtc) {
|
|||||||
|
|
||||||
@interface GroupCallThreadLocalContext : NSObject
|
@interface GroupCallThreadLocalContext : NSObject
|
||||||
|
|
||||||
- (instancetype _Nonnull)initWithQueue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue relaySdpAnswer:(void (^ _Nonnull)(NSString * _Nonnull))relaySdpAnswer videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer;
|
- (instancetype _Nonnull)initWithQueue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue relaySdpAnswer:(void (^ _Nonnull)(NSString * _Nonnull))relaySdpAnswer incomingVideoStreamListUpdated:(void (^ _Nonnull)(NSArray<NSString *> * _Nonnull))incomingVideoStreamListUpdated videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer;
|
||||||
|
|
||||||
- (void)emitOffer;
|
- (void)emitOffer;
|
||||||
- (void)setOfferSdp:(NSString * _Nonnull)offerSdp isPartial:(bool)isPartial;
|
- (void)setOfferSdp:(NSString * _Nonnull)offerSdp isPartial:(bool)isPartial;
|
||||||
- (void)setIsMuted:(bool)isMuted;
|
- (void)setIsMuted:(bool)isMuted;
|
||||||
|
- (void)makeIncomingVideoViewWithStreamId:(NSString * _Nonnull)streamId completion:(void (^_Nonnull)(UIView<OngoingCallThreadLocalContextWebrtcVideoView> * _Nullable))completion;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@ -806,7 +806,7 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
|||||||
|
|
||||||
@implementation GroupCallThreadLocalContext
|
@implementation GroupCallThreadLocalContext
|
||||||
|
|
||||||
- (instancetype _Nonnull)initWithQueue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue relaySdpAnswer:(void (^ _Nonnull)(NSString * _Nonnull))relaySdpAnswer videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer {
|
- (instancetype _Nonnull)initWithQueue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue relaySdpAnswer:(void (^ _Nonnull)(NSString * _Nonnull))relaySdpAnswer incomingVideoStreamListUpdated:(void (^ _Nonnull)(NSArray<NSString *> * _Nonnull))incomingVideoStreamListUpdated videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer {
|
||||||
self = [super init];
|
self = [super init];
|
||||||
if (self != nil) {
|
if (self != nil) {
|
||||||
_queue = queue;
|
_queue = queue;
|
||||||
@ -825,6 +825,19 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
|||||||
relaySdpAnswer(string);
|
relaySdpAnswer(string);
|
||||||
}];
|
}];
|
||||||
},
|
},
|
||||||
|
.incomingVideoStreamListUpdated = [weakSelf, queue, incomingVideoStreamListUpdated](std::vector<std::string> const &incomingVideoStreamList) {
|
||||||
|
NSMutableArray<NSString *> *mappedList = [[NSMutableArray alloc] init];
|
||||||
|
for (auto &it : incomingVideoStreamList) {
|
||||||
|
[mappedList addObject:[NSString stringWithUTF8String:it.c_str()]];
|
||||||
|
}
|
||||||
|
[queue dispatch:^{
|
||||||
|
__strong GroupCallThreadLocalContext *strongSelf = weakSelf;
|
||||||
|
if (strongSelf == nil) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
incomingVideoStreamListUpdated(mappedList);
|
||||||
|
}];
|
||||||
|
},
|
||||||
.videoCapture = [_videoCapturer getInterface]
|
.videoCapture = [_videoCapturer getInterface]
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@ -849,5 +862,43 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)makeIncomingVideoViewWithStreamId:(NSString * _Nonnull)streamId completion:(void (^_Nonnull)(UIView<OngoingCallThreadLocalContextWebrtcVideoView> * _Nullable))completion {
|
||||||
|
if (_instance) {
|
||||||
|
__weak GroupCallThreadLocalContext *weakSelf = self;
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
if ([VideoMetalView isSupported]) {
|
||||||
|
VideoMetalView *remoteRenderer = [[VideoMetalView alloc] initWithFrame:CGRectZero];
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
|
remoteRenderer.videoContentMode = UIViewContentModeScaleToFill;
|
||||||
|
#else
|
||||||
|
remoteRenderer.videoContentMode = UIViewContentModeScaleAspect;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink = [remoteRenderer getSink];
|
||||||
|
__strong GroupCallThreadLocalContext *strongSelf = weakSelf;
|
||||||
|
if (strongSelf) {
|
||||||
|
//[remoteRenderer setOrientation:strongSelf->_remoteVideoOrientation];
|
||||||
|
//strongSelf->_currentRemoteVideoRenderer = remoteRenderer;
|
||||||
|
strongSelf->_instance->setIncomingVideoOutput([streamId UTF8String], sink);
|
||||||
|
}
|
||||||
|
|
||||||
|
completion(remoteRenderer);
|
||||||
|
} else {
|
||||||
|
GLVideoView *remoteRenderer = [[GLVideoView alloc] initWithFrame:CGRectZero];
|
||||||
|
|
||||||
|
std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink = [remoteRenderer getSink];
|
||||||
|
__strong GroupCallThreadLocalContext *strongSelf = weakSelf;
|
||||||
|
if (strongSelf) {
|
||||||
|
//[remoteRenderer setOrientation:strongSelf->_remoteVideoOrientation];
|
||||||
|
//strongSelf->_currentRemoteVideoRenderer = remoteRenderer;
|
||||||
|
strongSelf->_instance->setIncomingVideoOutput([streamId UTF8String], sink);
|
||||||
|
}
|
||||||
|
|
||||||
|
completion(remoteRenderer);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user