Merge pull request #7566 from vector-im/mauroromito/broadcast_extension

Broadcast Upload Extension
This commit is contained in:
Mauro 2023-06-01 15:33:44 +02:00 committed by GitHub
commit c87b8e25e1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 767 additions and 0 deletions

View file

@ -0,0 +1,30 @@
//
// Copyright 2023 Vector Creations Ltd
//
// 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.
//
// Configuration settings file format documentation can be found at:
// https://help.apple.com/xcode/#/dev745c5c974
#include "Config/AppIdentifiers.xcconfig"
#include "Config/AppVersion.xcconfig"
PRODUCT_NAME = BroadcastUploadExtension
PRODUCT_BUNDLE_IDENTIFIER = $(BROADCAST_UPLOAD_EXTENSION_BUNDLE_IDENTIFIER)
INFOPLIST_FILE = BroadcastUploadExtension/SupportingFiles/Info.plist
CODE_SIGN_ENTITLEMENTS = BroadcastUploadExtension/SupportingFiles/BroadcastUploadExtension.entitlements
SKIP_INSTALL = YES

View file

@ -0,0 +1,21 @@
//
// Copyright 2020 Vector Creations Ltd
//
// 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.
//
// Configuration settings file format documentation can be found at:
// https://help.apple.com/xcode/#/dev745c5c974
#include "Common.xcconfig"
#include "Pods/Target Support Files/Pods-RiotPods-BroadcastUploadExtension/Pods-RiotPods-BroadcastUploadExtension.debug.xcconfig"

View file

@ -0,0 +1,26 @@
//
// Copyright 2020 Vector Creations Ltd
//
// 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.
//
// Configuration settings file format documentation can be found at:
// https://help.apple.com/xcode/#/dev745c5c974
#include "Common.xcconfig"
#include "Pods/Target Support Files/Pods-RiotPods-BroadcastUploadExtension/Pods-RiotPods-BroadcastUploadExtension.release.xcconfig"
PROVISIONING_PROFILE = $(BROADCAST_UPLOAD_EXTENSION_PROVISIONING_PROFILE)
PROVISIONING_PROFILE_SPECIFIER = $(BROADCAST_UPLOAD_EXTENSION_PROVISIONING_PROFILE_SPECIFIER)
COPY_PHASE_STRIP = NO

View file

@ -0,0 +1,41 @@
//
// License from the original repository:
// https://github.com/jitsi/jitsi-meet-sdk-samples/blob/master/LICENSE
//
// Atomic.swift
// Broadcast Extension
//
// Created by Maksym Shcheglov.
// https://www.onswiftwings.com/posts/atomic-property-wrapper/
//
import Foundation
@propertyWrapper
struct Atomic<Value> {
private var value: Value
private let lock = NSLock()
init(wrappedValue value: Value) {
self.value = value
}
var wrappedValue: Value {
get { load() }
set { store(newValue: newValue) }
}
func load() -> Value {
lock.lock()
defer { lock.unlock() }
return value
}
mutating func store(newValue: Value) {
lock.lock()
defer { lock.unlock() }
value = newValue
}
}

View file

@ -0,0 +1,33 @@
//
// License from the original repository:
// https://github.com/jitsi/jitsi-meet-sdk-samples/blob/master/LICENSE
//
// DarwinNotificationCenter.swift
// Broadcast Extension
//
// Created by Alex-Dan Bumbu on 23/03/2021.
// Copyright © 2021 8x8, Inc. All rights reserved.
//
import Foundation
enum DarwinNotification: String {
case broadcastStarted = "iOS_BroadcastStarted"
case broadcastStopped = "iOS_BroadcastStopped"
}
class DarwinNotificationCenter {
static let shared = DarwinNotificationCenter()
private let notificationCenter: CFNotificationCenter
init() {
notificationCenter = CFNotificationCenterGetDarwinNotifyCenter()
}
func postNotification(_ name: DarwinNotification) {
CFNotificationCenterPostNotification(notificationCenter, CFNotificationName(rawValue: name.rawValue as CFString), nil, nil, true)
}
}

View file

@ -0,0 +1,125 @@
//
// License from the original repository:
// https://github.com/jitsi/jitsi-meet-sdk-samples/blob/master/LICENSE
//
// SampleHandler.swift
// Broadcast Extension
//
// Created by Alex-Dan Bumbu on 04.06.2021.
//
import ReplayKit
import MatrixSDK
private enum Constants {
// the App Group ID value that the app and the broadcast extension targets are setup with. It differs for each app.
static let appGroupIdentifier = BuildSettings.applicationGroupIdentifier
}
class SampleHandler: RPBroadcastSampleHandler {
private var clientConnection: SocketConnection?
private var uploader: SampleUploader?
private var frameCount: Int = 0
private var socketFilePath: String {
let sharedContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: Constants.appGroupIdentifier)
return sharedContainer?.appendingPathComponent("rtc_SSFD").path ?? ""
}
override init() {
super.init()
setupLogger()
if let connection = SocketConnection(filePath: socketFilePath) {
clientConnection = connection
setupConnection()
uploader = SampleUploader(connection: connection)
}
}
override func broadcastStarted(withSetupInfo setupInfo: [String: NSObject]?) {
// User has requested to start the broadcast. Setup info from the UI extension can be supplied but optional.
frameCount = 0
DarwinNotificationCenter.shared.postNotification(.broadcastStarted)
openConnection()
}
override func broadcastPaused() {
// User has requested to pause the broadcast. Samples will stop being delivered.
}
override func broadcastResumed() {
// User has requested to resume the broadcast. Samples delivery will resume.
}
override func broadcastFinished() {
// User has requested to finish the broadcast.
DarwinNotificationCenter.shared.postNotification(.broadcastStopped)
clientConnection?.close()
}
override func processSampleBuffer(_ sampleBuffer: CMSampleBuffer, with sampleBufferType: RPSampleBufferType) {
switch sampleBufferType {
case RPSampleBufferType.video:
// very simple mechanism for adjusting frame rate by using every third frame
frameCount += 1
if frameCount % 3 == 0 {
uploader?.send(sample: sampleBuffer)
}
default:
break
}
}
}
private extension SampleHandler {
func setupConnection() {
clientConnection?.didClose = { [weak self] error in
MXLog.error("client connection did close", context: error)
if let error = error {
self?.finishBroadcastWithError(error)
} else {
// the displayed failure message is more user friendly when using NSError instead of Error
let JMScreenSharingStopped = 10001
let customError = NSError(domain: RPRecordingErrorDomain, code: JMScreenSharingStopped, userInfo: [NSLocalizedDescriptionKey: "Screen sharing stopped"])
self?.finishBroadcastWithError(customError)
}
}
}
func openConnection() {
let queue = DispatchQueue(label: "broadcast.connectTimer")
let timer = DispatchSource.makeTimerSource(queue: queue)
timer.schedule(deadline: .now(), repeating: .milliseconds(100), leeway: .milliseconds(500))
timer.setEventHandler { [weak self] in
guard self?.clientConnection?.open() == true else {
return
}
timer.cancel()
}
timer.resume()
}
func setupLogger() {
let configuration = MXLogConfiguration()
configuration.logLevel = .verbose
configuration.maxLogFilesCount = 100
configuration.logFilesSizeLimit = 10 * 1024 * 1024; // 10MB
configuration.subLogName = "broadcastUploadExtension"
if isatty(STDERR_FILENO) == 0 {
configuration.redirectLogsToFiles = true
}
MXLog.configure(configuration)
}
}

View file

@ -0,0 +1,151 @@
//
// License from the original repository:
// https://github.com/jitsi/jitsi-meet-sdk-samples/blob/master/LICENSE
//
// SampleUploader.swift
// Broadcast Extension
//
// Created by Alex-Dan Bumbu on 22/03/2021.
// Copyright © 2021 8x8, Inc. All rights reserved.
//
import Foundation
import ReplayKit
import MatrixSDK
private enum Constants {
static let bufferMaxLength = 10240
}
class SampleUploader {
private static var imageContext = CIContext(options: nil)
@Atomic private var isReady = false
private var connection: SocketConnection
private var dataToSend: Data?
private var byteIndex = 0
private let serialQueue: DispatchQueue
init(connection: SocketConnection) {
self.connection = connection
self.serialQueue = DispatchQueue(label: "org.jitsi.meet.broadcast.sampleUploader")
setupConnection()
}
@discardableResult func send(sample buffer: CMSampleBuffer) -> Bool {
guard isReady else {
return false
}
isReady = false
dataToSend = prepare(sample: buffer)
byteIndex = 0
serialQueue.async { [weak self] in
self?.sendDataChunk()
}
return true
}
}
private extension SampleUploader {
func setupConnection() {
connection.didOpen = { [weak self] in
self?.isReady = true
}
connection.streamHasSpaceAvailable = { [weak self] in
self?.serialQueue.async {
if let success = self?.sendDataChunk() {
self?.isReady = !success
}
}
}
}
@discardableResult func sendDataChunk() -> Bool {
guard let dataToSend = dataToSend else {
return false
}
var bytesLeft = dataToSend.count - byteIndex
var length = bytesLeft > Constants.bufferMaxLength ? Constants.bufferMaxLength : bytesLeft
length = dataToSend[byteIndex..<(byteIndex + length)].withUnsafeBytes {
guard let ptr = $0.bindMemory(to: UInt8.self).baseAddress else {
return 0
}
return connection.writeToStream(buffer: ptr, maxLength: length)
}
if length > 0 {
byteIndex += length
bytesLeft -= length
if bytesLeft == 0 {
self.dataToSend = nil
byteIndex = 0
}
} else {
MXLog.error("writeBufferToStream failure")
}
return true
}
func prepare(sample buffer: CMSampleBuffer) -> Data? {
guard let imageBuffer = CMSampleBufferGetImageBuffer(buffer) else {
MXLog.error("image buffer not available")
return nil
}
CVPixelBufferLockBaseAddress(imageBuffer, .readOnly)
let scaleFactor = 2.0
let width = CVPixelBufferGetWidth(imageBuffer)/Int(scaleFactor)
let height = CVPixelBufferGetHeight(imageBuffer)/Int(scaleFactor)
let orientation = CMGetAttachment(buffer, key: RPVideoSampleOrientationKey as CFString, attachmentModeOut: nil)?.uintValue ?? 0
let scaleTransform = CGAffineTransform(scaleX: CGFloat(1.0/scaleFactor), y: CGFloat(1.0/scaleFactor))
let bufferData = self.jpegData(from: imageBuffer, scale: scaleTransform)
CVPixelBufferUnlockBaseAddress(imageBuffer, .readOnly)
guard let messageData = bufferData else {
MXLog.error("corrupted image buffer")
return nil
}
let httpResponse = CFHTTPMessageCreateResponse(nil, 200, nil, kCFHTTPVersion1_1).takeRetainedValue()
CFHTTPMessageSetHeaderFieldValue(httpResponse, "Content-Length" as CFString, String(messageData.count) as CFString)
CFHTTPMessageSetHeaderFieldValue(httpResponse, "Buffer-Width" as CFString, String(width) as CFString)
CFHTTPMessageSetHeaderFieldValue(httpResponse, "Buffer-Height" as CFString, String(height) as CFString)
CFHTTPMessageSetHeaderFieldValue(httpResponse, "Buffer-Orientation" as CFString, String(orientation) as CFString)
CFHTTPMessageSetBody(httpResponse, messageData as CFData)
let serializedMessage = CFHTTPMessageCopySerializedMessage(httpResponse)?.takeRetainedValue() as Data?
return serializedMessage
}
func jpegData(from buffer: CVPixelBuffer, scale scaleTransform: CGAffineTransform) -> Data? {
let image = CIImage(cvPixelBuffer: buffer).transformed(by: scaleTransform)
guard let colorSpace = image.colorSpace else {
return nil
}
let options: [CIImageRepresentationOption: Float] = [kCGImageDestinationLossyCompressionQuality as CIImageRepresentationOption: 1.0]
return SampleUploader.imageContext.jpegRepresentation(of: image, colorSpace: colorSpace, options: options)
}
}

View file

@ -0,0 +1,203 @@
//
// License from the original repository:
// https://github.com/jitsi/jitsi-meet-sdk-samples/blob/master/LICENSE
//
// SocketConnection.swift
// Broadcast Extension
//
// Created by Alex-Dan Bumbu on 22/03/2021.
// Copyright © 2021 Atlassian Inc. All rights reserved.
//
import Foundation
import MatrixSDK
class SocketConnection: NSObject {
var didOpen: (() -> Void)?
var didClose: ((Error?) -> Void)?
var streamHasSpaceAvailable: (() -> Void)?
private let filePath: String
private var socketHandle: Int32 = -1
private var address: sockaddr_un?
private var inputStream: InputStream?
private var outputStream: OutputStream?
private var networkQueue: DispatchQueue?
private var shouldKeepRunning = false
init?(filePath path: String) {
filePath = path
socketHandle = Darwin.socket(AF_UNIX, SOCK_STREAM, 0)
guard socketHandle != -1 else {
MXLog.error("failure: create socket")
return nil
}
}
func open() -> Bool {
MXLog.info("open socket connection")
guard FileManager.default.fileExists(atPath: filePath) else {
MXLog.error("failure: socket file missing")
return false
}
guard setupAddress() == true else {
return false
}
guard connectSocket() == true else {
return false
}
setupStreams()
inputStream?.open()
outputStream?.open()
return true
}
func close() {
unscheduleStreams()
inputStream?.delegate = nil
outputStream?.delegate = nil
inputStream?.close()
outputStream?.close()
inputStream = nil
outputStream = nil
}
func writeToStream(buffer: UnsafePointer<UInt8>, maxLength length: Int) -> Int {
outputStream?.write(buffer, maxLength: length) ?? 0
}
}
extension SocketConnection: StreamDelegate {
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
switch eventCode {
case .openCompleted:
MXLog.info("client stream open completed")
if aStream == outputStream {
didOpen?()
}
case .hasBytesAvailable:
if aStream == inputStream {
var buffer: UInt8 = 0
let numberOfBytesRead = inputStream?.read(&buffer, maxLength: 1)
if numberOfBytesRead == 0 && aStream.streamStatus == .atEnd {
MXLog.info("server socket closed")
close()
notifyDidClose(error: nil)
}
}
case .hasSpaceAvailable:
if aStream == outputStream {
streamHasSpaceAvailable?()
}
case .errorOccurred:
MXLog.error("client stream error occured", context: aStream.streamError)
close()
notifyDidClose(error: aStream.streamError)
default:
break
}
}
}
private extension SocketConnection {
func setupAddress() -> Bool {
var addr = sockaddr_un()
guard filePath.count < MemoryLayout.size(ofValue: addr.sun_path) else {
MXLog.error("failure: fd path is too long")
return false
}
_ = withUnsafeMutablePointer(to: &addr.sun_path.0) { ptr in
filePath.withCString {
strncpy(ptr, $0, filePath.count)
}
}
address = addr
return true
}
func connectSocket() -> Bool {
guard var addr = address else {
return false
}
let status = withUnsafePointer(to: &addr) { ptr in
ptr.withMemoryRebound(to: sockaddr.self, capacity: 1) {
Darwin.connect(socketHandle, $0, socklen_t(MemoryLayout<sockaddr_un>.size))
}
}
guard status == noErr else {
MXLog.error("connect socket failure", context: status)
return false
}
return true
}
func setupStreams() {
var readStream: Unmanaged<CFReadStream>?
var writeStream: Unmanaged<CFWriteStream>?
CFStreamCreatePairWithSocket(kCFAllocatorDefault, socketHandle, &readStream, &writeStream)
inputStream = readStream?.takeRetainedValue()
inputStream?.delegate = self
inputStream?.setProperty(kCFBooleanTrue, forKey: Stream.PropertyKey(kCFStreamPropertyShouldCloseNativeSocket as String))
outputStream = writeStream?.takeRetainedValue()
outputStream?.delegate = self
outputStream?.setProperty(kCFBooleanTrue, forKey: Stream.PropertyKey(kCFStreamPropertyShouldCloseNativeSocket as String))
scheduleStreams()
}
func scheduleStreams() {
shouldKeepRunning = true
networkQueue = DispatchQueue.global(qos: .userInitiated)
networkQueue?.async { [weak self] in
self?.inputStream?.schedule(in: .current, forMode: .common)
self?.outputStream?.schedule(in: .current, forMode: .common)
RunLoop.current.run()
var isRunning = false
repeat {
isRunning = self?.shouldKeepRunning ?? false && RunLoop.current.run(mode: .default, before: .distantFuture)
} while (isRunning)
}
}
func unscheduleStreams() {
networkQueue?.sync { [weak self] in
self?.inputStream?.remove(from: .current, forMode: .common)
self?.outputStream?.remove(from: .current, forMode: .common)
}
shouldKeepRunning = false
}
func notifyDidClose(error: Error?) {
if didClose != nil {
didClose?(error)
}
}
}

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>$(APPLICATION_GROUP_IDENTIFIER)</string>
</array>
</dict>
</plist>

View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleDisplayName</key>
<string>$(BUNDLE_DISPLAY_NAME)</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.broadcast-services-upload</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).SampleHandler</string>
<key>RPBroadcastProcessMode</key>
<string>RPBroadcastProcessModeSampleBuffer</string>
</dict>
</dict>
</plist>

View file

@ -0,0 +1,42 @@
name: BroadcastUploadExtension
schemes:
BroadcastUploadExtension:
analyze:
config: Debug
archive:
config: Release
build:
targets:
BroadcastUploadExtension:
- running
- testing
- profiling
- analyzing
- archiving
profile:
config: Release
run:
askForAppToLaunch: true
config: Debug
debugEnabled: false
disableMainThreadChecker: true
launchAutomaticallySubstyle: 2
test:
config: Debug
disableMainThreadChecker: true
targets:
BroadcastUploadExtension:
platform: iOS
type: app-extension
configFiles:
Debug: Debug.xcconfig
Release: Release.xcconfig
sources:
- path: .
- path: ../Config/BuildSettings.swift
- path: ../Riot/Categories/Bundle.swift
- path: ../Riot/Modules/Room/TimelineCells/Styles/RoomTimelineStyleIdentifier.swift

View file

@ -37,3 +37,6 @@ SHARE_EXTENSION_PROVISIONING_PROFILE = 8c797ca0-0440-49bd-be8d-11d761152995
SIRI_INTENTS_PROVISIONING_PROFILE_SPECIFIER = "Vector Siri Intents: App Store" SIRI_INTENTS_PROVISIONING_PROFILE_SPECIFIER = "Vector Siri Intents: App Store"
SIRI_INTENTS_PROVISIONING_PROFILE = 1690e81a-5ad3-4d99-b578-02693579be71 SIRI_INTENTS_PROVISIONING_PROFILE = 1690e81a-5ad3-4d99-b578-02693579be71
BROADCAST_UPLOAD_EXTENSION_PROVISIONING_PROFILE_SPECIFIER = "Vector Broadcast Upload Extension: App Store"
BROADCAST_UPLOAD_EXTENSION_PROVISIONING_PROFILE = c86239f4-0d3a-47f4-a5f2-9f4763c42b5d

View file

@ -23,6 +23,7 @@
// Application constants // Application constants
KEYCHAIN_ACCESS_GROUP = $(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER).keychain.shared KEYCHAIN_ACCESS_GROUP = $(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER).keychain.shared
BROADCAST_UPLOAD_EXTENSION_BUNDLE_IDENTIFIER = $(BASE_BUNDLE_IDENTIFIER).broadcastUploadExtension
// Build settings // Build settings
IPHONEOS_DEPLOYMENT_TARGET = 14.0 IPHONEOS_DEPLOYMENT_TARGET = 14.0

View file

@ -127,6 +127,10 @@ abstract_target 'RiotPods' do
import_MatrixKit_pods import_MatrixKit_pods
end end
target "BroadcastUploadExtension" do
import_MatrixSDK
end
end end
post_install do |installer| post_install do |installer|

View file

@ -35,6 +35,7 @@ static NSString * _Nonnull kRCTTextViewClassName = @"RCTTextView";
Some feature flags defined in https://github.com/jitsi/jitsi-meet/blob/master/react/features/base/flags/constants.js Some feature flags defined in https://github.com/jitsi/jitsi-meet/blob/master/react/features/base/flags/constants.js
*/ */
static NSString * _Nonnull kJitsiFeatureFlagChatEnabled = @"chat.enabled"; static NSString * _Nonnull kJitsiFeatureFlagChatEnabled = @"chat.enabled";
static NSString * _Nonnull kJitsiFeatureFlagScreenSharingEnabled = @"ios.screensharing.enabled";
@interface JitsiViewController () <PictureInPicturable, JitsiMeetViewDelegate> @interface JitsiViewController () <PictureInPicturable, JitsiMeetViewDelegate>
@ -278,6 +279,7 @@ static NSString * _Nonnull kJitsiFeatureFlagChatEnabled = @"chat.enabled";
andAvatar:avatarUrl]; andAvatar:avatarUrl];
builder.token = self.jwtToken; builder.token = self.jwtToken;
[builder setFeatureFlag:kJitsiFeatureFlagChatEnabled withBoolean:NO]; [builder setFeatureFlag:kJitsiFeatureFlagChatEnabled withBoolean:NO];
[builder setFeatureFlag:kJitsiFeatureFlagScreenSharingEnabled withBoolean: YES];
}]; }];
[self.jitsiMeetView join:jitsiMeetConferenceOptions]; [self.jitsiMeetView join:jitsiMeetConferenceOptions];

View file

@ -4,6 +4,10 @@
<dict> <dict>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>en</string> <string>en</string>
<key>RTCAppGroupIdentifier</key>
<string>$(APPLICATION_GROUP_IDENTIFIER)</string>
<key>RTCScreenSharingExtension</key>
<string>$(BROADCAST_UPLOAD_EXTENSION_BUNDLE_IDENTIFIER)</string>
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>
<string>$(BUNDLE_DISPLAY_NAME)</string> <string>$(BUNDLE_DISPLAY_NAME)</string>
<key>CFBundleDocumentTypes</key> <key>CFBundleDocumentTypes</key>

View file

@ -37,6 +37,7 @@ targets:
- target: RiotShareExtension - target: RiotShareExtension
- target: SiriIntents - target: SiriIntents
- target: RiotNSE - target: RiotNSE
- target: BroadcastUploadExtension
- target: DesignKit - target: DesignKit
- target: CommonKit - target: CommonKit
- package: AnalyticsEvents - package: AnalyticsEvents

View file

@ -38,3 +38,6 @@ SHARE_EXTENSION_PROVISIONING_PROFILE = b47f96e0-647b-4274-b2bb-8103b9a97146
SIRI_INTENTS_PROVISIONING_PROFILE_SPECIFIER = "Alpha Vector Siri Intents Ad hoc" SIRI_INTENTS_PROVISIONING_PROFILE_SPECIFIER = "Alpha Vector Siri Intents Ad hoc"
SIRI_INTENTS_PROVISIONING_PROFILE = a9fdb684-a68c-4207-afc2-810f3287b1f5 SIRI_INTENTS_PROVISIONING_PROFILE = a9fdb684-a68c-4207-afc2-810f3287b1f5
BROADCAST_UPLOAD_EXTENSION_PROVISIONING_PROFILE_SPECIFIER = "Alpha Vector Broadcast Upload Extension Ad hoc"
BROADCAST_UPLOAD_EXTENSION_PROVISIONING_PROFILE = 3616761e-f7f6-4f38-bf74-13bc6b0a1000

View file

@ -7,11 +7,13 @@ MAIN_TARGET=Riot
SHARE_EXTENSION_TARGET=RiotShareExtension SHARE_EXTENSION_TARGET=RiotShareExtension
SIRI_INTENTS_EXTENSION_TARGET=SiriIntents SIRI_INTENTS_EXTENSION_TARGET=SiriIntents
NSE_TARGET=RiotNSE NSE_TARGET=RiotNSE
BROADCAST_UPLOAD_EXTENSION_TARGET=BroadcastUploadExtension
MAIN_BUNDLE_ID=im.vector.app.alpha MAIN_BUNDLE_ID=im.vector.app.alpha
SHARE_EXTENSION_BUNDLE_ID=im.vector.app.alpha.shareExtension SHARE_EXTENSION_BUNDLE_ID=im.vector.app.alpha.shareExtension
SIRI_INTENTS_EXTENSION_BUNDLE_ID=im.vector.app.alpha.SiriIntents SIRI_INTENTS_EXTENSION_BUNDLE_ID=im.vector.app.alpha.SiriIntents
NSE_BUNDLE_ID=im.vector.app.alpha.nse NSE_BUNDLE_ID=im.vector.app.alpha.nse
BROADCAST_UPLOAD_EXTENSION_BUNDLE_ID=im.vector.app.alpha.broadcastUploadExtension
## Build configuration ## Build configuration
@ -29,6 +31,7 @@ MAIN_PROVISIONING_PROFILE_FILENAME=main.mobileprovision
SHARE_EXTENSION_PROVISIONING_PROFILE_FILENAME=share_extension.mobileprovision SHARE_EXTENSION_PROVISIONING_PROFILE_FILENAME=share_extension.mobileprovision
SIRI_INTENTS_EXTENSION_PROVISIONING_PROFILE_FILENAME=siri_intents.mobileprovision SIRI_INTENTS_EXTENSION_PROVISIONING_PROFILE_FILENAME=siri_intents.mobileprovision
NSE_PROVISIONING_PROFILE_FILENAME=nse.mobileprovision NSE_PROVISIONING_PROFILE_FILENAME=nse.mobileprovision
BROADCAST_UPLOAD_EXTENSION_PROVISIONING_PROFILE_FILENAME=broadcast_upload_extension.mobileprovision
## App Store code signing ## App Store code signing
@ -39,6 +42,7 @@ APPSTORE_MAIN_PROVISIONING_PROFILE_SPECIFIER="Vector App Store"
APPSTORE_SHARE_EXTENSION_PROVISIONING_PROFILE_SPECIFIER="Vector Share Extension: App Store" APPSTORE_SHARE_EXTENSION_PROVISIONING_PROFILE_SPECIFIER="Vector Share Extension: App Store"
APPSTORE_SIRI_INTENTS_EXTENSION_PROVISIONING_PROFILE_SPECIFIER="Vector Siri Intents: App Store" APPSTORE_SIRI_INTENTS_EXTENSION_PROVISIONING_PROFILE_SPECIFIER="Vector Siri Intents: App Store"
APPSTORE_NSE_PROVISIONING_PROFILE_SPECIFIER="Vector NSE: App Store" APPSTORE_NSE_PROVISIONING_PROFILE_SPECIFIER="Vector NSE: App Store"
APPSTORE_BROADCAST_UPLOAD_EXTENSION_PROVISIONING_PROFILE_SPECIFIER="Vector Broadcast Upload Extension: App Store"
## Ad-Hoc code signing ## Ad-Hoc code signing
@ -46,6 +50,7 @@ ADHOC_MAIN_PROVISIONING_PROFILE_SPECIFIER="Alpha Vector App Ad hoc"
ADHOC_SHARE_EXTENSION_PROVISIONING_PROFILE_SPECIFIER="Alpha Vector Share Extension Ad hoc" ADHOC_SHARE_EXTENSION_PROVISIONING_PROFILE_SPECIFIER="Alpha Vector Share Extension Ad hoc"
ADHOC_SIRI_INTENTS_EXTENSION_PROVISIONING_PROFILE_SPECIFIER="Alpha Vector Siri Intents Ad hoc" ADHOC_SIRI_INTENTS_EXTENSION_PROVISIONING_PROFILE_SPECIFIER="Alpha Vector Siri Intents Ad hoc"
ADHOC_NSE_PROVISIONING_PROFILE_SPECIFIER="Alpha Vector NSE Ad hoc" ADHOC_NSE_PROVISIONING_PROFILE_SPECIFIER="Alpha Vector NSE Ad hoc"
ADHOC_BROADCAST_UPLOAD_EXTENSION_PROVISIONING_PROFILE_SPECIFIER="Alpha Vector Broadcast Upload Extension Ad hoc"
## Account information ## Account information

1
changelog.d/7566.feature Normal file
View file

@ -0,0 +1 @@
Broadcast Upload Extension added to the app targets to allow Jitsi screen sharing feature.

View file

@ -7,11 +7,13 @@ MAIN_TARGET=Riot
SHARE_EXTENSION_TARGET=RiotShareExtension SHARE_EXTENSION_TARGET=RiotShareExtension
SIRI_INTENTS_EXTENSION_TARGET=SiriIntents SIRI_INTENTS_EXTENSION_TARGET=SiriIntents
NSE_TARGET=RiotNSE NSE_TARGET=RiotNSE
BROADCAST_UPLOAD_EXTENSION_TARGET=BroadcastUploadExtension
MAIN_BUNDLE_ID=im.vector.app MAIN_BUNDLE_ID=im.vector.app
SHARE_EXTENSION_BUNDLE_ID=im.vector.app.shareExtension SHARE_EXTENSION_BUNDLE_ID=im.vector.app.shareExtension
SIRI_INTENTS_EXTENSION_BUNDLE_ID=im.vector.app.SiriIntents SIRI_INTENTS_EXTENSION_BUNDLE_ID=im.vector.app.SiriIntents
NSE_BUNDLE_ID=im.vector.app.nse NSE_BUNDLE_ID=im.vector.app.nse
BROADCAST_UPLOAD_EXTENSION_BUNDLE_ID=im.vector.app.broadcastUploadExtension
## Build configuration ## Build configuration
@ -29,6 +31,7 @@ MAIN_PROVISIONING_PROFILE_FILENAME=main.mobileprovision
SHARE_EXTENSION_PROVISIONING_PROFILE_FILENAME=share_extension.mobileprovision SHARE_EXTENSION_PROVISIONING_PROFILE_FILENAME=share_extension.mobileprovision
SIRI_INTENTS_EXTENSION_PROVISIONING_PROFILE_FILENAME=siri_intents.mobileprovision SIRI_INTENTS_EXTENSION_PROVISIONING_PROFILE_FILENAME=siri_intents.mobileprovision
NSE_PROVISIONING_PROFILE_FILENAME=nse.mobileprovision NSE_PROVISIONING_PROFILE_FILENAME=nse.mobileprovision
BROADCAST_UPLOAD_EXTENSION_PROVISIONING_PROFILE_FILENAME=broadcast_upload_extension.mobileprovision
## App Store code signing ## App Store code signing
@ -39,6 +42,7 @@ APPSTORE_MAIN_PROVISIONING_PROFILE_SPECIFIER="Vector App Store"
APPSTORE_SHARE_EXTENSION_PROVISIONING_PROFILE_SPECIFIER="Vector Share Extension: App Store" APPSTORE_SHARE_EXTENSION_PROVISIONING_PROFILE_SPECIFIER="Vector Share Extension: App Store"
APPSTORE_SIRI_INTENTS_EXTENSION_PROVISIONING_PROFILE_SPECIFIER="Vector Siri Intents: App Store" APPSTORE_SIRI_INTENTS_EXTENSION_PROVISIONING_PROFILE_SPECIFIER="Vector Siri Intents: App Store"
APPSTORE_NSE_PROVISIONING_PROFILE_SPECIFIER="Vector NSE: App Store" APPSTORE_NSE_PROVISIONING_PROFILE_SPECIFIER="Vector NSE: App Store"
APPSTORE_BROADCAST_UPLOAD_EXTENSION_PROVISIONING_PROFILE_SPECIFIER="Vector Broadcast Upload Extension: App Store"
## Ad-Hoc code signing ## Ad-Hoc code signing
@ -46,6 +50,7 @@ ADHOC_MAIN_PROVISIONING_PROFILE_SPECIFIER="Vector Ad Hoc"
ADHOC_SHARE_EXTENSION_PROVISIONING_PROFILE_SPECIFIER="Vector Share Extension: Ad Hoc" ADHOC_SHARE_EXTENSION_PROVISIONING_PROFILE_SPECIFIER="Vector Share Extension: Ad Hoc"
ADHOC_SIRI_INTENTS_EXTENSION_PROVISIONING_PROFILE_SPECIFIER="Vector Siri Intents: Ad Hoc" ADHOC_SIRI_INTENTS_EXTENSION_PROVISIONING_PROFILE_SPECIFIER="Vector Siri Intents: Ad Hoc"
ADHOC_NSE_PROVISIONING_PROFILE_SPECIFIER="Vector NSE: Ad Hoc" ADHOC_NSE_PROVISIONING_PROFILE_SPECIFIER="Vector NSE: Ad Hoc"
ADHOC_BROADCAST_UPLOAD_EXTENSION_PROVISIONING_PROFILE_SPECIFIER="Vector Broadcast Upload Extension: Ad Hoc"
## Account information ## Account information

View file

@ -290,12 +290,14 @@ platform :ios do
share_extension_provisioning_profile = ENV["ADHOC_SHARE_EXTENSION_PROVISIONING_PROFILE_SPECIFIER"] share_extension_provisioning_profile = ENV["ADHOC_SHARE_EXTENSION_PROVISIONING_PROFILE_SPECIFIER"]
siri_intents_provisioning_profile = ENV["ADHOC_SIRI_INTENTS_EXTENSION_PROVISIONING_PROFILE_SPECIFIER"] siri_intents_provisioning_profile = ENV["ADHOC_SIRI_INTENTS_EXTENSION_PROVISIONING_PROFILE_SPECIFIER"]
nse_provisioning_profile = ENV["ADHOC_NSE_PROVISIONING_PROFILE_SPECIFIER"] nse_provisioning_profile = ENV["ADHOC_NSE_PROVISIONING_PROFILE_SPECIFIER"]
broadcast_upload_extension_provisioning_profile = ENV["ADHOC_BROADCAST_UPLOAD_EXTENSION_PROVISIONING_PROFILE_SPECIFIER"]
else else
export_method = "app-store" export_method = "app-store"
main_provisioning_profile = ENV["APPSTORE_MAIN_PROVISIONING_PROFILE_SPECIFIER"] main_provisioning_profile = ENV["APPSTORE_MAIN_PROVISIONING_PROFILE_SPECIFIER"]
share_extension_provisioning_profile = ENV["APPSTORE_SHARE_EXTENSION_PROVISIONING_PROFILE_SPECIFIER"] share_extension_provisioning_profile = ENV["APPSTORE_SHARE_EXTENSION_PROVISIONING_PROFILE_SPECIFIER"]
siri_intents_provisioning_profile = ENV["APPSTORE_SIRI_INTENTS_EXTENSION_PROVISIONING_PROFILE_SPECIFIER"] siri_intents_provisioning_profile = ENV["APPSTORE_SIRI_INTENTS_EXTENSION_PROVISIONING_PROFILE_SPECIFIER"]
nse_provisioning_profile = ENV["APPSTORE_NSE_PROVISIONING_PROFILE_SPECIFIER"] nse_provisioning_profile = ENV["APPSTORE_NSE_PROVISIONING_PROFILE_SPECIFIER"]
broadcast_upload_extension_provisioning_profile = ENV["APPSTORE_BROADCAST_UPLOAD_EXTENSION_PROVISIONING_PROFILE_SPECIFIER"]
end end
# Build app and create ipa # Build app and create ipa
@ -321,6 +323,7 @@ platform :ios do
ENV["SHARE_EXTENSION_BUNDLE_ID"] => share_extension_provisioning_profile, ENV["SHARE_EXTENSION_BUNDLE_ID"] => share_extension_provisioning_profile,
ENV["NSE_BUNDLE_ID"] => nse_provisioning_profile, ENV["NSE_BUNDLE_ID"] => nse_provisioning_profile,
ENV["SIRI_INTENTS_EXTENSION_BUNDLE_ID"] => siri_intents_provisioning_profile, ENV["SIRI_INTENTS_EXTENSION_BUNDLE_ID"] => siri_intents_provisioning_profile,
ENV["BROADCAST_UPLOAD_EXTENSION_BUNDLE_ID"] => broadcast_upload_extension_provisioning_profile,
}, },
iCloudContainerEnvironment: "Production", iCloudContainerEnvironment: "Production",
}, },
@ -340,6 +343,7 @@ platform :ios do
share_extension_provisioning_name = adhoc ? ENV["ADHOC_SHARE_EXTENSION_PROVISIONING_PROFILE_SPECIFIER"] : ENV["APPSTORE_SHARE_EXTENSION_PROVISIONING_PROFILE_SPECIFIER"] share_extension_provisioning_name = adhoc ? ENV["ADHOC_SHARE_EXTENSION_PROVISIONING_PROFILE_SPECIFIER"] : ENV["APPSTORE_SHARE_EXTENSION_PROVISIONING_PROFILE_SPECIFIER"]
siri_intents_provisioning_name = adhoc ? ENV["ADHOC_SIRI_INTENTS_EXTENSION_PROVISIONING_PROFILE_SPECIFIER"] : ENV["APPSTORE_SIRI_INTENTS_EXTENSION_PROVISIONING_PROFILE_SPECIFIER"] siri_intents_provisioning_name = adhoc ? ENV["ADHOC_SIRI_INTENTS_EXTENSION_PROVISIONING_PROFILE_SPECIFIER"] : ENV["APPSTORE_SIRI_INTENTS_EXTENSION_PROVISIONING_PROFILE_SPECIFIER"]
notification_service_extension_provisioning_name = adhoc ? ENV["ADHOC_NSE_PROVISIONING_PROFILE_SPECIFIER"] : ENV["APPSTORE_NSE_PROVISIONING_PROFILE_SPECIFIER"] notification_service_extension_provisioning_name = adhoc ? ENV["ADHOC_NSE_PROVISIONING_PROFILE_SPECIFIER"] : ENV["APPSTORE_NSE_PROVISIONING_PROFILE_SPECIFIER"]
broadcast_upload_extension_provisioning_name = adhoc ? ENV["ADHOC_BROADCAST_UPLOAD_EXTENSION_PROVISIONING_PROFILE_SPECIFIER"] : ENV["APPSTORE_BROADCAST_UPLOAD_EXTENSION_PROVISIONING_PROFILE_SPECIFIER"]
# Main application # Main application
get_provisioning_profile( get_provisioning_profile(
@ -385,6 +389,17 @@ platform :ios do
filename: ENV["NSE_PROVISIONING_PROFILE_FILENAME"], filename: ENV["NSE_PROVISIONING_PROFILE_FILENAME"],
readonly: true, readonly: true,
) )
# Broadcast Upload Extension
get_provisioning_profile(
app_identifier: ENV["BROADCAST_UPLOAD_EXTENSION_BUNDLE_ID"],
provisioning_name: broadcast_upload_extension_provisioning_name,
ignore_profiles_with_different_name: true,
adhoc: adhoc,
skip_certificate_verification: skip_certificate_verification,
output_path: output_path,
filename: ENV["BROADCAST_UPLOAD_EXTENSION_PROVISIONING_PROFILE_FILENAME"],
readonly: true,
)
end end
desc "Update provisioning profiles for each target" desc "Update provisioning profiles for each target"
@ -421,6 +436,13 @@ platform :ios do
target_filter: ENV["NSE_TARGET"], target_filter: ENV["NSE_TARGET"],
build_configuration: build_configuration, build_configuration: build_configuration,
) )
# Broadcast Upload Extension
update_project_provisioning(
xcodeproj: xcodeproj,
profile: "#{provisioning_profiles_path}#{ENV["BROADCAST_UPLOAD_EXTENSION_PROVISIONING_PROFILE_FILENAME"]}",
target_filter: ENV["BROADCAST_UPLOAD_EXTENSION_TARGET"],
build_configuration: build_configuration,
)
end end
desc "Update application build number for all targets" desc "Update application build number for all targets"

View file

@ -32,6 +32,7 @@ include:
- path: RiotShareExtension/target.yml - path: RiotShareExtension/target.yml
- path: SiriIntents/target.yml - path: SiriIntents/target.yml
- path: RiotNSE/target.yml - path: RiotNSE/target.yml
- path: BroadcastUploadExtension/target.yml
- path: DesignKit/target.yml - path: DesignKit/target.yml
- path: RiotSwiftUI/target.yml - path: RiotSwiftUI/target.yml
- path: RiotSwiftUI/targetUnitTests.yml - path: RiotSwiftUI/targetUnitTests.yml