This commit is contained in:
Ali 2021-03-02 22:32:42 +04:00
commit 1a07e3f206
52 changed files with 15829 additions and 669 deletions

View File

@ -3,7 +3,7 @@
@implementation Serialization
- (NSUInteger)currentLayer {
return 124;
return 125;
}
- (id _Nullable)parseMessage:(NSData * _Nullable)data {

View File

@ -98,16 +98,11 @@ class BuildEnvironment:
self,
base_path,
bazel_path,
bazel_x86_64_path,
override_bazel_version,
override_xcode_version
):
self.base_path = os.path.expanduser(base_path)
self.bazel_path = os.path.expanduser(bazel_path)
if bazel_x86_64_path is not None:
self.bazel_x86_64_path = os.path.expanduser(bazel_x86_64_path)
else:
self.bazel_x86_64_path = None
configuration_path = os.path.join(self.base_path, 'versions.json')
with open(configuration_path) as file:

View File

@ -12,11 +12,10 @@ from ProjectGeneration import generate
class BazelCommandLine:
def __init__(self, bazel_path, bazel_x86_64_path, override_bazel_version, override_xcode_version, bazel_user_root):
def __init__(self, bazel_path, override_bazel_version, override_xcode_version, bazel_user_root):
self.build_environment = BuildEnvironment(
base_path=os.getcwd(),
bazel_path=bazel_path,
bazel_x86_64_path=bazel_x86_64_path,
override_bazel_version=override_bazel_version,
override_xcode_version=override_xcode_version
)
@ -298,7 +297,6 @@ class BazelCommandLine:
def clean(arguments):
bazel_command_line = BazelCommandLine(
bazel_path=arguments.bazel,
bazel_x86_64_path=None,
override_bazel_version=arguments.overrideBazelVersion,
override_xcode_version=arguments.overrideXcodeVersion,
bazel_user_root=arguments.bazelUserRoot
@ -337,14 +335,9 @@ def resolve_configuration(bazel_command_line: BazelCommandLine, arguments):
raise Exception('Neither configurationPath nor configurationGenerator are set')
def generate_project(arguments):
bazel_x86_64_path = None
if is_apple_silicon():
bazel_x86_64_path = arguments.bazel_x86_64
def generate_project(arguments):
bazel_command_line = BazelCommandLine(
bazel_path=arguments.bazel,
bazel_x86_64_path=bazel_x86_64_path,
override_bazel_version=arguments.overrideBazelVersion,
override_xcode_version=arguments.overrideXcodeVersion,
bazel_user_root=arguments.bazelUserRoot
@ -379,7 +372,6 @@ def generate_project(arguments):
def build(arguments):
bazel_command_line = BazelCommandLine(
bazel_path=arguments.bazel,
bazel_x86_64_path=None,
override_bazel_version=arguments.overrideBazelVersion,
override_xcode_version=arguments.overrideXcodeVersion,
bazel_user_root=arguments.bazelUserRoot
@ -491,13 +483,6 @@ if __name__ == '__main__':
)
generateProjectParser = subparsers.add_parser('generateProject', help='Generate Xcode project')
if is_apple_silicon():
generateProjectParser.add_argument(
'--bazel_x86_64',
required=True,
help='A standalone bazel x86_64 binary is required to generate a project on Apple Silicon.',
metavar='path'
)
generateProjectParser.add_argument(
'--buildNumber',
required=False,

View File

@ -20,23 +20,20 @@ def generate(build_environment: BuildEnvironment, disable_extensions, disable_pr
tulsi_path = os.path.join(project_path, 'Tulsi.app/Contents/MacOS/Tulsi')
if is_apple_silicon():
tulsi_build_bazel_path = build_environment.bazel_x86_64_path
if tulsi_build_bazel_path is None or not os.path.isfile(tulsi_build_bazel_path):
print('Could not find a valid bazel x86_64 binary at {}'.format(tulsi_build_bazel_path))
exit(1)
else:
tulsi_build_bazel_path = build_environment.bazel_path
tulsi_build_bazel_path = build_environment.bazel_path
current_dir = os.getcwd()
os.chdir(os.path.join(build_environment.base_path, 'build-system/tulsi'))
call_executable([
tulsi_build_bazel_path,
'build', '//:tulsi',
'--xcode_version={}'.format(build_environment.xcode_version),
'--use_top_level_targets_for_symlinks',
'--verbose_failures'
])
tulsi_build_command = []
tulsi_build_command += [tulsi_build_bazel_path]
tulsi_build_command += ['build', '//:tulsi']
if is_apple_silicon():
tulsi_build_command += ['--macos_cpus=arm64']
tulsi_build_command += ['--xcode_version={}'.format(build_environment.xcode_version)]
tulsi_build_command += ['--use_top_level_targets_for_symlinks']
tulsi_build_command += ['--verbose_failures']
os.chdir(current_dir)
bazel_wrapper_path = os.path.abspath('build-input/gen/project/bazel')

View File

@ -727,6 +727,6 @@ public protocol AccountContext: class {
func chatLocationOutgoingReadState(for location: ChatLocation, contextHolder: Atomic<ChatLocationContextHolder?>) -> Signal<MessageId?, NoError>
func applyMaxReadIndex(for location: ChatLocation, contextHolder: Atomic<ChatLocationContextHolder?>, messageIndex: MessageIndex)
func joinGroupCall(peerId: PeerId, activeCall: CachedChannelData.ActiveCall)
func joinGroupCall(peerId: PeerId, joinAsPeerId: PeerId?, activeCall: CachedChannelData.ActiveCall)
func requestCall(peerId: PeerId, isVideo: Bool, completion: @escaping () -> Void)
}

View File

@ -177,19 +177,22 @@ public struct PresentationGroupCallState: Equatable {
public var adminIds: Set<PeerId>
public var muteState: GroupCallParticipantsContext.Participant.MuteState?
public var defaultParticipantMuteState: DefaultParticipantMuteState?
public var recordingStartTimestamp: Int32?
public init(
networkState: NetworkState,
canManageCall: Bool,
adminIds: Set<PeerId>,
muteState: GroupCallParticipantsContext.Participant.MuteState?,
defaultParticipantMuteState: DefaultParticipantMuteState?
defaultParticipantMuteState: DefaultParticipantMuteState?,
recordingStartTimestamp: Int32?
) {
self.networkState = networkState
self.canManageCall = canManageCall
self.adminIds = adminIds
self.muteState = muteState
self.defaultParticipantMuteState = defaultParticipantMuteState
self.recordingStartTimestamp = recordingStartTimestamp
}
}
@ -279,6 +282,7 @@ public protocol PresentationGroupCall: class {
var accountContext: AccountContext { get }
var internalId: CallSessionInternalId { get }
var peerId: PeerId { get }
var myPeerId: PeerId { get }
var isVideo: Bool { get }
@ -306,6 +310,7 @@ public protocol PresentationGroupCall: class {
func setCurrentAudioOutput(_ output: AudioSessionOutput)
func updateMuteState(peerId: PeerId, isMuted: Bool) -> GroupCallParticipantsContext.Participant.MuteState?
func setShouldBeRecording(_ shouldBeRecording: Bool)
func invitePeer(_ peerId: PeerId) -> Bool
func removedPeer(_ peerId: PeerId)
@ -323,5 +328,5 @@ public protocol PresentationCallManager: class {
var currentGroupCallSignal: Signal<PresentationGroupCall?, NoError> { get }
func requestCall(context: AccountContext, peerId: PeerId, isVideo: Bool, endCurrentIfAny: Bool) -> RequestCallResult
func joinGroupCall(context: AccountContext, peerId: PeerId, initialCall: CachedChannelData.ActiveCall, endCurrentIfAny: Bool) -> JoinGroupCallManagerResult
func joinGroupCall(context: AccountContext, peerId: PeerId, joinAsPeerId: PeerId?, initialCall: CachedChannelData.ActiveCall, endCurrentIfAny: Bool) -> JoinGroupCallManagerResult
}

View File

@ -374,7 +374,7 @@ final class CallListControllerNode: ASDisplayNode {
}
if let activeCall = activeCall {
strongSelf.context.joinGroupCall(peerId: peerId, activeCall: activeCall)
strongSelf.context.joinGroupCall(peerId: peerId, joinAsPeerId: nil, activeCall: activeCall)
}
}))
})

View File

@ -822,7 +822,7 @@ public func channelVisibilityController(context: AccountContext, peerId: PeerId,
let peersDisablingAddressNameAssignment = Promise<[Peer]?>()
peersDisablingAddressNameAssignment.set(.single(nil) |> then(channelAddressNameAssignmentAvailability(account: context.account, peerId: peerId.namespace == Namespaces.Peer.CloudChannel ? peerId : nil) |> mapToSignal { result -> Signal<[Peer]?, NoError> in
if case .addressNameLimitReached = result {
return adminedPublicChannels(account: context.account, location: false)
return adminedPublicChannels(account: context.account, scope: .all)
|> map(Optional.init)
} else {
return .single([])

View File

@ -199,6 +199,7 @@ public final class CachedChannelData: CachedPeerData {
public let invitedBy: PeerId?
public let photo: TelegramMediaImage?
public let activeCall: ActiveCall?
public let callJoinPeerId: PeerId?
public let pendingSuggestions: [String]
public let peerIds: Set<PeerId>
@ -231,10 +232,11 @@ public final class CachedChannelData: CachedPeerData {
self.invitedBy = nil
self.photo = nil
self.activeCall = nil
self.callJoinPeerId = nil
self.pendingSuggestions = []
}
public init(isNotAccessible: Bool, flags: CachedChannelFlags, about: String?, participantsSummary: CachedChannelParticipantsSummary, exportedInvitation: ExportedInvitation?, botInfos: [CachedPeerBotInfo], peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, stickerPack: StickerPackCollectionInfo?, minAvailableMessageId: MessageId?, migrationReference: ChannelMigrationReference?, linkedDiscussionPeerId: LinkedDiscussionPeerId, peerGeoLocation: PeerGeoLocation?, slowModeTimeout: Int32?, slowModeValidUntilTimestamp: Int32?, hasScheduledMessages: Bool, statsDatacenterId: Int32, invitedBy: PeerId?, photo: TelegramMediaImage?, activeCall: ActiveCall?, autoremoveTimeout: CachedPeerAutoremoveTimeout, pendingSuggestions: [String]) {
public init(isNotAccessible: Bool, flags: CachedChannelFlags, about: String?, participantsSummary: CachedChannelParticipantsSummary, exportedInvitation: ExportedInvitation?, botInfos: [CachedPeerBotInfo], peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, stickerPack: StickerPackCollectionInfo?, minAvailableMessageId: MessageId?, migrationReference: ChannelMigrationReference?, linkedDiscussionPeerId: LinkedDiscussionPeerId, peerGeoLocation: PeerGeoLocation?, slowModeTimeout: Int32?, slowModeValidUntilTimestamp: Int32?, hasScheduledMessages: Bool, statsDatacenterId: Int32, invitedBy: PeerId?, photo: TelegramMediaImage?, activeCall: ActiveCall?, callJoinPeerId: PeerId?, autoremoveTimeout: CachedPeerAutoremoveTimeout, pendingSuggestions: [String]) {
self.isNotAccessible = isNotAccessible
self.flags = flags
self.about = about
@ -255,6 +257,7 @@ public final class CachedChannelData: CachedPeerData {
self.invitedBy = invitedBy
self.photo = photo
self.activeCall = activeCall
self.callJoinPeerId = callJoinPeerId
self.autoremoveTimeout = autoremoveTimeout
self.pendingSuggestions = pendingSuggestions
@ -284,91 +287,95 @@ public final class CachedChannelData: CachedPeerData {
}
public func withUpdatedIsNotAccessible(_ isNotAccessible: Bool) -> CachedChannelData {
return CachedChannelData(isNotAccessible: isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
return CachedChannelData(isNotAccessible: isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
}
public func withUpdatedFlags(_ flags: CachedChannelFlags) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
}
public func withUpdatedAbout(_ about: String?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
}
public func withUpdatedParticipantsSummary(_ participantsSummary: CachedChannelParticipantsSummary) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
}
public func withUpdatedExportedInvitation(_ exportedInvitation: ExportedInvitation?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
}
public func withUpdatedBotInfos(_ botInfos: [CachedPeerBotInfo]) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
}
public func withUpdatedPeerStatusSettings(_ peerStatusSettings: PeerStatusSettings?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
}
public func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
}
public func withUpdatedStickerPack(_ stickerPack: StickerPackCollectionInfo?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
}
public func withUpdatedMinAvailableMessageId(_ minAvailableMessageId: MessageId?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
}
public func withUpdatedMigrationReference(_ migrationReference: ChannelMigrationReference?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
}
public func withUpdatedLinkedDiscussionPeerId(_ linkedDiscussionPeerId: LinkedDiscussionPeerId) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
}
public func withUpdatedPeerGeoLocation(_ peerGeoLocation: PeerGeoLocation?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
}
public func withUpdatedSlowModeTimeout(_ slowModeTimeout: Int32?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
}
public func withUpdatedSlowModeValidUntilTimestamp(_ slowModeValidUntilTimestamp: Int32?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
}
public func withUpdatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
}
public func withUpdatedStatsDatacenterId(_ statsDatacenterId: Int32) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
}
public func withUpdatedInvitedBy(_ invitedBy: PeerId?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
}
public func withUpdatedPhoto(_ photo: TelegramMediaImage?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
}
public func withUpdatedActiveCall(_ activeCall: ActiveCall?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: activeCall, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
}
public func withUpdatedCallJoinPeerId(_ callJoinPeerId: PeerId?) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
}
public func withUpdatedAutoremoveTimeout(_ autoremoveTimeout: CachedPeerAutoremoveTimeout) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: autoremoveTimeout, pendingSuggestions: self.pendingSuggestions)
}
public func withUpdatedPendingSuggestions(_ pendingSuggestions: [String]) -> CachedChannelData {
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions)
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, callJoinPeerId: self.callJoinPeerId, autoremoveTimeout: self.autoremoveTimeout, pendingSuggestions: pendingSuggestions)
}
public init(decoder: PostboxDecoder) {
@ -399,6 +406,8 @@ public final class CachedChannelData: CachedPeerData {
self.activeCall = nil
}
self.callJoinPeerId = decoder.decodeOptionalInt64ForKey("callJoinPeerId").flatMap(PeerId.init)
if let stickerPack = decoder.decodeObjectForKey("sp", decoder: { StickerPackCollectionInfo(decoder: $0) }) as? StickerPackCollectionInfo {
self.stickerPack = stickerPack
} else {
@ -500,6 +509,12 @@ public final class CachedChannelData: CachedPeerData {
encoder.encodeNil(forKey: "activeCall")
}
if let callJoinPeerId = self.callJoinPeerId {
encoder.encodeInt64(callJoinPeerId.toInt64(), forKey: "callJoinPeerId")
} else {
encoder.encodeNil(forKey: "callJoinPeerId")
}
if let stickerPack = self.stickerPack {
encoder.encodeObject(stickerPack, forKey: "sp")
} else {
@ -653,6 +668,10 @@ public final class CachedChannelData: CachedPeerData {
return false
}
if other.callJoinPeerId != self.callJoinPeerId {
return false
}
if other.pendingSuggestions != self.pendingSuggestions {
return false
}

View File

@ -57,6 +57,7 @@ public final class CachedGroupData: CachedPeerData {
public let associatedHistoryMessageId: MessageId? = nil
public let activeCall: CachedChannelData.ActiveCall?
public let callJoinPeerId: PeerId?
public init() {
self.participants = nil
@ -74,9 +75,10 @@ public final class CachedGroupData: CachedPeerData {
self.autoremoveTimeout = .unknown
self.activeCall = nil
self.callJoinPeerId = nil
}
public init(participants: CachedGroupParticipants?, exportedInvitation: ExportedInvitation?, botInfos: [CachedPeerBotInfo], peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, about: String?, flags: CachedGroupFlags, hasScheduledMessages: Bool, invitedBy: PeerId?, photo: TelegramMediaImage?, activeCall: CachedChannelData.ActiveCall?, autoremoveTimeout: CachedPeerAutoremoveTimeout) {
public init(participants: CachedGroupParticipants?, exportedInvitation: ExportedInvitation?, botInfos: [CachedPeerBotInfo], peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, about: String?, flags: CachedGroupFlags, hasScheduledMessages: Bool, invitedBy: PeerId?, photo: TelegramMediaImage?, activeCall: CachedChannelData.ActiveCall?, autoremoveTimeout: CachedPeerAutoremoveTimeout, callJoinPeerId: PeerId?) {
self.participants = participants
self.exportedInvitation = exportedInvitation
self.botInfos = botInfos
@ -89,6 +91,7 @@ public final class CachedGroupData: CachedPeerData {
self.photo = photo
self.activeCall = activeCall
self.autoremoveTimeout = autoremoveTimeout
self.callJoinPeerId = callJoinPeerId
var messageIds = Set<MessageId>()
if let pinnedMessageId = self.pinnedMessageId {
@ -147,6 +150,8 @@ public final class CachedGroupData: CachedPeerData {
self.activeCall = nil
}
self.callJoinPeerId = decoder.decodeOptionalInt64ForKey("callJoinPeerId").flatMap(PeerId.init)
var messageIds = Set<MessageId>()
if let pinnedMessageId = self.pinnedMessageId {
messageIds.insert(pinnedMessageId)
@ -218,6 +223,12 @@ public final class CachedGroupData: CachedPeerData {
} else {
encoder.encodeNil(forKey: "activeCall")
}
if let callJoinPeerId = self.callJoinPeerId {
encoder.encodeInt64(callJoinPeerId.toInt64(), forKey: "callJoinPeerId")
} else {
encoder.encodeNil(forKey: "callJoinPeerId")
}
}
public func isEqual(to: CachedPeerData) -> Bool {
@ -229,54 +240,62 @@ public final class CachedGroupData: CachedPeerData {
return false
}
if self.callJoinPeerId != other.callJoinPeerId {
return false
}
return self.participants == other.participants && self.exportedInvitation == other.exportedInvitation && self.botInfos == other.botInfos && self.peerStatusSettings == other.peerStatusSettings && self.pinnedMessageId == other.pinnedMessageId && self.about == other.about && self.flags == other.flags && self.hasScheduledMessages == other.hasScheduledMessages && self.autoremoveTimeout == other.autoremoveTimeout && self.invitedBy == other.invitedBy
}
public func withUpdatedParticipants(_ participants: CachedGroupParticipants?) -> CachedGroupData {
return CachedGroupData(participants: participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
return CachedGroupData(participants: participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId)
}
public func withUpdatedExportedInvitation(_ exportedInvitation: ExportedInvitation?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
return CachedGroupData(participants: self.participants, exportedInvitation: exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId)
}
public func withUpdatedBotInfos(_ botInfos: [CachedPeerBotInfo]) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId)
}
public func withUpdatedPeerStatusSettings(_ peerStatusSettings: PeerStatusSettings?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId)
}
public func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId)
}
public func withUpdatedAbout(_ about: String?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId)
}
public func withUpdatedFlags(_ flags: CachedGroupFlags) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId)
}
public func withUpdatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId)
}
public func withUpdatedInvitedBy(_ invitedBy: PeerId?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId)
}
public func withUpdatedPhoto(_ photo: TelegramMediaImage?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout)
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId)
}
public func withUpdatedActiveCall(_ activeCall: CachedChannelData.ActiveCall?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: activeCall, autoremoveTimeout: self.autoremoveTimeout)
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: self.callJoinPeerId)
}
public func withUpdatedAutoremoveTimeout(_ autoremoveTimeout: CachedPeerAutoremoveTimeout) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: autoremoveTimeout)
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: autoremoveTimeout, callJoinPeerId: self.callJoinPeerId)
}
public func withUpdatedCallJoinPeerId(_ callJoinPeerId: PeerId?) -> CachedGroupData {
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy, photo: self.photo, activeCall: self.activeCall, autoremoveTimeout: self.autoremoveTimeout, callJoinPeerId: callJoinPeerId)
}
}

View File

@ -7,12 +7,12 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1255641564] = { return parseString($0) }
dict[-1240849242] = { return Api.messages.StickerSet.parse_stickerSet($0) }
dict[2004925620] = { return Api.GroupCall.parse_groupCallDiscarded($0) }
dict[1435512961] = { return Api.GroupCall.parse_groupCall($0) }
dict[-1061026514] = { return Api.GroupCall.parse_groupCall($0) }
dict[-457104426] = { return Api.InputGeoPoint.parse_inputGeoPointEmpty($0) }
dict[1210199983] = { return Api.InputGeoPoint.parse_inputGeoPoint($0) }
dict[-784000893] = { return Api.payments.ValidatedRequestedInfo.parse_validatedRequestedInfo($0) }
dict[-261341160] = { return Api.ChatFull.parse_chatFull($0) }
dict[625524791] = { return Api.ChatFull.parse_channelFull($0) }
dict[-1977734781] = { return Api.ChatFull.parse_chatFull($0) }
dict[1418477459] = { return Api.ChatFull.parse_channelFull($0) }
dict[-1159937629] = { return Api.PollResults.parse_pollResults($0) }
dict[-925415106] = { return Api.ChatParticipant.parse_chatParticipant($0) }
dict[-636267638] = { return Api.ChatParticipant.parse_chatParticipantCreator($0) }
@ -142,7 +142,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[767652808] = { return Api.InputEncryptedFile.parse_inputEncryptedFileBigUploaded($0) }
dict[1304052993] = { return Api.account.Takeout.parse_takeout($0) }
dict[-1456996667] = { return Api.messages.InactiveChats.parse_inactiveChats($0) }
dict[1690708501] = { return Api.GroupCallParticipant.parse_groupCallParticipant($0) }
dict[-763544865] = { return Api.GroupCallParticipant.parse_groupCallParticipant($0) }
dict[1443858741] = { return Api.messages.SentEncryptedMessage.parse_sentEncryptedMessage($0) }
dict[-1802240206] = { return Api.messages.SentEncryptedMessage.parse_sentEncryptedFile($0) }
dict[289586518] = { return Api.SavedContact.parse_savedPhoneContact($0) }
@ -277,8 +277,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-219423922] = { return Api.Update.parse_updateGroupCallParticipants($0) }
dict[-1537295973] = { return Api.Update.parse_updateGroupCall($0) }
dict[-1147422299] = { return Api.Update.parse_updatePeerHistoryTTL($0) }
dict[1620733652] = { return Api.Update.parse_updateChatParticipant($0) }
dict[1708307556] = { return Api.Update.parse_updateChannelParticipant($0) }
dict[-206342113] = { return Api.Update.parse_updateChatParticipant($0) }
dict[2146218476] = { return Api.Update.parse_updateChannelParticipant($0) }
dict[133777546] = { return Api.Update.parse_updateBotStopped($0) }
dict[136574537] = { return Api.messages.VotesList.parse_votesList($0) }
dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) }
@ -535,7 +535,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1495959709] = { return Api.MessageReplyHeader.parse_messageReplyHeader($0) }
dict[411017418] = { return Api.SecureValue.parse_secureValue($0) }
dict[-316748368] = { return Api.SecureValueHash.parse_secureValueHash($0) }
dict[1722485756] = { return Api.phone.GroupCall.parse_groupCall($0) }
dict[-1636664659] = { return Api.phone.GroupCall.parse_groupCall($0) }
dict[-398136321] = { return Api.messages.SearchCounter.parse_searchCounter($0) }
dict[-1188055347] = { return Api.PageListItem.parse_pageListItemText($0) }
dict[635466748] = { return Api.PageListItem.parse_pageListItemBlocks($0) }
@ -563,7 +563,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-2042159726] = { return Api.SecurePasswordKdfAlgo.parse_securePasswordKdfAlgoSHA512($0) }
dict[-1032140601] = { return Api.BotCommand.parse_botCommand($0) }
dict[1474462241] = { return Api.account.ContentSettings.parse_contentSettings($0) }
dict[-1661028051] = { return Api.phone.GroupParticipants.parse_groupParticipants($0) }
dict[-193506890] = { return Api.phone.GroupParticipants.parse_groupParticipants($0) }
dict[507405952] = { return Api.ChatInviteImporter.parse_chatInviteImporter($0) }
dict[-2066640507] = { return Api.messages.AffectedMessages.parse_affectedMessages($0) }
dict[-402498398] = { return Api.messages.SavedGifs.parse_savedGifsNotModified($0) }
@ -667,6 +667,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-667654413] = { return Api.InputFileLocation.parse_inputPhotoLegacyFileLocation($0) }
dict[668375447] = { return Api.InputFileLocation.parse_inputPeerPhotoFileLocation($0) }
dict[230353641] = { return Api.InputFileLocation.parse_inputStickerSetThumb($0) }
dict[-775148961] = { return Api.InputFileLocation.parse_inputGroupCallStream($0) }
dict[286776671] = { return Api.GeoPoint.parse_geoPointEmpty($0) }
dict[-1297942941] = { return Api.GeoPoint.parse_geoPoint($0) }
dict[506920429] = { return Api.InputPhoneCall.parse_inputPhoneCall($0) }

View File

@ -1,7 +1,7 @@
public extension Api {
public enum GroupCall: TypeConstructorDescription {
case groupCallDiscarded(id: Int64, accessHash: Int64, duration: Int32)
case groupCall(flags: Int32, id: Int64, accessHash: Int64, participantsCount: Int32, params: Api.DataJSON?, version: Int32)
case groupCall(flags: Int32, id: Int64, accessHash: Int64, participantsCount: Int32, params: Api.DataJSON?, title: String?, streamDcId: Int32?, recordStartDate: Int32?, version: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@ -13,15 +13,18 @@ public extension Api {
serializeInt64(accessHash, buffer: buffer, boxed: false)
serializeInt32(duration, buffer: buffer, boxed: false)
break
case .groupCall(let flags, let id, let accessHash, let participantsCount, let params, let version):
case .groupCall(let flags, let id, let accessHash, let participantsCount, let params, let title, let streamDcId, let recordStartDate, let version):
if boxed {
buffer.appendInt32(1435512961)
buffer.appendInt32(-1061026514)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt64(id, buffer: buffer, boxed: false)
serializeInt64(accessHash, buffer: buffer, boxed: false)
serializeInt32(participantsCount, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {params!.serialize(buffer, true)}
if Int(flags) & Int(1 << 3) != 0 {serializeString(title!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 4) != 0 {serializeInt32(streamDcId!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 5) != 0 {serializeInt32(recordStartDate!, buffer: buffer, boxed: false)}
serializeInt32(version, buffer: buffer, boxed: false)
break
}
@ -31,8 +34,8 @@ public extension Api {
switch self {
case .groupCallDiscarded(let id, let accessHash, let duration):
return ("groupCallDiscarded", [("id", id), ("accessHash", accessHash), ("duration", duration)])
case .groupCall(let flags, let id, let accessHash, let participantsCount, let params, let version):
return ("groupCall", [("flags", flags), ("id", id), ("accessHash", accessHash), ("participantsCount", participantsCount), ("params", params), ("version", version)])
case .groupCall(let flags, let id, let accessHash, let participantsCount, let params, let title, let streamDcId, let recordStartDate, let version):
return ("groupCall", [("flags", flags), ("id", id), ("accessHash", accessHash), ("participantsCount", participantsCount), ("params", params), ("title", title), ("streamDcId", streamDcId), ("recordStartDate", recordStartDate), ("version", version)])
}
}
@ -66,16 +69,25 @@ public extension Api {
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
_5 = Api.parse(reader, signature: signature) as? Api.DataJSON
} }
var _6: Int32?
_6 = reader.readInt32()
var _6: String?
if Int(_1!) & Int(1 << 3) != 0 {_6 = parseString(reader) }
var _7: Int32?
if Int(_1!) & Int(1 << 4) != 0 {_7 = reader.readInt32() }
var _8: Int32?
if Int(_1!) & Int(1 << 5) != 0 {_8 = reader.readInt32() }
var _9: Int32?
_9 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil
let _c6 = _6 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
return Api.GroupCall.groupCall(flags: _1!, id: _2!, accessHash: _3!, participantsCount: _4!, params: _5, version: _6!)
let _c6 = (Int(_1!) & Int(1 << 3) == 0) || _6 != nil
let _c7 = (Int(_1!) & Int(1 << 4) == 0) || _7 != nil
let _c8 = (Int(_1!) & Int(1 << 5) == 0) || _8 != nil
let _c9 = _9 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 {
return Api.GroupCall.groupCall(flags: _1!, id: _2!, accessHash: _3!, participantsCount: _4!, params: _5, title: _6, streamDcId: _7, recordStartDate: _8, version: _9!)
}
else {
return nil
@ -142,14 +154,14 @@ public extension Api {
}
public enum ChatFull: TypeConstructorDescription {
case chatFull(flags: Int32, id: Int32, about: String, participants: Api.ChatParticipants, chatPhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite?, botInfo: [Api.BotInfo]?, pinnedMsgId: Int32?, folderId: Int32?, call: Api.InputGroupCall?, ttlPeriod: Int32?)
case channelFull(flags: Int32, id: Int32, about: String, participantsCount: Int32?, adminsCount: Int32?, kickedCount: Int32?, bannedCount: Int32?, onlineCount: Int32?, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, chatPhoto: Api.Photo, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite?, botInfo: [Api.BotInfo], migratedFromChatId: Int32?, migratedFromMaxId: Int32?, pinnedMsgId: Int32?, stickerset: Api.StickerSet?, availableMinId: Int32?, folderId: Int32?, linkedChatId: Int32?, location: Api.ChannelLocation?, slowmodeSeconds: Int32?, slowmodeNextSendDate: Int32?, statsDc: Int32?, pts: Int32, call: Api.InputGroupCall?, ttlPeriod: Int32?, pendingSuggestions: [String]?)
case chatFull(flags: Int32, id: Int32, about: String, participants: Api.ChatParticipants, chatPhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite?, botInfo: [Api.BotInfo]?, pinnedMsgId: Int32?, folderId: Int32?, call: Api.InputGroupCall?, ttlPeriod: Int32?, groupcallDefaultJoinAs: Api.Peer?)
case channelFull(flags: Int32, id: Int32, about: String, participantsCount: Int32?, adminsCount: Int32?, kickedCount: Int32?, bannedCount: Int32?, onlineCount: Int32?, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, chatPhoto: Api.Photo, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite?, botInfo: [Api.BotInfo], migratedFromChatId: Int32?, migratedFromMaxId: Int32?, pinnedMsgId: Int32?, stickerset: Api.StickerSet?, availableMinId: Int32?, folderId: Int32?, linkedChatId: Int32?, location: Api.ChannelLocation?, slowmodeSeconds: Int32?, slowmodeNextSendDate: Int32?, statsDc: Int32?, pts: Int32, call: Api.InputGroupCall?, ttlPeriod: Int32?, pendingSuggestions: [String]?, groupcallDefaultJoinAs: Api.Peer?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId, let call, let ttlPeriod):
case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId, let call, let ttlPeriod, let groupcallDefaultJoinAs):
if boxed {
buffer.appendInt32(-261341160)
buffer.appendInt32(-1977734781)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(id, buffer: buffer, boxed: false)
@ -167,10 +179,11 @@ public extension Api {
if Int(flags) & Int(1 << 11) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 12) != 0 {call!.serialize(buffer, true)}
if Int(flags) & Int(1 << 14) != 0 {serializeInt32(ttlPeriod!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 15) != 0 {groupcallDefaultJoinAs!.serialize(buffer, true)}
break
case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts, let call, let ttlPeriod, let pendingSuggestions):
case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts, let call, let ttlPeriod, let pendingSuggestions, let groupcallDefaultJoinAs):
if boxed {
buffer.appendInt32(625524791)
buffer.appendInt32(1418477459)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(id, buffer: buffer, boxed: false)
@ -210,16 +223,17 @@ public extension Api {
for item in pendingSuggestions! {
serializeString(item, buffer: buffer, boxed: false)
}}
if Int(flags) & Int(1 << 26) != 0 {groupcallDefaultJoinAs!.serialize(buffer, true)}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId, let call, let ttlPeriod):
return ("chatFull", [("flags", flags), ("id", id), ("about", about), ("participants", participants), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("pinnedMsgId", pinnedMsgId), ("folderId", folderId), ("call", call), ("ttlPeriod", ttlPeriod)])
case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts, let call, let ttlPeriod, let pendingSuggestions):
return ("channelFull", [("flags", flags), ("id", id), ("about", about), ("participantsCount", participantsCount), ("adminsCount", adminsCount), ("kickedCount", kickedCount), ("bannedCount", bannedCount), ("onlineCount", onlineCount), ("readInboxMaxId", readInboxMaxId), ("readOutboxMaxId", readOutboxMaxId), ("unreadCount", unreadCount), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("migratedFromChatId", migratedFromChatId), ("migratedFromMaxId", migratedFromMaxId), ("pinnedMsgId", pinnedMsgId), ("stickerset", stickerset), ("availableMinId", availableMinId), ("folderId", folderId), ("linkedChatId", linkedChatId), ("location", location), ("slowmodeSeconds", slowmodeSeconds), ("slowmodeNextSendDate", slowmodeNextSendDate), ("statsDc", statsDc), ("pts", pts), ("call", call), ("ttlPeriod", ttlPeriod), ("pendingSuggestions", pendingSuggestions)])
case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId, let call, let ttlPeriod, let groupcallDefaultJoinAs):
return ("chatFull", [("flags", flags), ("id", id), ("about", about), ("participants", participants), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("pinnedMsgId", pinnedMsgId), ("folderId", folderId), ("call", call), ("ttlPeriod", ttlPeriod), ("groupcallDefaultJoinAs", groupcallDefaultJoinAs)])
case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts, let call, let ttlPeriod, let pendingSuggestions, let groupcallDefaultJoinAs):
return ("channelFull", [("flags", flags), ("id", id), ("about", about), ("participantsCount", participantsCount), ("adminsCount", adminsCount), ("kickedCount", kickedCount), ("bannedCount", bannedCount), ("onlineCount", onlineCount), ("readInboxMaxId", readInboxMaxId), ("readOutboxMaxId", readOutboxMaxId), ("unreadCount", unreadCount), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("migratedFromChatId", migratedFromChatId), ("migratedFromMaxId", migratedFromMaxId), ("pinnedMsgId", pinnedMsgId), ("stickerset", stickerset), ("availableMinId", availableMinId), ("folderId", folderId), ("linkedChatId", linkedChatId), ("location", location), ("slowmodeSeconds", slowmodeSeconds), ("slowmodeNextSendDate", slowmodeNextSendDate), ("statsDc", statsDc), ("pts", pts), ("call", call), ("ttlPeriod", ttlPeriod), ("pendingSuggestions", pendingSuggestions), ("groupcallDefaultJoinAs", groupcallDefaultJoinAs)])
}
}
@ -260,6 +274,10 @@ public extension Api {
} }
var _12: Int32?
if Int(_1!) & Int(1 << 14) != 0 {_12 = reader.readInt32() }
var _13: Api.Peer?
if Int(_1!) & Int(1 << 15) != 0 {if let signature = reader.readInt32() {
_13 = Api.parse(reader, signature: signature) as? Api.Peer
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
@ -272,8 +290,9 @@ public extension Api {
let _c10 = (Int(_1!) & Int(1 << 11) == 0) || _10 != nil
let _c11 = (Int(_1!) & Int(1 << 12) == 0) || _11 != nil
let _c12 = (Int(_1!) & Int(1 << 14) == 0) || _12 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 {
return Api.ChatFull.chatFull(flags: _1!, id: _2!, about: _3!, participants: _4!, chatPhoto: _5, notifySettings: _6!, exportedInvite: _7, botInfo: _8, pinnedMsgId: _9, folderId: _10, call: _11, ttlPeriod: _12)
let _c13 = (Int(_1!) & Int(1 << 15) == 0) || _13 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 {
return Api.ChatFull.chatFull(flags: _1!, id: _2!, about: _3!, participants: _4!, chatPhoto: _5, notifySettings: _6!, exportedInvite: _7, botInfo: _8, pinnedMsgId: _9, folderId: _10, call: _11, ttlPeriod: _12, groupcallDefaultJoinAs: _13)
}
else {
return nil
@ -356,6 +375,10 @@ public extension Api {
if Int(_1!) & Int(1 << 25) != 0 {if let _ = reader.readInt32() {
_30 = Api.parseVector(reader, elementSignature: -1255641564, elementType: String.self)
} }
var _31: Api.Peer?
if Int(_1!) & Int(1 << 26) != 0 {if let signature = reader.readInt32() {
_31 = Api.parse(reader, signature: signature) as? Api.Peer
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
@ -386,8 +409,9 @@ public extension Api {
let _c28 = (Int(_1!) & Int(1 << 21) == 0) || _28 != nil
let _c29 = (Int(_1!) & Int(1 << 24) == 0) || _29 != nil
let _c30 = (Int(_1!) & Int(1 << 25) == 0) || _30 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 && _c27 && _c28 && _c29 && _c30 {
return Api.ChatFull.channelFull(flags: _1!, id: _2!, about: _3!, participantsCount: _4, adminsCount: _5, kickedCount: _6, bannedCount: _7, onlineCount: _8, readInboxMaxId: _9!, readOutboxMaxId: _10!, unreadCount: _11!, chatPhoto: _12!, notifySettings: _13!, exportedInvite: _14, botInfo: _15!, migratedFromChatId: _16, migratedFromMaxId: _17, pinnedMsgId: _18, stickerset: _19, availableMinId: _20, folderId: _21, linkedChatId: _22, location: _23, slowmodeSeconds: _24, slowmodeNextSendDate: _25, statsDc: _26, pts: _27!, call: _28, ttlPeriod: _29, pendingSuggestions: _30)
let _c31 = (Int(_1!) & Int(1 << 26) == 0) || _31 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 && _c27 && _c28 && _c29 && _c30 && _c31 {
return Api.ChatFull.channelFull(flags: _1!, id: _2!, about: _3!, participantsCount: _4, adminsCount: _5, kickedCount: _6, bannedCount: _7, onlineCount: _8, readInboxMaxId: _9!, readOutboxMaxId: _10!, unreadCount: _11!, chatPhoto: _12!, notifySettings: _13!, exportedInvite: _14, botInfo: _15!, migratedFromChatId: _16, migratedFromMaxId: _17, pinnedMsgId: _18, stickerset: _19, availableMinId: _20, folderId: _21, linkedChatId: _22, location: _23, slowmodeSeconds: _24, slowmodeNextSendDate: _25, statsDc: _26, pts: _27!, call: _28, ttlPeriod: _29, pendingSuggestions: _30, groupcallDefaultJoinAs: _31)
}
else {
return nil
@ -3580,36 +3604,39 @@ public extension Api {
}
public enum GroupCallParticipant: TypeConstructorDescription {
case groupCallParticipant(flags: Int32, userId: Int32, date: Int32, activeDate: Int32?, source: Int32, volume: Int32?)
case groupCallParticipant(flags: Int32, peer: Api.Peer, date: Int32, activeDate: Int32?, source: Int32, volume: Int32?, about: String?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .groupCallParticipant(let flags, let userId, let date, let activeDate, let source, let volume):
case .groupCallParticipant(let flags, let peer, let date, let activeDate, let source, let volume, let about):
if boxed {
buffer.appendInt32(1690708501)
buffer.appendInt32(-763544865)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(userId, buffer: buffer, boxed: false)
peer.serialize(buffer, true)
serializeInt32(date, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 3) != 0 {serializeInt32(activeDate!, buffer: buffer, boxed: false)}
serializeInt32(source, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 7) != 0 {serializeInt32(volume!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 11) != 0 {serializeString(about!, buffer: buffer, boxed: false)}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .groupCallParticipant(let flags, let userId, let date, let activeDate, let source, let volume):
return ("groupCallParticipant", [("flags", flags), ("userId", userId), ("date", date), ("activeDate", activeDate), ("source", source), ("volume", volume)])
case .groupCallParticipant(let flags, let peer, let date, let activeDate, let source, let volume, let about):
return ("groupCallParticipant", [("flags", flags), ("peer", peer), ("date", date), ("activeDate", activeDate), ("source", source), ("volume", volume), ("about", about)])
}
}
public static func parse_groupCallParticipant(_ reader: BufferReader) -> GroupCallParticipant? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Int32?
_2 = reader.readInt32()
var _2: Api.Peer?
if let signature = reader.readInt32() {
_2 = Api.parse(reader, signature: signature) as? Api.Peer
}
var _3: Int32?
_3 = reader.readInt32()
var _4: Int32?
@ -3618,14 +3645,17 @@ public extension Api {
_5 = reader.readInt32()
var _6: Int32?
if Int(_1!) & Int(1 << 7) != 0 {_6 = reader.readInt32() }
var _7: String?
if Int(_1!) & Int(1 << 11) != 0 {_7 = parseString(reader) }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = (Int(_1!) & Int(1 << 3) == 0) || _4 != nil
let _c5 = _5 != nil
let _c6 = (Int(_1!) & Int(1 << 7) == 0) || _6 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
return Api.GroupCallParticipant.groupCallParticipant(flags: _1!, userId: _2!, date: _3!, activeDate: _4, source: _5!, volume: _6)
let _c7 = (Int(_1!) & Int(1 << 11) == 0) || _7 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
return Api.GroupCallParticipant.groupCallParticipant(flags: _1!, peer: _2!, date: _3!, activeDate: _4, source: _5!, volume: _6, about: _7)
}
else {
return nil
@ -4640,8 +4670,8 @@ public extension Api {
case updateGroupCallParticipants(call: Api.InputGroupCall, participants: [Api.GroupCallParticipant], version: Int32)
case updateGroupCall(chatId: Int32, call: Api.GroupCall)
case updatePeerHistoryTTL(flags: Int32, peer: Api.Peer, ttlPeriod: Int32?)
case updateChatParticipant(flags: Int32, chatId: Int32, date: Int32, userId: Int32, prevParticipant: Api.ChatParticipant?, newParticipant: Api.ChatParticipant?, qts: Int32)
case updateChannelParticipant(flags: Int32, channelId: Int32, date: Int32, userId: Int32, prevParticipant: Api.ChannelParticipant?, newParticipant: Api.ChannelParticipant?, qts: Int32)
case updateChatParticipant(flags: Int32, chatId: Int32, date: Int32, actorId: Int32, userId: Int32, prevParticipant: Api.ChatParticipant?, newParticipant: Api.ChatParticipant?, invite: Api.ExportedChatInvite?, qts: Int32)
case updateChannelParticipant(flags: Int32, channelId: Int32, date: Int32, actorId: Int32, userId: Int32, prevParticipant: Api.ChannelParticipant?, newParticipant: Api.ChannelParticipant?, invite: Api.ExportedChatInvite?, qts: Int32)
case updateBotStopped(userId: Int32, date: Int32, stopped: Api.Bool, qts: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
@ -5400,28 +5430,32 @@ public extension Api {
peer.serialize(buffer, true)
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(ttlPeriod!, buffer: buffer, boxed: false)}
break
case .updateChatParticipant(let flags, let chatId, let date, let userId, let prevParticipant, let newParticipant, let qts):
case .updateChatParticipant(let flags, let chatId, let date, let actorId, let userId, let prevParticipant, let newParticipant, let invite, let qts):
if boxed {
buffer.appendInt32(1620733652)
buffer.appendInt32(-206342113)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(chatId, buffer: buffer, boxed: false)
serializeInt32(date, buffer: buffer, boxed: false)
serializeInt32(actorId, buffer: buffer, boxed: false)
serializeInt32(userId, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {prevParticipant!.serialize(buffer, true)}
if Int(flags) & Int(1 << 1) != 0 {newParticipant!.serialize(buffer, true)}
if Int(flags) & Int(1 << 2) != 0 {invite!.serialize(buffer, true)}
serializeInt32(qts, buffer: buffer, boxed: false)
break
case .updateChannelParticipant(let flags, let channelId, let date, let userId, let prevParticipant, let newParticipant, let qts):
case .updateChannelParticipant(let flags, let channelId, let date, let actorId, let userId, let prevParticipant, let newParticipant, let invite, let qts):
if boxed {
buffer.appendInt32(1708307556)
buffer.appendInt32(2146218476)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(channelId, buffer: buffer, boxed: false)
serializeInt32(date, buffer: buffer, boxed: false)
serializeInt32(actorId, buffer: buffer, boxed: false)
serializeInt32(userId, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {prevParticipant!.serialize(buffer, true)}
if Int(flags) & Int(1 << 1) != 0 {newParticipant!.serialize(buffer, true)}
if Int(flags) & Int(1 << 2) != 0 {invite!.serialize(buffer, true)}
serializeInt32(qts, buffer: buffer, boxed: false)
break
case .updateBotStopped(let userId, let date, let stopped, let qts):
@ -5614,10 +5648,10 @@ public extension Api {
return ("updateGroupCall", [("chatId", chatId), ("call", call)])
case .updatePeerHistoryTTL(let flags, let peer, let ttlPeriod):
return ("updatePeerHistoryTTL", [("flags", flags), ("peer", peer), ("ttlPeriod", ttlPeriod)])
case .updateChatParticipant(let flags, let chatId, let date, let userId, let prevParticipant, let newParticipant, let qts):
return ("updateChatParticipant", [("flags", flags), ("chatId", chatId), ("date", date), ("userId", userId), ("prevParticipant", prevParticipant), ("newParticipant", newParticipant), ("qts", qts)])
case .updateChannelParticipant(let flags, let channelId, let date, let userId, let prevParticipant, let newParticipant, let qts):
return ("updateChannelParticipant", [("flags", flags), ("channelId", channelId), ("date", date), ("userId", userId), ("prevParticipant", prevParticipant), ("newParticipant", newParticipant), ("qts", qts)])
case .updateChatParticipant(let flags, let chatId, let date, let actorId, let userId, let prevParticipant, let newParticipant, let invite, let qts):
return ("updateChatParticipant", [("flags", flags), ("chatId", chatId), ("date", date), ("actorId", actorId), ("userId", userId), ("prevParticipant", prevParticipant), ("newParticipant", newParticipant), ("invite", invite), ("qts", qts)])
case .updateChannelParticipant(let flags, let channelId, let date, let actorId, let userId, let prevParticipant, let newParticipant, let invite, let qts):
return ("updateChannelParticipant", [("flags", flags), ("channelId", channelId), ("date", date), ("actorId", actorId), ("userId", userId), ("prevParticipant", prevParticipant), ("newParticipant", newParticipant), ("invite", invite), ("qts", qts)])
case .updateBotStopped(let userId, let date, let stopped, let qts):
return ("updateBotStopped", [("userId", userId), ("date", date), ("stopped", stopped), ("qts", qts)])
}
@ -7140,25 +7174,33 @@ public extension Api {
_3 = reader.readInt32()
var _4: Int32?
_4 = reader.readInt32()
var _5: Api.ChatParticipant?
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
_5 = Api.parse(reader, signature: signature) as? Api.ChatParticipant
} }
var _5: Int32?
_5 = reader.readInt32()
var _6: Api.ChatParticipant?
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
_6 = Api.parse(reader, signature: signature) as? Api.ChatParticipant
} }
var _7: Int32?
_7 = reader.readInt32()
var _7: Api.ChatParticipant?
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
_7 = Api.parse(reader, signature: signature) as? Api.ChatParticipant
} }
var _8: Api.ExportedChatInvite?
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
_8 = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite
} }
var _9: Int32?
_9 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil
let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil
let _c7 = _7 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
return Api.Update.updateChatParticipant(flags: _1!, chatId: _2!, date: _3!, userId: _4!, prevParticipant: _5, newParticipant: _6, qts: _7!)
let _c5 = _5 != nil
let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil
let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil
let _c8 = (Int(_1!) & Int(1 << 2) == 0) || _8 != nil
let _c9 = _9 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 {
return Api.Update.updateChatParticipant(flags: _1!, chatId: _2!, date: _3!, actorId: _4!, userId: _5!, prevParticipant: _6, newParticipant: _7, invite: _8, qts: _9!)
}
else {
return nil
@ -7173,25 +7215,33 @@ public extension Api {
_3 = reader.readInt32()
var _4: Int32?
_4 = reader.readInt32()
var _5: Api.ChannelParticipant?
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
_5 = Api.parse(reader, signature: signature) as? Api.ChannelParticipant
} }
var _5: Int32?
_5 = reader.readInt32()
var _6: Api.ChannelParticipant?
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
_6 = Api.parse(reader, signature: signature) as? Api.ChannelParticipant
} }
var _7: Int32?
_7 = reader.readInt32()
var _7: Api.ChannelParticipant?
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
_7 = Api.parse(reader, signature: signature) as? Api.ChannelParticipant
} }
var _8: Api.ExportedChatInvite?
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
_8 = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite
} }
var _9: Int32?
_9 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil
let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil
let _c7 = _7 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
return Api.Update.updateChannelParticipant(flags: _1!, channelId: _2!, date: _3!, userId: _4!, prevParticipant: _5, newParticipant: _6, qts: _7!)
let _c5 = _5 != nil
let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil
let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil
let _c8 = (Int(_1!) & Int(1 << 2) == 0) || _8 != nil
let _c9 = _9 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 {
return Api.Update.updateChannelParticipant(flags: _1!, channelId: _2!, date: _3!, actorId: _4!, userId: _5!, prevParticipant: _6, newParticipant: _7, invite: _8, qts: _9!)
}
else {
return nil
@ -16967,6 +17017,7 @@ public extension Api {
case inputPhotoLegacyFileLocation(id: Int64, accessHash: Int64, fileReference: Buffer, volumeId: Int64, localId: Int32, secret: Int64)
case inputPeerPhotoFileLocation(flags: Int32, peer: Api.InputPeer, volumeId: Int64, localId: Int32)
case inputStickerSetThumb(stickerset: Api.InputStickerSet, volumeId: Int64, localId: Int32)
case inputGroupCallStream(call: Api.InputGroupCall, date: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@ -17045,6 +17096,13 @@ public extension Api {
serializeInt64(volumeId, buffer: buffer, boxed: false)
serializeInt32(localId, buffer: buffer, boxed: false)
break
case .inputGroupCallStream(let call, let date):
if boxed {
buffer.appendInt32(-775148961)
}
call.serialize(buffer, true)
serializeInt32(date, buffer: buffer, boxed: false)
break
}
}
@ -17068,6 +17126,8 @@ public extension Api {
return ("inputPeerPhotoFileLocation", [("flags", flags), ("peer", peer), ("volumeId", volumeId), ("localId", localId)])
case .inputStickerSetThumb(let stickerset, let volumeId, let localId):
return ("inputStickerSetThumb", [("stickerset", stickerset), ("volumeId", volumeId), ("localId", localId)])
case .inputGroupCallStream(let call, let date):
return ("inputGroupCallStream", [("call", call), ("date", date)])
}
}
@ -17229,6 +17289,22 @@ public extension Api {
return nil
}
}
public static func parse_inputGroupCallStream(_ reader: BufferReader) -> InputFileLocation? {
var _1: Api.InputGroupCall?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.InputGroupCall
}
var _2: Int32?
_2 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.InputFileLocation.inputGroupCallStream(call: _1!, date: _2!)
}
else {
return nil
}
}
}
public enum GeoPoint: TypeConstructorDescription {

View File

@ -1611,13 +1611,13 @@ public struct photos {
public extension Api {
public struct phone {
public enum GroupCall: TypeConstructorDescription {
case groupCall(call: Api.GroupCall, participants: [Api.GroupCallParticipant], participantsNextOffset: String, users: [Api.User])
case groupCall(call: Api.GroupCall, participants: [Api.GroupCallParticipant], participantsNextOffset: String, chats: [Api.Chat], users: [Api.User])
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .groupCall(let call, let participants, let participantsNextOffset, let users):
case .groupCall(let call, let participants, let participantsNextOffset, let chats, let users):
if boxed {
buffer.appendInt32(1722485756)
buffer.appendInt32(-1636664659)
}
call.serialize(buffer, true)
buffer.appendInt32(481674261)
@ -1627,6 +1627,11 @@ public struct phone {
}
serializeString(participantsNextOffset, buffer: buffer, boxed: false)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(chats.count))
for item in chats {
item.serialize(buffer, true)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(users.count))
for item in users {
item.serialize(buffer, true)
@ -1637,8 +1642,8 @@ public struct phone {
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .groupCall(let call, let participants, let participantsNextOffset, let users):
return ("groupCall", [("call", call), ("participants", participants), ("participantsNextOffset", participantsNextOffset), ("users", users)])
case .groupCall(let call, let participants, let participantsNextOffset, let chats, let users):
return ("groupCall", [("call", call), ("participants", participants), ("participantsNextOffset", participantsNextOffset), ("chats", chats), ("users", users)])
}
}
@ -1653,16 +1658,21 @@ public struct phone {
}
var _3: String?
_3 = parseString(reader)
var _4: [Api.User]?
var _4: [Api.Chat]?
if let _ = reader.readInt32() {
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
}
var _5: [Api.User]?
if let _ = reader.readInt32() {
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
}
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
if _c1 && _c2 && _c3 && _c4 {
return Api.phone.GroupCall.groupCall(call: _1!, participants: _2!, participantsNextOffset: _3!, users: _4!)
let _c5 = _5 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 {
return Api.phone.GroupCall.groupCall(call: _1!, participants: _2!, participantsNextOffset: _3!, chats: _4!, users: _5!)
}
else {
return nil
@ -1671,13 +1681,13 @@ public struct phone {
}
public enum GroupParticipants: TypeConstructorDescription {
case groupParticipants(count: Int32, participants: [Api.GroupCallParticipant], nextOffset: String, users: [Api.User], version: Int32)
case groupParticipants(count: Int32, participants: [Api.GroupCallParticipant], nextOffset: String, chats: [Api.Chat], users: [Api.User], version: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .groupParticipants(let count, let participants, let nextOffset, let users, let version):
case .groupParticipants(let count, let participants, let nextOffset, let chats, let users, let version):
if boxed {
buffer.appendInt32(-1661028051)
buffer.appendInt32(-193506890)
}
serializeInt32(count, buffer: buffer, boxed: false)
buffer.appendInt32(481674261)
@ -1687,6 +1697,11 @@ public struct phone {
}
serializeString(nextOffset, buffer: buffer, boxed: false)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(chats.count))
for item in chats {
item.serialize(buffer, true)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(users.count))
for item in users {
item.serialize(buffer, true)
@ -1698,8 +1713,8 @@ public struct phone {
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .groupParticipants(let count, let participants, let nextOffset, let users, let version):
return ("groupParticipants", [("count", count), ("participants", participants), ("nextOffset", nextOffset), ("users", users), ("version", version)])
case .groupParticipants(let count, let participants, let nextOffset, let chats, let users, let version):
return ("groupParticipants", [("count", count), ("participants", participants), ("nextOffset", nextOffset), ("chats", chats), ("users", users), ("version", version)])
}
}
@ -1712,19 +1727,24 @@ public struct phone {
}
var _3: String?
_3 = parseString(reader)
var _4: [Api.User]?
var _4: [Api.Chat]?
if let _ = reader.readInt32() {
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
}
var _5: Int32?
_5 = reader.readInt32()
var _5: [Api.User]?
if let _ = reader.readInt32() {
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
}
var _6: Int32?
_6 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
let _c5 = _5 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 {
return Api.phone.GroupParticipants.groupParticipants(count: _1!, participants: _2!, nextOffset: _3!, users: _4!, version: _5!)
let _c6 = _6 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
return Api.phone.GroupParticipants.groupParticipants(count: _1!, participants: _2!, nextOffset: _3!, chats: _4!, users: _5!, version: _6!)
}
else {
return nil
@ -3617,13 +3637,15 @@ public extension Api {
})
}
public static func requestUrlAuth(peer: Api.InputPeer, msgId: Int32, buttonId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.UrlAuthResult>) {
public static func requestUrlAuth(flags: Int32, peer: Api.InputPeer?, msgId: Int32?, buttonId: Int32?, url: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.UrlAuthResult>) {
let buffer = Buffer()
buffer.appendInt32(-482388461)
peer.serialize(buffer, true)
serializeInt32(msgId, buffer: buffer, boxed: false)
serializeInt32(buttonId, buffer: buffer, boxed: false)
return (FunctionDescription(name: "messages.requestUrlAuth", parameters: [("peer", peer), ("msgId", msgId), ("buttonId", buttonId)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.UrlAuthResult? in
buffer.appendInt32(428848198)
serializeInt32(flags, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 1) != 0 {peer!.serialize(buffer, true)}
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(msgId!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(buttonId!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 2) != 0 {serializeString(url!, buffer: buffer, boxed: false)}
return (FunctionDescription(name: "messages.requestUrlAuth", parameters: [("flags", flags), ("peer", peer), ("msgId", msgId), ("buttonId", buttonId), ("url", url)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.UrlAuthResult? in
let reader = BufferReader(buffer)
var result: Api.UrlAuthResult?
if let signature = reader.readInt32() {
@ -3633,14 +3655,15 @@ public extension Api {
})
}
public static func acceptUrlAuth(flags: Int32, peer: Api.InputPeer, msgId: Int32, buttonId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.UrlAuthResult>) {
public static func acceptUrlAuth(flags: Int32, peer: Api.InputPeer?, msgId: Int32?, buttonId: Int32?, url: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.UrlAuthResult>) {
let buffer = Buffer()
buffer.appendInt32(-148247912)
buffer.appendInt32(-1322487515)
serializeInt32(flags, buffer: buffer, boxed: false)
peer.serialize(buffer, true)
serializeInt32(msgId, buffer: buffer, boxed: false)
serializeInt32(buttonId, buffer: buffer, boxed: false)
return (FunctionDescription(name: "messages.acceptUrlAuth", parameters: [("flags", flags), ("peer", peer), ("msgId", msgId), ("buttonId", buttonId)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.UrlAuthResult? in
if Int(flags) & Int(1 << 1) != 0 {peer!.serialize(buffer, true)}
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(msgId!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(buttonId!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 2) != 0 {serializeString(url!, buffer: buffer, boxed: false)}
return (FunctionDescription(name: "messages.acceptUrlAuth", parameters: [("flags", flags), ("peer", peer), ("msgId", msgId), ("buttonId", buttonId), ("url", url)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.UrlAuthResult? in
let reader = BufferReader(buffer)
var result: Api.UrlAuthResult?
if let signature = reader.readInt32() {
@ -4028,6 +4051,21 @@ public extension Api {
})
}
public static func getExportedChatInvite(peer: Api.InputPeer, link: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.ExportedChatInvite>) {
let buffer = Buffer()
buffer.appendInt32(1937010524)
peer.serialize(buffer, true)
serializeString(link, buffer: buffer, boxed: false)
return (FunctionDescription(name: "messages.getExportedChatInvite", parameters: [("peer", peer), ("link", link)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.ExportedChatInvite? in
let reader = BufferReader(buffer)
var result: Api.messages.ExportedChatInvite?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.messages.ExportedChatInvite
}
return result
})
}
public static func editExportedChatInvite(flags: Int32, peer: Api.InputPeer, link: String, expireDate: Int32?, usageLimit: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.ExportedChatInvite>) {
let buffer = Buffer()
buffer.appendInt32(48562110)
@ -7557,12 +7595,14 @@ public extension Api {
})
}
public static func createGroupCall(peer: Api.InputPeer, randomId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
public static func createGroupCall(flags: Int32, peer: Api.InputPeer, joinAs: Api.InputPeer?, randomId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer()
buffer.appendInt32(-1120031776)
buffer.appendInt32(534090322)
serializeInt32(flags, buffer: buffer, boxed: false)
peer.serialize(buffer, true)
if Int(flags) & Int(1 << 0) != 0 {joinAs!.serialize(buffer, true)}
serializeInt32(randomId, buffer: buffer, boxed: false)
return (FunctionDescription(name: "phone.createGroupCall", parameters: [("peer", peer), ("randomId", randomId)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
return (FunctionDescription(name: "phone.createGroupCall", parameters: [("flags", flags), ("peer", peer), ("joinAs", joinAs), ("randomId", randomId)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
let reader = BufferReader(buffer)
var result: Api.Updates?
if let signature = reader.readInt32() {
@ -7572,13 +7612,14 @@ public extension Api {
})
}
public static func joinGroupCall(flags: Int32, call: Api.InputGroupCall, params: Api.DataJSON) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
public static func joinGroupCall(flags: Int32, call: Api.InputGroupCall, joinAs: Api.InputPeer?, params: Api.DataJSON) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer()
buffer.appendInt32(1604095586)
buffer.appendInt32(780232376)
serializeInt32(flags, buffer: buffer, boxed: false)
call.serialize(buffer, true)
if Int(flags) & Int(1 << 1) != 0 {joinAs!.serialize(buffer, true)}
params.serialize(buffer, true)
return (FunctionDescription(name: "phone.joinGroupCall", parameters: [("flags", flags), ("call", call), ("params", params)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
return (FunctionDescription(name: "phone.joinGroupCall", parameters: [("flags", flags), ("call", call), ("joinAs", joinAs), ("params", params)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
let reader = BufferReader(buffer)
var result: Api.Updates?
if let signature = reader.readInt32() {
@ -7603,23 +7644,6 @@ public extension Api {
})
}
public static func editGroupCallMember(flags: Int32, call: Api.InputGroupCall, userId: Api.InputUser, volume: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer()
buffer.appendInt32(-1511559976)
serializeInt32(flags, buffer: buffer, boxed: false)
call.serialize(buffer, true)
userId.serialize(buffer, true)
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(volume!, buffer: buffer, boxed: false)}
return (FunctionDescription(name: "phone.editGroupCallMember", parameters: [("flags", flags), ("call", call), ("userId", userId), ("volume", volume)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
let reader = BufferReader(buffer)
var result: Api.Updates?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.Updates
}
return result
})
}
public static func inviteToGroupCall(call: Api.InputGroupCall, users: [Api.InputUser]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer()
buffer.appendInt32(2067345760)
@ -7723,6 +7747,53 @@ public extension Api {
return result
})
}
public static func toggleGroupCallRecord(call: Api.InputGroupCall, start: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer()
buffer.appendInt32(1990430344)
call.serialize(buffer, true)
start.serialize(buffer, true)
return (FunctionDescription(name: "phone.toggleGroupCallRecord", parameters: [("call", call), ("start", start)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
let reader = BufferReader(buffer)
var result: Api.Updates?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.Updates
}
return result
})
}
public static func editGroupCallParticipant(flags: Int32, call: Api.InputGroupCall, participant: Api.InputPeer, volume: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer()
buffer.appendInt32(1192486819)
serializeInt32(flags, buffer: buffer, boxed: false)
call.serialize(buffer, true)
participant.serialize(buffer, true)
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(volume!, buffer: buffer, boxed: false)}
return (FunctionDescription(name: "phone.editGroupCallParticipant", parameters: [("flags", flags), ("call", call), ("participant", participant), ("volume", volume)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
let reader = BufferReader(buffer)
var result: Api.Updates?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.Updates
}
return result
})
}
public static func editGroupCallTitle(call: Api.InputGroupCall, title: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer()
buffer.appendInt32(480685066)
call.serialize(buffer, true)
serializeString(title, buffer: buffer, boxed: false)
return (FunctionDescription(name: "phone.editGroupCallTitle", parameters: [("call", call), ("title", title)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
let reader = BufferReader(buffer)
var result: Api.Updates?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.Updates
}
return result
})
}
}
}
}

View File

@ -405,6 +405,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
}
strongSelf.joinGroupCall(
peerId: groupCallPanelData.peerId,
joinAsPeerId: nil,
info: groupCallPanelData.info
)
})
@ -851,7 +852,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
})]
}
open func joinGroupCall(peerId: PeerId, info: GroupCallInfo) {
self.context.joinGroupCall(peerId: peerId, activeCall: CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash))
open func joinGroupCall(peerId: PeerId, joinAsPeerId: PeerId?, info: GroupCallInfo) {
self.context.joinGroupCall(peerId: peerId, joinAsPeerId: joinAsPeerId, activeCall: CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash))
}
}

View File

@ -624,9 +624,9 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
}
}
public func joinGroupCall(context: AccountContext, peerId: PeerId, initialCall: CachedChannelData.ActiveCall, endCurrentIfAny: Bool) -> JoinGroupCallManagerResult {
public func joinGroupCall(context: AccountContext, peerId: PeerId, joinAsPeerId: PeerId?, initialCall: CachedChannelData.ActiveCall, endCurrentIfAny: Bool) -> JoinGroupCallManagerResult {
let begin: () -> Void = { [weak self] in
let _ = self?.startGroupCall(accountContext: context, peerId: peerId, initialCall: initialCall).start()
let _ = self?.startGroupCall(accountContext: context, peerId: peerId, joinAsPeerId: joinAsPeerId, initialCall: initialCall).start()
}
if let currentGroupCall = self.currentGroupCallValue {
@ -660,6 +660,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
private func startGroupCall(
accountContext: AccountContext,
peerId: PeerId,
joinAsPeerId: PeerId?,
initialCall: CachedChannelData.ActiveCall,
internalId: CallSessionInternalId = CallSessionInternalId()
) -> Signal<Bool, NoError> {
@ -710,7 +711,8 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
initialCall: initialCall,
internalId: internalId,
peerId: peerId,
peer: nil
peer: nil,
joinAsPeerId: joinAsPeerId
)
strongSelf.updateCurrentGroupCall(call)
strongSelf.currentGroupCallPromise.set(.single(call))

View File

@ -70,7 +70,10 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext {
id: call.id,
accessHash: call.accessHash,
participantCount: 0,
clientParams: nil
clientParams: nil,
streamDcId: nil,
title: nil,
recordingStartTimestamp: nil
),
topParticipants: [],
participantCount: 0,
@ -110,7 +113,7 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext {
}
return GroupCallPanelData(
peerId: peerId,
info: GroupCallInfo(id: call.id, accessHash: call.accessHash, participantCount: state.totalCount, clientParams: nil),
info: GroupCallInfo(id: call.id, accessHash: call.accessHash, participantCount: state.totalCount, clientParams: nil, streamDcId: nil, title: nil, recordingStartTimestamp: nil),
topParticipants: topParticipants,
participantCount: state.totalCount,
activeSpeakers: activeSpeakers,
@ -194,7 +197,8 @@ private extension PresentationGroupCallState {
canManageCall: false,
adminIds: Set(),
muteState: GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false),
defaultParticipantMuteState: nil
defaultParticipantMuteState: nil,
recordingStartTimestamp: nil
)
}
}
@ -333,6 +337,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
private var initialCall: CachedChannelData.ActiveCall?
public let internalId: CallSessionInternalId
public let peerId: PeerId
private let joinAsPeerId: PeerId
public let myPeerId: PeerId
public let peer: Peer?
public private(set) var isVideo: Bool
@ -502,7 +508,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
initialCall: CachedChannelData.ActiveCall?,
internalId: CallSessionInternalId,
peerId: PeerId,
peer: Peer?
peer: Peer?,
joinAsPeerId: PeerId?
) {
self.account = accountContext.account
self.accountContext = accountContext
@ -514,6 +521,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
self.internalId = internalId
self.peerId = peerId
self.peer = peer
self.joinAsPeerId = joinAsPeerId ?? accountContext.account.peerId
self.myPeerId = joinAsPeerId ?? accountContext.account.peerId
self.temporaryJoinTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
@ -632,12 +641,12 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
if case .left = participantUpdate.participationStatusChange {
removedSsrc.append(participantUpdate.ssrc)
if participantUpdate.peerId == strongSelf.accountContext.account.peerId {
if participantUpdate.peerId == strongSelf.joinAsPeerId {
if case let .estabilished(_, _, ssrc, _) = strongSelf.internalState, ssrc == participantUpdate.ssrc {
strongSelf._canBeRemoved.set(.single(true))
}
}
} else if participantUpdate.peerId == strongSelf.accountContext.account.peerId {
} else if participantUpdate.peerId == strongSelf.joinAsPeerId {
if case let .estabilished(_, _, ssrc, _) = strongSelf.internalState, ssrc != participantUpdate.ssrc {
strongSelf._canBeRemoved.set(.single(true))
}
@ -647,7 +656,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
addedParticipants.append((participantUpdate.ssrc, participantUpdate.jsonParams))
}
}
case let .call(isTerminated, _):
case let .call(isTerminated, _, _, _):
if isTerminated {
strongSelf._canBeRemoved.set(.single(true))
}
@ -683,15 +692,15 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
impl.get(account: accountContext.account, peerId: peerId, call: CachedChannelData.ActiveCall(id: initialCall.id, accessHash: initialCall.accessHash))
}) {
if let participantsContext = temporaryParticipantsContext.context.participantsContext {
let accountPeerId = self.accountContext.account.peerId
let accountPeer = self.accountContext.account.postbox.transaction { transaction -> Peer? in
return transaction.getPeer(accountPeerId)
let myPeerId = self.joinAsPeerId
let myPeer = self.accountContext.account.postbox.transaction { transaction -> Peer? in
return transaction.getPeer(myPeerId)
}
self.participantsContextStateDisposable.set(combineLatest(queue: .mainQueue(),
accountPeer,
myPeer,
participantsContext.state,
participantsContext.activeSpeakers
).start(next: { [weak self] accountPeer, state, activeSpeakers in
).start(next: { [weak self] myPeer, state, activeSpeakers in
guard let strongSelf = self else {
return
}
@ -710,17 +719,18 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
var participants = state.participants
if !participants.contains(where: { $0.peer.id == accountPeerId }) {
if let accountPeer = accountPeer {
if !participants.contains(where: { $0.peer.id == myPeerId }) {
if let myPeer = myPeer {
participants.append(GroupCallParticipantsContext.Participant(
peer: accountPeer,
peer: myPeer,
ssrc: 0,
jsonParams: nil,
joinTimestamp: strongSelf.temporaryJoinTimestamp,
activityTimestamp: nil,
activityRank: nil,
muteState: GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false),
volume: nil
volume: nil,
about: nil
))
participants.sort()
}
@ -845,7 +855,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
}
strongSelf.maybeRequestParticipants(ssrcs: ssrcs)
}
}, demoAudioStream: self.accountContext.sharedContext.immediateExperimentalUISettings.demoAudioStream)
}, audioStreamData: OngoingGroupCallContext.AudioStreamData(account: self.accountContext.account, callId: callInfo.id, accessHash: callInfo.accessHash, datacenterId: callInfo.streamDcId.flatMap(Int.init)))
self.incomingVideoSourcePromise.set(callContext.videoSources
|> deliverOnMainQueue
|> map { [weak self] sources -> [PeerId: UInt32] in
@ -878,6 +888,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
strongSelf.requestDisposable.set((joinGroupCall(
account: strongSelf.account,
peerId: strongSelf.peerId,
joinAs: strongSelf.joinAsPeerId == strongSelf.account.peerId ? nil : strongSelf.joinAsPeerId,
callId: callInfo.id,
accessHash: callInfo.accessHash,
preferMuted: true,
@ -984,7 +995,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
let ssrcValue: UInt32
switch ssrcKey {
case .local:
peerId = strongSelf.accountContext.account.peerId
peerId = strongSelf.joinAsPeerId
ssrcValue = 0
case let .source(ssrc):
peerId = strongSelf.ssrcMapping[ssrc]
@ -1027,6 +1038,9 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
if self.stateValue.canManageCall && initialState.defaultParticipantsAreMuted.canChange {
self.stateValue.defaultParticipantMuteState = initialState.defaultParticipantsAreMuted.isMuted ? .muted : .unmuted
}
if self.stateValue.recordingStartTimestamp != initialState.recordingStartTimestamp {
self.stateValue.recordingStartTimestamp = initialState.recordingStartTimestamp
}
let accountContext = self.accountContext
let peerId = self.peerId
@ -1115,7 +1129,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
strongSelf.ssrcMapping[participant.ssrc] = participant.peer.id
if participant.peer.id == strongSelf.accountContext.account.peerId {
if participant.peer.id == strongSelf.joinAsPeerId {
if let muteState = participant.muteState {
if muteState.canUnmute {
switch strongSelf.isMutedValue {
@ -1162,6 +1176,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
if (state.isCreator || strongSelf.stateValue.adminIds.contains(strongSelf.accountContext.account.peerId)) && state.defaultParticipantsAreMuted.canChange {
strongSelf.stateValue.defaultParticipantMuteState = state.defaultParticipantsAreMuted.isMuted ? .muted : .unmuted
}
strongSelf.stateValue.recordingStartTimestamp = state.recordingStartTimestamp
strongSelf.summaryParticipantsState.set(.single(SummaryParticipantsState(
participantCount: state.totalCount,
@ -1385,11 +1400,11 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
case let .muted(isPushToTalkActive):
isEffectivelyMuted = !isPushToTalkActive
isVisuallyMuted = true
let _ = self.updateMuteState(peerId: self.accountContext.account.peerId, isMuted: true)
let _ = self.updateMuteState(peerId: self.joinAsPeerId, isMuted: true)
case .unmuted:
isEffectivelyMuted = false
isVisuallyMuted = false
let _ = self.updateMuteState(peerId: self.accountContext.account.peerId, isMuted: false)
let _ = self.updateMuteState(peerId: self.joinAsPeerId, isMuted: false)
}
self.callContext?.setIsMuted(isEffectivelyMuted)
@ -1516,7 +1531,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
let canThenUnmute: Bool
if isMuted {
var mutedByYou = false
if peerId == self.accountContext.account.peerId {
if peerId == self.joinAsPeerId {
canThenUnmute = true
} else if self.stateValue.canManageCall {
if self.stateValue.adminIds.contains(peerId) {
@ -1535,7 +1550,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
self.participantsContext?.updateMuteState(peerId: peerId, muteState: muteState, volume: nil)
return muteState
} else {
if peerId == self.accountContext.account.peerId {
if peerId == self.joinAsPeerId {
self.participantsContext?.updateMuteState(peerId: peerId, muteState: nil, volume: nil)
return nil
} else if self.stateValue.canManageCall || self.stateValue.adminIds.contains(self.accountContext.account.peerId) {
@ -1550,6 +1565,16 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
}
}
public func setShouldBeRecording(_ shouldBeRecording: Bool) {
if !self.stateValue.canManageCall {
return
}
if (self.stateValue.recordingStartTimestamp != nil) == shouldBeRecording {
return
}
self.participantsContext?.updateShouldBeRecording(shouldBeRecording)
}
private func requestCall() {
self.callContext?.stop()
self.callContext = nil

View File

@ -370,6 +370,7 @@ public final class VoiceChatController: ViewController {
}
var peer: Peer
var myPeerId: PeerId
var ssrc: UInt32
var presence: TelegramUserPresence?
var activityTimestamp: Int32
@ -387,6 +388,9 @@ public final class VoiceChatController: ViewController {
if !lhs.peer.isEqual(rhs.peer) {
return false
}
if lhs.myPeerId != rhs.myPeerId {
return false
}
if lhs.ssrc != rhs.ssrc {
return false
}
@ -540,7 +544,7 @@ public final class VoiceChatController: ViewController {
let revealOptions: [VoiceChatParticipantItem.RevealOption] = []
return VoiceChatParticipantItem(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, context: context, peer: peer, ssrc: peerEntry.ssrc, presence: peerEntry.presence, text: text, icon: icon, enabled: true, selectable: peer.id != context.account.peerId || peerEntry.canManageCall, getAudioLevel: { return interaction.getAudioLevel(peer.id) }, getVideo: {
return VoiceChatParticipantItem(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, context: context, peer: peer, ssrc: peerEntry.ssrc, presence: peerEntry.presence, text: text, icon: icon, enabled: true, selectable: peer.id != peerEntry.myPeerId || peerEntry.canManageCall, getAudioLevel: { return interaction.getAudioLevel(peer.id) }, getVideo: {
return interaction.getPeerVideo(peerEntry.ssrc)
}, revealOptions: revealOptions, revealed: peerEntry.revealed, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
interaction.setPeerIdWithRevealedOptions(peerId, fromPeerId)
@ -608,7 +612,6 @@ public final class VoiceChatController: ViewController {
private var currentSpeakingPeers: Set<PeerId>?
private var currentContentOffset: CGFloat?
private var ignoreScrolling = false
private var accountPeer: Peer?
private var currentAudioButtonColor: UIColor?
private var currentEntries: [ListEntry] = []
@ -824,7 +827,7 @@ public final class VoiceChatController: ViewController {
}
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
if peer.id == strongSelf.context.account.peerId {
if peer.id == strongSelf.call.myPeerId {
return
}
if let participant = participant {
@ -1047,7 +1050,7 @@ public final class VoiceChatController: ViewController {
f(.default)
})))*/
if peer.id != strongSelf.context.account.peerId {
if peer.id != strongSelf.call.myPeerId {
if let callState = strongSelf.callState, (callState.canManageCall || callState.adminIds.contains(strongSelf.context.account.peerId)) {
if callState.adminIds.contains(peer.id) {
if let _ = muteState {
@ -1296,8 +1299,8 @@ public final class VoiceChatController: ViewController {
}
})
self.peerViewDisposable = (combineLatest(self.context.account.viewTracker.peerView(self.call.peerId), self.context.account.postbox.loadedPeerWithId(self.context.account.peerId))
|> deliverOnMainQueue).start(next: { [weak self] view, accountPeer in
self.peerViewDisposable = (self.context.account.viewTracker.peerView(self.call.peerId)
|> deliverOnMainQueue).start(next: { [weak self] view in
guard let strongSelf = self else {
return
}
@ -1307,7 +1310,6 @@ public final class VoiceChatController: ViewController {
strongSelf.currentTitle = peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)
}
if !strongSelf.didSetDataReady {
strongSelf.accountPeer = accountPeer
strongSelf.updateMembers(muteState: strongSelf.effectiveMuteState, callMembers: strongSelf.currentCallMembers ?? ([], nil), invitedPeers: strongSelf.currentInvitedPeers ?? [], speakingPeers: strongSelf.currentSpeakingPeers ?? Set())
strongSelf.didSetDataReady = true
@ -1337,7 +1339,7 @@ public final class VoiceChatController: ViewController {
}
var levels = levels
if strongSelf.effectiveMuteState != nil {
levels = levels.filter { $0.0 != strongSelf.context.account.peerId }
levels = levels.filter { $0.0 != strongSelf.call.myPeerId }
}
var maxLevelWithVideo: (PeerId, UInt32, Float)?
@ -1456,6 +1458,15 @@ public final class VoiceChatController: ViewController {
})])
strongSelf.controller?.present(alert, in: .window(.root))
})))
//TODO:localize
items.append(.action(ContextMenuActionItem(text: callState.recordingStartTimestamp == nil ? "Begin Recording" : "End Recording", textColor: .destructive, icon: { _ in
return nil
}, action: { _, f in
f(.dismissWithoutContent)
strongSelf.call.setShouldBeRecording(callState.recordingStartTimestamp == nil ? true : false)
})))
}
if items.isEmpty {
@ -1747,7 +1758,7 @@ public final class VoiceChatController: ViewController {
self.call.setIsMuted(action: .muted(isPushToTalkActive: false))
}
self.itemInteraction?.updateAudioLevels([(self.context.account.peerId, 0, 0.0, false)], reset: true)
self.itemInteraction?.updateAudioLevels([(self.call.myPeerId, 0, 0.0, false)], reset: true)
if let (layout, navigationHeight) = self.validLayout {
self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.3, curve: .spring))
@ -2497,7 +2508,7 @@ public final class VoiceChatController: ViewController {
let memberState: PeerEntry.State
var memberMuteState: GroupCallParticipantsContext.Participant.MuteState?
if member.peer.id == self.context.account.peerId {
if member.peer.id == self.call.myPeerId {
if muteState == nil {
memberState = speakingPeers.contains(member.peer.id) ? .speaking : .listening
} else {
@ -2511,6 +2522,7 @@ public final class VoiceChatController: ViewController {
entries.append(.peer(PeerEntry(
peer: member.peer,
myPeerId: self.call.myPeerId,
ssrc: member.ssrc,
presence: nil,
activityTimestamp: Int32.max - 1 - index,
@ -2522,19 +2534,6 @@ public final class VoiceChatController: ViewController {
index += 1
}
if let accountPeer = self.accountPeer, !processedPeerIds.contains(accountPeer.id) {
entries.insert(.peer(PeerEntry(
peer: accountPeer,
ssrc: 0,
presence: nil,
activityTimestamp: Int32.max - 1 - index,
state: .listening,
muteState: GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false),
canManageCall: self.callState?.canManageCall ?? false,
volume: nil
)), at: 1)
}
for peer in invitedPeers {
if processedPeerIds.contains(peer.id) {
continue
@ -2543,6 +2542,7 @@ public final class VoiceChatController: ViewController {
entries.append(.peer(PeerEntry(
peer: peer,
myPeerId: self.call.myPeerId,
ssrc: 0,
presence: nil,
activityTimestamp: Int32.max - 1 - index,

View File

@ -2987,13 +2987,13 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
})
switch call {
case let .groupCall(flags, _, _, _, _, _):
case let .groupCall(flags, _, _, _, _, title, streamDcId, recordStartDate, _):
let isMuted = (flags & (1 << 1)) != 0
let canChange = (flags & (1 << 2)) != 0
let defaultParticipantsAreMuted = GroupCallParticipantsContext.State.DefaultParticipantsAreMuted(isMuted: isMuted, canChange: canChange)
updatedGroupCallParticipants.append((
info.id,
.call(isTerminated: false, defaultParticipantsAreMuted: defaultParticipantsAreMuted)
.call(isTerminated: false, defaultParticipantsAreMuted: defaultParticipantsAreMuted, title: title, recordingStartTimestamp: recordStartDate)
))
default:
break
@ -3002,7 +3002,7 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
case let .groupCallDiscarded(callId, _, _):
updatedGroupCallParticipants.append((
callId,
.call(isTerminated: true, defaultParticipantsAreMuted: GroupCallParticipantsContext.State.DefaultParticipantsAreMuted(isMuted: false, canChange: false))
.call(isTerminated: true, defaultParticipantsAreMuted: GroupCallParticipantsContext.State.DefaultParticipantsAreMuted(isMuted: false, canChange: false), title: nil, recordingStartTimestamp: nil)
))
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in

View File

@ -188,10 +188,21 @@ public func checkPublicChannelCreationAvailability(account: Account, location: B
}
}
public func adminedPublicChannels(account: Account, location: Bool = false) -> Signal<[Peer], NoError> {
public enum AdminedPublicChannelsScope {
case all
case forLocation
case forVoiceChat
}
public func adminedPublicChannels(account: Account, scope: AdminedPublicChannelsScope = .all) -> Signal<[Peer], NoError> {
var flags: Int32 = 0
if location {
switch scope {
case .all:
break
case .forLocation:
flags |= (1 << 0)
case .forVoiceChat:
flags |= (1 << 2)
}
return account.network.request(Api.functions.channels.getAdminedPublicChannels(flags: flags))

View File

@ -24,7 +24,7 @@ public struct BotPaymentInvoiceFields: OptionSet {
public static let flexibleShipping = BotPaymentInvoiceFields(rawValue: 1 << 4)
}
public struct BotPaymentPrice {
public struct BotPaymentPrice : Equatable {
public let label: String
public let amount: Int64
@ -34,14 +34,14 @@ public struct BotPaymentPrice {
}
}
public struct BotPaymentInvoice {
public struct BotPaymentInvoice : Equatable {
public let isTest: Bool
public let requestedFields: BotPaymentInvoiceFields
public let currency: String
public let prices: [BotPaymentPrice]
}
public struct BotPaymentNativeProvider {
public struct BotPaymentNativeProvider : Equatable {
public let name: String
public let params: String
}
@ -62,28 +62,6 @@ public struct BotPaymentShippingAddress: Equatable {
self.countryIso2 = countryIso2
self.postCode = postCode
}
public static func ==(lhs: BotPaymentShippingAddress, rhs: BotPaymentShippingAddress) -> Bool {
if lhs.streetLine1 != rhs.streetLine1 {
return false
}
if lhs.streetLine2 != rhs.streetLine2 {
return false
}
if lhs.city != rhs.city {
return false
}
if lhs.state != rhs.state {
return false
}
if lhs.countryIso2 != rhs.countryIso2 {
return false
}
if lhs.postCode != rhs.postCode {
return false
}
return true
}
}
public struct BotPaymentRequestedInfo: Equatable {
@ -98,22 +76,6 @@ public struct BotPaymentRequestedInfo: Equatable {
self.email = email
self.shippingAddress = shippingAddress
}
public static func ==(lhs: BotPaymentRequestedInfo, rhs: BotPaymentRequestedInfo) -> Bool {
if lhs.name != rhs.name {
return false
}
if lhs.phone != rhs.phone {
return false
}
if lhs.email != rhs.email {
return false
}
if lhs.shippingAddress != rhs.shippingAddress {
return false
}
return true
}
}
public enum BotPaymentSavedCredentials: Equatable {
@ -131,7 +93,7 @@ public enum BotPaymentSavedCredentials: Equatable {
}
}
public struct BotPaymentForm {
public struct BotPaymentForm : Equatable {
public let canSaveCredentials: Bool
public let passwordMissing: Bool
public let invoice: BotPaymentInvoice
@ -243,13 +205,13 @@ public enum ValidateBotPaymentFormError {
case phoneInvalid
}
public struct BotPaymentShippingOption {
public struct BotPaymentShippingOption : Equatable {
public let id: String
public let title: String
public let prices: [BotPaymentPrice]
}
public struct BotPaymentValidatedFormInfo {
public struct BotPaymentValidatedFormInfo : Equatable {
public let id: String?
public let shippingOptions: [BotPaymentShippingOption]?
}
@ -379,7 +341,7 @@ public func sendBotPaymentForm(account: Account, messageId: MessageId, validated
}
}
public struct BotPaymentReceipt {
public struct BotPaymentReceipt : Equatable {
public let invoice: BotPaymentInvoice
public let info: BotPaymentRequestedInfo?
public let shippingOption: BotPaymentShippingOption?

View File

@ -325,7 +325,7 @@ class Download: NSObject, MTRequestMessageServiceDelegate {
}
}
func rawRequest(_ data: (FunctionDescription, Buffer, (Buffer) -> Any?)) -> Signal<Any, MTRpcError> {
func rawRequest(_ data: (FunctionDescription, Buffer, (Buffer) -> Any?), automaticFloodWait: Bool = true) -> Signal<Any, MTRpcError> {
let requestService = self.requestService
return Signal { subscriber in
let request = MTRequest()
@ -340,6 +340,12 @@ class Download: NSObject, MTRequestMessageServiceDelegate {
request.dependsOnPasswordEntry = false
request.shouldContinueExecutionWithErrorContext = { errorContext in
guard let errorContext = errorContext else {
return true
}
if errorContext.floodWaitSeconds > 0 && !automaticFloodWait {
return false
}
return true
}

View File

@ -9,17 +9,26 @@ public struct GroupCallInfo: Equatable {
public var accessHash: Int64
public var participantCount: Int
public var clientParams: String?
public var streamDcId: Int32?
public var title: String?
public var recordingStartTimestamp: Int32?
public init(
id: Int64,
accessHash: Int64,
participantCount: Int,
clientParams: String?
clientParams: String?,
streamDcId: Int32?,
title: String?,
recordingStartTimestamp: Int32?
) {
self.id = id
self.accessHash = accessHash
self.participantCount = participantCount
self.clientParams = clientParams
self.streamDcId = streamDcId
self.title = title
self.recordingStartTimestamp = recordingStartTimestamp
}
}
@ -31,7 +40,7 @@ public struct GroupCallSummary: Equatable {
extension GroupCallInfo {
init?(_ call: Api.GroupCall) {
switch call {
case let .groupCall(_, id, accessHash, participantCount, params, _):
case let .groupCall(_, id, accessHash, participantCount, params, title, recordStartDate, streamDcId, _):
var clientParams: String?
if let params = params {
switch params {
@ -43,7 +52,10 @@ extension GroupCallInfo {
id: id,
accessHash: accessHash,
participantCount: Int(participantCount),
clientParams: clientParams
clientParams: clientParams,
streamDcId: streamDcId,
title: title,
recordingStartTimestamp: recordStartDate
)
case .groupCallDiscarded:
return nil
@ -62,7 +74,7 @@ public func getCurrentGroupCall(account: Account, callId: Int64, accessHash: Int
}
|> mapToSignal { result -> Signal<GroupCallSummary?, GetCurrentGroupCallError> in
switch result {
case let .groupCall(call, participants, _, users):
case let .groupCall(call, participants, _, chats, users):
return account.postbox.transaction { transaction -> GroupCallSummary? in
guard let info = GroupCallInfo(call) else {
return nil
@ -79,6 +91,12 @@ public func getCurrentGroupCall(account: Account, callId: Int64, accessHash: Int
}
}
for chat in chats {
if let peer = parseTelegramGroupOrChannel(chat: chat) {
peers.append(peer)
}
}
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
return updated
})
@ -88,8 +106,18 @@ public func getCurrentGroupCall(account: Account, callId: Int64, accessHash: Int
loop: for participant in participants {
switch participant {
case let .groupCallParticipant(flags, userId, date, activeDate, source, volume):
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about):
let peerId: PeerId
switch apiPeerId {
case let .peerUser(userId):
peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
case let .peerChat(chatId):
peerId = PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId)
case let .peerChannel(channelId):
peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)
}
let ssrc = UInt32(bitPattern: source)
guard let peer = transaction.getPeer(peerId) else {
continue loop
@ -118,7 +146,8 @@ public func getCurrentGroupCall(account: Account, callId: Int64, accessHash: Int
activityTimestamp: activeDate.flatMap(Double.init),
activityRank: nil,
muteState: muteState,
volume: volume
volume: volume,
about: about
))
}
}
@ -139,17 +168,26 @@ public enum CreateGroupCallError {
case anonymousNotAllowed
}
public func createGroupCall(account: Account, peerId: PeerId) -> Signal<GroupCallInfo, CreateGroupCallError> {
return account.postbox.transaction { transaction -> Api.InputPeer? in
return transaction.getPeer(peerId).flatMap(apiInputPeer)
public func createGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?) -> Signal<GroupCallInfo, CreateGroupCallError> {
return account.postbox.transaction { transaction -> (Api.InputPeer?, Api.InputPeer?) in
let callPeer = transaction.getPeer(peerId).flatMap(apiInputPeer)
if let joinAs = joinAs {
return (callPeer, transaction.getPeer(joinAs).flatMap(apiInputPeer))
} else {
return (callPeer, nil)
}
}
|> castError(CreateGroupCallError.self)
|> mapToSignal { inputPeer -> Signal<GroupCallInfo, CreateGroupCallError> in
|> mapToSignal { (inputPeer, inputJoinAs) -> Signal<GroupCallInfo, CreateGroupCallError> in
guard let inputPeer = inputPeer else {
return .fail(.generic)
}
return account.network.request(Api.functions.phone.createGroupCall(peer: inputPeer, randomId: Int32.random(in: Int32.min ... Int32.max)))
var flags: Int32 = 0
if let _ = inputJoinAs {
flags |= (1 << 0)
}
return account.network.request(Api.functions.phone.createGroupCall(flags: flags, peer: inputPeer, joinAs: inputJoinAs, randomId: Int32.random(in: Int32.min ... Int32.max)))
|> mapError { error -> CreateGroupCallError in
if error.errorDescription == "ANONYMOUS_CALLS_DISABLED" {
return .anonymousNotAllowed
@ -209,7 +247,7 @@ public func getGroupCallParticipants(account: Account, callId: Int64, accessHash
let nextParticipantsFetchOffset: String?
switch result {
case let .groupParticipants(count, participants, nextOffset, users, apiVersion):
case let .groupParticipants(count, participants, nextOffset, chats, users, apiVersion):
totalCount = Int(count)
version = apiVersion
@ -230,6 +268,12 @@ public func getGroupCallParticipants(account: Account, callId: Int64, accessHash
}
}
for chat in chats {
if let peer = parseTelegramGroupOrChannel(chat: chat) {
peers.append(peer)
}
}
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
return updated
})
@ -237,8 +281,17 @@ public func getGroupCallParticipants(account: Account, callId: Int64, accessHash
loop: for participant in participants {
switch participant {
case let .groupCallParticipant(flags, userId, date, activeDate, source, volume):
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about):
let peerId: PeerId
switch apiPeerId {
case let .peerUser(userId):
peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
case let .peerChat(chatId):
peerId = PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId)
case let .peerChannel(channelId):
peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)
}
let ssrc = UInt32(bitPattern: source)
guard let peer = transaction.getPeer(peerId) else {
continue loop
@ -267,7 +320,8 @@ public func getGroupCallParticipants(account: Account, callId: Int64, accessHash
activityTimestamp: activeDate.flatMap(Double.init),
activityRank: nil,
muteState: muteState,
volume: volume
volume: volume,
about: about
))
}
}
@ -281,6 +335,8 @@ public func getGroupCallParticipants(account: Account, callId: Int64, accessHash
adminIds: Set(),
isCreator: false,
defaultParticipantsAreMuted: GroupCallParticipantsContext.State.DefaultParticipantsAreMuted(isMuted: false, canChange: false),
recordingStartTimestamp: nil,
title: nil,
totalCount: totalCount,
version: version
)
@ -300,184 +356,210 @@ public struct JoinGroupCallResult {
public var state: GroupCallParticipantsContext.State
}
public func joinGroupCall(account: Account, peerId: PeerId, callId: Int64, accessHash: Int64, preferMuted: Bool, joinPayload: String) -> Signal<JoinGroupCallResult, JoinGroupCallError> {
var flags: Int32 = 0
if preferMuted {
flags |= (1 << 0)
}
return account.network.request(Api.functions.phone.joinGroupCall(flags: flags, call: .inputGroupCall(id: callId, accessHash: accessHash), params: .dataJSON(data: joinPayload)))
|> mapError { error -> JoinGroupCallError in
if error.errorDescription == "GROUPCALL_ANONYMOUS_FORBIDDEN" {
return .anonymousNotAllowed
} else if error.errorDescription == "GROUPCALL_PARTICIPANTS_TOO_MUCH" {
return .tooManyParticipants
}
return .generic
}
|> mapToSignal { updates -> Signal<JoinGroupCallResult, JoinGroupCallError> in
let admins: Signal<(Set<PeerId>, [Api.User]), JoinGroupCallError>
if peerId.namespace == Namespaces.Peer.CloudChannel {
admins = account.postbox.transaction { transaction -> Api.InputChannel? in
return transaction.getPeer(peerId).flatMap(apiInputChannel)
}
|> castError(JoinGroupCallError.self)
|> mapToSignal { inputChannel -> Signal<Api.channels.ChannelParticipants, JoinGroupCallError> in
guard let inputChannel = inputChannel else {
return .fail(.generic)
}
return account.network.request(Api.functions.channels.getParticipants(channel: inputChannel, filter: .channelParticipantsAdmins, offset: 0, limit: 100, hash: 0))
|> mapError { _ -> JoinGroupCallError in
return .generic
}
}
|> map { admins -> (Set<PeerId>, [Api.User]) in
var adminIds = Set<PeerId>()
var apiUsers: [Api.User] = []
switch admins {
case let .channelParticipants(_, participants, users):
apiUsers.append(contentsOf: users)
for participant in participants {
let parsedParticipant = ChannelParticipant(apiParticipant: participant)
switch parsedParticipant {
case .creator:
adminIds.insert(parsedParticipant.peerId)
case let .member(_, _, adminInfo, _, _):
if let adminInfo = adminInfo, adminInfo.rights.rights.contains(.canManageCalls) {
adminIds.insert(parsedParticipant.peerId)
}
}
}
default:
break
}
return (adminIds, apiUsers)
}
} else if peerId.namespace == Namespaces.Peer.CloudGroup {
admins = account.postbox.transaction { transaction -> (Set<PeerId>, [Api.User]) in
var result = Set<PeerId>()
if let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedGroupData {
if let participants = cachedData.participants {
for participant in participants.participants {
if case .creator = participant {
result.insert(participant.peerId)
} else if case .admin = participant {
result.insert(participant.peerId)
}
}
}
}
return (result, [])
}
|> castError(JoinGroupCallError.self)
public func joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?, callId: Int64, accessHash: Int64, preferMuted: Bool, joinPayload: String) -> Signal<JoinGroupCallResult, JoinGroupCallError> {
return account.postbox.transaction { transaction -> Api.InputPeer? in
if let joinAs = joinAs {
return transaction.getPeer(joinAs).flatMap(apiInputPeer)
} else {
admins = .fail(.generic)
return .inputPeerSelf
}
}
|> castError(JoinGroupCallError.self)
|> mapToSignal { inputJoinAs in
var flags: Int32 = 0
if preferMuted {
flags |= (1 << 0)
}
let peer = account.postbox.transaction { transaction -> Peer? in
return transaction.getPeer(peerId)
if let _ = inputJoinAs {
flags |= (1 << 1)
}
|> castError(JoinGroupCallError.self)
return combineLatest(
account.network.request(Api.functions.phone.getGroupCall(call: .inputGroupCall(id: callId, accessHash: accessHash)))
|> mapError { _ -> JoinGroupCallError in
return .generic
},
getGroupCallParticipants(account: account, callId: callId, accessHash: accessHash, offset: "", ssrcs: [], limit: 100)
|> mapError { _ -> JoinGroupCallError in
return .generic
},
admins,
peer
)
|> mapToSignal { result, state, admins, peer -> Signal<JoinGroupCallResult, JoinGroupCallError> in
guard let peer = peer else {
return .fail(.generic)
return account.network.request(Api.functions.phone.joinGroupCall(flags: flags, call: .inputGroupCall(id: callId, accessHash: accessHash), joinAs: inputJoinAs, params: .dataJSON(data: joinPayload)))
|> mapError { error -> JoinGroupCallError in
if error.errorDescription == "GROUPCALL_ANONYMOUS_FORBIDDEN" {
return .anonymousNotAllowed
} else if error.errorDescription == "GROUPCALL_PARTICIPANTS_TOO_MUCH" {
return .tooManyParticipants
}
var state = state
if let channel = peer as? TelegramChannel {
state.isCreator = channel.flags.contains(.isCreator)
} else if let group = peer as? TelegramGroup {
if case .creator = group.role {
state.isCreator = true
} else {
state.isCreator = false
return .generic
}
|> mapToSignal { updates -> Signal<JoinGroupCallResult, JoinGroupCallError> in
let admins: Signal<(Set<PeerId>, [Api.User]), JoinGroupCallError>
if peerId.namespace == Namespaces.Peer.CloudChannel {
admins = account.postbox.transaction { transaction -> Api.InputChannel? in
return transaction.getPeer(peerId).flatMap(apiInputChannel)
}
}
account.stateManager.addUpdates(updates)
var maybeParsedCall: GroupCallInfo?
loop: for update in updates.allUpdates {
switch update {
case let .updateGroupCall(_, call):
maybeParsedCall = GroupCallInfo(call)
|> castError(JoinGroupCallError.self)
|> mapToSignal { inputChannel -> Signal<Api.channels.ChannelParticipants, JoinGroupCallError> in
guard let inputChannel = inputChannel else {
return .fail(.generic)
}
switch call {
case let .groupCall(flags, _, _, _, _, _):
let isMuted = (flags & (1 << 1)) != 0
let canChange = (flags & (1 << 2)) != 0
state.defaultParticipantsAreMuted = GroupCallParticipantsContext.State.DefaultParticipantsAreMuted(isMuted: isMuted, canChange: canChange)
return account.network.request(Api.functions.channels.getParticipants(channel: inputChannel, filter: .channelParticipantsAdmins, offset: 0, limit: 100, hash: 0))
|> mapError { _ -> JoinGroupCallError in
return .generic
}
}
|> map { admins -> (Set<PeerId>, [Api.User]) in
var adminIds = Set<PeerId>()
var apiUsers: [Api.User] = []
switch admins {
case let .channelParticipants(_, participants, users):
apiUsers.append(contentsOf: users)
for participant in participants {
let parsedParticipant = ChannelParticipant(apiParticipant: participant)
switch parsedParticipant {
case .creator:
adminIds.insert(parsedParticipant.peerId)
case let .member(_, _, adminInfo, _, _):
if let adminInfo = adminInfo, adminInfo.rights.rights.contains(.canManageCalls) {
adminIds.insert(parsedParticipant.peerId)
}
}
}
default:
break
}
break loop
default:
break
return (adminIds, apiUsers)
}
} else if peerId.namespace == Namespaces.Peer.CloudGroup {
admins = account.postbox.transaction { transaction -> (Set<PeerId>, [Api.User]) in
var result = Set<PeerId>()
if let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedGroupData {
if let participants = cachedData.participants {
for participant in participants.participants {
if case .creator = participant {
result.insert(participant.peerId)
} else if case .admin = participant {
result.insert(participant.peerId)
}
}
}
}
return (result, [])
}
|> castError(JoinGroupCallError.self)
} else {
admins = .fail(.generic)
}
guard let parsedCall = maybeParsedCall else {
return .fail(.generic)
let peer = account.postbox.transaction { transaction -> Peer? in
return transaction.getPeer(peerId)
}
|> castError(JoinGroupCallError.self)
var apiUsers: [Api.User] = []
let (adminIds, adminUsers) = admins
apiUsers.append(contentsOf: adminUsers)
state.adminIds = adminIds
switch result {
case let .groupCall(call, _, _, users):
guard let _ = GroupCallInfo(call) else {
return combineLatest(
account.network.request(Api.functions.phone.getGroupCall(call: .inputGroupCall(id: callId, accessHash: accessHash)))
|> mapError { _ -> JoinGroupCallError in
return .generic
},
getGroupCallParticipants(account: account, callId: callId, accessHash: accessHash, offset: "", ssrcs: [], limit: 100)
|> mapError { _ -> JoinGroupCallError in
return .generic
},
admins,
peer
)
|> mapToSignal { result, state, admins, peer -> Signal<JoinGroupCallResult, JoinGroupCallError> in
guard let peer = peer else {
return .fail(.generic)
}
apiUsers.append(contentsOf: users)
var peers: [Peer] = []
var peerPresences: [PeerId: PeerPresence] = [:]
for user in apiUsers {
let telegramUser = TelegramUser(user: user)
peers.append(telegramUser)
if let presence = TelegramUserPresence(apiUser: user) {
peerPresences[telegramUser.id] = presence
var state = state
if let channel = peer as? TelegramChannel {
state.isCreator = channel.flags.contains(.isCreator)
} else if let group = peer as? TelegramGroup {
if case .creator = group.role {
state.isCreator = true
} else {
state.isCreator = false
}
}
return account.postbox.transaction { transaction -> JoinGroupCallResult in
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
return updated
})
updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences)
return JoinGroupCallResult(
callInfo: parsedCall,
state: state
)
account.stateManager.addUpdates(updates)
var maybeParsedCall: GroupCallInfo?
loop: for update in updates.allUpdates {
switch update {
case let .updateGroupCall(_, call):
maybeParsedCall = GroupCallInfo(call)
switch call {
case let .groupCall(flags, _, _, _, _, title, streamDcId, recordStartDate, _):
let isMuted = (flags & (1 << 1)) != 0
let canChange = (flags & (1 << 2)) != 0
state.defaultParticipantsAreMuted = GroupCallParticipantsContext.State.DefaultParticipantsAreMuted(isMuted: isMuted, canChange: canChange)
state.title = title
state.recordingStartTimestamp = recordStartDate
default:
break
}
break loop
default:
break
}
}
guard let parsedCall = maybeParsedCall else {
return .fail(.generic)
}
var apiUsers: [Api.User] = []
let (adminIds, adminUsers) = admins
apiUsers.append(contentsOf: adminUsers)
state.adminIds = adminIds
switch result {
case let .groupCall(call, _, _, chats, users):
guard let _ = GroupCallInfo(call) else {
return .fail(.generic)
}
apiUsers.append(contentsOf: users)
var peers: [Peer] = []
var peerPresences: [PeerId: PeerPresence] = [:]
for user in apiUsers {
let telegramUser = TelegramUser(user: user)
peers.append(telegramUser)
if let presence = TelegramUserPresence(apiUser: user) {
peerPresences[telegramUser.id] = presence
}
}
for chat in chats {
if let peer = parseTelegramGroupOrChannel(chat: chat) {
peers.append(peer)
}
}
return account.postbox.transaction { transaction -> JoinGroupCallResult in
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
return updated
})
updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences)
return JoinGroupCallResult(
callInfo: parsedCall,
state: state
)
}
|> castError(JoinGroupCallError.self)
}
|> castError(JoinGroupCallError.self)
}
}
}
}
public enum LeaveGroupCallError {
@ -598,7 +680,7 @@ public final class GroupCallParticipantsContext {
public var activityRank: Int?
public var muteState: MuteState?
public var volume: Int32?
public var about: String?
public init(
peer: Peer,
ssrc: UInt32,
@ -607,7 +689,8 @@ public final class GroupCallParticipantsContext {
activityTimestamp: Double?,
activityRank: Int?,
muteState: MuteState?,
volume: Int32?
volume: Int32?,
about: String?
) {
self.peer = peer
self.ssrc = ssrc
@ -617,6 +700,7 @@ public final class GroupCallParticipantsContext {
self.activityRank = activityRank
self.muteState = muteState
self.volume = volume
self.about = about
}
public static func ==(lhs: Participant, rhs: Participant) -> Bool {
@ -641,6 +725,9 @@ public final class GroupCallParticipantsContext {
if lhs.volume != rhs.volume {
return false
}
if lhs.about != rhs.about {
return false
}
return true
}
@ -684,6 +771,8 @@ public final class GroupCallParticipantsContext {
public var adminIds: Set<PeerId>
public var isCreator: Bool
public var defaultParticipantsAreMuted: DefaultParticipantsAreMuted
public var recordingStartTimestamp: Int32?
public var title: String?
public var totalCount: Int
public var version: Int32
}
@ -740,7 +829,7 @@ public final class GroupCallParticipantsContext {
public var muteState: Participant.MuteState?
public var participationStatusChange: ParticipationStatusChange
public var volume: Int32?
public var about: String?
init(
peerId: PeerId,
ssrc: UInt32,
@ -749,7 +838,8 @@ public final class GroupCallParticipantsContext {
activityTimestamp: Double?,
muteState: Participant.MuteState?,
participationStatusChange: ParticipationStatusChange,
volume: Int32?
volume: Int32?,
about: String?
) {
self.peerId = peerId
self.ssrc = ssrc
@ -759,6 +849,7 @@ public final class GroupCallParticipantsContext {
self.muteState = muteState
self.participationStatusChange = participationStatusChange
self.volume = volume
self.about = about
}
}
@ -769,7 +860,7 @@ public final class GroupCallParticipantsContext {
}
case state(update: StateUpdate)
case call(isTerminated: Bool, defaultParticipantsAreMuted: State.DefaultParticipantsAreMuted)
case call(isTerminated: Bool, defaultParticipantsAreMuted: State.DefaultParticipantsAreMuted, title: String?, recordingStartTimestamp: Int32?)
}
public final class MemberEvent {
@ -846,6 +937,7 @@ public final class GroupCallParticipantsContext {
private var activityRankResetTimer: SwiftSignalKit.Timer?
private let updateDefaultMuteDisposable = MetaDisposable()
private let updateShouldBeRecordingDisposable = MetaDisposable()
public init(account: Account, peerId: PeerId, id: Int64, accessHash: Int64, state: State) {
self.account = account
@ -926,6 +1018,8 @@ public final class GroupCallParticipantsContext {
adminIds: strongSelf.stateValue.state.adminIds,
isCreator: strongSelf.stateValue.state.isCreator,
defaultParticipantsAreMuted: strongSelf.stateValue.state.defaultParticipantsAreMuted,
recordingStartTimestamp: strongSelf.stateValue.state.recordingStartTimestamp,
title: strongSelf.stateValue.state.title,
totalCount: strongSelf.stateValue.state.totalCount,
version: strongSelf.stateValue.state.version
),
@ -971,6 +1065,7 @@ public final class GroupCallParticipantsContext {
self.updatesDisposable.dispose()
self.activitiesDisposable?.dispose()
self.updateDefaultMuteDisposable.dispose()
self.updateShouldBeRecordingDisposable.dispose()
self.activityRankResetTimer?.invalidate()
}
@ -979,8 +1074,13 @@ public final class GroupCallParticipantsContext {
for update in updates {
if case let .state(update) = update {
stateUpdates.append(update)
} else if case let .call(_, defaultParticipantsAreMuted) = update {
self.stateValue.state.defaultParticipantsAreMuted = defaultParticipantsAreMuted
} else if case let .call(_, defaultParticipantsAreMuted, title, recordingStartTimestamp) = update {
var state = self.stateValue.state
state.defaultParticipantsAreMuted = defaultParticipantsAreMuted
state.recordingStartTimestamp = recordingStartTimestamp
state.title = title
self.stateValue.state = state
}
}
@ -1050,6 +1150,8 @@ public final class GroupCallParticipantsContext {
adminIds: strongSelf.stateValue.state.adminIds,
isCreator: strongSelf.stateValue.state.isCreator,
defaultParticipantsAreMuted: strongSelf.stateValue.state.defaultParticipantsAreMuted,
recordingStartTimestamp: strongSelf.stateValue.state.recordingStartTimestamp,
title: strongSelf.stateValue.state.title,
totalCount: strongSelf.stateValue.state.totalCount,
version: strongSelf.stateValue.state.version
),
@ -1213,7 +1315,8 @@ public final class GroupCallParticipantsContext {
activityTimestamp: activityTimestamp,
activityRank: previousActivityRank,
muteState: participantUpdate.muteState,
volume: participantUpdate.volume
volume: participantUpdate.volume,
about: participantUpdate.about
)
updatedParticipants.append(participant)
}
@ -1230,6 +1333,8 @@ public final class GroupCallParticipantsContext {
let adminIds = strongSelf.stateValue.state.adminIds
let isCreator = strongSelf.stateValue.state.isCreator
let defaultParticipantsAreMuted = strongSelf.stateValue.state.defaultParticipantsAreMuted
let recordingStartTimestamp = strongSelf.stateValue.state.recordingStartTimestamp
let title = strongSelf.stateValue.state.title
updatedParticipants.sort()
@ -1240,6 +1345,8 @@ public final class GroupCallParticipantsContext {
adminIds: adminIds,
isCreator: isCreator,
defaultParticipantsAreMuted: defaultParticipantsAreMuted,
recordingStartTimestamp: recordingStartTimestamp,
title: title,
totalCount: updatedTotalCount,
version: update.version
),
@ -1300,11 +1407,11 @@ public final class GroupCallParticipantsContext {
let id = self.id
let accessHash = self.accessHash
let signal: Signal<Api.Updates?, NoError> = self.account.postbox.transaction { transaction -> Api.InputUser? in
return transaction.getPeer(peerId).flatMap(apiInputUser)
let signal: Signal<Api.Updates?, NoError> = self.account.postbox.transaction { transaction -> Api.InputPeer? in
return transaction.getPeer(peerId).flatMap(apiInputPeer)
}
|> mapToSignal { inputUser -> Signal<Api.Updates?, NoError> in
guard let inputUser = inputUser else {
|> mapToSignal { inputPeer -> Signal<Api.Updates?, NoError> in
guard let inputPeer = inputPeer else {
return .single(nil)
}
var flags: Int32 = 0
@ -1314,8 +1421,8 @@ public final class GroupCallParticipantsContext {
if let muteState = muteState, (!muteState.canUnmute || peerId == account.peerId || muteState.mutedByYou) {
flags |= 1 << 0
}
return account.network.request(Api.functions.phone.editGroupCallMember(flags: flags, call: .inputGroupCall(id: id, accessHash: accessHash), userId: inputUser, volume: volume))
return account.network.request(Api.functions.phone.editGroupCallParticipant(flags: flags, call: .inputGroupCall(id: id, accessHash: accessHash), participant: inputPeer, volume: volume))
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
return .single(nil)
@ -1355,6 +1462,16 @@ public final class GroupCallParticipantsContext {
}))
}
public func updateShouldBeRecording(_ shouldBeRecording: Bool) {
self.updateShouldBeRecordingDisposable.set((self.account.network.request(Api.functions.phone.toggleGroupCallRecord(call: .inputGroupCall(id: self.id, accessHash: self.accessHash), start: shouldBeRecording ? .boolTrue : .boolFalse))
|> deliverOnMainQueue).start(next: { [weak self] updates in
guard let strongSelf = self else {
return
}
strongSelf.account.stateManager.addUpdates(updates)
}))
}
public func updateDefaultParticipantsAreMuted(isMuted: Bool) {
if isMuted == self.stateValue.state.defaultParticipantsAreMuted.isMuted {
return
@ -1407,8 +1524,16 @@ public final class GroupCallParticipantsContext {
extension GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate {
init(_ apiParticipant: Api.GroupCallParticipant) {
switch apiParticipant {
case let .groupCallParticipant(flags, userId, date, activeDate, source, volume):
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about):
let peerId: PeerId
switch apiPeerId {
case let .peerUser(userId):
peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
case let .peerChat(chatId):
peerId = PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId)
case let .peerChannel(channelId):
peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)
}
let ssrc = UInt32(bitPattern: source)
let muted = (flags & (1 << 0)) != 0
let mutedByYou = (flags & (1 << 9)) != 0
@ -1447,7 +1572,8 @@ extension GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate {
activityTimestamp: activeDate.flatMap(Double.init),
muteState: muteState,
participationStatusChange: participationStatusChange,
volume: volume
volume: volume,
about: about
)
}
}
@ -1458,8 +1584,16 @@ extension GroupCallParticipantsContext.Update.StateUpdate {
var participantUpdates: [GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate] = []
for participant in participants {
switch participant {
case let .groupCallParticipant(flags, userId, date, activeDate, source, volume):
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
case let .groupCallParticipant(flags, apiPeerId, date, activeDate, source, volume, about):
let peerId: PeerId
switch apiPeerId {
case let .peerUser(userId):
peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
case let .peerChat(chatId):
peerId = PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId)
case let .peerChannel(channelId):
peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)
}
let ssrc = UInt32(bitPattern: source)
let muted = (flags & (1 << 0)) != 0
let mutedByYou = (flags & (1 << 9)) != 0
@ -1498,7 +1632,8 @@ extension GroupCallParticipantsContext.Update.StateUpdate {
activityTimestamp: activeDate.flatMap(Double.init),
muteState: muteState,
participationStatusChange: participationStatusChange,
volume: volume
volume: volume,
about: about
))
}
}
@ -1540,6 +1675,20 @@ public func inviteToGroupCall(account: Account, callId: Int64, accessHash: Int64
}
}
public enum EditGroupCallTitleError {
case generic
}
public func editGroupCallTitle(account: Account, callId: Int64, accessHash: Int64, title: String) -> Signal<Never, EditGroupCallTitleError> {
return account.network.request(Api.functions.phone.editGroupCallTitle(call: .inputGroupCall(id: callId, accessHash: accessHash), title: title)) |> mapError { _ -> EditGroupCallTitleError in
return .generic
}
|> mapToSignal { result -> Signal<Never, EditGroupCallTitleError> in
account.stateManager.addUpdates(result)
return .complete()
}
}
public func updatedCurrentPeerGroupCall(account: Account, peerId: PeerId) -> Signal<CachedChannelData.ActiveCall?, NoError> {
return fetchAndUpdateCachedPeerData(accountPeerId: account.peerId, peerId: peerId, network: account.network, postbox: account.postbox)
|> mapToSignal { _ -> Signal<CachedChannelData.ActiveCall?, NoError> in
@ -1568,3 +1717,22 @@ private func mergeAndSortParticipants(current currentParticipants: [GroupCallPar
return mergedParticipants
}
public func getAudioBroadcastPart(account: Account, callId: Int64, accessHash: Int64, datacenterId: Int?, timestampId: Int32) -> Signal<Data?, NoError> {
return account.network.multiplexedRequestManager.request(to: .main(account.network.datacenterId), consumerId: Int64.random(in: 0 ..< Int64.max), data: Api.functions.upload.getFile(flags: 0, location: .inputGroupCallStream(call: .inputGroupCall(id: callId, accessHash: accessHash), date: timestampId), offset: 0, limit: 128 * 1024), tag: nil, continueInBackground: false, automaticFloodWait: false)
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.upload.File?, NoError> in
return .single(nil)
}
|> map { result -> Data? in
guard let result = result else {
return nil
}
switch result {
case let .file(_, _, bytes):
return bytes.makeData()
case .fileCdnRedirect:
return nil
}
}
}

View File

@ -33,7 +33,7 @@ private final class MessageMediaPreuploadManagerContext {
assert(self.queue.isCurrent())
}
func add(network: Network, postbox: Postbox, id: Int64, encrypt: Bool, tag: MediaResourceFetchTag?, source: Signal<MediaResourceData, NoError>) {
func add(network: Network, postbox: Postbox, id: Int64, encrypt: Bool, tag: MediaResourceFetchTag?, source: Signal<MediaResourceData, NoError>, onComplete:(()->Void)? = nil) {
let context = MessageMediaPreuploadManagerUploadContext()
self.uploadContexts[id] = context
let queue = self.queue
@ -47,6 +47,7 @@ private final class MessageMediaPreuploadManagerContext {
default:
print("result")
context.result = next
onComplete?()
}
for subscriber in context.subscribers.copyItems() {
subscriber(next)
@ -112,9 +113,9 @@ public final class MessageMediaPreuploadManager {
})
}
public func add(network: Network, postbox: Postbox, id: Int64, encrypt: Bool, tag: MediaResourceFetchTag?, source: Signal<MediaResourceData, NoError>) {
public func add(network: Network, postbox: Postbox, id: Int64, encrypt: Bool, tag: MediaResourceFetchTag?, source: Signal<MediaResourceData, NoError>, onComplete:(()->Void)? = nil) {
self.impl.with { context in
context.add(network: network, postbox: postbox, id: id, encrypt: encrypt, tag: tag, source: source)
context.add(network: network, postbox: postbox, id: id, encrypt: encrypt, tag: tag, source: source, onComplete: onComplete)
}
}

View File

@ -22,17 +22,19 @@ private final class RequestData {
let payload: Buffer
let tag: MediaResourceFetchTag?
let continueInBackground: Bool
let automaticFloodWait: Bool
let deserializeResponse: (Buffer) -> Any?
let completed: (Any) -> Void
let error: (MTRpcError) -> Void
init(id: Int32, consumerId: Int64, target: MultiplexedRequestTarget, functionDescription: FunctionDescription, payload: Buffer, tag: MediaResourceFetchTag?, continueInBackground: Bool, deserializeResponse: @escaping (Buffer) -> Any?, completed: @escaping (Any) -> Void, error: @escaping (MTRpcError) -> Void) {
init(id: Int32, consumerId: Int64, target: MultiplexedRequestTarget, functionDescription: FunctionDescription, payload: Buffer, tag: MediaResourceFetchTag?, continueInBackground: Bool, automaticFloodWait: Bool, deserializeResponse: @escaping (Buffer) -> Any?, completed: @escaping (Any) -> Void, error: @escaping (MTRpcError) -> Void) {
self.id = id
self.consumerId = consumerId
self.target = target
self.functionDescription = functionDescription
self.tag = tag
self.continueInBackground = continueInBackground
self.automaticFloodWait = automaticFloodWait
self.payload = payload
self.deserializeResponse = deserializeResponse
self.completed = completed
@ -98,12 +100,12 @@ private final class MultiplexedRequestManagerContext {
}
}
func request(to target: MultiplexedRequestTarget, consumerId: Int64, data: (FunctionDescription, Buffer, (Buffer) -> Any?), tag: MediaResourceFetchTag?, continueInBackground: Bool, completed: @escaping (Any) -> Void, error: @escaping (MTRpcError) -> Void) -> Disposable {
func request(to target: MultiplexedRequestTarget, consumerId: Int64, data: (FunctionDescription, Buffer, (Buffer) -> Any?), tag: MediaResourceFetchTag?, continueInBackground: Bool, automaticFloodWait: Bool, completed: @escaping (Any) -> Void, error: @escaping (MTRpcError) -> Void) -> Disposable {
let targetKey = MultiplexedRequestTargetKey(target: target, continueInBackground: continueInBackground)
let requestId = self.nextId
self.nextId += 1
self.queuedRequests.append(RequestData(id: requestId, consumerId: consumerId, target: target, functionDescription: data.0, payload: data.1, tag: tag, continueInBackground: continueInBackground, deserializeResponse: { buffer in
self.queuedRequests.append(RequestData(id: requestId, consumerId: consumerId, target: target, functionDescription: data.0, payload: data.1, tag: tag, continueInBackground: continueInBackground, automaticFloodWait: automaticFloodWait, deserializeResponse: { buffer in
return data.2(buffer)
}, completed: { result in
completed(result)
@ -178,7 +180,7 @@ private final class MultiplexedRequestManagerContext {
let requestId = request.id
selectedContext.requests.append(ExecutingRequestData(requestId: requestId, disposable: disposable))
let queue = self.queue
disposable.set(selectedContext.worker.rawRequest((request.functionDescription, request.payload, request.deserializeResponse)).start(next: { [weak self, weak selectedContext] result in
disposable.set(selectedContext.worker.rawRequest((request.functionDescription, request.payload, request.deserializeResponse), automaticFloodWait: request.automaticFloodWait).start(next: { [weak self, weak selectedContext] result in
queue.async {
guard let strongSelf = self else {
return
@ -267,13 +269,13 @@ final class MultiplexedRequestManager {
})
}
func request<T>(to target: MultiplexedRequestTarget, consumerId: Int64, data: (FunctionDescription, Buffer, DeserializeFunctionResponse<T>), tag: MediaResourceFetchTag?, continueInBackground: Bool) -> Signal<T, MTRpcError> {
func request<T>(to target: MultiplexedRequestTarget, consumerId: Int64, data: (FunctionDescription, Buffer, DeserializeFunctionResponse<T>), tag: MediaResourceFetchTag?, continueInBackground: Bool, automaticFloodWait: Bool = true) -> Signal<T, MTRpcError> {
return Signal { subscriber in
let disposable = MetaDisposable()
self.context.with { context in
disposable.set(context.request(to: target, consumerId: consumerId, data: (data.0, data.1, { buffer in
return data.2.parse(buffer)
}), tag: tag, continueInBackground: continueInBackground, completed: { result in
}), tag: tag, continueInBackground: continueInBackground, automaticFloodWait: automaticFloodWait, completed: { result in
if let result = result as? T {
subscriber.putNext(result)
subscriber.putCompletion()

View File

@ -188,14 +188,17 @@ public func requestMessageActionUrlAuth(account: Account, subject: MessageAction
|> castError(MTRpcError.self)
|> mapToSignal { peer -> Signal<Api.UrlAuthResult?, MTRpcError> in
if let inputPeer = apiInputPeer(peer) {
return account.network.request(Api.functions.messages.requestUrlAuth(peer: inputPeer, msgId: messageId.id, buttonId: buttonId))
let flags: Int32 = 1 << 1
return account.network.request(Api.functions.messages.requestUrlAuth(flags: flags, peer: inputPeer, msgId: messageId.id, buttonId: buttonId, url: nil))
|> map(Optional.init)
} else {
return .single(nil)
}
}
case let .url(url):
request = account.network.request(Api.functions.messages.requestUrlAuth(peer: .inputPeerEmpty, msgId: 0, buttonId: 0))
var flags: Int32 = 1 << 1
flags |= (1 << 2)
request = account.network.request(Api.functions.messages.requestUrlAuth(flags: flags, peer: .inputPeerEmpty, msgId: 0, buttonId: 0, url: url))
|> map(Optional.init)
}
@ -232,14 +235,17 @@ public func acceptMessageActionUrlAuth(account: Account, subject: MessageActionU
|> castError(MTRpcError.self)
|> mapToSignal { peer -> Signal<Api.UrlAuthResult?, MTRpcError> in
if let inputPeer = apiInputPeer(peer) {
return account.network.request(Api.functions.messages.acceptUrlAuth(flags: flags, peer: inputPeer, msgId: messageId.id, buttonId: buttonId))
let flags: Int32 = 1 << 1
return account.network.request(Api.functions.messages.acceptUrlAuth(flags: flags, peer: inputPeer, msgId: messageId.id, buttonId: buttonId, url: nil))
|> map(Optional.init)
} else {
return .single(nil)
}
}
case let .url(url):
request = account.network.request(Api.functions.messages.acceptUrlAuth(flags: flags, peer: .inputPeerEmpty, msgId: 0, buttonId: 0))
var flags: Int32 = 1 << 1
flags |= (1 << 2)
request = account.network.request(Api.functions.messages.acceptUrlAuth(flags: flags, peer: .inputPeerEmpty, msgId: 0, buttonId: 0, url: url))
|> map(Optional.init)
}

View File

@ -210,7 +210,7 @@ public class BoxedMessage: NSObject {
public class Serialization: NSObject, MTSerialization {
public func currentLayer() -> UInt {
return 124
return 125
}
public func parseMessage(_ data: Data!) -> Any! {

View File

@ -300,6 +300,8 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
}
}
let groupcallDefaultJoinAs = chatFull.groupcallDefaultJoinAs
transaction.updatePeerCachedData(peerIds: [peerId], update: { _, current in
let previous: CachedGroupData
if let current = current as? CachedGroupData {
@ -318,6 +320,7 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
.withUpdatedInvitedBy(invitedBy)
.withUpdatedPhoto(photo)
.withUpdatedActiveCall(updatedActiveCall)
.withUpdatedCallJoinPeerId(groupcallDefaultJoinAs?.peerId)
})
case .channelFull:
break
@ -355,7 +358,7 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
}
switch fullChat {
case let .channelFull(flags, _, about, participantsCount, adminsCount, kickedCount, bannedCount, _, _, _, _, chatPhoto, _, apiExportedInvite, apiBotInfos, migratedFromChatId, migratedFromMaxId, pinnedMsgId, stickerSet, minAvailableMsgId, folderId, linkedChatId, location, slowmodeSeconds, slowmodeNextSendDate, statsDc, pts, inputCall, ttl, pendingSuggestions):
case let .channelFull(flags, _, about, participantsCount, adminsCount, kickedCount, bannedCount, _, _, _, _, chatPhoto, _, apiExportedInvite, apiBotInfos, migratedFromChatId, migratedFromMaxId, pinnedMsgId, stickerSet, minAvailableMsgId, folderId, linkedChatId, location, slowmodeSeconds, slowmodeNextSendDate, statsDc, pts, inputCall, ttl, pendingSuggestions, groupcallDefaultJoinAs):
var channelFlags = CachedChannelFlags()
if (flags & (1 << 3)) != 0 {
channelFlags.insert(.canDisplayParticipants)
@ -530,6 +533,7 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
.withUpdatedInvitedBy(invitedBy)
.withUpdatedPhoto(photo)
.withUpdatedActiveCall(updatedActiveCall)
.withUpdatedCallJoinPeerId(groupcallDefaultJoinAs?.peerId)
.withUpdatedAutoremoveTimeout(autoremoveTimeout)
.withUpdatedPendingSuggestions(pendingSuggestions ?? [])
})

View File

@ -300,8 +300,8 @@ public final class AccountContextImpl: AccountContext {
}
}
public func joinGroupCall(peerId: PeerId, activeCall: CachedChannelData.ActiveCall) {
let callResult = self.sharedContext.callManager?.joinGroupCall(context: self, peerId: peerId, initialCall: activeCall, endCurrentIfAny: false)
public func joinGroupCall(peerId: PeerId, joinAsPeerId: PeerId?, activeCall: CachedChannelData.ActiveCall) {
let callResult = self.sharedContext.callManager?.joinGroupCall(context: self, peerId: peerId, joinAsPeerId: joinAsPeerId, initialCall: activeCall, endCurrentIfAny: false)
if let callResult = callResult, case let .alreadyInProgress(currentPeerId) = callResult {
if currentPeerId == peerId {
self.sharedContext.navigateToCurrentCall()
@ -323,14 +323,14 @@ public final class AccountContextImpl: AccountContext {
guard let strongSelf = self else {
return
}
let _ = strongSelf.sharedContext.callManager?.joinGroupCall(context: strongSelf, peerId: peer.id, initialCall: activeCall, endCurrentIfAny: true)
let _ = strongSelf.sharedContext.callManager?.joinGroupCall(context: strongSelf, peerId: peer.id, joinAsPeerId: joinAsPeerId, initialCall: activeCall, endCurrentIfAny: true)
})]), on: .root)
} else {
strongSelf.sharedContext.mainWindow?.present(textAlertController(context: strongSelf, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressVoiceChatMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
guard let strongSelf = self else {
return
}
let _ = strongSelf.sharedContext.callManager?.joinGroupCall(context: strongSelf, peerId: peer.id, initialCall: activeCall, endCurrentIfAny: true)
let _ = strongSelf.sharedContext.callManager?.joinGroupCall(context: strongSelf, peerId: peer.id, joinAsPeerId: joinAsPeerId, initialCall: activeCall, endCurrentIfAny: true)
})]), on: .root)
}
} else {

View File

@ -534,7 +534,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
case .groupPhoneCall, .inviteToGroupPhoneCall:
if let activeCall = strongSelf.presentationInterfaceState.activeGroupCallInfo?.activeCall {
strongSelf.context.joinGroupCall(peerId: message.id.peerId, activeCall: CachedChannelData.ActiveCall(id: activeCall.id, accessHash: activeCall.accessHash))
strongSelf.context.joinGroupCall(peerId: message.id.peerId, joinAsPeerId: nil, activeCall: CachedChannelData.ActiveCall(id: activeCall.id, accessHash: activeCall.accessHash))
} else {
var canManageGroupCalls = false
if let channel = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer as? TelegramChannel {
@ -563,12 +563,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
statusController?.dismiss()
}
strongSelf.present(statusController, in: .window(.root))
strongSelf.createVoiceChatDisposable.set((createGroupCall(account: strongSelf.context.account, peerId: message.id.peerId)
strongSelf.createVoiceChatDisposable.set((createGroupCall(account: strongSelf.context.account, peerId: message.id.peerId, joinAs: nil)
|> deliverOnMainQueue).start(next: { [weak self] info in
guard let strongSelf = self else {
return
}
strongSelf.context.joinGroupCall(peerId: message.id.peerId, activeCall: CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash))
strongSelf.context.joinGroupCall(peerId: message.id.peerId, joinAsPeerId: nil, activeCall: CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash))
}, error: { [weak self] error in
dismissStatus?()
@ -6402,7 +6402,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
guard let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else {
return
}
strongSelf.context.joinGroupCall(peerId: peer.id, activeCall: activeCall)
strongSelf.context.joinGroupCall(peerId: peer.id, joinAsPeerId: nil, activeCall: activeCall)
}, presentInviteMembers: { [weak self] in
guard let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else {
return
@ -11847,9 +11847,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return inputShortcuts + otherShortcuts
}
public override func joinGroupCall(peerId: PeerId, info: GroupCallInfo) {
public override func joinGroupCall(peerId: PeerId, joinAsPeerId: PeerId?, info: GroupCallInfo) {
let _ = self.presentVoiceMessageDiscardAlert(action: {
super.joinGroupCall(peerId: peerId, info: info)
super.joinGroupCall(peerId: peerId, joinAsPeerId: joinAsPeerId, info: info)
})
}

View File

@ -993,9 +993,16 @@ func peerInfoHeaderButtons(peer: Peer?, cachedData: CachedPeerData?, isOpenedFro
var canViewStats = false
var hasDiscussion = false
var hasVoiceChat = false
var displayMore = true
if let cachedChannelData = cachedData as? CachedChannelData {
canViewStats = cachedChannelData.flags.contains(.canViewStats)
}
if channel.flags.contains(.hasVoiceChat) {
hasVoiceChat = true
}
if channel.flags.contains(.isCreator) || channel.hasPermission(.manageCalls) {
displayMore = true
}
switch channel.info {
case let .broadcast(info):
if !channel.flags.contains(.isCreator) {
@ -1009,9 +1016,6 @@ func peerInfoHeaderButtons(peer: Peer?, cachedData: CachedPeerData?, isOpenedFro
if channel.flags.contains(.isCreator) || channel.hasPermission(.inviteMembers) {
result.append(.addMember)
}
if channel.flags.contains(.hasVoiceChat) {
hasVoiceChat = true
}
}
switch channel.participationStatus {
case .member:
@ -1033,7 +1037,6 @@ func peerInfoHeaderButtons(peer: Peer?, cachedData: CachedPeerData?, isOpenedFro
if displayLeave {
result.append(.leave)
}
var displayMore = true
if displayLeave && !channel.flags.contains(.isCreator) {
if let _ = channel.adminRights {
displayMore = false

View File

@ -57,6 +57,7 @@ import InviteLinksUI
import UndoUI
import MediaResources
import HashtagSearchUI
import ActionSheetPeerItem
protocol PeerInfoScreenItem: class {
var id: AnyHashable { get }
@ -3256,9 +3257,69 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
self.requestCall(isVideo: true)
case .voiceChat:
if let cachedData = self.data?.cachedData as? CachedGroupData, let activeCall = cachedData.activeCall {
self.context.joinGroupCall(peerId: self.peerId, activeCall: activeCall)
self.context.joinGroupCall(peerId: self.peerId, joinAsPeerId: nil, activeCall: activeCall)
} else if let cachedData = self.data?.cachedData as? CachedChannelData, let activeCall = cachedData.activeCall {
self.context.joinGroupCall(peerId: self.peerId, activeCall: activeCall)
let accountPeerId = self.context.account.peerId
let _ = (adminedPublicChannels(account: self.context.account, scope: .forVoiceChat)
|> deliverOnMainQueue).start(next: { [weak self] channelPeers in
guard let strongSelf = self else {
return
}
let _ = (strongSelf.context.account.postbox.transaction { transaction -> Peer? in
return transaction.getPeer(accountPeerId)
}
|> deliverOnMainQueue).start(next: { accountPeer in
guard let strongSelf = self else {
return
}
if channelPeers.isEmpty {
strongSelf.context.joinGroupCall(peerId: strongSelf.peerId, joinAsPeerId: nil, activeCall: activeCall)
} else {
let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData)
let dismissAction: () -> Void = { [weak actionSheet] in
actionSheet?.dismissAnimated()
}
let selectAction: (PeerId) -> Void = { joinAsPeerId in
dismissAction()
guard let strongSelf = self else {
return
}
strongSelf.context.joinGroupCall(peerId: strongSelf.peerId, joinAsPeerId: joinAsPeerId == strongSelf.context.account.peerId ? nil : joinAsPeerId, activeCall: activeCall)
}
var items: [ActionSheetItem] = []
var allPeers: [Peer] = []
if let accountPeer = accountPeer {
allPeers.append(accountPeer)
}
var channelPeers = channelPeers
for i in 0 ..< channelPeers.count {
if channelPeers[i].id == strongSelf.peerId {
let peer = channelPeers[i]
channelPeers.remove(at: i)
channelPeers.insert(peer, at: 0)
break
}
}
allPeers.append(contentsOf: channelPeers)
for peer in allPeers {
items.append(ActionSheetPeerItem(context: strongSelf.context, peer: peer, title: peer.debugDisplayTitle, isSelected: false, strings: strongSelf.presentationData.strings, theme: strongSelf.presentationData.theme, action: {
selectAction(peer.id)
}))
}
actionSheet.setItemGroups([
ActionSheetItemGroup(items: items),
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, action: { dismissAction() })])
])
strongSelf.view.endEditing(true)
controller.present(actionSheet, in: .window(.root))
}
})
})
}
case .mute:
if let notificationSettings = self.data?.notificationSettings, case .muted = notificationSettings.muteState {
@ -3428,7 +3489,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}
}
} else if let channel = peer as? TelegramChannel {
if case .group = channel.info, !channel.flags.contains(.hasVoiceChat) {
if !channel.flags.contains(.hasVoiceChat) {
if channel.flags.contains(.isCreator) || channel.hasPermission(.manageCalls) {
items.append(ActionSheetButtonItem(title: presentationData.strings.ChannelInfo_CreateVoiceChat, color: .accent, action: { [weak self] in
dismissAction()
@ -3684,7 +3745,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}
if let activeCall = cachedChannelData.activeCall {
self.context.joinGroupCall(peerId: peer.id, activeCall: activeCall)
self.context.joinGroupCall(peerId: peer.id, joinAsPeerId: nil, activeCall: activeCall)
} else {
self.createAndJoinGroupCall(peerId: peer.id)
}
@ -3695,7 +3756,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}
if let activeCall = cachedGroupData.activeCall {
self.context.joinGroupCall(peerId: peer.id, activeCall: activeCall)
self.context.joinGroupCall(peerId: peer.id, joinAsPeerId: nil, activeCall: activeCall)
} else {
self.createAndJoinGroupCall(peerId: peer.id)
}
@ -3729,12 +3790,12 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
statusController?.dismiss()
}
strongSelf.controller?.present(statusController, in: .window(.root))
strongSelf.activeActionDisposable.set((createGroupCall(account: strongSelf.context.account, peerId: peerId)
strongSelf.activeActionDisposable.set((createGroupCall(account: strongSelf.context.account, peerId: peerId, joinAs: nil)
|> deliverOnMainQueue).start(next: { [weak self] info in
guard let strongSelf = self else {
return
}
strongSelf.context.joinGroupCall(peerId: peerId, activeCall: CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash))
strongSelf.context.joinGroupCall(peerId: peerId, joinAsPeerId: nil, activeCall: CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash))
}, error: { [weak self] error in
dismissStatus?()

View File

@ -4,6 +4,8 @@ import TgVoipWebrtc
import UniversalMediaPlayer
import AppBundle
import OpusBinding
import Postbox
import TelegramCore
private final class ContextQueueImpl: NSObject, OngoingCallThreadLocalContextQueueWebrtc {
private let queue: Queue
@ -29,92 +31,89 @@ private final class ContextQueueImpl: NSObject, OngoingCallThreadLocalContextQue
}
}
private final class DemoBroadcastPacketSource {
private protocol BroadcastPartSource: class {
var parts: Signal<[OngoingGroupCallBroadcastPart], NoError> { get }
}
private final class NetworkBroadcastPartSource: BroadcastPartSource {
private let queue: Queue
private let account: Account
private let callId: Int64
private let accessHash: Int64
private let datacenterId: Int?
private let packetsPipe = ValuePipe<[OngoingGroupCallBroadcastPacket]>()
var packets: Signal<[OngoingGroupCallBroadcastPacket], NoError> {
return self.packetsPipe.signal()
private let partsPipe = ValuePipe<[OngoingGroupCallBroadcastPart]>()
var parts: Signal<[OngoingGroupCallBroadcastPart], NoError> {
return self.partsPipe.signal()
}
private var timer: SwiftSignalKit.Timer?
private let disposable = MetaDisposable()
private var enqueuedPackets: [OngoingGroupCallBroadcastPacket] = []
private var delayTimer: SwiftSignalKit.Timer?
private var nextTimestampId: Int32?
private var nextIndex: Int = 0
init(queue: Queue) {
init(queue: Queue, account: Account, callId: Int64, accessHash: Int64, datacenterId: Int?) {
self.queue = queue
self.account = account
self.callId = callId
self.accessHash = accessHash
self.datacenterId = datacenterId
self.emitPacketAndStartTimer()
self.check()
}
deinit {
self.timer?.invalidate()
self.delayTimer?.invalidate()
self.disposable.dispose()
}
private func emitPacketAndStartTimer() {
let demoPacketCount = 200
let index = self.nextIndex % demoPacketCount
self.nextIndex += 1
var packets: [OngoingGroupCallBroadcastPacket] = []
let fileName = String(format: "%04d", index)
if let path = getAppBundle().path(forResource: fileName, ofType: "ogg") {
let source1 = SoftwareAudioSource(path: path)
let source2 = SoftwareAudioSource(path: path)
let frames = OggOpusReader.extractFrames(try! Data(contentsOf: URL(fileURLWithPath: path)))!
while true {
if true {
if let (frame, numSamples) = source1.readEncodedFrame() {
let decodedFrame = source2.readFrame()!
packets.append(OngoingGroupCallBroadcastPacket(numSamples: Int32(numSamples), data: frames[packets.count].data, decodedData: decodedFrame))
} else {
break
}
} else {
if let frame = source2.readFrame() {
packets.append(OngoingGroupCallBroadcastPacket(numSamples: Int32(frame.count / 2), data: frame, decodedData: frame))
} else {
break
}
}
}
private func check() {
let timestampId: Int32
if let nextTimestampId = self.nextTimestampId {
timestampId = nextTimestampId
} else {
timestampId = Int32(Date().timeIntervalSince1970)
}
if !packets.isEmpty {
self.enqueuedPackets.append(contentsOf: packets)
self.startDelayTimer()
}
let timer = SwiftSignalKit.Timer(timeout: 1.0, repeat: false, completion: { [weak self] in
self?.emitPacketAndStartTimer()
}, queue: self.queue)
self.timer = timer
timer.start()
}
private func startDelayTimer() {
let delayTimer = SwiftSignalKit.Timer(timeout: Double.random(in: 0.1 ... 0.3), repeat: false, completion: { [weak self] in
self.disposable.set((getAudioBroadcastPart(account: self.account, callId: self.callId, accessHash: self.accessHash, datacenterId: self.datacenterId, timestampId: timestampId)
|> deliverOn(self.queue)).start(next: { [weak self] data in
guard let strongSelf = self else {
return
}
let packets = strongSelf.enqueuedPackets
strongSelf.enqueuedPackets.removeAll()
if !packets.isEmpty {
strongSelf.packetsPipe.putNext(packets)
if let data = data {
var parts: [OngoingGroupCallBroadcastPart] = []
parts.append(OngoingGroupCallBroadcastPart(timestamp: timestampId, oggData: data))
strongSelf.nextTimestampId = timestampId + 1
if !parts.isEmpty {
strongSelf.partsPipe.putNext(parts)
}
}
}, queue: self.queue)
self.delayTimer = delayTimer
delayTimer.start()
strongSelf.timer?.invalidate()
strongSelf.timer = SwiftSignalKit.Timer(timeout: 0.5, repeat: false, completion: {
self?.check()
}, queue: strongSelf.queue)
strongSelf.timer?.start()
}))
}
}
public final class OngoingGroupCallContext {
public struct AudioStreamData {
public var account: Account
public var callId: Int64
public var accessHash: Int64
public var datacenterId: Int?
public init(account: Account, callId: Int64, accessHash: Int64, datacenterId: Int?) {
self.account = account
self.callId = callId
self.accessHash = accessHash
self.datacenterId = datacenterId
}
}
public enum NetworkState {
case connecting
case connected
@ -138,10 +137,10 @@ public final class OngoingGroupCallContext {
let videoSources = ValuePromise<Set<UInt32>>(Set(), ignoreRepeated: true)
private var broadcastPacketSource: DemoBroadcastPacketSource?
private var broadcastPacketsDisposable: Disposable?
private var broadcastPartsSource: BroadcastPartSource?
private var broadcastPartsDisposable: Disposable?
init(queue: Queue, inputDeviceId: String, outputDeviceId: String, video: OngoingCallVideoCapturer?, participantDescriptionsRequired: @escaping (Set<UInt32>) -> Void, demoAudioStream: Bool) {
init(queue: Queue, inputDeviceId: String, outputDeviceId: String, video: OngoingCallVideoCapturer?, participantDescriptionsRequired: @escaping (Set<UInt32>) -> Void, audioStreamData: AudioStreamData?) {
self.queue = queue
var networkStateUpdatedImpl: ((GroupCallNetworkState) -> Void)?
@ -164,6 +163,33 @@ public final class OngoingGroupCallContext {
},
participantDescriptionsRequired: { ssrcs in
participantDescriptionsRequired(Set(ssrcs.map { $0.uint32Value }))
},
externalDecodeOgg: { sourceData in
let tempFile = TempBox.shared.tempFile(fileName: "audio.ogg")
defer {
TempBox.shared.dispose(tempFile)
}
guard let _ = try? sourceData.write(to: URL(fileURLWithPath: tempFile.path), options: .atomic) else {
return nil
}
var resultData = Data()
let source = SoftwareAudioSource(path: tempFile.path)
while true {
if let frame = source.readFrame() {
resultData.append(frame)
} else {
break
}
}
if resultData.isEmpty {
return nil
} else {
return resultData
}
}
)
@ -216,54 +242,22 @@ public final class OngoingGroupCallContext {
}
})
if demoAudioStream {
let broadcastPacketSource = DemoBroadcastPacketSource(queue: queue)
self.broadcastPacketSource = broadcastPacketSource
self.broadcastPacketsDisposable = (broadcastPacketSource.packets
|> deliverOn(queue)).start(next: { [weak self] packets in
if let audioStreamData = audioStreamData {
let broadcastPartsSource = NetworkBroadcastPartSource(queue: queue, account: audioStreamData.account, callId: audioStreamData.callId, accessHash: audioStreamData.accessHash, datacenterId: audioStreamData.datacenterId)
//let broadcastPartsSource = DemoBroadcastPartSource(queue: queue)
self.broadcastPartsSource = broadcastPartsSource
self.broadcastPartsDisposable = (broadcastPartsSource.parts
|> deliverOn(queue)).start(next: { [weak self] parts in
guard let strongSelf = self else {
return
}
strongSelf.context.add(packets)
strongSelf.context.add(parts)
})
}
/*var packets: [OngoingGroupCallBroadcastPacket] = []
for i in 0 ..< 200 {
let fileName = String(format: "%04d", i)
if let path = getAppBundle().path(forResource: fileName, ofType: "ogg") {
if let data = try? Data(contentsOf: URL(fileURLWithPath: path)) {
if let frames = OggOpusReader.extractFrames(data) {
for frame in frames {
packets.append(OngoingGroupCallBroadcastPacket(numSamples: frame.numSamples, data: frame.data))
}
}
}
continue
let source = SoftwareAudioSource(path: path)
while true {
if let (frame, numSamples) = source.readEncodedFrame() {
if numSamples != 960 {
continue
}
packets.append(OngoingGroupCallBroadcastPacket(numSamples: Int32(numSamples), data: frame))
} else {
break
}
/*if let frame = source.readFrame() {
packets.append(frame)
} else {
break
}*/
}
}
}
context.add(packets);*/
}
deinit {
self.broadcastPacketsDisposable?.dispose()
self.broadcastPartsDisposable?.dispose()
}
func setJoinResponse(payload: String, participants: [(UInt32, String?)]) {
@ -480,10 +474,10 @@ public final class OngoingGroupCallContext {
}
}
public init(inputDeviceId: String = "", outputDeviceId: String = "", video: OngoingCallVideoCapturer?, participantDescriptionsRequired: @escaping (Set<UInt32>) -> Void, demoAudioStream: Bool) {
public init(inputDeviceId: String = "", outputDeviceId: String = "", video: OngoingCallVideoCapturer?, participantDescriptionsRequired: @escaping (Set<UInt32>) -> Void, audioStreamData: AudioStreamData?) {
let queue = self.queue
self.impl = QueueLocalObject(queue: queue, generate: {
return Impl(queue: queue, inputDeviceId: inputDeviceId, outputDeviceId: outputDeviceId, video: video, participantDescriptionsRequired: participantDescriptionsRequired, demoAudioStream: demoAudioStream)
return Impl(queue: queue, inputDeviceId: inputDeviceId, outputDeviceId: outputDeviceId, video: video, participantDescriptionsRequired: participantDescriptionsRequired, audioStreamData: audioStreamData)
})
}

View File

@ -51,6 +51,8 @@ objc_library(
"//submodules/MtProtoKit:MtProtoKit",
"//third-party/boringssl:crypto",
"//third-party/boringssl:ssl",
"//third-party/ogg:ogg",
"//third-party/opusfile:opusfile",
],
sdk_frameworks = [
"Foundation",

View File

@ -165,19 +165,18 @@ typedef NS_ENUM(int32_t, GroupCallNetworkState) {
@end
@interface OngoingGroupCallBroadcastPacket : NSObject
@interface OngoingGroupCallBroadcastPart : NSObject
@property (nonatomic, readonly) int numSamples;
@property (nonatomic, strong, readonly) NSData * _Nonnull data;
@property (nonatomic, strong, readonly) NSData * _Nonnull decodedData;
@property (nonatomic, readonly) int32_t timestamp;
@property (nonatomic, strong, readonly) NSData * _Nonnull oggData;
- (instancetype _Nonnull)initWithNumSamples:(int)numSamples data:(NSData * _Nonnull)data decodedData:(NSData * _Nonnull)decodedData;
- (instancetype _Nonnull)initWithTimestamp:(int32_t)timestamp oggData:(NSData * _Nonnull)oggData;
@end
@interface GroupCallThreadLocalContext : NSObject
- (instancetype _Nonnull)initWithQueue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue networkStateUpdated:(void (^ _Nonnull)(GroupCallNetworkState))networkStateUpdated audioLevelsUpdated:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))audioLevelsUpdated inputDeviceId:(NSString * _Nonnull)inputDeviceId outputDeviceId:(NSString * _Nonnull)outputDeviceId videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer incomingVideoSourcesUpdated:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))incomingVideoSourcesUpdated participantDescriptionsRequired:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))participantDescriptionsRequired;
- (instancetype _Nonnull)initWithQueue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue networkStateUpdated:(void (^ _Nonnull)(GroupCallNetworkState))networkStateUpdated audioLevelsUpdated:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))audioLevelsUpdated inputDeviceId:(NSString * _Nonnull)inputDeviceId outputDeviceId:(NSString * _Nonnull)outputDeviceId videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer incomingVideoSourcesUpdated:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))incomingVideoSourcesUpdated participantDescriptionsRequired:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))participantDescriptionsRequired externalDecodeOgg:(NSData * _Nullable (^ _Nullable)(NSData * _Nonnull))externalDecodeOgg;
- (void)stop;
@ -196,7 +195,7 @@ typedef NS_ENUM(int32_t, GroupCallNetworkState) {
- (void)switchAudioInput:(NSString * _Nonnull)deviceId;
- (void)makeIncomingVideoViewWithSsrc:(uint32_t)ssrc completion:(void (^_Nonnull)(UIView<OngoingCallThreadLocalContextWebrtcVideoView> * _Nullable))completion;
- (void)addBroadcastPackets:(NSArray<OngoingGroupCallBroadcastPacket *> * _Nonnull)packets;
- (void)addBroadcastParts:(NSArray<OngoingGroupCallBroadcastPart *> * _Nonnull)parts;
@end

View File

@ -833,7 +833,7 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
@implementation GroupCallThreadLocalContext
- (instancetype _Nonnull)initWithQueue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue networkStateUpdated:(void (^ _Nonnull)(GroupCallNetworkState))networkStateUpdated audioLevelsUpdated:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))audioLevelsUpdated inputDeviceId:(NSString * _Nonnull)inputDeviceId outputDeviceId:(NSString * _Nonnull)outputDeviceId videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer incomingVideoSourcesUpdated:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))incomingVideoSourcesUpdated participantDescriptionsRequired:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))participantDescriptionsRequired {
- (instancetype _Nonnull)initWithQueue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue networkStateUpdated:(void (^ _Nonnull)(GroupCallNetworkState))networkStateUpdated audioLevelsUpdated:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))audioLevelsUpdated inputDeviceId:(NSString * _Nonnull)inputDeviceId outputDeviceId:(NSString * _Nonnull)outputDeviceId videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer incomingVideoSourcesUpdated:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))incomingVideoSourcesUpdated participantDescriptionsRequired:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))participantDescriptionsRequired externalDecodeOgg:(NSData * _Nullable (^ _Nullable)(NSData * _Nonnull))externalDecodeOgg {
self = [super init];
if (self != nil) {
_queue = queue;
@ -841,6 +841,19 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
_networkStateUpdated = [networkStateUpdated copy];
_videoCapturer = videoCapturer;
std::function<void(std::vector<uint8_t> &, std::vector<uint8_t> const &)> externalDecodeOggFunction;
if (externalDecodeOgg) {
externalDecodeOggFunction = [externalDecodeOgg](std::vector<uint8_t> &outPcm, std::vector<uint8_t> sourceData) {
@autoreleasepool {
NSData *result = externalDecodeOgg([NSData dataWithBytes:sourceData.data() length:sourceData.size()]);
if (result) {
outPcm.resize(result.length);
[result getBytes:outPcm.data() length:result.length];
}
}
};
}
__weak GroupCallThreadLocalContext *weakSelf = self;
_instance.reset(new tgcalls::GroupInstanceCustomImpl((tgcalls::GroupInstanceDescriptor){
.networkStateUpdated = [weakSelf, queue, networkStateUpdated](bool isConnected) {
@ -877,7 +890,8 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
[mappedSources addObject:@(it)];
}
participantDescriptionsRequired(mappedSources);
}
},
.externalDecodeOgg = externalDecodeOggFunction
}));
}
return self;
@ -1398,25 +1412,21 @@ static void processJoinPayload(tgcalls::GroupJoinPayload &payload, void (^ _Nonn
}
}
- (void)addBroadcastPackets:(NSArray<OngoingGroupCallBroadcastPacket *> * _Nonnull)packets {
- (void)addBroadcastParts:(NSArray<OngoingGroupCallBroadcastPart *> * _Nonnull)parts {
if (!_instance) {
return;
}
std::vector<tgcalls::BroadcastPacket> parsedPackets;
for (OngoingGroupCallBroadcastPacket *packet in packets) {
tgcalls::BroadcastPacket parsedPacket;
parsedPacket.numSamples = packet.numSamples;
std::vector<tgcalls::BroadcastPart> parsedParts;
for (OngoingGroupCallBroadcastPart *part in parts) {
tgcalls::BroadcastPart parsedPart;
parsedPart.timestamp = part.timestamp;
parsedPart.oggData.resize(part.oggData.length);
[part.oggData getBytes:parsedPart.oggData.data() length:part.oggData.length];
parsedPacket.data.resize(packet.data.length);
[packet.data getBytes:parsedPacket.data.data() length:packet.data.length];
parsedPacket.decodedData.resize(packet.decodedData.length);
[packet.decodedData getBytes:parsedPacket.decodedData.data() length:packet.decodedData.length];
parsedPackets.push_back(std::move(parsedPacket));
parsedParts.push_back(std::move(parsedPart));
}
((tgcalls::GroupInstanceCustomImpl *)(_instance.get()))->addBroadcastPackets(std::move(parsedPackets));
((tgcalls::GroupInstanceCustomImpl *)(_instance.get()))->addBroadcastParts(std::move(parsedParts));
}
@end
@ -1434,14 +1444,13 @@ static void processJoinPayload(tgcalls::GroupJoinPayload &payload, void (^ _Nonn
@end
@implementation OngoingGroupCallBroadcastPacket
@implementation OngoingGroupCallBroadcastPart
- (instancetype _Nonnull)initWithNumSamples:(int)numSamples data:(NSData * _Nonnull)data decodedData:(NSData * _Nonnull)decodedData {
- (instancetype _Nonnull)initWithTimestamp:(int32_t)timestamp oggData:(NSData * _Nonnull)oggData {
self = [super init];
if (self != nil) {
_numSamples = numSamples;
_data = data;
_decodedData = decodedData;
_timestamp = timestamp;
_oggData = oggData;
}
return self;
}

@ -1 +1 @@
Subproject commit 35a4725bd37e4e0a07eb2d3ba626b80f9dc73fce
Subproject commit adbf89527e97bf082115324dfdceed45a75a5e71

20
third-party/ogg/BUILD vendored Normal file
View File

@ -0,0 +1,20 @@
objc_library(
name = "ogg",
enable_modules = True,
module_name = "ogg",
srcs = glob([
"Sources/*.c",
]),
hdrs = glob([
"include/ogg/*.h",
]),
includes = [
"include",
],
copts = [
],
visibility = [
"//visibility:public",
],
)

1087
third-party/ogg/Sources/bitwise.c vendored Normal file

File diff suppressed because it is too large Load Diff

278
third-party/ogg/Sources/crctable.h vendored Normal file
View File

@ -0,0 +1,278 @@
/********************************************************************
* *
* THIS FILE IS PART OF THE Ogg CONTAINER SOURCE CODE. *
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
* *
* THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2018 *
* by the Xiph.Org Foundation http://www.xiph.org/ *
* *
********************************************************************/
#include <ogg/os_types.h>
static const ogg_uint32_t crc_lookup[8][256]={
{0x00000000,0x04c11db7,0x09823b6e,0x0d4326d9,0x130476dc,0x17c56b6b,0x1a864db2,0x1e475005,
0x2608edb8,0x22c9f00f,0x2f8ad6d6,0x2b4bcb61,0x350c9b64,0x31cd86d3,0x3c8ea00a,0x384fbdbd,
0x4c11db70,0x48d0c6c7,0x4593e01e,0x4152fda9,0x5f15adac,0x5bd4b01b,0x569796c2,0x52568b75,
0x6a1936c8,0x6ed82b7f,0x639b0da6,0x675a1011,0x791d4014,0x7ddc5da3,0x709f7b7a,0x745e66cd,
0x9823b6e0,0x9ce2ab57,0x91a18d8e,0x95609039,0x8b27c03c,0x8fe6dd8b,0x82a5fb52,0x8664e6e5,
0xbe2b5b58,0xbaea46ef,0xb7a96036,0xb3687d81,0xad2f2d84,0xa9ee3033,0xa4ad16ea,0xa06c0b5d,
0xd4326d90,0xd0f37027,0xddb056fe,0xd9714b49,0xc7361b4c,0xc3f706fb,0xceb42022,0xca753d95,
0xf23a8028,0xf6fb9d9f,0xfbb8bb46,0xff79a6f1,0xe13ef6f4,0xe5ffeb43,0xe8bccd9a,0xec7dd02d,
0x34867077,0x30476dc0,0x3d044b19,0x39c556ae,0x278206ab,0x23431b1c,0x2e003dc5,0x2ac12072,
0x128e9dcf,0x164f8078,0x1b0ca6a1,0x1fcdbb16,0x018aeb13,0x054bf6a4,0x0808d07d,0x0cc9cdca,
0x7897ab07,0x7c56b6b0,0x71159069,0x75d48dde,0x6b93dddb,0x6f52c06c,0x6211e6b5,0x66d0fb02,
0x5e9f46bf,0x5a5e5b08,0x571d7dd1,0x53dc6066,0x4d9b3063,0x495a2dd4,0x44190b0d,0x40d816ba,
0xaca5c697,0xa864db20,0xa527fdf9,0xa1e6e04e,0xbfa1b04b,0xbb60adfc,0xb6238b25,0xb2e29692,
0x8aad2b2f,0x8e6c3698,0x832f1041,0x87ee0df6,0x99a95df3,0x9d684044,0x902b669d,0x94ea7b2a,
0xe0b41de7,0xe4750050,0xe9362689,0xedf73b3e,0xf3b06b3b,0xf771768c,0xfa325055,0xfef34de2,
0xc6bcf05f,0xc27dede8,0xcf3ecb31,0xcbffd686,0xd5b88683,0xd1799b34,0xdc3abded,0xd8fba05a,
0x690ce0ee,0x6dcdfd59,0x608edb80,0x644fc637,0x7a089632,0x7ec98b85,0x738aad5c,0x774bb0eb,
0x4f040d56,0x4bc510e1,0x46863638,0x42472b8f,0x5c007b8a,0x58c1663d,0x558240e4,0x51435d53,
0x251d3b9e,0x21dc2629,0x2c9f00f0,0x285e1d47,0x36194d42,0x32d850f5,0x3f9b762c,0x3b5a6b9b,
0x0315d626,0x07d4cb91,0x0a97ed48,0x0e56f0ff,0x1011a0fa,0x14d0bd4d,0x19939b94,0x1d528623,
0xf12f560e,0xf5ee4bb9,0xf8ad6d60,0xfc6c70d7,0xe22b20d2,0xe6ea3d65,0xeba91bbc,0xef68060b,
0xd727bbb6,0xd3e6a601,0xdea580d8,0xda649d6f,0xc423cd6a,0xc0e2d0dd,0xcda1f604,0xc960ebb3,
0xbd3e8d7e,0xb9ff90c9,0xb4bcb610,0xb07daba7,0xae3afba2,0xaafbe615,0xa7b8c0cc,0xa379dd7b,
0x9b3660c6,0x9ff77d71,0x92b45ba8,0x9675461f,0x8832161a,0x8cf30bad,0x81b02d74,0x857130c3,
0x5d8a9099,0x594b8d2e,0x5408abf7,0x50c9b640,0x4e8ee645,0x4a4ffbf2,0x470cdd2b,0x43cdc09c,
0x7b827d21,0x7f436096,0x7200464f,0x76c15bf8,0x68860bfd,0x6c47164a,0x61043093,0x65c52d24,
0x119b4be9,0x155a565e,0x18197087,0x1cd86d30,0x029f3d35,0x065e2082,0x0b1d065b,0x0fdc1bec,
0x3793a651,0x3352bbe6,0x3e119d3f,0x3ad08088,0x2497d08d,0x2056cd3a,0x2d15ebe3,0x29d4f654,
0xc5a92679,0xc1683bce,0xcc2b1d17,0xc8ea00a0,0xd6ad50a5,0xd26c4d12,0xdf2f6bcb,0xdbee767c,
0xe3a1cbc1,0xe760d676,0xea23f0af,0xeee2ed18,0xf0a5bd1d,0xf464a0aa,0xf9278673,0xfde69bc4,
0x89b8fd09,0x8d79e0be,0x803ac667,0x84fbdbd0,0x9abc8bd5,0x9e7d9662,0x933eb0bb,0x97ffad0c,
0xafb010b1,0xab710d06,0xa6322bdf,0xa2f33668,0xbcb4666d,0xb8757bda,0xb5365d03,0xb1f740b4},
{0x00000000,0xd219c1dc,0xa0f29e0f,0x72eb5fd3,0x452421a9,0x973de075,0xe5d6bfa6,0x37cf7e7a,
0x8a484352,0x5851828e,0x2abadd5d,0xf8a31c81,0xcf6c62fb,0x1d75a327,0x6f9efcf4,0xbd873d28,
0x10519b13,0xc2485acf,0xb0a3051c,0x62bac4c0,0x5575baba,0x876c7b66,0xf58724b5,0x279ee569,
0x9a19d841,0x4800199d,0x3aeb464e,0xe8f28792,0xdf3df9e8,0x0d243834,0x7fcf67e7,0xadd6a63b,
0x20a33626,0xf2baf7fa,0x8051a829,0x524869f5,0x6587178f,0xb79ed653,0xc5758980,0x176c485c,
0xaaeb7574,0x78f2b4a8,0x0a19eb7b,0xd8002aa7,0xefcf54dd,0x3dd69501,0x4f3dcad2,0x9d240b0e,
0x30f2ad35,0xe2eb6ce9,0x9000333a,0x4219f2e6,0x75d68c9c,0xa7cf4d40,0xd5241293,0x073dd34f,
0xbabaee67,0x68a32fbb,0x1a487068,0xc851b1b4,0xff9ecfce,0x2d870e12,0x5f6c51c1,0x8d75901d,
0x41466c4c,0x935fad90,0xe1b4f243,0x33ad339f,0x04624de5,0xd67b8c39,0xa490d3ea,0x76891236,
0xcb0e2f1e,0x1917eec2,0x6bfcb111,0xb9e570cd,0x8e2a0eb7,0x5c33cf6b,0x2ed890b8,0xfcc15164,
0x5117f75f,0x830e3683,0xf1e56950,0x23fca88c,0x1433d6f6,0xc62a172a,0xb4c148f9,0x66d88925,
0xdb5fb40d,0x094675d1,0x7bad2a02,0xa9b4ebde,0x9e7b95a4,0x4c625478,0x3e890bab,0xec90ca77,
0x61e55a6a,0xb3fc9bb6,0xc117c465,0x130e05b9,0x24c17bc3,0xf6d8ba1f,0x8433e5cc,0x562a2410,
0xebad1938,0x39b4d8e4,0x4b5f8737,0x994646eb,0xae893891,0x7c90f94d,0x0e7ba69e,0xdc626742,
0x71b4c179,0xa3ad00a5,0xd1465f76,0x035f9eaa,0x3490e0d0,0xe689210c,0x94627edf,0x467bbf03,
0xfbfc822b,0x29e543f7,0x5b0e1c24,0x8917ddf8,0xbed8a382,0x6cc1625e,0x1e2a3d8d,0xcc33fc51,
0x828cd898,0x50951944,0x227e4697,0xf067874b,0xc7a8f931,0x15b138ed,0x675a673e,0xb543a6e2,
0x08c49bca,0xdadd5a16,0xa83605c5,0x7a2fc419,0x4de0ba63,0x9ff97bbf,0xed12246c,0x3f0be5b0,
0x92dd438b,0x40c48257,0x322fdd84,0xe0361c58,0xd7f96222,0x05e0a3fe,0x770bfc2d,0xa5123df1,
0x189500d9,0xca8cc105,0xb8679ed6,0x6a7e5f0a,0x5db12170,0x8fa8e0ac,0xfd43bf7f,0x2f5a7ea3,
0xa22feebe,0x70362f62,0x02dd70b1,0xd0c4b16d,0xe70bcf17,0x35120ecb,0x47f95118,0x95e090c4,
0x2867adec,0xfa7e6c30,0x889533e3,0x5a8cf23f,0x6d438c45,0xbf5a4d99,0xcdb1124a,0x1fa8d396,
0xb27e75ad,0x6067b471,0x128ceba2,0xc0952a7e,0xf75a5404,0x254395d8,0x57a8ca0b,0x85b10bd7,
0x383636ff,0xea2ff723,0x98c4a8f0,0x4add692c,0x7d121756,0xaf0bd68a,0xdde08959,0x0ff94885,
0xc3cab4d4,0x11d37508,0x63382adb,0xb121eb07,0x86ee957d,0x54f754a1,0x261c0b72,0xf405caae,
0x4982f786,0x9b9b365a,0xe9706989,0x3b69a855,0x0ca6d62f,0xdebf17f3,0xac544820,0x7e4d89fc,
0xd39b2fc7,0x0182ee1b,0x7369b1c8,0xa1707014,0x96bf0e6e,0x44a6cfb2,0x364d9061,0xe45451bd,
0x59d36c95,0x8bcaad49,0xf921f29a,0x2b383346,0x1cf74d3c,0xceee8ce0,0xbc05d333,0x6e1c12ef,
0xe36982f2,0x3170432e,0x439b1cfd,0x9182dd21,0xa64da35b,0x74546287,0x06bf3d54,0xd4a6fc88,
0x6921c1a0,0xbb38007c,0xc9d35faf,0x1bca9e73,0x2c05e009,0xfe1c21d5,0x8cf77e06,0x5eeebfda,
0xf33819e1,0x2121d83d,0x53ca87ee,0x81d34632,0xb61c3848,0x6405f994,0x16eea647,0xc4f7679b,
0x79705ab3,0xab699b6f,0xd982c4bc,0x0b9b0560,0x3c547b1a,0xee4dbac6,0x9ca6e515,0x4ebf24c9},
{0x00000000,0x01d8ac87,0x03b1590e,0x0269f589,0x0762b21c,0x06ba1e9b,0x04d3eb12,0x050b4795,
0x0ec56438,0x0f1dc8bf,0x0d743d36,0x0cac91b1,0x09a7d624,0x087f7aa3,0x0a168f2a,0x0bce23ad,
0x1d8ac870,0x1c5264f7,0x1e3b917e,0x1fe33df9,0x1ae87a6c,0x1b30d6eb,0x19592362,0x18818fe5,
0x134fac48,0x129700cf,0x10fef546,0x112659c1,0x142d1e54,0x15f5b2d3,0x179c475a,0x1644ebdd,
0x3b1590e0,0x3acd3c67,0x38a4c9ee,0x397c6569,0x3c7722fc,0x3daf8e7b,0x3fc67bf2,0x3e1ed775,
0x35d0f4d8,0x3408585f,0x3661add6,0x37b90151,0x32b246c4,0x336aea43,0x31031fca,0x30dbb34d,
0x269f5890,0x2747f417,0x252e019e,0x24f6ad19,0x21fdea8c,0x2025460b,0x224cb382,0x23941f05,
0x285a3ca8,0x2982902f,0x2beb65a6,0x2a33c921,0x2f388eb4,0x2ee02233,0x2c89d7ba,0x2d517b3d,
0x762b21c0,0x77f38d47,0x759a78ce,0x7442d449,0x714993dc,0x70913f5b,0x72f8cad2,0x73206655,
0x78ee45f8,0x7936e97f,0x7b5f1cf6,0x7a87b071,0x7f8cf7e4,0x7e545b63,0x7c3daeea,0x7de5026d,
0x6ba1e9b0,0x6a794537,0x6810b0be,0x69c81c39,0x6cc35bac,0x6d1bf72b,0x6f7202a2,0x6eaaae25,
0x65648d88,0x64bc210f,0x66d5d486,0x670d7801,0x62063f94,0x63de9313,0x61b7669a,0x606fca1d,
0x4d3eb120,0x4ce61da7,0x4e8fe82e,0x4f5744a9,0x4a5c033c,0x4b84afbb,0x49ed5a32,0x4835f6b5,
0x43fbd518,0x4223799f,0x404a8c16,0x41922091,0x44996704,0x4541cb83,0x47283e0a,0x46f0928d,
0x50b47950,0x516cd5d7,0x5305205e,0x52dd8cd9,0x57d6cb4c,0x560e67cb,0x54679242,0x55bf3ec5,
0x5e711d68,0x5fa9b1ef,0x5dc04466,0x5c18e8e1,0x5913af74,0x58cb03f3,0x5aa2f67a,0x5b7a5afd,
0xec564380,0xed8eef07,0xefe71a8e,0xee3fb609,0xeb34f19c,0xeaec5d1b,0xe885a892,0xe95d0415,
0xe29327b8,0xe34b8b3f,0xe1227eb6,0xe0fad231,0xe5f195a4,0xe4293923,0xe640ccaa,0xe798602d,
0xf1dc8bf0,0xf0042777,0xf26dd2fe,0xf3b57e79,0xf6be39ec,0xf766956b,0xf50f60e2,0xf4d7cc65,
0xff19efc8,0xfec1434f,0xfca8b6c6,0xfd701a41,0xf87b5dd4,0xf9a3f153,0xfbca04da,0xfa12a85d,
0xd743d360,0xd69b7fe7,0xd4f28a6e,0xd52a26e9,0xd021617c,0xd1f9cdfb,0xd3903872,0xd24894f5,
0xd986b758,0xd85e1bdf,0xda37ee56,0xdbef42d1,0xdee40544,0xdf3ca9c3,0xdd555c4a,0xdc8df0cd,
0xcac91b10,0xcb11b797,0xc978421e,0xc8a0ee99,0xcdaba90c,0xcc73058b,0xce1af002,0xcfc25c85,
0xc40c7f28,0xc5d4d3af,0xc7bd2626,0xc6658aa1,0xc36ecd34,0xc2b661b3,0xc0df943a,0xc10738bd,
0x9a7d6240,0x9ba5cec7,0x99cc3b4e,0x981497c9,0x9d1fd05c,0x9cc77cdb,0x9eae8952,0x9f7625d5,
0x94b80678,0x9560aaff,0x97095f76,0x96d1f3f1,0x93dab464,0x920218e3,0x906bed6a,0x91b341ed,
0x87f7aa30,0x862f06b7,0x8446f33e,0x859e5fb9,0x8095182c,0x814db4ab,0x83244122,0x82fceda5,
0x8932ce08,0x88ea628f,0x8a839706,0x8b5b3b81,0x8e507c14,0x8f88d093,0x8de1251a,0x8c39899d,
0xa168f2a0,0xa0b05e27,0xa2d9abae,0xa3010729,0xa60a40bc,0xa7d2ec3b,0xa5bb19b2,0xa463b535,
0xafad9698,0xae753a1f,0xac1ccf96,0xadc46311,0xa8cf2484,0xa9178803,0xab7e7d8a,0xaaa6d10d,
0xbce23ad0,0xbd3a9657,0xbf5363de,0xbe8bcf59,0xbb8088cc,0xba58244b,0xb831d1c2,0xb9e97d45,
0xb2275ee8,0xb3fff26f,0xb19607e6,0xb04eab61,0xb545ecf4,0xb49d4073,0xb6f4b5fa,0xb72c197d},
{0x00000000,0xdc6d9ab7,0xbc1a28d9,0x6077b26e,0x7cf54c05,0xa098d6b2,0xc0ef64dc,0x1c82fe6b,
0xf9ea980a,0x258702bd,0x45f0b0d3,0x999d2a64,0x851fd40f,0x59724eb8,0x3905fcd6,0xe5686661,
0xf7142da3,0x2b79b714,0x4b0e057a,0x97639fcd,0x8be161a6,0x578cfb11,0x37fb497f,0xeb96d3c8,
0x0efeb5a9,0xd2932f1e,0xb2e49d70,0x6e8907c7,0x720bf9ac,0xae66631b,0xce11d175,0x127c4bc2,
0xeae946f1,0x3684dc46,0x56f36e28,0x8a9ef49f,0x961c0af4,0x4a719043,0x2a06222d,0xf66bb89a,
0x1303defb,0xcf6e444c,0xaf19f622,0x73746c95,0x6ff692fe,0xb39b0849,0xd3ecba27,0x0f812090,
0x1dfd6b52,0xc190f1e5,0xa1e7438b,0x7d8ad93c,0x61082757,0xbd65bde0,0xdd120f8e,0x017f9539,
0xe417f358,0x387a69ef,0x580ddb81,0x84604136,0x98e2bf5d,0x448f25ea,0x24f89784,0xf8950d33,
0xd1139055,0x0d7e0ae2,0x6d09b88c,0xb164223b,0xade6dc50,0x718b46e7,0x11fcf489,0xcd916e3e,
0x28f9085f,0xf49492e8,0x94e32086,0x488eba31,0x540c445a,0x8861deed,0xe8166c83,0x347bf634,
0x2607bdf6,0xfa6a2741,0x9a1d952f,0x46700f98,0x5af2f1f3,0x869f6b44,0xe6e8d92a,0x3a85439d,
0xdfed25fc,0x0380bf4b,0x63f70d25,0xbf9a9792,0xa31869f9,0x7f75f34e,0x1f024120,0xc36fdb97,
0x3bfad6a4,0xe7974c13,0x87e0fe7d,0x5b8d64ca,0x470f9aa1,0x9b620016,0xfb15b278,0x277828cf,
0xc2104eae,0x1e7dd419,0x7e0a6677,0xa267fcc0,0xbee502ab,0x6288981c,0x02ff2a72,0xde92b0c5,
0xcceefb07,0x108361b0,0x70f4d3de,0xac994969,0xb01bb702,0x6c762db5,0x0c019fdb,0xd06c056c,
0x3504630d,0xe969f9ba,0x891e4bd4,0x5573d163,0x49f12f08,0x959cb5bf,0xf5eb07d1,0x29869d66,
0xa6e63d1d,0x7a8ba7aa,0x1afc15c4,0xc6918f73,0xda137118,0x067eebaf,0x660959c1,0xba64c376,
0x5f0ca517,0x83613fa0,0xe3168dce,0x3f7b1779,0x23f9e912,0xff9473a5,0x9fe3c1cb,0x438e5b7c,
0x51f210be,0x8d9f8a09,0xede83867,0x3185a2d0,0x2d075cbb,0xf16ac60c,0x911d7462,0x4d70eed5,
0xa81888b4,0x74751203,0x1402a06d,0xc86f3ada,0xd4edc4b1,0x08805e06,0x68f7ec68,0xb49a76df,
0x4c0f7bec,0x9062e15b,0xf0155335,0x2c78c982,0x30fa37e9,0xec97ad5e,0x8ce01f30,0x508d8587,
0xb5e5e3e6,0x69887951,0x09ffcb3f,0xd5925188,0xc910afe3,0x157d3554,0x750a873a,0xa9671d8d,
0xbb1b564f,0x6776ccf8,0x07017e96,0xdb6ce421,0xc7ee1a4a,0x1b8380fd,0x7bf43293,0xa799a824,
0x42f1ce45,0x9e9c54f2,0xfeebe69c,0x22867c2b,0x3e048240,0xe26918f7,0x821eaa99,0x5e73302e,
0x77f5ad48,0xab9837ff,0xcbef8591,0x17821f26,0x0b00e14d,0xd76d7bfa,0xb71ac994,0x6b775323,
0x8e1f3542,0x5272aff5,0x32051d9b,0xee68872c,0xf2ea7947,0x2e87e3f0,0x4ef0519e,0x929dcb29,
0x80e180eb,0x5c8c1a5c,0x3cfba832,0xe0963285,0xfc14ccee,0x20795659,0x400ee437,0x9c637e80,
0x790b18e1,0xa5668256,0xc5113038,0x197caa8f,0x05fe54e4,0xd993ce53,0xb9e47c3d,0x6589e68a,
0x9d1cebb9,0x4171710e,0x2106c360,0xfd6b59d7,0xe1e9a7bc,0x3d843d0b,0x5df38f65,0x819e15d2,
0x64f673b3,0xb89be904,0xd8ec5b6a,0x0481c1dd,0x18033fb6,0xc46ea501,0xa419176f,0x78748dd8,
0x6a08c61a,0xb6655cad,0xd612eec3,0x0a7f7474,0x16fd8a1f,0xca9010a8,0xaae7a2c6,0x768a3871,
0x93e25e10,0x4f8fc4a7,0x2ff876c9,0xf395ec7e,0xef171215,0x337a88a2,0x530d3acc,0x8f60a07b},
{0x00000000,0x490d678d,0x921acf1a,0xdb17a897,0x20f48383,0x69f9e40e,0xb2ee4c99,0xfbe32b14,
0x41e90706,0x08e4608b,0xd3f3c81c,0x9afeaf91,0x611d8485,0x2810e308,0xf3074b9f,0xba0a2c12,
0x83d20e0c,0xcadf6981,0x11c8c116,0x58c5a69b,0xa3268d8f,0xea2bea02,0x313c4295,0x78312518,
0xc23b090a,0x8b366e87,0x5021c610,0x192ca19d,0xe2cf8a89,0xabc2ed04,0x70d54593,0x39d8221e,
0x036501af,0x4a686622,0x917fceb5,0xd872a938,0x2391822c,0x6a9ce5a1,0xb18b4d36,0xf8862abb,
0x428c06a9,0x0b816124,0xd096c9b3,0x999bae3e,0x6278852a,0x2b75e2a7,0xf0624a30,0xb96f2dbd,
0x80b70fa3,0xc9ba682e,0x12adc0b9,0x5ba0a734,0xa0438c20,0xe94eebad,0x3259433a,0x7b5424b7,
0xc15e08a5,0x88536f28,0x5344c7bf,0x1a49a032,0xe1aa8b26,0xa8a7ecab,0x73b0443c,0x3abd23b1,
0x06ca035e,0x4fc764d3,0x94d0cc44,0xddddabc9,0x263e80dd,0x6f33e750,0xb4244fc7,0xfd29284a,
0x47230458,0x0e2e63d5,0xd539cb42,0x9c34accf,0x67d787db,0x2edae056,0xf5cd48c1,0xbcc02f4c,
0x85180d52,0xcc156adf,0x1702c248,0x5e0fa5c5,0xa5ec8ed1,0xece1e95c,0x37f641cb,0x7efb2646,
0xc4f10a54,0x8dfc6dd9,0x56ebc54e,0x1fe6a2c3,0xe40589d7,0xad08ee5a,0x761f46cd,0x3f122140,
0x05af02f1,0x4ca2657c,0x97b5cdeb,0xdeb8aa66,0x255b8172,0x6c56e6ff,0xb7414e68,0xfe4c29e5,
0x444605f7,0x0d4b627a,0xd65ccaed,0x9f51ad60,0x64b28674,0x2dbfe1f9,0xf6a8496e,0xbfa52ee3,
0x867d0cfd,0xcf706b70,0x1467c3e7,0x5d6aa46a,0xa6898f7e,0xef84e8f3,0x34934064,0x7d9e27e9,
0xc7940bfb,0x8e996c76,0x558ec4e1,0x1c83a36c,0xe7608878,0xae6deff5,0x757a4762,0x3c7720ef,
0x0d9406bc,0x44996131,0x9f8ec9a6,0xd683ae2b,0x2d60853f,0x646de2b2,0xbf7a4a25,0xf6772da8,
0x4c7d01ba,0x05706637,0xde67cea0,0x976aa92d,0x6c898239,0x2584e5b4,0xfe934d23,0xb79e2aae,
0x8e4608b0,0xc74b6f3d,0x1c5cc7aa,0x5551a027,0xaeb28b33,0xe7bfecbe,0x3ca84429,0x75a523a4,
0xcfaf0fb6,0x86a2683b,0x5db5c0ac,0x14b8a721,0xef5b8c35,0xa656ebb8,0x7d41432f,0x344c24a2,
0x0ef10713,0x47fc609e,0x9cebc809,0xd5e6af84,0x2e058490,0x6708e31d,0xbc1f4b8a,0xf5122c07,
0x4f180015,0x06156798,0xdd02cf0f,0x940fa882,0x6fec8396,0x26e1e41b,0xfdf64c8c,0xb4fb2b01,
0x8d23091f,0xc42e6e92,0x1f39c605,0x5634a188,0xadd78a9c,0xe4daed11,0x3fcd4586,0x76c0220b,
0xccca0e19,0x85c76994,0x5ed0c103,0x17dda68e,0xec3e8d9a,0xa533ea17,0x7e244280,0x3729250d,
0x0b5e05e2,0x4253626f,0x9944caf8,0xd049ad75,0x2baa8661,0x62a7e1ec,0xb9b0497b,0xf0bd2ef6,
0x4ab702e4,0x03ba6569,0xd8adcdfe,0x91a0aa73,0x6a438167,0x234ee6ea,0xf8594e7d,0xb15429f0,
0x888c0bee,0xc1816c63,0x1a96c4f4,0x539ba379,0xa878886d,0xe175efe0,0x3a624777,0x736f20fa,
0xc9650ce8,0x80686b65,0x5b7fc3f2,0x1272a47f,0xe9918f6b,0xa09ce8e6,0x7b8b4071,0x328627fc,
0x083b044d,0x413663c0,0x9a21cb57,0xd32cacda,0x28cf87ce,0x61c2e043,0xbad548d4,0xf3d82f59,
0x49d2034b,0x00df64c6,0xdbc8cc51,0x92c5abdc,0x692680c8,0x202be745,0xfb3c4fd2,0xb231285f,
0x8be90a41,0xc2e46dcc,0x19f3c55b,0x50fea2d6,0xab1d89c2,0xe210ee4f,0x390746d8,0x700a2155,
0xca000d47,0x830d6aca,0x581ac25d,0x1117a5d0,0xeaf48ec4,0xa3f9e949,0x78ee41de,0x31e32653},
{0x00000000,0x1b280d78,0x36501af0,0x2d781788,0x6ca035e0,0x77883898,0x5af02f10,0x41d82268,
0xd9406bc0,0xc26866b8,0xef107130,0xf4387c48,0xb5e05e20,0xaec85358,0x83b044d0,0x989849a8,
0xb641ca37,0xad69c74f,0x8011d0c7,0x9b39ddbf,0xdae1ffd7,0xc1c9f2af,0xecb1e527,0xf799e85f,
0x6f01a1f7,0x7429ac8f,0x5951bb07,0x4279b67f,0x03a19417,0x1889996f,0x35f18ee7,0x2ed9839f,
0x684289d9,0x736a84a1,0x5e129329,0x453a9e51,0x04e2bc39,0x1fcab141,0x32b2a6c9,0x299aabb1,
0xb102e219,0xaa2aef61,0x8752f8e9,0x9c7af591,0xdda2d7f9,0xc68ada81,0xebf2cd09,0xf0dac071,
0xde0343ee,0xc52b4e96,0xe853591e,0xf37b5466,0xb2a3760e,0xa98b7b76,0x84f36cfe,0x9fdb6186,
0x0743282e,0x1c6b2556,0x311332de,0x2a3b3fa6,0x6be31dce,0x70cb10b6,0x5db3073e,0x469b0a46,
0xd08513b2,0xcbad1eca,0xe6d50942,0xfdfd043a,0xbc252652,0xa70d2b2a,0x8a753ca2,0x915d31da,
0x09c57872,0x12ed750a,0x3f956282,0x24bd6ffa,0x65654d92,0x7e4d40ea,0x53355762,0x481d5a1a,
0x66c4d985,0x7decd4fd,0x5094c375,0x4bbcce0d,0x0a64ec65,0x114ce11d,0x3c34f695,0x271cfbed,
0xbf84b245,0xa4acbf3d,0x89d4a8b5,0x92fca5cd,0xd32487a5,0xc80c8add,0xe5749d55,0xfe5c902d,
0xb8c79a6b,0xa3ef9713,0x8e97809b,0x95bf8de3,0xd467af8b,0xcf4fa2f3,0xe237b57b,0xf91fb803,
0x6187f1ab,0x7aaffcd3,0x57d7eb5b,0x4cffe623,0x0d27c44b,0x160fc933,0x3b77debb,0x205fd3c3,
0x0e86505c,0x15ae5d24,0x38d64aac,0x23fe47d4,0x622665bc,0x790e68c4,0x54767f4c,0x4f5e7234,
0xd7c63b9c,0xccee36e4,0xe196216c,0xfabe2c14,0xbb660e7c,0xa04e0304,0x8d36148c,0x961e19f4,
0xa5cb3ad3,0xbee337ab,0x939b2023,0x88b32d5b,0xc96b0f33,0xd243024b,0xff3b15c3,0xe41318bb,
0x7c8b5113,0x67a35c6b,0x4adb4be3,0x51f3469b,0x102b64f3,0x0b03698b,0x267b7e03,0x3d53737b,
0x138af0e4,0x08a2fd9c,0x25daea14,0x3ef2e76c,0x7f2ac504,0x6402c87c,0x497adff4,0x5252d28c,
0xcaca9b24,0xd1e2965c,0xfc9a81d4,0xe7b28cac,0xa66aaec4,0xbd42a3bc,0x903ab434,0x8b12b94c,
0xcd89b30a,0xd6a1be72,0xfbd9a9fa,0xe0f1a482,0xa12986ea,0xba018b92,0x97799c1a,0x8c519162,
0x14c9d8ca,0x0fe1d5b2,0x2299c23a,0x39b1cf42,0x7869ed2a,0x6341e052,0x4e39f7da,0x5511faa2,
0x7bc8793d,0x60e07445,0x4d9863cd,0x56b06eb5,0x17684cdd,0x0c4041a5,0x2138562d,0x3a105b55,
0xa28812fd,0xb9a01f85,0x94d8080d,0x8ff00575,0xce28271d,0xd5002a65,0xf8783ded,0xe3503095,
0x754e2961,0x6e662419,0x431e3391,0x58363ee9,0x19ee1c81,0x02c611f9,0x2fbe0671,0x34960b09,
0xac0e42a1,0xb7264fd9,0x9a5e5851,0x81765529,0xc0ae7741,0xdb867a39,0xf6fe6db1,0xedd660c9,
0xc30fe356,0xd827ee2e,0xf55ff9a6,0xee77f4de,0xafafd6b6,0xb487dbce,0x99ffcc46,0x82d7c13e,
0x1a4f8896,0x016785ee,0x2c1f9266,0x37379f1e,0x76efbd76,0x6dc7b00e,0x40bfa786,0x5b97aafe,
0x1d0ca0b8,0x0624adc0,0x2b5cba48,0x3074b730,0x71ac9558,0x6a849820,0x47fc8fa8,0x5cd482d0,
0xc44ccb78,0xdf64c600,0xf21cd188,0xe934dcf0,0xa8ecfe98,0xb3c4f3e0,0x9ebce468,0x8594e910,
0xab4d6a8f,0xb06567f7,0x9d1d707f,0x86357d07,0xc7ed5f6f,0xdcc55217,0xf1bd459f,0xea9548e7,
0x720d014f,0x69250c37,0x445d1bbf,0x5f7516c7,0x1ead34af,0x058539d7,0x28fd2e5f,0x33d52327},
{0x00000000,0x4f576811,0x9eaed022,0xd1f9b833,0x399cbdf3,0x76cbd5e2,0xa7326dd1,0xe86505c0,
0x73397be6,0x3c6e13f7,0xed97abc4,0xa2c0c3d5,0x4aa5c615,0x05f2ae04,0xd40b1637,0x9b5c7e26,
0xe672f7cc,0xa9259fdd,0x78dc27ee,0x378b4fff,0xdfee4a3f,0x90b9222e,0x41409a1d,0x0e17f20c,
0x954b8c2a,0xda1ce43b,0x0be55c08,0x44b23419,0xacd731d9,0xe38059c8,0x3279e1fb,0x7d2e89ea,
0xc824f22f,0x87739a3e,0x568a220d,0x19dd4a1c,0xf1b84fdc,0xbeef27cd,0x6f169ffe,0x2041f7ef,
0xbb1d89c9,0xf44ae1d8,0x25b359eb,0x6ae431fa,0x8281343a,0xcdd65c2b,0x1c2fe418,0x53788c09,
0x2e5605e3,0x61016df2,0xb0f8d5c1,0xffafbdd0,0x17cab810,0x589dd001,0x89646832,0xc6330023,
0x5d6f7e05,0x12381614,0xc3c1ae27,0x8c96c636,0x64f3c3f6,0x2ba4abe7,0xfa5d13d4,0xb50a7bc5,
0x9488f9e9,0xdbdf91f8,0x0a2629cb,0x457141da,0xad14441a,0xe2432c0b,0x33ba9438,0x7cedfc29,
0xe7b1820f,0xa8e6ea1e,0x791f522d,0x36483a3c,0xde2d3ffc,0x917a57ed,0x4083efde,0x0fd487cf,
0x72fa0e25,0x3dad6634,0xec54de07,0xa303b616,0x4b66b3d6,0x0431dbc7,0xd5c863f4,0x9a9f0be5,
0x01c375c3,0x4e941dd2,0x9f6da5e1,0xd03acdf0,0x385fc830,0x7708a021,0xa6f11812,0xe9a67003,
0x5cac0bc6,0x13fb63d7,0xc202dbe4,0x8d55b3f5,0x6530b635,0x2a67de24,0xfb9e6617,0xb4c90e06,
0x2f957020,0x60c21831,0xb13ba002,0xfe6cc813,0x1609cdd3,0x595ea5c2,0x88a71df1,0xc7f075e0,
0xbadefc0a,0xf589941b,0x24702c28,0x6b274439,0x834241f9,0xcc1529e8,0x1dec91db,0x52bbf9ca,
0xc9e787ec,0x86b0effd,0x574957ce,0x181e3fdf,0xf07b3a1f,0xbf2c520e,0x6ed5ea3d,0x2182822c,
0x2dd0ee65,0x62878674,0xb37e3e47,0xfc295656,0x144c5396,0x5b1b3b87,0x8ae283b4,0xc5b5eba5,
0x5ee99583,0x11befd92,0xc04745a1,0x8f102db0,0x67752870,0x28224061,0xf9dbf852,0xb68c9043,
0xcba219a9,0x84f571b8,0x550cc98b,0x1a5ba19a,0xf23ea45a,0xbd69cc4b,0x6c907478,0x23c71c69,
0xb89b624f,0xf7cc0a5e,0x2635b26d,0x6962da7c,0x8107dfbc,0xce50b7ad,0x1fa90f9e,0x50fe678f,
0xe5f41c4a,0xaaa3745b,0x7b5acc68,0x340da479,0xdc68a1b9,0x933fc9a8,0x42c6719b,0x0d91198a,
0x96cd67ac,0xd99a0fbd,0x0863b78e,0x4734df9f,0xaf51da5f,0xe006b24e,0x31ff0a7d,0x7ea8626c,
0x0386eb86,0x4cd18397,0x9d283ba4,0xd27f53b5,0x3a1a5675,0x754d3e64,0xa4b48657,0xebe3ee46,
0x70bf9060,0x3fe8f871,0xee114042,0xa1462853,0x49232d93,0x06744582,0xd78dfdb1,0x98da95a0,
0xb958178c,0xf60f7f9d,0x27f6c7ae,0x68a1afbf,0x80c4aa7f,0xcf93c26e,0x1e6a7a5d,0x513d124c,
0xca616c6a,0x8536047b,0x54cfbc48,0x1b98d459,0xf3fdd199,0xbcaab988,0x6d5301bb,0x220469aa,
0x5f2ae040,0x107d8851,0xc1843062,0x8ed35873,0x66b65db3,0x29e135a2,0xf8188d91,0xb74fe580,
0x2c139ba6,0x6344f3b7,0xb2bd4b84,0xfdea2395,0x158f2655,0x5ad84e44,0x8b21f677,0xc4769e66,
0x717ce5a3,0x3e2b8db2,0xefd23581,0xa0855d90,0x48e05850,0x07b73041,0xd64e8872,0x9919e063,
0x02459e45,0x4d12f654,0x9ceb4e67,0xd3bc2676,0x3bd923b6,0x748e4ba7,0xa577f394,0xea209b85,
0x970e126f,0xd8597a7e,0x09a0c24d,0x46f7aa5c,0xae92af9c,0xe1c5c78d,0x303c7fbe,0x7f6b17af,
0xe4376989,0xab600198,0x7a99b9ab,0x35ced1ba,0xddabd47a,0x92fcbc6b,0x43050458,0x0c526c49},
{0x00000000,0x5ba1dcca,0xb743b994,0xece2655e,0x6a466e9f,0x31e7b255,0xdd05d70b,0x86a40bc1,
0xd48cdd3e,0x8f2d01f4,0x63cf64aa,0x386eb860,0xbecab3a1,0xe56b6f6b,0x09890a35,0x5228d6ff,
0xadd8a7cb,0xf6797b01,0x1a9b1e5f,0x413ac295,0xc79ec954,0x9c3f159e,0x70dd70c0,0x2b7cac0a,
0x79547af5,0x22f5a63f,0xce17c361,0x95b61fab,0x1312146a,0x48b3c8a0,0xa451adfe,0xfff07134,
0x5f705221,0x04d18eeb,0xe833ebb5,0xb392377f,0x35363cbe,0x6e97e074,0x8275852a,0xd9d459e0,
0x8bfc8f1f,0xd05d53d5,0x3cbf368b,0x671eea41,0xe1bae180,0xba1b3d4a,0x56f95814,0x0d5884de,
0xf2a8f5ea,0xa9092920,0x45eb4c7e,0x1e4a90b4,0x98ee9b75,0xc34f47bf,0x2fad22e1,0x740cfe2b,
0x262428d4,0x7d85f41e,0x91679140,0xcac64d8a,0x4c62464b,0x17c39a81,0xfb21ffdf,0xa0802315,
0xbee0a442,0xe5417888,0x09a31dd6,0x5202c11c,0xd4a6cadd,0x8f071617,0x63e57349,0x3844af83,
0x6a6c797c,0x31cda5b6,0xdd2fc0e8,0x868e1c22,0x002a17e3,0x5b8bcb29,0xb769ae77,0xecc872bd,
0x13380389,0x4899df43,0xa47bba1d,0xffda66d7,0x797e6d16,0x22dfb1dc,0xce3dd482,0x959c0848,
0xc7b4deb7,0x9c15027d,0x70f76723,0x2b56bbe9,0xadf2b028,0xf6536ce2,0x1ab109bc,0x4110d576,
0xe190f663,0xba312aa9,0x56d34ff7,0x0d72933d,0x8bd698fc,0xd0774436,0x3c952168,0x6734fda2,
0x351c2b5d,0x6ebdf797,0x825f92c9,0xd9fe4e03,0x5f5a45c2,0x04fb9908,0xe819fc56,0xb3b8209c,
0x4c4851a8,0x17e98d62,0xfb0be83c,0xa0aa34f6,0x260e3f37,0x7dafe3fd,0x914d86a3,0xcaec5a69,
0x98c48c96,0xc365505c,0x2f873502,0x7426e9c8,0xf282e209,0xa9233ec3,0x45c15b9d,0x1e608757,
0x79005533,0x22a189f9,0xce43eca7,0x95e2306d,0x13463bac,0x48e7e766,0xa4058238,0xffa45ef2,
0xad8c880d,0xf62d54c7,0x1acf3199,0x416eed53,0xc7cae692,0x9c6b3a58,0x70895f06,0x2b2883cc,
0xd4d8f2f8,0x8f792e32,0x639b4b6c,0x383a97a6,0xbe9e9c67,0xe53f40ad,0x09dd25f3,0x527cf939,
0x00542fc6,0x5bf5f30c,0xb7179652,0xecb64a98,0x6a124159,0x31b39d93,0xdd51f8cd,0x86f02407,
0x26700712,0x7dd1dbd8,0x9133be86,0xca92624c,0x4c36698d,0x1797b547,0xfb75d019,0xa0d40cd3,
0xf2fcda2c,0xa95d06e6,0x45bf63b8,0x1e1ebf72,0x98bab4b3,0xc31b6879,0x2ff90d27,0x7458d1ed,
0x8ba8a0d9,0xd0097c13,0x3ceb194d,0x674ac587,0xe1eece46,0xba4f128c,0x56ad77d2,0x0d0cab18,
0x5f247de7,0x0485a12d,0xe867c473,0xb3c618b9,0x35621378,0x6ec3cfb2,0x8221aaec,0xd9807626,
0xc7e0f171,0x9c412dbb,0x70a348e5,0x2b02942f,0xada69fee,0xf6074324,0x1ae5267a,0x4144fab0,
0x136c2c4f,0x48cdf085,0xa42f95db,0xff8e4911,0x792a42d0,0x228b9e1a,0xce69fb44,0x95c8278e,
0x6a3856ba,0x31998a70,0xdd7bef2e,0x86da33e4,0x007e3825,0x5bdfe4ef,0xb73d81b1,0xec9c5d7b,
0xbeb48b84,0xe515574e,0x09f73210,0x5256eeda,0xd4f2e51b,0x8f5339d1,0x63b15c8f,0x38108045,
0x9890a350,0xc3317f9a,0x2fd31ac4,0x7472c60e,0xf2d6cdcf,0xa9771105,0x4595745b,0x1e34a891,
0x4c1c7e6e,0x17bda2a4,0xfb5fc7fa,0xa0fe1b30,0x265a10f1,0x7dfbcc3b,0x9119a965,0xcab875af,
0x3548049b,0x6ee9d851,0x820bbd0f,0xd9aa61c5,0x5f0e6a04,0x04afb6ce,0xe84dd390,0xb3ec0f5a,
0xe1c4d9a5,0xba65056f,0x56876031,0x0d26bcfb,0x8b82b73a,0xd0236bf0,0x3cc10eae,0x6760d264}};

2109
third-party/ogg/Sources/framing.c vendored Normal file

File diff suppressed because it is too large Load Diff

209
third-party/ogg/include/ogg/ogg.h vendored Normal file
View File

@ -0,0 +1,209 @@
/********************************************************************
* *
* THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
* *
* THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 *
* by the Xiph.Org Foundation http://www.xiph.org/ *
* *
********************************************************************
function: toplevel libogg include
********************************************************************/
#ifndef _OGG_H
#define _OGG_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <ogg/os_types.h>
typedef struct {
void *iov_base;
size_t iov_len;
} ogg_iovec_t;
typedef struct {
long endbyte;
int endbit;
unsigned char *buffer;
unsigned char *ptr;
long storage;
} oggpack_buffer;
/* ogg_page is used to encapsulate the data in one Ogg bitstream page *****/
typedef struct {
unsigned char *header;
long header_len;
unsigned char *body;
long body_len;
} ogg_page;
/* ogg_stream_state contains the current encode/decode state of a logical
Ogg bitstream **********************************************************/
typedef struct {
unsigned char *body_data; /* bytes from packet bodies */
long body_storage; /* storage elements allocated */
long body_fill; /* elements stored; fill mark */
long body_returned; /* elements of fill returned */
int *lacing_vals; /* The values that will go to the segment table */
ogg_int64_t *granule_vals; /* granulepos values for headers. Not compact
this way, but it is simple coupled to the
lacing fifo */
long lacing_storage;
long lacing_fill;
long lacing_packet;
long lacing_returned;
unsigned char header[282]; /* working space for header encode */
int header_fill;
int e_o_s; /* set when we have buffered the last packet in the
logical bitstream */
int b_o_s; /* set after we've written the initial page
of a logical bitstream */
long serialno;
long pageno;
ogg_int64_t packetno; /* sequence number for decode; the framing
knows where there's a hole in the data,
but we need coupling so that the codec
(which is in a separate abstraction
layer) also knows about the gap */
ogg_int64_t granulepos;
} ogg_stream_state;
/* ogg_packet is used to encapsulate the data and metadata belonging
to a single raw Ogg/Vorbis packet *************************************/
typedef struct {
unsigned char *packet;
long bytes;
long b_o_s;
long e_o_s;
ogg_int64_t granulepos;
ogg_int64_t packetno; /* sequence number for decode; the framing
knows where there's a hole in the data,
but we need coupling so that the codec
(which is in a separate abstraction
layer) also knows about the gap */
} ogg_packet;
typedef struct {
unsigned char *data;
int storage;
int fill;
int returned;
int unsynced;
int headerbytes;
int bodybytes;
} ogg_sync_state;
/* Ogg BITSTREAM PRIMITIVES: bitstream ************************/
extern void oggpack_writeinit(oggpack_buffer *b);
extern int oggpack_writecheck(oggpack_buffer *b);
extern void oggpack_writetrunc(oggpack_buffer *b,long bits);
extern void oggpack_writealign(oggpack_buffer *b);
extern void oggpack_writecopy(oggpack_buffer *b,void *source,long bits);
extern void oggpack_reset(oggpack_buffer *b);
extern void oggpack_writeclear(oggpack_buffer *b);
extern void oggpack_readinit(oggpack_buffer *b,unsigned char *buf,int bytes);
extern void oggpack_write(oggpack_buffer *b,unsigned long value,int bits);
extern long oggpack_look(oggpack_buffer *b,int bits);
extern long oggpack_look1(oggpack_buffer *b);
extern void oggpack_adv(oggpack_buffer *b,int bits);
extern void oggpack_adv1(oggpack_buffer *b);
extern long oggpack_read(oggpack_buffer *b,int bits);
extern long oggpack_read1(oggpack_buffer *b);
extern long oggpack_bytes(oggpack_buffer *b);
extern long oggpack_bits(oggpack_buffer *b);
extern unsigned char *oggpack_get_buffer(oggpack_buffer *b);
extern void oggpackB_writeinit(oggpack_buffer *b);
extern int oggpackB_writecheck(oggpack_buffer *b);
extern void oggpackB_writetrunc(oggpack_buffer *b,long bits);
extern void oggpackB_writealign(oggpack_buffer *b);
extern void oggpackB_writecopy(oggpack_buffer *b,void *source,long bits);
extern void oggpackB_reset(oggpack_buffer *b);
extern void oggpackB_writeclear(oggpack_buffer *b);
extern void oggpackB_readinit(oggpack_buffer *b,unsigned char *buf,int bytes);
extern void oggpackB_write(oggpack_buffer *b,unsigned long value,int bits);
extern long oggpackB_look(oggpack_buffer *b,int bits);
extern long oggpackB_look1(oggpack_buffer *b);
extern void oggpackB_adv(oggpack_buffer *b,int bits);
extern void oggpackB_adv1(oggpack_buffer *b);
extern long oggpackB_read(oggpack_buffer *b,int bits);
extern long oggpackB_read1(oggpack_buffer *b);
extern long oggpackB_bytes(oggpack_buffer *b);
extern long oggpackB_bits(oggpack_buffer *b);
extern unsigned char *oggpackB_get_buffer(oggpack_buffer *b);
/* Ogg BITSTREAM PRIMITIVES: encoding **************************/
extern int ogg_stream_packetin(ogg_stream_state *os, ogg_packet *op);
extern int ogg_stream_iovecin(ogg_stream_state *os, ogg_iovec_t *iov,
int count, long e_o_s, ogg_int64_t granulepos);
extern int ogg_stream_pageout(ogg_stream_state *os, ogg_page *og);
extern int ogg_stream_pageout_fill(ogg_stream_state *os, ogg_page *og, int nfill);
extern int ogg_stream_flush(ogg_stream_state *os, ogg_page *og);
extern int ogg_stream_flush_fill(ogg_stream_state *os, ogg_page *og, int nfill);
/* Ogg BITSTREAM PRIMITIVES: decoding **************************/
extern int ogg_sync_init(ogg_sync_state *oy);
extern int ogg_sync_clear(ogg_sync_state *oy);
extern int ogg_sync_reset(ogg_sync_state *oy);
extern int ogg_sync_destroy(ogg_sync_state *oy);
extern int ogg_sync_check(ogg_sync_state *oy);
extern char *ogg_sync_buffer(ogg_sync_state *oy, long size);
extern int ogg_sync_wrote(ogg_sync_state *oy, long bytes);
extern long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og);
extern int ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og);
extern int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og);
extern int ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op);
extern int ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op);
/* Ogg BITSTREAM PRIMITIVES: general ***************************/
extern int ogg_stream_init(ogg_stream_state *os,int serialno);
extern int ogg_stream_clear(ogg_stream_state *os);
extern int ogg_stream_reset(ogg_stream_state *os);
extern int ogg_stream_reset_serialno(ogg_stream_state *os,int serialno);
extern int ogg_stream_destroy(ogg_stream_state *os);
extern int ogg_stream_check(ogg_stream_state *os);
extern int ogg_stream_eos(ogg_stream_state *os);
extern void ogg_page_checksum_set(ogg_page *og);
extern int ogg_page_version(const ogg_page *og);
extern int ogg_page_continued(const ogg_page *og);
extern int ogg_page_bos(const ogg_page *og);
extern int ogg_page_eos(const ogg_page *og);
extern ogg_int64_t ogg_page_granulepos(const ogg_page *og);
extern int ogg_page_serialno(const ogg_page *og);
extern long ogg_page_pageno(const ogg_page *og);
extern int ogg_page_packets(const ogg_page *og);
extern void ogg_packet_clear(ogg_packet *op);
#ifdef __cplusplus
}
#endif
#endif /* _OGG_H */

158
third-party/ogg/include/ogg/os_types.h vendored Normal file
View File

@ -0,0 +1,158 @@
/********************************************************************
* *
* THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
* *
* THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 *
* by the Xiph.Org Foundation http://www.xiph.org/ *
* *
********************************************************************
function: Define a consistent set of types on each platform.
********************************************************************/
#ifndef _OS_TYPES_H
#define _OS_TYPES_H
/* make it easy on the folks that want to compile the libs with a
different malloc than stdlib */
#define _ogg_malloc malloc
#define _ogg_calloc calloc
#define _ogg_realloc realloc
#define _ogg_free free
#if defined(_WIN32)
# if defined(__CYGWIN__)
# include <stdint.h>
typedef int16_t ogg_int16_t;
typedef uint16_t ogg_uint16_t;
typedef int32_t ogg_int32_t;
typedef uint32_t ogg_uint32_t;
typedef int64_t ogg_int64_t;
typedef uint64_t ogg_uint64_t;
# elif defined(__MINGW32__)
# include <sys/types.h>
typedef short ogg_int16_t;
typedef unsigned short ogg_uint16_t;
typedef int ogg_int32_t;
typedef unsigned int ogg_uint32_t;
typedef long long ogg_int64_t;
typedef unsigned long long ogg_uint64_t;
# elif defined(__MWERKS__)
typedef long long ogg_int64_t;
typedef unsigned long long ogg_uint64_t;
typedef int ogg_int32_t;
typedef unsigned int ogg_uint32_t;
typedef short ogg_int16_t;
typedef unsigned short ogg_uint16_t;
# else
# if defined(_MSC_VER) && (_MSC_VER >= 1800) /* MSVC 2013 and newer */
# include <stdint.h>
typedef int16_t ogg_int16_t;
typedef uint16_t ogg_uint16_t;
typedef int32_t ogg_int32_t;
typedef uint32_t ogg_uint32_t;
typedef int64_t ogg_int64_t;
typedef uint64_t ogg_uint64_t;
# else
/* MSVC/Borland */
typedef __int64 ogg_int64_t;
typedef __int32 ogg_int32_t;
typedef unsigned __int32 ogg_uint32_t;
typedef unsigned __int64 ogg_uint64_t;
typedef __int16 ogg_int16_t;
typedef unsigned __int16 ogg_uint16_t;
# endif
# endif
#elif (defined(__APPLE__) && defined(__MACH__)) /* MacOS X Framework build */
# include <sys/types.h>
typedef int16_t ogg_int16_t;
typedef uint16_t ogg_uint16_t;
typedef int32_t ogg_int32_t;
typedef uint32_t ogg_uint32_t;
typedef int64_t ogg_int64_t;
typedef uint64_t ogg_uint64_t;
#elif defined(__HAIKU__)
/* Haiku */
# include <sys/types.h>
typedef short ogg_int16_t;
typedef unsigned short ogg_uint16_t;
typedef int ogg_int32_t;
typedef unsigned int ogg_uint32_t;
typedef long long ogg_int64_t;
typedef unsigned long long ogg_uint64_t;
#elif defined(__BEOS__)
/* Be */
# include <inttypes.h>
typedef int16_t ogg_int16_t;
typedef uint16_t ogg_uint16_t;
typedef int32_t ogg_int32_t;
typedef uint32_t ogg_uint32_t;
typedef int64_t ogg_int64_t;
typedef uint64_t ogg_uint64_t;
#elif defined (__EMX__)
/* OS/2 GCC */
typedef short ogg_int16_t;
typedef unsigned short ogg_uint16_t;
typedef int ogg_int32_t;
typedef unsigned int ogg_uint32_t;
typedef long long ogg_int64_t;
typedef unsigned long long ogg_uint64_t;
#elif defined (DJGPP)
/* DJGPP */
typedef short ogg_int16_t;
typedef int ogg_int32_t;
typedef unsigned int ogg_uint32_t;
typedef long long ogg_int64_t;
typedef unsigned long long ogg_uint64_t;
#elif defined(R5900)
/* PS2 EE */
typedef long ogg_int64_t;
typedef unsigned long ogg_uint64_t;
typedef int ogg_int32_t;
typedef unsigned ogg_uint32_t;
typedef short ogg_int16_t;
#elif defined(__SYMBIAN32__)
/* Symbian GCC */
typedef signed short ogg_int16_t;
typedef unsigned short ogg_uint16_t;
typedef signed int ogg_int32_t;
typedef unsigned int ogg_uint32_t;
typedef long long int ogg_int64_t;
typedef unsigned long long int ogg_uint64_t;
#elif defined(__TMS320C6X__)
/* TI C64x compiler */
typedef signed short ogg_int16_t;
typedef unsigned short ogg_uint16_t;
typedef signed int ogg_int32_t;
typedef unsigned int ogg_uint32_t;
typedef long long int ogg_int64_t;
typedef unsigned long long int ogg_uint64_t;
#else
# include <ogg/config_types.h>
#endif
#endif /* _OS_TYPES_H */

25
third-party/opusfile/BUILD vendored Normal file
View File

@ -0,0 +1,25 @@
objc_library(
name = "opusfile",
enable_modules = True,
module_name = "opusfile",
srcs = glob([
"Sources/*.c",
]),
hdrs = glob([
"include/opusfile/*.h",
]),
includes = [
"include",
],
copts = [
"-Ithird-party/opusfile/include/opusfile",
],
deps = [
"//third-party/ogg:ogg",
"//third-party/opus:opus",
],
visibility = [
"//visibility:public",
],
)

3592
third-party/opusfile/Sources/http.c vendored Normal file

File diff suppressed because it is too large Load Diff

775
third-party/opusfile/Sources/info.c vendored Normal file
View File

@ -0,0 +1,775 @@
/********************************************************************
* *
* THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. *
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
* *
* THE libopusfile SOURCE CODE IS (C) COPYRIGHT 2012-2020 *
* by the Xiph.Org Foundation and contributors https://xiph.org/ *
* *
********************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "internal.h"
#include <limits.h>
#include <string.h>
static unsigned op_parse_uint16le(const unsigned char *_data){
return _data[0]|_data[1]<<8;
}
static int op_parse_int16le(const unsigned char *_data){
int ret;
ret=_data[0]|_data[1]<<8;
return (ret^0x8000)-0x8000;
}
static opus_uint32 op_parse_uint32le(const unsigned char *_data){
return _data[0]|(opus_uint32)_data[1]<<8|
(opus_uint32)_data[2]<<16|(opus_uint32)_data[3]<<24;
}
static opus_uint32 op_parse_uint32be(const unsigned char *_data){
return _data[3]|(opus_uint32)_data[2]<<8|
(opus_uint32)_data[1]<<16|(opus_uint32)_data[0]<<24;
}
int opus_head_parse(OpusHead *_head,const unsigned char *_data,size_t _len){
OpusHead head;
if(_len<8)return OP_ENOTFORMAT;
if(memcmp(_data,"OpusHead",8)!=0)return OP_ENOTFORMAT;
if(_len<9)return OP_EBADHEADER;
head.version=_data[8];
if(head.version>15)return OP_EVERSION;
if(_len<19)return OP_EBADHEADER;
head.channel_count=_data[9];
head.pre_skip=op_parse_uint16le(_data+10);
head.input_sample_rate=op_parse_uint32le(_data+12);
head.output_gain=op_parse_int16le(_data+16);
head.mapping_family=_data[18];
if(head.mapping_family==0){
if(head.channel_count<1||head.channel_count>2)return OP_EBADHEADER;
if(head.version<=1&&_len>19)return OP_EBADHEADER;
head.stream_count=1;
head.coupled_count=head.channel_count-1;
if(_head!=NULL){
_head->mapping[0]=0;
_head->mapping[1]=1;
}
}
else if(head.mapping_family==1){
size_t size;
int ci;
if(head.channel_count<1||head.channel_count>8)return OP_EBADHEADER;
size=21+head.channel_count;
if(_len<size||head.version<=1&&_len>size)return OP_EBADHEADER;
head.stream_count=_data[19];
if(head.stream_count<1)return OP_EBADHEADER;
head.coupled_count=_data[20];
if(head.coupled_count>head.stream_count)return OP_EBADHEADER;
for(ci=0;ci<head.channel_count;ci++){
if(_data[21+ci]>=head.stream_count+head.coupled_count
&&_data[21+ci]!=255){
return OP_EBADHEADER;
}
}
if(_head!=NULL)memcpy(_head->mapping,_data+21,head.channel_count);
}
/*General purpose players should not attempt to play back content with
channel mapping family 255.*/
else if(head.mapping_family==255)return OP_EIMPL;
/*No other channel mapping families are currently defined.*/
else return OP_EBADHEADER;
if(_head!=NULL)memcpy(_head,&head,head.mapping-(unsigned char *)&head);
return 0;
}
void opus_tags_init(OpusTags *_tags){
memset(_tags,0,sizeof(*_tags));
}
void opus_tags_clear(OpusTags *_tags){
int ncomments;
int ci;
ncomments=_tags->comments;
if(_tags->user_comments!=NULL)ncomments++;
else{
OP_ASSERT(ncomments==0);
}
for(ci=ncomments;ci-->0;)_ogg_free(_tags->user_comments[ci]);
_ogg_free(_tags->user_comments);
_ogg_free(_tags->comment_lengths);
_ogg_free(_tags->vendor);
}
/*Ensure there's room for up to _ncomments comments.*/
static int op_tags_ensure_capacity(OpusTags *_tags,size_t _ncomments){
char **user_comments;
int *comment_lengths;
int cur_ncomments;
size_t size;
if(OP_UNLIKELY(_ncomments>=(size_t)INT_MAX))return OP_EFAULT;
size=sizeof(*_tags->comment_lengths)*(_ncomments+1);
if(size/sizeof(*_tags->comment_lengths)!=_ncomments+1)return OP_EFAULT;
cur_ncomments=_tags->comments;
/*We only support growing.
Trimming requires cleaning up the allocated strings in the old space, and
is best handled separately if it's ever needed.*/
OP_ASSERT(_ncomments>=(size_t)cur_ncomments);
comment_lengths=(int *)_ogg_realloc(_tags->comment_lengths,size);
if(OP_UNLIKELY(comment_lengths==NULL))return OP_EFAULT;
if(_tags->comment_lengths==NULL){
OP_ASSERT(cur_ncomments==0);
comment_lengths[cur_ncomments]=0;
}
comment_lengths[_ncomments]=comment_lengths[cur_ncomments];
_tags->comment_lengths=comment_lengths;
size=sizeof(*_tags->user_comments)*(_ncomments+1);
if(size/sizeof(*_tags->user_comments)!=_ncomments+1)return OP_EFAULT;
user_comments=(char **)_ogg_realloc(_tags->user_comments,size);
if(OP_UNLIKELY(user_comments==NULL))return OP_EFAULT;
if(_tags->user_comments==NULL){
OP_ASSERT(cur_ncomments==0);
user_comments[cur_ncomments]=NULL;
}
user_comments[_ncomments]=user_comments[cur_ncomments];
_tags->user_comments=user_comments;
return 0;
}
/*Duplicate a (possibly non-NUL terminated) string with a known length.*/
static char *op_strdup_with_len(const char *_s,size_t _len){
size_t size;
char *ret;
size=sizeof(*ret)*(_len+1);
if(OP_UNLIKELY(size<_len))return NULL;
ret=(char *)_ogg_malloc(size);
if(OP_LIKELY(ret!=NULL)){
ret=(char *)memcpy(ret,_s,sizeof(*ret)*_len);
ret[_len]='\0';
}
return ret;
}
/*The actual implementation of opus_tags_parse().
Unlike the public API, this function requires _tags to already be
initialized, modifies its contents before success is guaranteed, and assumes
the caller will clear it on error.*/
static int opus_tags_parse_impl(OpusTags *_tags,
const unsigned char *_data,size_t _len){
opus_uint32 count;
size_t len;
int ncomments;
int ci;
len=_len;
if(len<8)return OP_ENOTFORMAT;
if(memcmp(_data,"OpusTags",8)!=0)return OP_ENOTFORMAT;
if(len<16)return OP_EBADHEADER;
_data+=8;
len-=8;
count=op_parse_uint32le(_data);
_data+=4;
len-=4;
if(count>len)return OP_EBADHEADER;
if(_tags!=NULL){
_tags->vendor=op_strdup_with_len((char *)_data,count);
if(_tags->vendor==NULL)return OP_EFAULT;
}
_data+=count;
len-=count;
if(len<4)return OP_EBADHEADER;
count=op_parse_uint32le(_data);
_data+=4;
len-=4;
/*Check to make sure there's minimally sufficient data left in the packet.*/
if(count>len>>2)return OP_EBADHEADER;
/*Check for overflow (the API limits this to an int).*/
if(count>(opus_uint32)INT_MAX-1)return OP_EFAULT;
if(_tags!=NULL){
int ret;
ret=op_tags_ensure_capacity(_tags,count);
if(ret<0)return ret;
}
ncomments=(int)count;
for(ci=0;ci<ncomments;ci++){
/*Check to make sure there's minimally sufficient data left in the packet.*/
if((size_t)(ncomments-ci)>len>>2)return OP_EBADHEADER;
count=op_parse_uint32le(_data);
_data+=4;
len-=4;
if(count>len)return OP_EBADHEADER;
/*Check for overflow (the API limits this to an int).*/
if(count>(opus_uint32)INT_MAX)return OP_EFAULT;
if(_tags!=NULL){
_tags->user_comments[ci]=op_strdup_with_len((char *)_data,count);
if(_tags->user_comments[ci]==NULL)return OP_EFAULT;
_tags->comment_lengths[ci]=(int)count;
_tags->comments=ci+1;
/*Needed by opus_tags_clear() if we fail before parsing the (optional)
binary metadata.*/
_tags->user_comments[ci+1]=NULL;
}
_data+=count;
len-=count;
}
if(len>0&&(_data[0]&1)){
if(len>(opus_uint32)INT_MAX)return OP_EFAULT;
if(_tags!=NULL){
_tags->user_comments[ncomments]=(char *)_ogg_malloc(len);
if(OP_UNLIKELY(_tags->user_comments[ncomments]==NULL))return OP_EFAULT;
memcpy(_tags->user_comments[ncomments],_data,len);
_tags->comment_lengths[ncomments]=(int)len;
}
}
return 0;
}
int opus_tags_parse(OpusTags *_tags,const unsigned char *_data,size_t _len){
if(_tags!=NULL){
OpusTags tags;
int ret;
opus_tags_init(&tags);
ret=opus_tags_parse_impl(&tags,_data,_len);
if(ret<0)opus_tags_clear(&tags);
else *_tags=*&tags;
return ret;
}
else return opus_tags_parse_impl(NULL,_data,_len);
}
/*The actual implementation of opus_tags_copy().
Unlike the public API, this function requires _dst to already be
initialized, modifies its contents before success is guaranteed, and assumes
the caller will clear it on error.*/
static int opus_tags_copy_impl(OpusTags *_dst,const OpusTags *_src){
char *vendor;
int ncomments;
int ret;
int ci;
vendor=_src->vendor;
_dst->vendor=op_strdup_with_len(vendor,strlen(vendor));
if(OP_UNLIKELY(_dst->vendor==NULL))return OP_EFAULT;
ncomments=_src->comments;
ret=op_tags_ensure_capacity(_dst,ncomments);
if(OP_UNLIKELY(ret<0))return ret;
for(ci=0;ci<ncomments;ci++){
int len;
len=_src->comment_lengths[ci];
OP_ASSERT(len>=0);
_dst->user_comments[ci]=op_strdup_with_len(_src->user_comments[ci],len);
if(OP_UNLIKELY(_dst->user_comments[ci]==NULL))return OP_EFAULT;
_dst->comment_lengths[ci]=len;
_dst->comments=ci+1;
}
if(_src->comment_lengths!=NULL){
int len;
len=_src->comment_lengths[ncomments];
if(len>0){
_dst->user_comments[ncomments]=(char *)_ogg_malloc(len);
if(OP_UNLIKELY(_dst->user_comments[ncomments]==NULL))return OP_EFAULT;
memcpy(_dst->user_comments[ncomments],_src->user_comments[ncomments],len);
_dst->comment_lengths[ncomments]=len;
}
}
return 0;
}
int opus_tags_copy(OpusTags *_dst,const OpusTags *_src){
OpusTags dst;
int ret;
opus_tags_init(&dst);
ret=opus_tags_copy_impl(&dst,_src);
if(OP_UNLIKELY(ret<0))opus_tags_clear(&dst);
else *_dst=*&dst;
return ret;
}
int opus_tags_add(OpusTags *_tags,const char *_tag,const char *_value){
char *comment;
size_t tag_len;
size_t value_len;
int ncomments;
int ret;
ncomments=_tags->comments;
ret=op_tags_ensure_capacity(_tags,ncomments+1);
if(OP_UNLIKELY(ret<0))return ret;
tag_len=strlen(_tag);
value_len=strlen(_value);
/*+2 for '=' and '\0'.*/
if(tag_len+value_len<tag_len)return OP_EFAULT;
if(tag_len+value_len>(size_t)INT_MAX-2)return OP_EFAULT;
comment=(char *)_ogg_malloc(sizeof(*comment)*(tag_len+value_len+2));
if(OP_UNLIKELY(comment==NULL))return OP_EFAULT;
memcpy(comment,_tag,sizeof(*comment)*tag_len);
comment[tag_len]='=';
memcpy(comment+tag_len+1,_value,sizeof(*comment)*(value_len+1));
_tags->user_comments[ncomments]=comment;
_tags->comment_lengths[ncomments]=(int)(tag_len+value_len+1);
_tags->comments=ncomments+1;
return 0;
}
int opus_tags_add_comment(OpusTags *_tags,const char *_comment){
char *comment;
int comment_len;
int ncomments;
int ret;
ncomments=_tags->comments;
ret=op_tags_ensure_capacity(_tags,ncomments+1);
if(OP_UNLIKELY(ret<0))return ret;
comment_len=(int)strlen(_comment);
comment=op_strdup_with_len(_comment,comment_len);
if(OP_UNLIKELY(comment==NULL))return OP_EFAULT;
_tags->user_comments[ncomments]=comment;
_tags->comment_lengths[ncomments]=comment_len;
_tags->comments=ncomments+1;
return 0;
}
int opus_tags_set_binary_suffix(OpusTags *_tags,
const unsigned char *_data,int _len){
unsigned char *binary_suffix_data;
int ncomments;
int ret;
if(_len<0||_len>0&&(_data==NULL||!(_data[0]&1)))return OP_EINVAL;
ncomments=_tags->comments;
ret=op_tags_ensure_capacity(_tags,ncomments);
if(OP_UNLIKELY(ret<0))return ret;
binary_suffix_data=
(unsigned char *)_ogg_realloc(_tags->user_comments[ncomments],_len);
if(OP_UNLIKELY(binary_suffix_data==NULL))return OP_EFAULT;
memcpy(binary_suffix_data,_data,_len);
_tags->user_comments[ncomments]=(char *)binary_suffix_data;
_tags->comment_lengths[ncomments]=_len;
return 0;
}
int opus_tagcompare(const char *_tag_name,const char *_comment){
size_t tag_len;
tag_len=strlen(_tag_name);
if(OP_UNLIKELY(tag_len>(size_t)INT_MAX))return -1;
return opus_tagncompare(_tag_name,(int)tag_len,_comment);
}
int opus_tagncompare(const char *_tag_name,int _tag_len,const char *_comment){
int ret;
OP_ASSERT(_tag_len>=0);
ret=op_strncasecmp(_tag_name,_comment,_tag_len);
return ret?ret:'='-_comment[_tag_len];
}
const char *opus_tags_query(const OpusTags *_tags,const char *_tag,int _count){
char **user_comments;
size_t tag_len;
int found;
int ncomments;
int ci;
tag_len=strlen(_tag);
if(OP_UNLIKELY(tag_len>(size_t)INT_MAX))return NULL;
ncomments=_tags->comments;
user_comments=_tags->user_comments;
found=0;
for(ci=0;ci<ncomments;ci++){
if(!opus_tagncompare(_tag,(int)tag_len,user_comments[ci])){
/*We return a pointer to the data, not a copy.*/
if(_count==found++)return user_comments[ci]+tag_len+1;
}
}
/*Didn't find anything.*/
return NULL;
}
int opus_tags_query_count(const OpusTags *_tags,const char *_tag){
char **user_comments;
size_t tag_len;
int found;
int ncomments;
int ci;
tag_len=strlen(_tag);
if(OP_UNLIKELY(tag_len>(size_t)INT_MAX))return 0;
ncomments=_tags->comments;
user_comments=_tags->user_comments;
found=0;
for(ci=0;ci<ncomments;ci++){
if(!opus_tagncompare(_tag,(int)tag_len,user_comments[ci]))found++;
}
return found;
}
const unsigned char *opus_tags_get_binary_suffix(const OpusTags *_tags,
int *_len){
int ncomments;
int len;
ncomments=_tags->comments;
len=_tags->comment_lengths==NULL?0:_tags->comment_lengths[ncomments];
*_len=len;
OP_ASSERT(len==0||_tags->user_comments!=NULL);
return len>0?(const unsigned char *)_tags->user_comments[ncomments]:NULL;
}
static int opus_tags_get_gain(const OpusTags *_tags,int *_gain_q8,
const char *_tag_name,size_t _tag_len){
char **comments;
int ncomments;
int ci;
comments=_tags->user_comments;
ncomments=_tags->comments;
/*Look for the first valid tag with the name _tag_name and use that.*/
for(ci=0;ci<ncomments;ci++){
OP_ASSERT(_tag_len<=(size_t)INT_MAX);
if(opus_tagncompare(_tag_name,(int)_tag_len,comments[ci])==0){
char *p;
opus_int32 gain_q8;
int negative;
p=comments[ci]+_tag_len+1;
negative=0;
if(*p=='-'){
negative=-1;
p++;
}
else if(*p=='+')p++;
gain_q8=0;
while(*p>='0'&&*p<='9'){
gain_q8=10*gain_q8+*p-'0';
if(gain_q8>32767-negative)break;
p++;
}
/*This didn't look like a signed 16-bit decimal integer.
Not a valid gain tag.*/
if(*p!='\0')continue;
*_gain_q8=(int)(gain_q8+negative^negative);
return 0;
}
}
return OP_FALSE;
}
int opus_tags_get_album_gain(const OpusTags *_tags,int *_gain_q8){
return opus_tags_get_gain(_tags,_gain_q8,"R128_ALBUM_GAIN",15);
}
int opus_tags_get_track_gain(const OpusTags *_tags,int *_gain_q8){
return opus_tags_get_gain(_tags,_gain_q8,"R128_TRACK_GAIN",15);
}
static int op_is_jpeg(const unsigned char *_buf,size_t _buf_sz){
return _buf_sz>=3&&memcmp(_buf,"\xFF\xD8\xFF",3)==0;
}
/*Tries to extract the width, height, bits per pixel, and palette size of a
JPEG.
On failure, simply leaves its outputs unmodified.*/
static void op_extract_jpeg_params(const unsigned char *_buf,size_t _buf_sz,
opus_uint32 *_width,opus_uint32 *_height,
opus_uint32 *_depth,opus_uint32 *_colors,int *_has_palette){
if(op_is_jpeg(_buf,_buf_sz)){
size_t offs;
offs=2;
for(;;){
size_t segment_len;
int marker;
while(offs<_buf_sz&&_buf[offs]!=0xFF)offs++;
while(offs<_buf_sz&&_buf[offs]==0xFF)offs++;
marker=_buf[offs];
offs++;
/*If we hit EOI* (end of image), or another SOI* (start of image),
or SOS (start of scan), then stop now.*/
if(offs>=_buf_sz||(marker>=0xD8&&marker<=0xDA))break;
/*RST* (restart markers): skip (no segment length).*/
else if(marker>=0xD0&&marker<=0xD7)continue;
/*Read the length of the marker segment.*/
if(_buf_sz-offs<2)break;
segment_len=_buf[offs]<<8|_buf[offs+1];
if(segment_len<2||_buf_sz-offs<segment_len)break;
if(marker==0xC0||(marker>0xC0&&marker<0xD0&&(marker&3)!=0)){
/*Found a SOFn (start of frame) marker segment:*/
if(segment_len>=8){
*_height=_buf[offs+3]<<8|_buf[offs+4];
*_width=_buf[offs+5]<<8|_buf[offs+6];
*_depth=_buf[offs+2]*_buf[offs+7];
*_colors=0;
*_has_palette=0;
}
break;
}
/*Other markers: skip the whole marker segment.*/
offs+=segment_len;
}
}
}
static int op_is_png(const unsigned char *_buf,size_t _buf_sz){
return _buf_sz>=8&&memcmp(_buf,"\x89PNG\x0D\x0A\x1A\x0A",8)==0;
}
/*Tries to extract the width, height, bits per pixel, and palette size of a
PNG.
On failure, simply leaves its outputs unmodified.*/
static void op_extract_png_params(const unsigned char *_buf,size_t _buf_sz,
opus_uint32 *_width,opus_uint32 *_height,
opus_uint32 *_depth,opus_uint32 *_colors,int *_has_palette){
if(op_is_png(_buf,_buf_sz)){
size_t offs;
offs=8;
while(_buf_sz-offs>=12){
ogg_uint32_t chunk_len;
chunk_len=op_parse_uint32be(_buf+offs);
if(chunk_len>_buf_sz-(offs+12))break;
else if(chunk_len==13&&memcmp(_buf+offs+4,"IHDR",4)==0){
int color_type;
*_width=op_parse_uint32be(_buf+offs+8);
*_height=op_parse_uint32be(_buf+offs+12);
color_type=_buf[offs+17];
if(color_type==3){
*_depth=24;
*_has_palette=1;
}
else{
int sample_depth;
sample_depth=_buf[offs+16];
if(color_type==0)*_depth=sample_depth;
else if(color_type==2)*_depth=sample_depth*3;
else if(color_type==4)*_depth=sample_depth*2;
else if(color_type==6)*_depth=sample_depth*4;
*_colors=0;
*_has_palette=0;
break;
}
}
else if(*_has_palette>0&&memcmp(_buf+offs+4,"PLTE",4)==0){
*_colors=chunk_len/3;
break;
}
offs+=12+chunk_len;
}
}
}
static int op_is_gif(const unsigned char *_buf,size_t _buf_sz){
return _buf_sz>=6&&(memcmp(_buf,"GIF87a",6)==0||memcmp(_buf,"GIF89a",6)==0);
}
/*Tries to extract the width, height, bits per pixel, and palette size of a
GIF.
On failure, simply leaves its outputs unmodified.*/
static void op_extract_gif_params(const unsigned char *_buf,size_t _buf_sz,
opus_uint32 *_width,opus_uint32 *_height,
opus_uint32 *_depth,opus_uint32 *_colors,int *_has_palette){
if(op_is_gif(_buf,_buf_sz)&&_buf_sz>=14){
*_width=_buf[6]|_buf[7]<<8;
*_height=_buf[8]|_buf[9]<<8;
/*libFLAC hard-codes the depth to 24.*/
*_depth=24;
*_colors=1<<((_buf[10]&7)+1);
*_has_palette=1;
}
}
/*The actual implementation of opus_picture_tag_parse().
Unlike the public API, this function requires _pic to already be
initialized, modifies its contents before success is guaranteed, and assumes
the caller will clear it on error.*/
static int opus_picture_tag_parse_impl(OpusPictureTag *_pic,const char *_tag,
unsigned char *_buf,size_t _buf_sz,size_t _base64_sz){
opus_int32 picture_type;
opus_uint32 mime_type_length;
char *mime_type;
opus_uint32 description_length;
char *description;
opus_uint32 width;
opus_uint32 height;
opus_uint32 depth;
opus_uint32 colors;
opus_uint32 data_length;
opus_uint32 file_width;
opus_uint32 file_height;
opus_uint32 file_depth;
opus_uint32 file_colors;
int format;
int has_palette;
int colors_set;
size_t i;
/*Decode the BASE64 data.*/
OP_ASSERT(_base64_sz>=11);
for(i=0;i<_base64_sz;i++){
opus_uint32 value;
int j;
value=0;
for(j=0;j<4;j++){
unsigned c;
unsigned d;
c=(unsigned char)_tag[4*i+j];
if(c=='+')d=62;
else if(c=='/')d=63;
else if(c>='0'&&c<='9')d=52+c-'0';
else if(c>='a'&&c<='z')d=26+c-'a';
else if(c>='A'&&c<='Z')d=c-'A';
else if(c=='='&&3*i+j>_buf_sz)d=0;
else return OP_ENOTFORMAT;
value=value<<6|d;
}
_buf[3*i]=(unsigned char)(value>>16);
if(3*i+1<_buf_sz){
_buf[3*i+1]=(unsigned char)(value>>8);
if(3*i+2<_buf_sz)_buf[3*i+2]=(unsigned char)value;
}
}
i=0;
picture_type=op_parse_uint32be(_buf+i);
i+=4;
/*Extract the MIME type.*/
mime_type_length=op_parse_uint32be(_buf+i);
i+=4;
if(mime_type_length>_buf_sz-32)return OP_ENOTFORMAT;
mime_type=(char *)_ogg_malloc(sizeof(*_pic->mime_type)*(mime_type_length+1));
if(mime_type==NULL)return OP_EFAULT;
memcpy(mime_type,_buf+i,sizeof(*mime_type)*mime_type_length);
mime_type[mime_type_length]='\0';
_pic->mime_type=mime_type;
i+=mime_type_length;
/*Extract the description string.*/
description_length=op_parse_uint32be(_buf+i);
i+=4;
if(description_length>_buf_sz-mime_type_length-32)return OP_ENOTFORMAT;
description=
(char *)_ogg_malloc(sizeof(*_pic->mime_type)*(description_length+1));
if(description==NULL)return OP_EFAULT;
memcpy(description,_buf+i,sizeof(*description)*description_length);
description[description_length]='\0';
_pic->description=description;
i+=description_length;
/*Extract the remaining fields.*/
width=op_parse_uint32be(_buf+i);
i+=4;
height=op_parse_uint32be(_buf+i);
i+=4;
depth=op_parse_uint32be(_buf+i);
i+=4;
colors=op_parse_uint32be(_buf+i);
i+=4;
/*If one of these is set, they all must be, but colors==0 is a valid value.*/
colors_set=width!=0||height!=0||depth!=0||colors!=0;
if((width==0||height==0||depth==0)&&colors_set)return OP_ENOTFORMAT;
data_length=op_parse_uint32be(_buf+i);
i+=4;
if(data_length>_buf_sz-i)return OP_ENOTFORMAT;
/*Trim extraneous data so we don't copy it below.*/
_buf_sz=i+data_length;
/*Attempt to determine the image format.*/
format=OP_PIC_FORMAT_UNKNOWN;
if(mime_type_length==3&&strcmp(mime_type,"-->")==0){
format=OP_PIC_FORMAT_URL;
/*Picture type 1 must be a 32x32 PNG.*/
if(picture_type==1&&(width!=0||height!=0)&&(width!=32||height!=32)){
return OP_ENOTFORMAT;
}
/*Append a terminating NUL for the convenience of our callers.*/
_buf[_buf_sz++]='\0';
}
else{
if(mime_type_length==10
&&op_strncasecmp(mime_type,"image/jpeg",mime_type_length)==0){
if(op_is_jpeg(_buf+i,data_length))format=OP_PIC_FORMAT_JPEG;
}
else if(mime_type_length==9
&&op_strncasecmp(mime_type,"image/png",mime_type_length)==0){
if(op_is_png(_buf+i,data_length))format=OP_PIC_FORMAT_PNG;
}
else if(mime_type_length==9
&&op_strncasecmp(mime_type,"image/gif",mime_type_length)==0){
if(op_is_gif(_buf+i,data_length))format=OP_PIC_FORMAT_GIF;
}
else if(mime_type_length==0||(mime_type_length==6
&&op_strncasecmp(mime_type,"image/",mime_type_length)==0)){
if(op_is_jpeg(_buf+i,data_length))format=OP_PIC_FORMAT_JPEG;
else if(op_is_png(_buf+i,data_length))format=OP_PIC_FORMAT_PNG;
else if(op_is_gif(_buf+i,data_length))format=OP_PIC_FORMAT_GIF;
}
file_width=file_height=file_depth=file_colors=0;
has_palette=-1;
switch(format){
case OP_PIC_FORMAT_JPEG:{
op_extract_jpeg_params(_buf+i,data_length,
&file_width,&file_height,&file_depth,&file_colors,&has_palette);
}break;
case OP_PIC_FORMAT_PNG:{
op_extract_png_params(_buf+i,data_length,
&file_width,&file_height,&file_depth,&file_colors,&has_palette);
}break;
case OP_PIC_FORMAT_GIF:{
op_extract_gif_params(_buf+i,data_length,
&file_width,&file_height,&file_depth,&file_colors,&has_palette);
}break;
}
if(has_palette>=0){
/*If we successfully extracted these parameters from the image, override
any declared values.*/
width=file_width;
height=file_height;
depth=file_depth;
colors=file_colors;
}
/*Picture type 1 must be a 32x32 PNG.*/
if(picture_type==1&&(format!=OP_PIC_FORMAT_PNG||width!=32||height!=32)){
return OP_ENOTFORMAT;
}
}
/*Adjust _buf_sz instead of using data_length to capture the terminating NUL
for URLs.*/
_buf_sz-=i;
memmove(_buf,_buf+i,sizeof(*_buf)*_buf_sz);
_buf=(unsigned char *)_ogg_realloc(_buf,_buf_sz);
if(_buf_sz>0&&_buf==NULL)return OP_EFAULT;
_pic->type=picture_type;
_pic->width=width;
_pic->height=height;
_pic->depth=depth;
_pic->colors=colors;
_pic->data_length=data_length;
_pic->data=_buf;
_pic->format=format;
return 0;
}
int opus_picture_tag_parse(OpusPictureTag *_pic,const char *_tag){
OpusPictureTag pic;
unsigned char *buf;
size_t base64_sz;
size_t buf_sz;
size_t tag_length;
int ret;
if(opus_tagncompare("METADATA_BLOCK_PICTURE",22,_tag)==0)_tag+=23;
/*Figure out how much BASE64-encoded data we have.*/
tag_length=strlen(_tag);
if(tag_length&3)return OP_ENOTFORMAT;
base64_sz=tag_length>>2;
buf_sz=3*base64_sz;
if(buf_sz<32)return OP_ENOTFORMAT;
if(_tag[tag_length-1]=='=')buf_sz--;
if(_tag[tag_length-2]=='=')buf_sz--;
if(buf_sz<32)return OP_ENOTFORMAT;
/*Allocate an extra byte to allow appending a terminating NUL to URL data.*/
buf=(unsigned char *)_ogg_malloc(sizeof(*buf)*(buf_sz+1));
if(buf==NULL)return OP_EFAULT;
opus_picture_tag_init(&pic);
ret=opus_picture_tag_parse_impl(&pic,_tag,buf,buf_sz,base64_sz);
if(ret<0){
opus_picture_tag_clear(&pic);
_ogg_free(buf);
}
else *_pic=*&pic;
return ret;
}
void opus_picture_tag_init(OpusPictureTag *_pic){
memset(_pic,0,sizeof(*_pic));
}
void opus_picture_tag_clear(OpusPictureTag *_pic){
_ogg_free(_pic->description);
_ogg_free(_pic->mime_type);
_ogg_free(_pic->data);
}

42
third-party/opusfile/Sources/internal.c vendored Normal file
View File

@ -0,0 +1,42 @@
/********************************************************************
* *
* THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. *
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
* *
* THE libopusfile SOURCE CODE IS (C) COPYRIGHT 2012-2020 *
* by the Xiph.Org Foundation and contributors https://xiph.org/ *
* *
********************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "internal.h"
#if defined(OP_ENABLE_ASSERTIONS)
void op_fatal_impl(const char *_str,const char *_file,int _line){
fprintf(stderr,"Fatal (internal) error in %s, line %i: %s\n",
_file,_line,_str);
abort();
}
#endif
/*A version of strncasecmp() that is guaranteed to only ignore the case of
ASCII characters.*/
int op_strncasecmp(const char *_a,const char *_b,int _n){
int i;
for(i=0;i<_n;i++){
int a;
int b;
int d;
a=_a[i];
b=_b[i];
if(a>='a'&&a<='z')a-='a'-'A';
if(b>='a'&&b<='z')b-='a'-'A';
d=a-b;
if(d)return d;
}
return 0;
}

259
third-party/opusfile/Sources/internal.h vendored Normal file
View File

@ -0,0 +1,259 @@
/********************************************************************
* *
* THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. *
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
* *
* THE libopusfile SOURCE CODE IS (C) COPYRIGHT 2012-2020 *
* by the Xiph.Org Foundation and contributors https://xiph.org/ *
* *
********************************************************************/
#if !defined(_opusfile_internal_h)
# define _opusfile_internal_h (1)
# if !defined(_REENTRANT)
# define _REENTRANT
# endif
# if !defined(_GNU_SOURCE)
# define _GNU_SOURCE
# endif
# if !defined(_LARGEFILE_SOURCE)
# define _LARGEFILE_SOURCE
# endif
# if !defined(_LARGEFILE64_SOURCE)
# define _LARGEFILE64_SOURCE
# endif
# if !defined(_FILE_OFFSET_BITS)
# define _FILE_OFFSET_BITS 64
# endif
# include <stdlib.h>
# include <opusfile.h>
typedef struct OggOpusLink OggOpusLink;
# if defined(OP_FIXED_POINT)
typedef opus_int16 op_sample;
# else
typedef float op_sample;
/*We're using this define to test for libopus 1.1 or later until libopus
provides a better mechanism.*/
# if defined(OPUS_GET_EXPERT_FRAME_DURATION_REQUEST)
/*Enable soft clipping prevention in 16-bit decodes.*/
# define OP_SOFT_CLIP (1)
# endif
# endif
# if OP_GNUC_PREREQ(4,2)
/*Disable excessive warnings about the order of operations.*/
# pragma GCC diagnostic ignored "-Wparentheses"
# elif defined(_MSC_VER)
/*Disable excessive warnings about the order of operations.*/
# pragma warning(disable:4554)
/*Disable warnings about "deprecated" POSIX functions.*/
# pragma warning(disable:4996)
# endif
# if OP_GNUC_PREREQ(3,0)
/*Another alternative is
(__builtin_constant_p(_x)?!!(_x):__builtin_expect(!!(_x),1))
but that evaluates _x multiple times, which may be bad.*/
# define OP_LIKELY(_x) (__builtin_expect(!!(_x),1))
# define OP_UNLIKELY(_x) (__builtin_expect(!!(_x),0))
# else
# define OP_LIKELY(_x) (!!(_x))
# define OP_UNLIKELY(_x) (!!(_x))
# endif
# if defined(OP_ENABLE_ASSERTIONS)
# if OP_GNUC_PREREQ(2,5)||__SUNPRO_C>=0x590
__attribute__((noreturn))
# endif
void op_fatal_impl(const char *_str,const char *_file,int _line);
# define OP_FATAL(_str) (op_fatal_impl(_str,__FILE__,__LINE__))
# define OP_ASSERT(_cond) \
do{ \
if(OP_UNLIKELY(!(_cond)))OP_FATAL("assertion failed: " #_cond); \
} \
while(0)
# define OP_ALWAYS_TRUE(_cond) OP_ASSERT(_cond)
# else
# define OP_FATAL(_str) abort()
# define OP_ASSERT(_cond)
# define OP_ALWAYS_TRUE(_cond) ((void)(_cond))
# endif
# define OP_INT64_MAX (2*(((ogg_int64_t)1<<62)-1)|1)
# define OP_INT64_MIN (-OP_INT64_MAX-1)
# define OP_INT32_MAX (2*(((ogg_int32_t)1<<30)-1)|1)
# define OP_INT32_MIN (-OP_INT32_MAX-1)
# define OP_MIN(_a,_b) ((_a)<(_b)?(_a):(_b))
# define OP_MAX(_a,_b) ((_a)>(_b)?(_a):(_b))
# define OP_CLAMP(_lo,_x,_hi) (OP_MAX(_lo,OP_MIN(_x,_hi)))
/*Advance a file offset by the given amount, clamping against OP_INT64_MAX.
This is used to advance a known offset by things like OP_CHUNK_SIZE or
OP_PAGE_SIZE_MAX, while making sure to avoid signed overflow.
It assumes that both _offset and _amount are non-negative.*/
#define OP_ADV_OFFSET(_offset,_amount) \
(OP_MIN(_offset,OP_INT64_MAX-(_amount))+(_amount))
/*The maximum channel count for any mapping we'll actually decode.*/
# define OP_NCHANNELS_MAX (8)
/*Initial state.*/
# define OP_NOTOPEN (0)
/*We've found the first Opus stream in the first link.*/
# define OP_PARTOPEN (1)
# define OP_OPENED (2)
/*We've found the first Opus stream in the current link.*/
# define OP_STREAMSET (3)
/*We've initialized the decoder for the chosen Opus stream in the current
link.*/
# define OP_INITSET (4)
/*Information cached for a single link in a chained Ogg Opus file.
We choose the first Opus stream encountered in each link to play back (and
require at least one).*/
struct OggOpusLink{
/*The byte offset of the first header page in this link.*/
opus_int64 offset;
/*The byte offset of the first data page from the chosen Opus stream in this
link (after the headers).*/
opus_int64 data_offset;
/*The byte offset of the last page from the chosen Opus stream in this link.
This is used when seeking to ensure we find a page before the last one, so
that end-trimming calculations work properly.
This is only valid for seekable sources.*/
opus_int64 end_offset;
/*The total duration of all prior links.
This is always zero for non-seekable sources.*/
ogg_int64_t pcm_file_offset;
/*The granule position of the last sample.
This is only valid for seekable sources.*/
ogg_int64_t pcm_end;
/*The granule position before the first sample.*/
ogg_int64_t pcm_start;
/*The serial number.*/
ogg_uint32_t serialno;
/*The contents of the info header.*/
OpusHead head;
/*The contents of the comment header.*/
OpusTags tags;
};
struct OggOpusFile{
/*The callbacks used to access the stream.*/
OpusFileCallbacks callbacks;
/*A FILE *, memory buffer, etc.*/
void *stream;
/*Whether or not we can seek with this stream.*/
int seekable;
/*The number of links in this chained Ogg Opus file.*/
int nlinks;
/*The cached information from each link in a chained Ogg Opus file.
If stream isn't seekable (e.g., it's a pipe), only the current link
appears.*/
OggOpusLink *links;
/*The number of serial numbers from a single link.*/
int nserialnos;
/*The capacity of the list of serial numbers from a single link.*/
int cserialnos;
/*Storage for the list of serial numbers from a single link.
This is a scratch buffer used when scanning the BOS pages at the start of
each link.*/
ogg_uint32_t *serialnos;
/*This is the current offset of the data processed by the ogg_sync_state.
After a seek, this should be set to the target offset so that we can track
the byte offsets of subsequent pages.
After a call to op_get_next_page(), this will point to the first byte after
that page.*/
opus_int64 offset;
/*The total size of this stream, or -1 if it's unseekable.*/
opus_int64 end;
/*Used to locate pages in the stream.*/
ogg_sync_state oy;
/*One of OP_NOTOPEN, OP_PARTOPEN, OP_OPENED, OP_STREAMSET, OP_INITSET.*/
int ready_state;
/*The current link being played back.*/
int cur_link;
/*The number of decoded samples to discard from the start of decoding.*/
opus_int32 cur_discard_count;
/*The granule position of the previous packet (current packet start time).*/
ogg_int64_t prev_packet_gp;
/*The stream offset of the most recent page with completed packets, or -1.
This is only needed to recover continued packet data in the seeking logic,
when we use the current position as one of our bounds, only to later
discover it was the correct starting point.*/
opus_int64 prev_page_offset;
/*The number of bytes read since the last bitrate query, including framing.*/
opus_int64 bytes_tracked;
/*The number of samples decoded since the last bitrate query.*/
ogg_int64_t samples_tracked;
/*Takes physical pages and welds them into a logical stream of packets.*/
ogg_stream_state os;
/*Re-timestamped packets from a single page.
Buffering these relies on the undocumented libogg behavior that ogg_packet
pointers remain valid until the next page is submitted to the
ogg_stream_state they came from.*/
ogg_packet op[255];
/*The index of the next packet to return.*/
int op_pos;
/*The total number of packets available.*/
int op_count;
/*Central working state for the packet-to-PCM decoder.*/
OpusMSDecoder *od;
/*The application-provided packet decode callback.*/
op_decode_cb_func decode_cb;
/*The application-provided packet decode callback context.*/
void *decode_cb_ctx;
/*The stream count used to initialize the decoder.*/
int od_stream_count;
/*The coupled stream count used to initialize the decoder.*/
int od_coupled_count;
/*The channel count used to initialize the decoder.*/
int od_channel_count;
/*The channel mapping used to initialize the decoder.*/
unsigned char od_mapping[OP_NCHANNELS_MAX];
/*The buffered data for one decoded packet.*/
op_sample *od_buffer;
/*The current position in the decoded buffer.*/
int od_buffer_pos;
/*The number of valid samples in the decoded buffer.*/
int od_buffer_size;
/*The type of gain offset to apply.
One of OP_HEADER_GAIN, OP_ALBUM_GAIN, OP_TRACK_GAIN, or OP_ABSOLUTE_GAIN.*/
int gain_type;
/*The offset to apply to the gain.*/
opus_int32 gain_offset_q8;
/*Internal state for soft clipping and dithering float->short output.*/
#if !defined(OP_FIXED_POINT)
# if defined(OP_SOFT_CLIP)
float clip_state[OP_NCHANNELS_MAX];
# endif
float dither_a[OP_NCHANNELS_MAX*4];
float dither_b[OP_NCHANNELS_MAX*4];
opus_uint32 dither_seed;
int dither_mute;
int dither_disabled;
/*The number of channels represented by the internal state.
This gets set to 0 whenever anything that would prevent state propagation
occurs (switching between the float/short APIs, or between the
stereo/multistream APIs).*/
int state_channel_count;
#endif
};
int op_strncasecmp(const char *_a,const char *_b,int _n);
#endif

3340
third-party/opusfile/Sources/opusfile.c vendored Normal file

File diff suppressed because it is too large Load Diff

415
third-party/opusfile/Sources/stream.c vendored Normal file
View File

@ -0,0 +1,415 @@
/********************************************************************
* *
* THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. *
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
* *
* THE libopusfile SOURCE CODE IS (C) COPYRIGHT 1994-2018 *
* by the Xiph.Org Foundation and contributors https://xiph.org/ *
* *
********************************************************************
function: stdio-based convenience library for opening/seeking/decoding
last mod: $Id: vorbisfile.c 17573 2010-10-27 14:53:59Z xiphmont $
********************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "internal.h"
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#if defined(_WIN32)
# include <io.h>
#endif
typedef struct OpusMemStream OpusMemStream;
#define OP_MEM_SIZE_MAX (~(size_t)0>>1)
#define OP_MEM_DIFF_MAX ((ptrdiff_t)OP_MEM_SIZE_MAX)
/*The context information needed to read from a block of memory as if it were a
file.*/
struct OpusMemStream{
/*The block of memory to read from.*/
const unsigned char *data;
/*The total size of the block.
This must be at most OP_MEM_SIZE_MAX to prevent signed overflow while
seeking.*/
ptrdiff_t size;
/*The current file position.
This is allowed to be set arbitrarily greater than size (i.e., past the end
of the block, though we will not read data past the end of the block), but
is not allowed to be negative (i.e., before the beginning of the block).*/
ptrdiff_t pos;
};
static int op_fread(void *_stream,unsigned char *_ptr,int _buf_size){
FILE *stream;
size_t ret;
/*Check for empty read.*/
if(_buf_size<=0)return 0;
stream=(FILE *)_stream;
ret=fread(_ptr,1,_buf_size,stream);
OP_ASSERT(ret<=(size_t)_buf_size);
/*If ret==0 and !feof(stream), there was a read error.*/
return ret>0||feof(stream)?(int)ret:OP_EREAD;
}
static int op_fseek(void *_stream,opus_int64 _offset,int _whence){
#if defined(_WIN32)
/*_fseeki64() is not exposed until MSVCRT80.
This is the default starting with MSVC 2005 (_MSC_VER>=1400), but we want
to allow linking against older MSVCRT versions for compatibility back to
XP without installing extra runtime libraries.
i686-pc-mingw32 does not have fseeko() and requires
__MSVCRT_VERSION__>=0x800 for _fseeki64(), which screws up linking with
other libraries (that don't use MSVCRT80 from MSVC 2005 by default).
i686-w64-mingw32 does have fseeko() and respects _FILE_OFFSET_BITS, but I
don't know how to detect that at compile time.
We could just use fseeko64() (which is available in both), but it's
implemented using fgetpos()/fsetpos() just like this code, except without
the overflow checking, so we prefer our version.*/
opus_int64 pos;
/*We don't use fpos_t directly because it might be a struct if __STDC__ is
non-zero or _INTEGRAL_MAX_BITS < 64.
I'm not certain when the latter is true, but someone could in theory set
the former.
Either way, it should be binary compatible with a normal 64-bit int (this
assumption is not portable, but I believe it is true for MSVCRT).*/
OP_ASSERT(sizeof(pos)==sizeof(fpos_t));
/*Translate the seek to an absolute one.*/
if(_whence==SEEK_CUR){
int ret;
ret=fgetpos((FILE *)_stream,(fpos_t *)&pos);
if(ret)return ret;
}
else if(_whence==SEEK_END)pos=_filelengthi64(_fileno((FILE *)_stream));
else if(_whence==SEEK_SET)pos=0;
else return -1;
/*Check for errors or overflow.*/
if(pos<0||_offset<-pos||_offset>OP_INT64_MAX-pos)return -1;
pos+=_offset;
return fsetpos((FILE *)_stream,(fpos_t *)&pos);
#else
/*This function actually conforms to the SUSv2 and POSIX.1-2001, so we prefer
it except on Windows.*/
return fseeko((FILE *)_stream,(off_t)_offset,_whence);
#endif
}
static opus_int64 op_ftell(void *_stream){
#if defined(_WIN32)
/*_ftelli64() is not exposed until MSVCRT80, and ftello()/ftello64() have
the same problems as fseeko()/fseeko64() in MingW.
See above for a more detailed explanation.*/
opus_int64 pos;
OP_ASSERT(sizeof(pos)==sizeof(fpos_t));
return fgetpos((FILE *)_stream,(fpos_t *)&pos)?-1:pos;
#else
/*This function actually conforms to the SUSv2 and POSIX.1-2001, so we prefer
it except on Windows.*/
return ftello((FILE *)_stream);
#endif
}
static const OpusFileCallbacks OP_FILE_CALLBACKS={
op_fread,
op_fseek,
op_ftell,
(op_close_func)fclose
};
#if defined(_WIN32)
# include <stddef.h>
# include <errno.h>
/*Windows doesn't accept UTF-8 by default, and we don't have a wchar_t API,
so if we just pass the path to fopen(), then there'd be no way for a user
of our API to open a Unicode filename.
Instead, we translate from UTF-8 to UTF-16 and use Windows' wchar_t API.
This makes this API more consistent with platforms where the character set
used by fopen is the same as used on disk, which is generally UTF-8, and
with our metadata API, which always uses UTF-8.*/
static wchar_t *op_utf8_to_utf16(const char *_src){
wchar_t *dst;
size_t len;
len=strlen(_src);
/*Worst-case output is 1 wide character per 1 input character.*/
dst=(wchar_t *)_ogg_malloc(sizeof(*dst)*(len+1));
if(dst!=NULL){
size_t si;
size_t di;
for(di=si=0;si<len;si++){
int c0;
c0=(unsigned char)_src[si];
if(!(c0&0x80)){
/*Start byte says this is a 1-byte sequence.*/
dst[di++]=(wchar_t)c0;
continue;
}
else{
int c1;
/*This is safe, because c0 was not 0 and _src is NUL-terminated.*/
c1=(unsigned char)_src[si+1];
if((c1&0xC0)==0x80){
/*Found at least one continuation byte.*/
if((c0&0xE0)==0xC0){
wchar_t w;
/*Start byte says this is a 2-byte sequence.*/
w=(c0&0x1F)<<6|c1&0x3F;
if(w>=0x80U){
/*This is a 2-byte sequence that is not overlong.*/
dst[di++]=w;
si++;
continue;
}
}
else{
int c2;
/*This is safe, because c1 was not 0 and _src is NUL-terminated.*/
c2=(unsigned char)_src[si+2];
if((c2&0xC0)==0x80){
/*Found at least two continuation bytes.*/
if((c0&0xF0)==0xE0){
wchar_t w;
/*Start byte says this is a 3-byte sequence.*/
w=(c0&0xF)<<12|(c1&0x3F)<<6|c2&0x3F;
if(w>=0x800U&&(w<0xD800||w>=0xE000)&&w<0xFFFE){
/*This is a 3-byte sequence that is not overlong, not a
UTF-16 surrogate pair value, and not a 'not a character'
value.*/
dst[di++]=w;
si+=2;
continue;
}
}
else{
int c3;
/*This is safe, because c2 was not 0 and _src is
NUL-terminated.*/
c3=(unsigned char)_src[si+3];
if((c3&0xC0)==0x80){
/*Found at least three continuation bytes.*/
if((c0&0xF8)==0xF0){
opus_uint32 w;
/*Start byte says this is a 4-byte sequence.*/
w=(c0&7)<<18|(c1&0x3F)<<12|(c2&0x3F)<<6&(c3&0x3F);
if(w>=0x10000U&&w<0x110000U){
/*This is a 4-byte sequence that is not overlong and not
greater than the largest valid Unicode code point.
Convert it to a surrogate pair.*/
w-=0x10000;
dst[di++]=(wchar_t)(0xD800+(w>>10));
dst[di++]=(wchar_t)(0xDC00+(w&0x3FF));
si+=3;
continue;
}
}
}
}
}
}
}
}
/*If we got here, we encountered an illegal UTF-8 sequence.*/
_ogg_free(dst);
return NULL;
}
OP_ASSERT(di<=len);
dst[di]='\0';
}
return dst;
}
/*fsetpos() internally dispatches to the win32 API call SetFilePointer().
According to SetFilePointer()'s documentation [0], the behavior is
undefined if you do not call it on "a file stored on a seeking device".
However, none of the MSVCRT seeking functions verify what kind of file is
being used before calling it (which I believe is a bug, since they are
supposed to fail and return an error, but it is a bug that has been there
for multiple decades now).
In practice, SetFilePointer() appears to succeed for things like stdin,
even when you are not just piping in a regular file, which prevents the use
of this API to determine whether it is possible to seek in a file at all.
Therefore, we take the approach recommended by the SetFilePointer()
documentation and confirm the type of file using GetFileType() first.
We do this once, when the file is opened, and return the corresponding
callback in order to avoid an extra win32 API call on every seek in the
common case.
Hopefully the return value of GetFileType() cannot actually change for the
lifetime of a file handle.
[0] https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-setfilepointer
*/
static int op_fseek_fail(void *_stream,opus_int64 _offset,int _whence){
(void)_stream;
(void)_offset;
(void)_whence;
return -1;
}
static const OpusFileCallbacks OP_UNSEEKABLE_FILE_CALLBACKS={
op_fread,
op_fseek_fail,
op_ftell,
(op_close_func)fclose
};
# define WIN32_LEAN_AND_MEAN
# define WIN32_EXTRA_LEAN
# include <windows.h>
static const OpusFileCallbacks *op_get_file_callbacks(FILE *_fp){
intptr_t h_file;
h_file=_get_osfhandle(_fileno(_fp));
if(h_file!=-1
&&(GetFileType((HANDLE)h_file)&~FILE_TYPE_REMOTE)==FILE_TYPE_DISK){
return &OP_FILE_CALLBACKS;
}
return &OP_UNSEEKABLE_FILE_CALLBACKS;
}
#else
static const OpusFileCallbacks *op_get_file_callbacks(FILE *_fp){
(void)_fp;
return &OP_FILE_CALLBACKS;
}
#endif
void *op_fopen(OpusFileCallbacks *_cb,const char *_path,const char *_mode){
FILE *fp;
#if !defined(_WIN32)
fp=fopen(_path,_mode);
#else
fp=NULL;
{
wchar_t *wpath;
wchar_t *wmode;
wpath=op_utf8_to_utf16(_path);
wmode=op_utf8_to_utf16(_mode);
if(wmode==NULL)errno=EINVAL;
else if(wpath==NULL)errno=ENOENT;
else fp=_wfopen(wpath,wmode);
_ogg_free(wmode);
_ogg_free(wpath);
}
#endif
if(fp!=NULL)*_cb=*op_get_file_callbacks(fp);
return fp;
}
void *op_fdopen(OpusFileCallbacks *_cb,int _fd,const char *_mode){
FILE *fp;
fp=fdopen(_fd,_mode);
if(fp!=NULL)*_cb=*op_get_file_callbacks(fp);
return fp;
}
void *op_freopen(OpusFileCallbacks *_cb,const char *_path,const char *_mode,
void *_stream){
FILE *fp;
#if !defined(_WIN32)
fp=freopen(_path,_mode,(FILE *)_stream);
#else
fp=NULL;
{
wchar_t *wpath;
wchar_t *wmode;
wpath=op_utf8_to_utf16(_path);
wmode=op_utf8_to_utf16(_mode);
if(wmode==NULL)errno=EINVAL;
else if(wpath==NULL)errno=ENOENT;
else fp=_wfreopen(wpath,wmode,(FILE *)_stream);
_ogg_free(wmode);
_ogg_free(wpath);
}
#endif
if(fp!=NULL)*_cb=*op_get_file_callbacks(fp);
return fp;
}
static int op_mem_read(void *_stream,unsigned char *_ptr,int _buf_size){
OpusMemStream *stream;
ptrdiff_t size;
ptrdiff_t pos;
stream=(OpusMemStream *)_stream;
/*Check for empty read.*/
if(_buf_size<=0)return 0;
size=stream->size;
pos=stream->pos;
/*Check for EOF.*/
if(pos>=size)return 0;
/*Check for a short read.*/
_buf_size=(int)OP_MIN(size-pos,_buf_size);
memcpy(_ptr,stream->data+pos,_buf_size);
pos+=_buf_size;
stream->pos=pos;
return _buf_size;
}
static int op_mem_seek(void *_stream,opus_int64 _offset,int _whence){
OpusMemStream *stream;
ptrdiff_t pos;
stream=(OpusMemStream *)_stream;
pos=stream->pos;
OP_ASSERT(pos>=0);
switch(_whence){
case SEEK_SET:{
/*Check for overflow:*/
if(_offset<0||_offset>OP_MEM_DIFF_MAX)return -1;
pos=(ptrdiff_t)_offset;
}break;
case SEEK_CUR:{
/*Check for overflow:*/
if(_offset<-pos||_offset>OP_MEM_DIFF_MAX-pos)return -1;
pos=(ptrdiff_t)(pos+_offset);
}break;
case SEEK_END:{
ptrdiff_t size;
size=stream->size;
OP_ASSERT(size>=0);
/*Check for overflow:*/
if(_offset<-size||_offset>OP_MEM_DIFF_MAX-size)return -1;
pos=(ptrdiff_t)(size+_offset);
}break;
default:return -1;
}
stream->pos=pos;
return 0;
}
static opus_int64 op_mem_tell(void *_stream){
OpusMemStream *stream;
stream=(OpusMemStream *)_stream;
return (ogg_int64_t)stream->pos;
}
static int op_mem_close(void *_stream){
_ogg_free(_stream);
return 0;
}
static const OpusFileCallbacks OP_MEM_CALLBACKS={
op_mem_read,
op_mem_seek,
op_mem_tell,
op_mem_close
};
void *op_mem_stream_create(OpusFileCallbacks *_cb,
const unsigned char *_data,size_t _size){
OpusMemStream *stream;
if(_size>OP_MEM_SIZE_MAX)return NULL;
stream=(OpusMemStream *)_ogg_malloc(sizeof(*stream));
if(stream!=NULL){
*_cb=*&OP_MEM_CALLBACKS;
stream->data=_data;
stream->size=_size;
stream->pos=0;
}
return stream;
}

173
third-party/opusfile/Sources/wincerts.c vendored Normal file
View File

@ -0,0 +1,173 @@
/********************************************************************
* *
* THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. *
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
* *
* THE libopusfile SOURCE CODE IS (C) COPYRIGHT 2013-2016 *
* by the Xiph.Org Foundation and contributors https://xiph.org/ *
* *
********************************************************************/
/*This should really be part of OpenSSL, but there's been a patch [1] sitting
in their bugtracker for over two years that implements this, without any
action, so I'm giving up and re-implementing it locally.
[1] <https://rt.openssl.org/Ticket/Display.html?id=2158>*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "internal.h"
#if defined(OP_ENABLE_HTTP)&&defined(_WIN32)
/*You must include windows.h before wincrypt.h and x509.h.*/
# define WIN32_LEAN_AND_MEAN
# define WIN32_EXTRA_LEAN
# include <windows.h>
/*You must include wincrypt.h before x509.h, too, or X509_NAME doesn't get
defined properly.*/
# include <wincrypt.h>
# include <openssl/ssl.h>
# include <openssl/err.h>
# include <openssl/x509.h>
static int op_capi_new(X509_LOOKUP *_lu){
HCERTSTORE h_store;
h_store=CertOpenStore(CERT_STORE_PROV_SYSTEM_A,0,0,
CERT_STORE_OPEN_EXISTING_FLAG|CERT_STORE_READONLY_FLAG|
CERT_SYSTEM_STORE_CURRENT_USER|CERT_STORE_SHARE_CONTEXT_FLAG,"ROOT");
if(h_store!=NULL){
_lu->method_data=(char *)h_store;
return 1;
}
return 0;
}
static void op_capi_free(X509_LOOKUP *_lu){
HCERTSTORE h_store;
h_store=(HCERTSTORE)_lu->method_data;
# if defined(OP_ENABLE_ASSERTIONS)
OP_ALWAYS_TRUE(CertCloseStore(h_store,CERT_CLOSE_STORE_CHECK_FLAG));
# else
CertCloseStore(h_store,0);
# endif
}
static int op_capi_retrieve_by_subject(X509_LOOKUP *_lu,int _type,
X509_NAME *_name,X509_OBJECT *_ret){
X509_OBJECT *obj;
CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
obj=X509_OBJECT_retrieve_by_subject(_lu->store_ctx->objs,_type,_name);
CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
if(obj!=NULL){
_ret->type=obj->type;
memcpy(&_ret->data,&obj->data,sizeof(_ret->data));
return 1;
}
return 0;
}
static int op_capi_get_by_subject(X509_LOOKUP *_lu,int _type,X509_NAME *_name,
X509_OBJECT *_ret){
HCERTSTORE h_store;
if(_name==NULL)return 0;
if(_name->bytes==NULL||_name->bytes->length<=0||_name->modified){
if(i2d_X509_NAME(_name,NULL)<0)return 0;
OP_ASSERT(_name->bytes->length>0);
}
h_store=(HCERTSTORE)_lu->method_data;
switch(_type){
case X509_LU_X509:{
CERT_NAME_BLOB find_para;
PCCERT_CONTEXT cert;
X509 *x;
int ret;
/*Although X509_NAME contains a canon_enc field, that "canonical" [1]
encoding was just made up by OpenSSL.
It doesn't correspond to any actual standard, and since it drops the
initial sequence header, won't be recognized by the Crypto API.
The assumption here is that CertFindCertificateInStore() will allow any
appropriate variations in the encoding when it does its comparison.
This is, however, emphatically not true under Wine, which just compares
the encodings with memcmp().
Most of the time things work anyway, though, and there isn't really
anything we can do to make the situation better.
[1] A "canonical form" is defined as the one where, if you locked 10
mathematicians in a room and asked them to come up with a
representation for something, it's the answer that 9 of them would
give you back.
I don't think OpenSSL's encoding qualifies.*/
if(OP_UNLIKELY(_name->bytes->length>MAXDWORD))return 0;
find_para.cbData=(DWORD)_name->bytes->length;
find_para.pbData=(unsigned char *)_name->bytes->data;
cert=CertFindCertificateInStore(h_store,X509_ASN_ENCODING,0,
CERT_FIND_SUBJECT_NAME,&find_para,NULL);
if(cert==NULL)return 0;
x=d2i_X509(NULL,(const unsigned char **)&cert->pbCertEncoded,
cert->cbCertEncoded);
CertFreeCertificateContext(cert);
if(x==NULL)return 0;
ret=X509_STORE_add_cert(_lu->store_ctx,x);
X509_free(x);
if(ret)return op_capi_retrieve_by_subject(_lu,_type,_name,_ret);
}break;
case X509_LU_CRL:{
CERT_INFO cert_info;
CERT_CONTEXT find_para;
PCCRL_CONTEXT crl;
X509_CRL *x;
int ret;
ret=op_capi_retrieve_by_subject(_lu,_type,_name,_ret);
if(ret>0)return ret;
memset(&cert_info,0,sizeof(cert_info));
if(OP_UNLIKELY(_name->bytes->length>MAXDWORD))return 0;
cert_info.Issuer.cbData=(DWORD)_name->bytes->length;
cert_info.Issuer.pbData=(unsigned char *)_name->bytes->data;
memset(&find_para,0,sizeof(find_para));
find_para.pCertInfo=&cert_info;
crl=CertFindCRLInStore(h_store,0,0,CRL_FIND_ISSUED_BY,&find_para,NULL);
if(crl==NULL)return 0;
x=d2i_X509_CRL(NULL,(const unsigned char **)&crl->pbCrlEncoded,
crl->cbCrlEncoded);
CertFreeCRLContext(crl);
if(x==NULL)return 0;
ret=X509_STORE_add_crl(_lu->store_ctx,x);
X509_CRL_free(x);
if(ret)return op_capi_retrieve_by_subject(_lu,_type,_name,_ret);
}break;
}
return 0;
}
/*This is not const because OpenSSL doesn't allow it, even though it won't
write to it.*/
static X509_LOOKUP_METHOD X509_LOOKUP_CAPI={
"Load Crypto API store into cache",
op_capi_new,
op_capi_free,
NULL,
NULL,
NULL,
op_capi_get_by_subject,
NULL,
NULL,
NULL
};
int SSL_CTX_set_default_verify_paths_win32(SSL_CTX *_ssl_ctx){
X509_STORE *store;
X509_LOOKUP *lu;
/*We intentionally do not add the normal default paths, as they are usually
wrong, and are just asking to be used as an exploit vector.*/
store=SSL_CTX_get_cert_store(_ssl_ctx);
OP_ASSERT(store!=NULL);
lu=X509_STORE_add_lookup(store,&X509_LOOKUP_CAPI);
if(lu==NULL)return 0;
ERR_clear_error();
return 1;
}
#endif

90
third-party/opusfile/Sources/winerrno.h vendored Normal file
View File

@ -0,0 +1,90 @@
/********************************************************************
* *
* THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. *
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
* *
* THE libopusfile SOURCE CODE IS (C) COPYRIGHT 2012-2013 *
* by the Xiph.Org Foundation and contributors https://xiph.org/ *
* *
********************************************************************/
#if !defined(_opusfile_winerrno_h)
# define _opusfile_winerrno_h (1)
# include <errno.h>
# include <winerror.h>
/*These conflict with the MSVC errno.h definitions, but we don't need to use
the original ones in any file that deals with sockets.
We could map the WSA errors to the errno.h ones (most of which are only
available on sufficiently new versions of MSVC), but they aren't ordered the
same, and given how rarely we actually look at the values, I don't think
it's worth a lookup table.*/
# undef EWOULDBLOCK
# undef EINPROGRESS
# undef EALREADY
# undef ENOTSOCK
# undef EDESTADDRREQ
# undef EMSGSIZE
# undef EPROTOTYPE
# undef ENOPROTOOPT
# undef EPROTONOSUPPORT
# undef EOPNOTSUPP
# undef EAFNOSUPPORT
# undef EADDRINUSE
# undef EADDRNOTAVAIL
# undef ENETDOWN
# undef ENETUNREACH
# undef ENETRESET
# undef ECONNABORTED
# undef ECONNRESET
# undef ENOBUFS
# undef EISCONN
# undef ENOTCONN
# undef ETIMEDOUT
# undef ECONNREFUSED
# undef ELOOP
# undef ENAMETOOLONG
# undef EHOSTUNREACH
# undef ENOTEMPTY
# define EWOULDBLOCK (WSAEWOULDBLOCK-WSABASEERR)
# define EINPROGRESS (WSAEINPROGRESS-WSABASEERR)
# define EALREADY (WSAEALREADY-WSABASEERR)
# define ENOTSOCK (WSAENOTSOCK-WSABASEERR)
# define EDESTADDRREQ (WSAEDESTADDRREQ-WSABASEERR)
# define EMSGSIZE (WSAEMSGSIZE-WSABASEERR)
# define EPROTOTYPE (WSAEPROTOTYPE-WSABASEERR)
# define ENOPROTOOPT (WSAENOPROTOOPT-WSABASEERR)
# define EPROTONOSUPPORT (WSAEPROTONOSUPPORT-WSABASEERR)
# define ESOCKTNOSUPPORT (WSAESOCKTNOSUPPORT-WSABASEERR)
# define EOPNOTSUPP (WSAEOPNOTSUPP-WSABASEERR)
# define EPFNOSUPPORT (WSAEPFNOSUPPORT-WSABASEERR)
# define EAFNOSUPPORT (WSAEAFNOSUPPORT-WSABASEERR)
# define EADDRINUSE (WSAEADDRINUSE-WSABASEERR)
# define EADDRNOTAVAIL (WSAEADDRNOTAVAIL-WSABASEERR)
# define ENETDOWN (WSAENETDOWN-WSABASEERR)
# define ENETUNREACH (WSAENETUNREACH-WSABASEERR)
# define ENETRESET (WSAENETRESET-WSABASEERR)
# define ECONNABORTED (WSAECONNABORTED-WSABASEERR)
# define ECONNRESET (WSAECONNRESET-WSABASEERR)
# define ENOBUFS (WSAENOBUFS-WSABASEERR)
# define EISCONN (WSAEISCONN-WSABASEERR)
# define ENOTCONN (WSAENOTCONN-WSABASEERR)
# define ESHUTDOWN (WSAESHUTDOWN-WSABASEERR)
# define ETOOMANYREFS (WSAETOOMANYREFS-WSABASEERR)
# define ETIMEDOUT (WSAETIMEDOUT-WSABASEERR)
# define ECONNREFUSED (WSAECONNREFUSED-WSABASEERR)
# define ELOOP (WSAELOOP-WSABASEERR)
# define ENAMETOOLONG (WSAENAMETOOLONG-WSABASEERR)
# define EHOSTDOWN (WSAEHOSTDOWN-WSABASEERR)
# define EHOSTUNREACH (WSAEHOSTUNREACH-WSABASEERR)
# define ENOTEMPTY (WSAENOTEMPTY-WSABASEERR)
# define EPROCLIM (WSAEPROCLIM-WSABASEERR)
# define EUSERS (WSAEUSERS-WSABASEERR)
# define EDQUOT (WSAEDQUOT-WSABASEERR)
# define ESTALE (WSAESTALE-WSABASEERR)
# define EREMOTE (WSAEREMOTE-WSABASEERR)
#endif

File diff suppressed because it is too large Load Diff