rockcord 4.0.0-dev.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -0
- package/README.md +48 -0
- package/package.json +107 -0
- package/src/WebSocket.js +39 -0
- package/src/client/BaseClient.js +86 -0
- package/src/client/Client.js +946 -0
- package/src/client/WebhookClient.js +61 -0
- package/src/client/actions/Action.js +116 -0
- package/src/client/actions/ActionsManager.js +80 -0
- package/src/client/actions/ApplicationCommandPermissionsUpdate.js +34 -0
- package/src/client/actions/AutoModerationActionExecution.js +27 -0
- package/src/client/actions/AutoModerationRuleCreate.js +28 -0
- package/src/client/actions/AutoModerationRuleDelete.js +32 -0
- package/src/client/actions/AutoModerationRuleUpdate.js +30 -0
- package/src/client/actions/ChannelCreate.js +23 -0
- package/src/client/actions/ChannelDelete.js +39 -0
- package/src/client/actions/ChannelUpdate.js +43 -0
- package/src/client/actions/GuildAuditLogEntryCreate.js +29 -0
- package/src/client/actions/GuildBanAdd.js +20 -0
- package/src/client/actions/GuildBanRemove.js +25 -0
- package/src/client/actions/GuildChannelsPositionUpdate.js +21 -0
- package/src/client/actions/GuildDelete.js +65 -0
- package/src/client/actions/GuildEmojiCreate.js +20 -0
- package/src/client/actions/GuildEmojiDelete.js +21 -0
- package/src/client/actions/GuildEmojiUpdate.js +20 -0
- package/src/client/actions/GuildEmojisUpdate.js +34 -0
- package/src/client/actions/GuildIntegrationsUpdate.js +19 -0
- package/src/client/actions/GuildMemberRemove.js +33 -0
- package/src/client/actions/GuildMemberUpdate.js +44 -0
- package/src/client/actions/GuildRoleCreate.js +25 -0
- package/src/client/actions/GuildRoleDelete.js +31 -0
- package/src/client/actions/GuildRoleUpdate.js +39 -0
- package/src/client/actions/GuildRolesPositionUpdate.js +21 -0
- package/src/client/actions/GuildScheduledEventCreate.js +27 -0
- package/src/client/actions/GuildScheduledEventDelete.js +31 -0
- package/src/client/actions/GuildScheduledEventUpdate.js +30 -0
- package/src/client/actions/GuildScheduledEventUserAdd.js +32 -0
- package/src/client/actions/GuildScheduledEventUserRemove.js +32 -0
- package/src/client/actions/GuildStickerCreate.js +20 -0
- package/src/client/actions/GuildStickerDelete.js +21 -0
- package/src/client/actions/GuildStickerUpdate.js +20 -0
- package/src/client/actions/GuildStickersUpdate.js +34 -0
- package/src/client/actions/GuildUpdate.js +33 -0
- package/src/client/actions/InviteCreate.js +28 -0
- package/src/client/actions/InviteDelete.js +30 -0
- package/src/client/actions/MessageCreate.js +50 -0
- package/src/client/actions/MessageDelete.js +32 -0
- package/src/client/actions/MessageDeleteBulk.js +46 -0
- package/src/client/actions/MessagePollVoteAdd.js +33 -0
- package/src/client/actions/MessagePollVoteRemove.js +33 -0
- package/src/client/actions/MessageReactionAdd.js +68 -0
- package/src/client/actions/MessageReactionRemove.js +50 -0
- package/src/client/actions/MessageReactionRemoveAll.js +33 -0
- package/src/client/actions/MessageReactionRemoveEmoji.js +28 -0
- package/src/client/actions/MessageUpdate.js +26 -0
- package/src/client/actions/PresenceUpdate.js +50 -0
- package/src/client/actions/StageInstanceCreate.js +28 -0
- package/src/client/actions/StageInstanceDelete.js +33 -0
- package/src/client/actions/StageInstanceUpdate.js +30 -0
- package/src/client/actions/ThreadCreate.js +24 -0
- package/src/client/actions/ThreadDelete.js +32 -0
- package/src/client/actions/ThreadListSync.js +59 -0
- package/src/client/actions/ThreadMemberUpdate.js +30 -0
- package/src/client/actions/ThreadMembersUpdate.js +34 -0
- package/src/client/actions/TypingStart.js +29 -0
- package/src/client/actions/UserUpdate.js +35 -0
- package/src/client/actions/VoiceStateUpdate.js +50 -0
- package/src/client/actions/WebhooksUpdate.js +20 -0
- package/src/client/voice/ClientVoiceManager.js +151 -0
- package/src/client/voice/VoiceConnection.js +1295 -0
- package/src/client/voice/dispatcher/AnnexBDispatcher.js +136 -0
- package/src/client/voice/dispatcher/AudioDispatcher.js +145 -0
- package/src/client/voice/dispatcher/BaseDispatcher.js +473 -0
- package/src/client/voice/dispatcher/VPxDispatcher.js +58 -0
- package/src/client/voice/dispatcher/VideoDispatcher.js +68 -0
- package/src/client/voice/networking/DAVE.js +184 -0
- package/src/client/voice/networking/VoiceUDPClient.js +190 -0
- package/src/client/voice/networking/VoiceWebSocket.js +366 -0
- package/src/client/voice/player/MediaPlayer.js +321 -0
- package/src/client/voice/player/processing/AnnexBNalSplitter.js +267 -0
- package/src/client/voice/player/processing/IvfSplitter.js +106 -0
- package/src/client/voice/player/processing/PCMInsertSilence.js +37 -0
- package/src/client/voice/receiver/PacketHandler.js +329 -0
- package/src/client/voice/receiver/Receiver.js +115 -0
- package/src/client/voice/receiver/Recorder.js +197 -0
- package/src/client/voice/util/Function.js +122 -0
- package/src/client/voice/util/PlayInterface.js +122 -0
- package/src/client/voice/util/Secretbox.js +64 -0
- package/src/client/voice/util/Silence.js +16 -0
- package/src/client/voice/util/Socket.js +68 -0
- package/src/client/voice/util/VideoFrames.js +183 -0
- package/src/client/voice/util/VolumeInterface.js +105 -0
- package/src/client/websocket/WebSocketManager.js +405 -0
- package/src/client/websocket/WebSocketShard.js +908 -0
- package/src/client/websocket/handlers/APPLICATION_COMMAND_CREATE.js +18 -0
- package/src/client/websocket/handlers/APPLICATION_COMMAND_DELETE.js +20 -0
- package/src/client/websocket/handlers/APPLICATION_COMMAND_PERMISSIONS_UPDATE.js +5 -0
- package/src/client/websocket/handlers/APPLICATION_COMMAND_UPDATE.js +20 -0
- package/src/client/websocket/handlers/AUTO_MODERATION_ACTION_EXECUTION.js +5 -0
- package/src/client/websocket/handlers/AUTO_MODERATION_RULE_CREATE.js +5 -0
- package/src/client/websocket/handlers/AUTO_MODERATION_RULE_DELETE.js +5 -0
- package/src/client/websocket/handlers/AUTO_MODERATION_RULE_UPDATE.js +5 -0
- package/src/client/websocket/handlers/CALL_CREATE.js +14 -0
- package/src/client/websocket/handlers/CALL_DELETE.js +11 -0
- package/src/client/websocket/handlers/CALL_UPDATE.js +11 -0
- package/src/client/websocket/handlers/CHANNEL_CREATE.js +5 -0
- package/src/client/websocket/handlers/CHANNEL_DELETE.js +5 -0
- package/src/client/websocket/handlers/CHANNEL_PINS_UPDATE.js +22 -0
- package/src/client/websocket/handlers/CHANNEL_RECIPIENT_ADD.js +19 -0
- package/src/client/websocket/handlers/CHANNEL_RECIPIENT_REMOVE.js +16 -0
- package/src/client/websocket/handlers/CHANNEL_UPDATE.js +16 -0
- package/src/client/websocket/handlers/GUILD_AUDIT_LOG_ENTRY_CREATE.js +5 -0
- package/src/client/websocket/handlers/GUILD_BAN_ADD.js +5 -0
- package/src/client/websocket/handlers/GUILD_BAN_REMOVE.js +5 -0
- package/src/client/websocket/handlers/GUILD_CREATE.js +52 -0
- package/src/client/websocket/handlers/GUILD_DELETE.js +5 -0
- package/src/client/websocket/handlers/GUILD_EMOJIS_UPDATE.js +5 -0
- package/src/client/websocket/handlers/GUILD_INTEGRATIONS_UPDATE.js +5 -0
- package/src/client/websocket/handlers/GUILD_MEMBERS_CHUNK.js +39 -0
- package/src/client/websocket/handlers/GUILD_MEMBER_ADD.js +20 -0
- package/src/client/websocket/handlers/GUILD_MEMBER_REMOVE.js +5 -0
- package/src/client/websocket/handlers/GUILD_MEMBER_UPDATE.js +5 -0
- package/src/client/websocket/handlers/GUILD_ROLE_CREATE.js +5 -0
- package/src/client/websocket/handlers/GUILD_ROLE_DELETE.js +5 -0
- package/src/client/websocket/handlers/GUILD_ROLE_UPDATE.js +5 -0
- package/src/client/websocket/handlers/GUILD_SCHEDULED_EVENT_CREATE.js +5 -0
- package/src/client/websocket/handlers/GUILD_SCHEDULED_EVENT_DELETE.js +5 -0
- package/src/client/websocket/handlers/GUILD_SCHEDULED_EVENT_UPDATE.js +5 -0
- package/src/client/websocket/handlers/GUILD_SCHEDULED_EVENT_USER_ADD.js +5 -0
- package/src/client/websocket/handlers/GUILD_SCHEDULED_EVENT_USER_REMOVE.js +5 -0
- package/src/client/websocket/handlers/GUILD_STICKERS_UPDATE.js +5 -0
- package/src/client/websocket/handlers/GUILD_UPDATE.js +5 -0
- package/src/client/websocket/handlers/INTERACTION_MODAL_CREATE.js +12 -0
- package/src/client/websocket/handlers/INVITE_CREATE.js +5 -0
- package/src/client/websocket/handlers/INVITE_DELETE.js +5 -0
- package/src/client/websocket/handlers/MESSAGE_CREATE.js +5 -0
- package/src/client/websocket/handlers/MESSAGE_DELETE.js +5 -0
- package/src/client/websocket/handlers/MESSAGE_DELETE_BULK.js +5 -0
- package/src/client/websocket/handlers/MESSAGE_POLL_VOTE_ADD.js +5 -0
- package/src/client/websocket/handlers/MESSAGE_POLL_VOTE_REMOVE.js +5 -0
- package/src/client/websocket/handlers/MESSAGE_REACTION_ADD.js +5 -0
- package/src/client/websocket/handlers/MESSAGE_REACTION_REMOVE.js +5 -0
- package/src/client/websocket/handlers/MESSAGE_REACTION_REMOVE_ALL.js +5 -0
- package/src/client/websocket/handlers/MESSAGE_REACTION_REMOVE_EMOJI.js +5 -0
- package/src/client/websocket/handlers/MESSAGE_UPDATE.js +16 -0
- package/src/client/websocket/handlers/PRESENCE_UPDATE.js +5 -0
- package/src/client/websocket/handlers/READY.js +121 -0
- package/src/client/websocket/handlers/RELATIONSHIP_ADD.js +19 -0
- package/src/client/websocket/handlers/RELATIONSHIP_REMOVE.js +17 -0
- package/src/client/websocket/handlers/RELATIONSHIP_UPDATE.js +41 -0
- package/src/client/websocket/handlers/RESUMED.js +14 -0
- package/src/client/websocket/handlers/STAGE_INSTANCE_CREATE.js +5 -0
- package/src/client/websocket/handlers/STAGE_INSTANCE_DELETE.js +5 -0
- package/src/client/websocket/handlers/STAGE_INSTANCE_UPDATE.js +5 -0
- package/src/client/websocket/handlers/THREAD_CREATE.js +5 -0
- package/src/client/websocket/handlers/THREAD_DELETE.js +5 -0
- package/src/client/websocket/handlers/THREAD_LIST_SYNC.js +5 -0
- package/src/client/websocket/handlers/THREAD_MEMBERS_UPDATE.js +5 -0
- package/src/client/websocket/handlers/THREAD_MEMBER_UPDATE.js +5 -0
- package/src/client/websocket/handlers/THREAD_UPDATE.js +16 -0
- package/src/client/websocket/handlers/TYPING_START.js +5 -0
- package/src/client/websocket/handlers/USER_GUILD_SETTINGS_UPDATE.js +6 -0
- package/src/client/websocket/handlers/USER_NOTE_UPDATE.js +5 -0
- package/src/client/websocket/handlers/USER_REQUIRED_ACTION_UPDATE.js +78 -0
- package/src/client/websocket/handlers/USER_SETTINGS_UPDATE.js +5 -0
- package/src/client/websocket/handlers/USER_UPDATE.js +5 -0
- package/src/client/websocket/handlers/VOICE_CHANNEL_EFFECT_SEND.js +16 -0
- package/src/client/websocket/handlers/VOICE_CHANNEL_STATUS_UPDATE.js +12 -0
- package/src/client/websocket/handlers/VOICE_SERVER_UPDATE.js +6 -0
- package/src/client/websocket/handlers/VOICE_STATE_UPDATE.js +5 -0
- package/src/client/websocket/handlers/WEBHOOKS_UPDATE.js +5 -0
- package/src/client/websocket/handlers/index.js +84 -0
- package/src/errors/DJSError.js +61 -0
- package/src/errors/Messages.js +219 -0
- package/src/errors/index.js +4 -0
- package/src/index.js +218 -0
- package/src/managers/ApplicationCommandManager.js +264 -0
- package/src/managers/ApplicationCommandPermissionsManager.js +417 -0
- package/src/managers/AutoModerationRuleManager.js +296 -0
- package/src/managers/BaseGuildEmojiManager.js +80 -0
- package/src/managers/BaseManager.js +19 -0
- package/src/managers/BillingManager.js +66 -0
- package/src/managers/CachedManager.js +71 -0
- package/src/managers/ChannelManager.js +148 -0
- package/src/managers/ClientUserSettingManager.js +372 -0
- package/src/managers/DataManager.js +61 -0
- package/src/managers/GuildBanManager.js +250 -0
- package/src/managers/GuildChannelManager.js +488 -0
- package/src/managers/GuildEmojiManager.js +171 -0
- package/src/managers/GuildEmojiRoleManager.js +118 -0
- package/src/managers/GuildForumThreadManager.js +94 -0
- package/src/managers/GuildInviteManager.js +213 -0
- package/src/managers/GuildManager.js +338 -0
- package/src/managers/GuildMemberManager.js +599 -0
- package/src/managers/GuildMemberRoleManager.js +195 -0
- package/src/managers/GuildScheduledEventManager.js +314 -0
- package/src/managers/GuildSettingManager.js +155 -0
- package/src/managers/GuildStickerManager.js +179 -0
- package/src/managers/GuildTextThreadManager.js +98 -0
- package/src/managers/InteractionManager.js +39 -0
- package/src/managers/MessageManager.js +426 -0
- package/src/managers/PermissionOverwriteManager.js +164 -0
- package/src/managers/PresenceManager.js +71 -0
- package/src/managers/ReactionManager.js +67 -0
- package/src/managers/ReactionUserManager.js +73 -0
- package/src/managers/RelationshipManager.js +278 -0
- package/src/managers/RoleManager.js +448 -0
- package/src/managers/SessionManager.js +66 -0
- package/src/managers/StageInstanceManager.js +162 -0
- package/src/managers/ThreadManager.js +175 -0
- package/src/managers/ThreadMemberManager.js +186 -0
- package/src/managers/UserManager.js +136 -0
- package/src/managers/UserNoteManager.js +53 -0
- package/src/managers/VoiceStateManager.js +59 -0
- package/src/rest/APIRequest.js +149 -0
- package/src/rest/APIRouter.js +53 -0
- package/src/rest/DiscordAPIError.js +121 -0
- package/src/rest/HTTPError.js +64 -0
- package/src/rest/RESTManager.js +97 -0
- package/src/rest/RateLimitError.js +55 -0
- package/src/rest/RequestHandler.js +476 -0
- package/src/sharding/Shard.js +476 -0
- package/src/sharding/ShardClientUtil.js +279 -0
- package/src/sharding/ShardingManager.js +319 -0
- package/src/structures/AnonymousGuild.js +98 -0
- package/src/structures/ApplicationCommand.js +593 -0
- package/src/structures/ApplicationRoleConnectionMetadata.js +48 -0
- package/src/structures/AutoModerationActionExecution.js +89 -0
- package/src/structures/AutoModerationRule.js +294 -0
- package/src/structures/AutocompleteInteraction.js +107 -0
- package/src/structures/Base.js +43 -0
- package/src/structures/BaseCommandInteraction.js +211 -0
- package/src/structures/BaseGuild.js +116 -0
- package/src/structures/BaseGuildEmoji.js +56 -0
- package/src/structures/BaseGuildTextChannel.js +191 -0
- package/src/structures/BaseGuildVoiceChannel.js +241 -0
- package/src/structures/BaseMessageComponent.js +181 -0
- package/src/structures/ButtonInteraction.js +11 -0
- package/src/structures/CallState.js +63 -0
- package/src/structures/CategoryChannel.js +85 -0
- package/src/structures/Channel.js +284 -0
- package/src/structures/ClientPresence.js +77 -0
- package/src/structures/ClientUser.js +449 -0
- package/src/structures/CommandInteraction.js +41 -0
- package/src/structures/CommandInteractionOptionResolver.js +276 -0
- package/src/structures/ContainerComponent.js +68 -0
- package/src/structures/ContextMenuInteraction.js +65 -0
- package/src/structures/DMChannel.js +219 -0
- package/src/structures/DirectoryChannel.js +20 -0
- package/src/structures/Emoji.js +148 -0
- package/src/structures/FileComponent.js +49 -0
- package/src/structures/ForumChannel.js +31 -0
- package/src/structures/GroupDMChannel.js +394 -0
- package/src/structures/Guild.js +1638 -0
- package/src/structures/GuildAuditLogs.js +746 -0
- package/src/structures/GuildBan.js +59 -0
- package/src/structures/GuildBoost.js +108 -0
- package/src/structures/GuildChannel.js +470 -0
- package/src/structures/GuildEmoji.js +161 -0
- package/src/structures/GuildMember.js +639 -0
- package/src/structures/GuildPreview.js +191 -0
- package/src/structures/GuildPreviewEmoji.js +27 -0
- package/src/structures/GuildScheduledEvent.js +536 -0
- package/src/structures/GuildTemplate.js +236 -0
- package/src/structures/Integration.js +188 -0
- package/src/structures/IntegrationApplication.js +96 -0
- package/src/structures/Interaction.js +290 -0
- package/src/structures/InteractionCollector.js +248 -0
- package/src/structures/InteractionWebhook.js +43 -0
- package/src/structures/Invite.js +358 -0
- package/src/structures/InviteGuild.js +23 -0
- package/src/structures/InviteStageInstance.js +86 -0
- package/src/structures/MediaChannel.js +11 -0
- package/src/structures/MediaGalleryComponent.js +41 -0
- package/src/structures/MediaGalleryItem.js +47 -0
- package/src/structures/Message.js +1257 -0
- package/src/structures/MessageActionRow.js +105 -0
- package/src/structures/MessageAttachment.js +216 -0
- package/src/structures/MessageButton.js +166 -0
- package/src/structures/MessageCollector.js +146 -0
- package/src/structures/MessageComponentInteraction.js +120 -0
- package/src/structures/MessageContextMenuInteraction.js +20 -0
- package/src/structures/MessageEmbed.js +596 -0
- package/src/structures/MessageMentions.js +273 -0
- package/src/structures/MessagePayload.js +352 -0
- package/src/structures/MessageReaction.js +184 -0
- package/src/structures/MessageSelectMenu.js +141 -0
- package/src/structures/Modal.js +161 -0
- package/src/structures/ModalSubmitFieldsResolver.js +53 -0
- package/src/structures/ModalSubmitInteraction.js +119 -0
- package/src/structures/NewsChannel.js +32 -0
- package/src/structures/OAuth2Guild.js +28 -0
- package/src/structures/PermissionOverwrites.js +198 -0
- package/src/structures/Poll.js +108 -0
- package/src/structures/PollAnswer.js +88 -0
- package/src/structures/Presence.js +1105 -0
- package/src/structures/ReactionCollector.js +229 -0
- package/src/structures/ReactionEmoji.js +31 -0
- package/src/structures/Role.js +590 -0
- package/src/structures/SectionComponent.js +48 -0
- package/src/structures/SelectMenuInteraction.js +21 -0
- package/src/structures/SeparatorComponent.js +48 -0
- package/src/structures/Session.js +81 -0
- package/src/structures/StageChannel.js +104 -0
- package/src/structures/StageInstance.js +208 -0
- package/src/structures/Sticker.js +310 -0
- package/src/structures/StickerPack.js +95 -0
- package/src/structures/StoreChannel.js +56 -0
- package/src/structures/Team.js +118 -0
- package/src/structures/TeamMember.js +80 -0
- package/src/structures/TextChannel.js +33 -0
- package/src/structures/TextDisplayComponent.js +40 -0
- package/src/structures/TextInputComponent.js +132 -0
- package/src/structures/ThreadChannel.js +605 -0
- package/src/structures/ThreadMember.js +105 -0
- package/src/structures/ThreadOnlyChannel.js +249 -0
- package/src/structures/ThumbnailComponent.js +57 -0
- package/src/structures/Typing.js +74 -0
- package/src/structures/UnfurledMediaItem.js +29 -0
- package/src/structures/User.js +624 -0
- package/src/structures/UserContextMenuInteraction.js +29 -0
- package/src/structures/VoiceChannel.js +110 -0
- package/src/structures/VoiceChannelEffect.js +69 -0
- package/src/structures/VoiceRegion.js +53 -0
- package/src/structures/VoiceState.js +354 -0
- package/src/structures/WebEmbed.js +373 -0
- package/src/structures/Webhook.js +478 -0
- package/src/structures/WelcomeChannel.js +60 -0
- package/src/structures/WelcomeScreen.js +48 -0
- package/src/structures/Widget.js +87 -0
- package/src/structures/WidgetMember.js +99 -0
- package/src/structures/interfaces/Application.js +825 -0
- package/src/structures/interfaces/Collector.js +301 -0
- package/src/structures/interfaces/InteractionResponses.js +313 -0
- package/src/structures/interfaces/TextBasedChannel.js +743 -0
- package/src/util/APITypes.js +59 -0
- package/src/util/ActivityFlags.js +44 -0
- package/src/util/ApplicationFlags.js +76 -0
- package/src/util/AttachmentFlags.js +38 -0
- package/src/util/BitField.js +182 -0
- package/src/util/ChannelFlags.js +45 -0
- package/src/util/Constants.js +1927 -0
- package/src/util/DataResolver.js +146 -0
- package/src/util/Formatters.js +228 -0
- package/src/util/GuildMemberFlags.js +43 -0
- package/src/util/Intents.js +74 -0
- package/src/util/InviteFlags.js +34 -0
- package/src/util/LimitedCollection.js +134 -0
- package/src/util/MessageFlags.js +63 -0
- package/src/util/Options.js +358 -0
- package/src/util/Permissions.js +202 -0
- package/src/util/PremiumUsageFlags.js +31 -0
- package/src/util/PurchasedFlags.js +33 -0
- package/src/util/Redact.js +26 -0
- package/src/util/RemoteAuth.js +449 -0
- package/src/util/RoleFlags.js +37 -0
- package/src/util/SnowflakeUtil.js +92 -0
- package/src/util/Speaking.js +33 -0
- package/src/util/Sweepers.js +466 -0
- package/src/util/SystemChannelFlags.js +55 -0
- package/src/util/ThreadMemberFlags.js +30 -0
- package/src/util/UserFlags.js +104 -0
- package/src/util/Util.js +1079 -0
- package/typings/enums.d.ts +437 -0
- package/typings/index.d.ts +8197 -0
- package/typings/rawDataTypes.d.ts +403 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const dgram = require('dgram');
|
|
4
|
+
const { setImmediate } = require('node:timers');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {Object} InterfaceAddresses
|
|
8
|
+
* @property {string} [udp4] - IPv4 address
|
|
9
|
+
* @property {string} [udp6] - IPv6 address
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Get the interface address for a given socket type.
|
|
14
|
+
* @param {"udp4"|"udp6"} type - The socket type.
|
|
15
|
+
* @param {InterfaceAddresses} [interfaceAddresses] - The interface addresses mapping.
|
|
16
|
+
* @returns {string|undefined} The interface address if available.
|
|
17
|
+
*/
|
|
18
|
+
function interfaceAddress(type, interfaceAddresses) {
|
|
19
|
+
return interfaceAddresses ? interfaceAddresses[type] : undefined;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Get a random available port.
|
|
24
|
+
* @param {"udp4"|"udp6"} [protocol="udp4"] - The socket type.
|
|
25
|
+
* @param {InterfaceAddresses} [interfaceAddresses] - The interface addresses mapping.
|
|
26
|
+
* @returns {Promise<number>} The assigned random port.
|
|
27
|
+
*/
|
|
28
|
+
async function randomPort(protocol = 'udp4', interfaceAddresses) {
|
|
29
|
+
const socket = dgram.createSocket(protocol);
|
|
30
|
+
|
|
31
|
+
setImmediate(() =>
|
|
32
|
+
socket.bind({
|
|
33
|
+
port: 0,
|
|
34
|
+
address: interfaceAddress(protocol, interfaceAddresses),
|
|
35
|
+
}),
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
await new Promise((resolve, reject) => {
|
|
39
|
+
socket.once('error', reject);
|
|
40
|
+
socket.once('listening', resolve);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const port = socket.address()?.port;
|
|
44
|
+
await new Promise(resolve => socket.close(resolve));
|
|
45
|
+
return port;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Get multiple random available ports.
|
|
50
|
+
* @param {number} num - Number of ports to find.
|
|
51
|
+
* @param {"udp4"|"udp6"} [protocol="udp4"] - The socket type.
|
|
52
|
+
* @param {InterfaceAddresses} [interfaceAddresses] - The interface addresses mapping.
|
|
53
|
+
* @returns {Promise<number[]>} An array of assigned random ports.
|
|
54
|
+
*/
|
|
55
|
+
async function randomPorts(num, protocol = 'udp4', interfaceAddresses) {
|
|
56
|
+
return Promise.all(Array.from({ length: num }).map(() => randomPort(protocol, interfaceAddresses)));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Find an available port within a given range.
|
|
61
|
+
* @param {number} min - The minimum port number.
|
|
62
|
+
* @param {number} max - The maximum port number.
|
|
63
|
+
* @param {"udp4"|"udp6"} [protocol="udp4"] - The socket type.
|
|
64
|
+
* @param {InterfaceAddresses} [interfaceAddresses] - The interface addresses mapping.
|
|
65
|
+
* @returns {Promise<number>} The available port within range.
|
|
66
|
+
* @throws {Error} If no port is found within the range.
|
|
67
|
+
*/
|
|
68
|
+
async function findPort(min, max, protocol = 'udp4', interfaceAddresses) {
|
|
69
|
+
let port;
|
|
70
|
+
|
|
71
|
+
for (let i = min; i <= max; i++) {
|
|
72
|
+
const socket = dgram.createSocket(protocol);
|
|
73
|
+
|
|
74
|
+
setImmediate(() =>
|
|
75
|
+
socket.bind({
|
|
76
|
+
port: i,
|
|
77
|
+
address: interfaceAddress(protocol, interfaceAddresses),
|
|
78
|
+
}),
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const error = await new Promise(resolve => {
|
|
82
|
+
socket.once('error', resolve);
|
|
83
|
+
socket.once('listening', () => resolve(null));
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
if (error) {
|
|
87
|
+
try {
|
|
88
|
+
socket.close();
|
|
89
|
+
} catch {
|
|
90
|
+
/* A failed bind may have already closed the socket. */
|
|
91
|
+
}
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const addressInfo = socket.address();
|
|
96
|
+
await new Promise(resolve => socket.close(resolve));
|
|
97
|
+
if (addressInfo && addressInfo.port >= min && addressInfo.port <= max) {
|
|
98
|
+
port = addressInfo.port;
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (!port) throw new Error('port not found');
|
|
104
|
+
return port;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function parseStreamKey(key) {
|
|
108
|
+
const Arr = key.split(':');
|
|
109
|
+
const type = Arr[0];
|
|
110
|
+
const guildId = type == 'guild' ? Arr[1] : null;
|
|
111
|
+
const channelId = type == 'guild' ? Arr[2] : Arr[1];
|
|
112
|
+
const userId = type == 'guild' ? Arr[3] : Arr[2];
|
|
113
|
+
return { type, guildId, channelId, userId };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
module.exports = {
|
|
117
|
+
randomPort,
|
|
118
|
+
randomPorts,
|
|
119
|
+
findPort,
|
|
120
|
+
interfaceAddress,
|
|
121
|
+
parseStreamKey,
|
|
122
|
+
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { Readable } = require('stream');
|
|
4
|
+
const prism = require('prism-media');
|
|
5
|
+
const { Error } = require('../../../errors');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Options that can be passed to stream-playing methods:
|
|
9
|
+
* @typedef {Object} StreamOptions
|
|
10
|
+
* @property {StreamType} [type='unknown'] The type of stream.
|
|
11
|
+
* @property {number} [seek=0] The time to seek to, will be ignored when playing `ogg/opus` or `webm/opus` streams
|
|
12
|
+
* @property {number|boolean} [volume=1] The volume to play at. Set this to false to disable volume transforms for
|
|
13
|
+
* this stream to improve performance.
|
|
14
|
+
* @property {number} [plp] Expected packet loss percentage
|
|
15
|
+
* @property {boolean} [fec] Enabled forward error correction
|
|
16
|
+
* @property {number|string} [bitrate=96] The bitrate (quality) of the audio in kbps.
|
|
17
|
+
* If set to 'auto', the voice channel's bitrate will be used
|
|
18
|
+
* @property {number} [highWaterMark=12] The maximum number of opus packets to make and store before they are
|
|
19
|
+
* actually needed. See https://nodejs.org/en/docs/guides/backpressuring-in-streams/. Setting this value to
|
|
20
|
+
* 1 means that changes in volume will be more instant.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* An option passed as part of `StreamOptions` specifying the type of the stream.
|
|
25
|
+
* * `unknown`: The default type, streams/input will be passed through to ffmpeg before encoding.
|
|
26
|
+
* Will play most streams.
|
|
27
|
+
* * `converted`: Play a stream of 16bit signed stereo PCM data, skipping ffmpeg.
|
|
28
|
+
* * `opus`: Play a stream of opus packets, skipping ffmpeg. You lose the ability to alter volume.
|
|
29
|
+
* * `ogg/opus`: Play an ogg file with the opus encoding, skipping ffmpeg. You lose the ability to alter volume.
|
|
30
|
+
* * `webm/opus`: Play a webm file with opus audio, skipping ffmpeg. You lose the ability to alter volume.
|
|
31
|
+
* @typedef {string} StreamType
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* An interface class to allow you to play audio over VoiceConnections.
|
|
36
|
+
*/
|
|
37
|
+
class PlayInterface {
|
|
38
|
+
constructor(player) {
|
|
39
|
+
this.player = player;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Play an audio resource.
|
|
44
|
+
* @param {ReadableStream|string} resource The resource to play.
|
|
45
|
+
* @param {StreamOptions} [options] The options to play.
|
|
46
|
+
* @example
|
|
47
|
+
* // Play a local audio file
|
|
48
|
+
* connection.playAudio('/home/hydrabolt/audio.mp3', { volume: 0.5 });
|
|
49
|
+
* @example
|
|
50
|
+
* // Play a ReadableStream
|
|
51
|
+
* connection.playAudio(ytdl('https://www.youtube.com/watch?v=ZlAU_w7-Xp8', { quality: 'highestaudio' }));
|
|
52
|
+
* @example
|
|
53
|
+
* // Using different protocols: https://ffmpeg.org/ffmpeg-protocols.html
|
|
54
|
+
* connection.playAudio('http://www.sample-videos.com/audio/mp3/wave.mp3');
|
|
55
|
+
* @returns {AudioDispatcher}
|
|
56
|
+
*/
|
|
57
|
+
playAudio(resource, options = {}) {
|
|
58
|
+
if (resource instanceof Readable || typeof resource === 'string') {
|
|
59
|
+
const type = options.type || 'unknown';
|
|
60
|
+
if (type === 'unknown') {
|
|
61
|
+
return this.player.playUnknown(resource, options);
|
|
62
|
+
} else if (type === 'converted') {
|
|
63
|
+
return this.player.playPCMStream(resource, options);
|
|
64
|
+
} else if (type === 'opus') {
|
|
65
|
+
return this.player.playOpusStream(resource, options);
|
|
66
|
+
} else if (type === 'ogg/opus') {
|
|
67
|
+
if (!(resource instanceof Readable)) throw new Error('VOICE_PRISM_DEMUXERS_NEED_STREAM');
|
|
68
|
+
return this.player.playOpusStream(resource.pipe(new prism.opus.OggDemuxer()), options);
|
|
69
|
+
} else if (type === 'webm/opus') {
|
|
70
|
+
if (!(resource instanceof Readable)) throw new Error('VOICE_PRISM_DEMUXERS_NEED_STREAM');
|
|
71
|
+
return this.player.playOpusStream(resource.pipe(new prism.opus.WebmDemuxer()), options);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
throw new Error('VOICE_PLAY_INTERFACE_BAD_TYPE');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Options that can be passed to stream-playing methods:
|
|
79
|
+
* @typedef {Object} VideoOptions
|
|
80
|
+
* @property {number} [seek=0] The time to seek to
|
|
81
|
+
* @property {number} [fps] Video fps
|
|
82
|
+
* @property {number} [highWaterMark=12] The maximum number of opus packets to make and store before they are
|
|
83
|
+
* actually needed. See https://nodejs.org/en/docs/guides/backpressuring-in-streams/. Setting this value to
|
|
84
|
+
* 1 means that changes in volume will be more instant.
|
|
85
|
+
* @property {'ultrafast' | 'superfast' | 'veryfast' | 'faster' | 'fast' | 'medium' | 'slow' | 'slower' | 'veryslow'} [presetH26x='veryfast'] ffmpeg preset h264
|
|
86
|
+
* @property {boolean} [hwAccel=false] Enables hardware accelerated video decoding. Enabling this option might result in an exception
|
|
87
|
+
* being thrown by Ffmpeg process if your system does not support hardware acceleration
|
|
88
|
+
* @property {string[]} [inputFFmpegArgs] input ffmpeg
|
|
89
|
+
* Ex: ['-config1', 'value1', '-config2', 'value2']
|
|
90
|
+
* @property {string[]} [outputFFmpegArgs] output ffmpeg
|
|
91
|
+
* Ex: ['-config1', 'value1', '-config2', 'value2']
|
|
92
|
+
* @property {number|'auto'} [bitrate=2000] The bitrate (quality) of the video in kbps.
|
|
93
|
+
* If set to 'auto', ffmpeg will automatically select
|
|
94
|
+
*/
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Play a video resource.
|
|
98
|
+
* @param {ReadableStream|string} resource The resource to play.
|
|
99
|
+
* @param {VideoOptions} [options] The options to play.
|
|
100
|
+
* @example
|
|
101
|
+
* // Play a local video file
|
|
102
|
+
* connection.playVideo('/home/hydrabolt/video.mp4');
|
|
103
|
+
* @example
|
|
104
|
+
* // Using different protocols: https://ffmpeg.org/ffmpeg-protocols.html
|
|
105
|
+
* connection.playVideo('http://www.sample-videos.com/video/mp4/wave.mp4');
|
|
106
|
+
* @returns {VideoDispatcher}
|
|
107
|
+
*/
|
|
108
|
+
playVideo(resource, options = {}) {
|
|
109
|
+
if (resource instanceof Readable || typeof resource === 'string') {
|
|
110
|
+
return this.player.playUnknownVideo(resource, options);
|
|
111
|
+
}
|
|
112
|
+
throw new Error('VOICE_PLAY_INTERFACE_BAD_TYPE');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
static applyToClass(structure) {
|
|
116
|
+
for (const prop of ['playAudio', 'playVideo']) {
|
|
117
|
+
Object.defineProperty(structure.prototype, prop, Object.getOwnPropertyDescriptor(PlayInterface.prototype, prop));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
module.exports = PlayInterface;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const libs = {
|
|
4
|
+
sodium: sodium => ({
|
|
5
|
+
crypto_aead_xchacha20poly1305_ietf_encrypt: (plaintext, additionalData, nonce, key) =>
|
|
6
|
+
sodium.api.crypto_aead_xchacha20poly1305_ietf_encrypt(plaintext, additionalData, null, nonce, key),
|
|
7
|
+
crypto_aead_xchacha20poly1305_ietf_decrypt: (plaintext, additionalData, nonce, key) =>
|
|
8
|
+
sodium.api.crypto_aead_xchacha20poly1305_ietf_decrypt(plaintext, additionalData, null, nonce, key),
|
|
9
|
+
}),
|
|
10
|
+
'libsodium-wrappers': sodium => ({
|
|
11
|
+
crypto_aead_xchacha20poly1305_ietf_encrypt: (plaintext, additionalData, nonce, key) =>
|
|
12
|
+
sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(plaintext, additionalData, null, nonce, key),
|
|
13
|
+
crypto_aead_xchacha20poly1305_ietf_decrypt: (plaintext, additionalData, nonce, key) =>
|
|
14
|
+
sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(null, plaintext, additionalData, nonce, key),
|
|
15
|
+
}),
|
|
16
|
+
'@stablelib/xchacha20poly1305': stablelib => ({
|
|
17
|
+
crypto_aead_xchacha20poly1305_ietf_encrypt(cipherText, additionalData, nonce, key) {
|
|
18
|
+
const crypto = new stablelib.XChaCha20Poly1305(key);
|
|
19
|
+
return crypto.seal(nonce, cipherText, additionalData);
|
|
20
|
+
},
|
|
21
|
+
crypto_aead_xchacha20poly1305_ietf_decrypt(plaintext, additionalData, nonce, key) {
|
|
22
|
+
const crypto = new stablelib.XChaCha20Poly1305(key);
|
|
23
|
+
return crypto.open(nonce, plaintext, additionalData);
|
|
24
|
+
},
|
|
25
|
+
}),
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
function NoLib() {
|
|
29
|
+
throw new Error(
|
|
30
|
+
'Cannot play audio as no valid encryption package is installed.\n- Install sodium, libsodium-wrappers, or @stablelib/xchacha20poly1305.',
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
exports.methods = {
|
|
35
|
+
crypto_aead_xchacha20poly1305_ietf_encrypt: NoLib,
|
|
36
|
+
crypto_aead_xchacha20poly1305_ietf_decrypt: NoLib,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
async function importModule(name, usingImport = false) {
|
|
40
|
+
try {
|
|
41
|
+
if (usingImport) {
|
|
42
|
+
return await import(name);
|
|
43
|
+
} else {
|
|
44
|
+
return require(name);
|
|
45
|
+
}
|
|
46
|
+
} catch (e) {
|
|
47
|
+
if (e.code == 'ERR_REQUIRE_ESM') {
|
|
48
|
+
return importModule(name, true);
|
|
49
|
+
} else {
|
|
50
|
+
throw e;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
(async () => {
|
|
56
|
+
for (const libName of Object.keys(libs)) {
|
|
57
|
+
try {
|
|
58
|
+
const lib = await importModule(libName);
|
|
59
|
+
if (libName === 'libsodium-wrappers' && lib.ready) await lib.ready; // eslint-disable-line no-await-in-loop
|
|
60
|
+
exports.methods = libs[libName](lib);
|
|
61
|
+
break;
|
|
62
|
+
} catch {} // eslint-disable-line no-empty
|
|
63
|
+
}
|
|
64
|
+
})();
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { Buffer } = require('node:buffer');
|
|
4
|
+
const { Readable } = require('stream');
|
|
5
|
+
|
|
6
|
+
const SILENCE_FRAME = Buffer.from([0xf8, 0xff, 0xfe]);
|
|
7
|
+
|
|
8
|
+
class Silence extends Readable {
|
|
9
|
+
_read() {
|
|
10
|
+
this.push(SILENCE_FRAME);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
Silence.SILENCE_FRAME = SILENCE_FRAME;
|
|
15
|
+
|
|
16
|
+
module.exports = Silence;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
Credit: https://github.com/dank074/Discord-video-stream
|
|
5
|
+
The use of video streaming in this library is an incomplete implementation with many bugs, primarily aimed at lazy users.
|
|
6
|
+
The video streaming features in this library are sourced from https://github.com/dank074/Discord-video-stream.
|
|
7
|
+
|
|
8
|
+
Please use the @dank074/discord-video-stream library to access all advanced and professional features,
|
|
9
|
+
along with comprehensive support. I will not actively fix bugs related to streaming and encourage everyone to
|
|
10
|
+
use https://github.com/dank074/Discord-video-stream for stable and smooth streaming.
|
|
11
|
+
|
|
12
|
+
To reiterate: This is an incomplete implementation of the library https://github.com/dank074/Discord-video-stream.
|
|
13
|
+
|
|
14
|
+
Thanks to dank074 and longnguyen2004 for implementing new codecs (H264, H265).
|
|
15
|
+
Thanks to mrjvs for discovering how Discord transmits data and the VP8 codec.
|
|
16
|
+
|
|
17
|
+
Please use the @dank074/discord-video-stream library for the best support.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const net = require('net');
|
|
21
|
+
const { randomUUID } = require('node:crypto');
|
|
22
|
+
const os = require('node:os');
|
|
23
|
+
const path = require('path');
|
|
24
|
+
const process = require('process');
|
|
25
|
+
|
|
26
|
+
let counter = 0;
|
|
27
|
+
|
|
28
|
+
class UnixStream {
|
|
29
|
+
constructor(stream, onSocket) {
|
|
30
|
+
if (process.platform === 'win32') {
|
|
31
|
+
const pipePrefix = '\\\\.\\pipe\\';
|
|
32
|
+
const pipeName = `node-webrtc.${++counter}.sock`;
|
|
33
|
+
this.socketPath = path.join(pipePrefix, pipeName);
|
|
34
|
+
this.url = this.socketPath;
|
|
35
|
+
} else {
|
|
36
|
+
this.socketPath = path.join(os.tmpdir(), `djs-${randomUUID()}.sock`);
|
|
37
|
+
this.url = `unix:${this.socketPath}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const server = net.createServer(onSocket);
|
|
41
|
+
this.server = server;
|
|
42
|
+
this.connections = new Set();
|
|
43
|
+
server.on('connection', socket => {
|
|
44
|
+
this.connections.add(socket);
|
|
45
|
+
socket.once('close', () => this.connections.delete(socket));
|
|
46
|
+
});
|
|
47
|
+
server.on('error', error => stream.destroy(error));
|
|
48
|
+
stream.on('finish', () => {
|
|
49
|
+
server.close();
|
|
50
|
+
});
|
|
51
|
+
server.listen(this.socketPath);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
destroy() {
|
|
55
|
+
for (const socket of this.connections) socket.destroy();
|
|
56
|
+
this.server.close();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function StreamInput(stream) {
|
|
61
|
+
return new UnixStream(stream, socket => stream.pipe(socket));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function StreamOutput(stream) {
|
|
65
|
+
return new UnixStream(stream, socket => socket.pipe(stream));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
module.exports = { StreamOutput, StreamInput, UnixStream };
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { Buffer } = require('node:buffer');
|
|
4
|
+
|
|
5
|
+
const START = Buffer.from([0, 0, 0, 1]);
|
|
6
|
+
const MAX_FRAME = 32 * 1024 * 1024;
|
|
7
|
+
|
|
8
|
+
function lengthPrefixedToAnnexB(frame) {
|
|
9
|
+
const nalus = [];
|
|
10
|
+
let offset = 0;
|
|
11
|
+
while (offset < frame.length) {
|
|
12
|
+
if (offset + 4 > frame.length) throw new RangeError('Truncated NAL length');
|
|
13
|
+
const size = frame.readUInt32BE(offset);
|
|
14
|
+
offset += 4;
|
|
15
|
+
if (!size || offset + size > frame.length) throw new RangeError('Invalid NAL length');
|
|
16
|
+
nalus.push(START, frame.subarray(offset, offset + size));
|
|
17
|
+
offset += size;
|
|
18
|
+
}
|
|
19
|
+
return Buffer.concat(nalus);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function annexBNalus(frame) {
|
|
23
|
+
const starts = [];
|
|
24
|
+
for (let i = 0; i + 3 < frame.length; i++) {
|
|
25
|
+
if (frame[i] || frame[i + 1]) continue;
|
|
26
|
+
const size = frame[i + 2] === 1 ? 3 : frame[i + 2] === 0 && frame[i + 3] === 1 ? 4 : 0;
|
|
27
|
+
if (size) {
|
|
28
|
+
starts.push({ offset: i, size });
|
|
29
|
+
i += size - 1;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (!starts.length || starts[0].offset !== 0) throw new RangeError('Missing Annex B start code');
|
|
33
|
+
return starts.map((start, i) => frame.subarray(start.offset + start.size, starts[i + 1]?.offset ?? frame.length));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function packetize(frame, codec, mtu = 1200, pictureId = 0) {
|
|
37
|
+
if (!Number.isInteger(mtu) || mtu < 16) throw new RangeError('Invalid video MTU');
|
|
38
|
+
const output = [];
|
|
39
|
+
if (codec === 'VP8') {
|
|
40
|
+
for (let offset = 0; offset < frame.length; offset += mtu - 4) {
|
|
41
|
+
const descriptor = Buffer.from([offset ? 0x80 : 0x90, 0x80, 0x80 | ((pictureId >> 8) & 0x7f), pictureId & 0xff]);
|
|
42
|
+
output.push(Buffer.concat([descriptor, frame.subarray(offset, offset + mtu - 4)]));
|
|
43
|
+
}
|
|
44
|
+
return output;
|
|
45
|
+
}
|
|
46
|
+
if (!['H264', 'H265'].includes(codec)) throw new RangeError(`Unsupported video codec: ${codec}`);
|
|
47
|
+
for (const nalu of annexBNalus(frame)) {
|
|
48
|
+
if (nalu.length <= mtu) {
|
|
49
|
+
output.push(nalu);
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
const headerSize = codec === 'H264' ? 1 : 2;
|
|
53
|
+
const fuSize = headerSize + 1;
|
|
54
|
+
const type = codec === 'H264' ? nalu[0] & 31 : (nalu[0] >> 1) & 63;
|
|
55
|
+
for (let offset = headerSize; offset < nalu.length; offset += mtu - fuSize) {
|
|
56
|
+
const end = Math.min(nalu.length, offset + mtu - fuSize);
|
|
57
|
+
const flags = (offset === headerSize ? 0x80 : 0) | (end === nalu.length ? 0x40 : 0);
|
|
58
|
+
const fu =
|
|
59
|
+
codec === 'H264'
|
|
60
|
+
? Buffer.from([(nalu[0] & 0xe0) | 28, flags | type])
|
|
61
|
+
: Buffer.from([(nalu[0] & 0x81) | (49 << 1), nalu[1], flags | type]);
|
|
62
|
+
output.push(Buffer.concat([fu, nalu.subarray(offset, end)]));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return output;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function depacketize(payloads, codec) {
|
|
69
|
+
const parts = [];
|
|
70
|
+
let fragment = false;
|
|
71
|
+
for (const payload of payloads) {
|
|
72
|
+
if (!payload.length) throw new RangeError('Empty video payload');
|
|
73
|
+
if (codec === 'VP8') {
|
|
74
|
+
if (!parts.length && !(payload[0] & 0x10)) throw new RangeError('Missing first VP8 partition');
|
|
75
|
+
let offset = 1;
|
|
76
|
+
if (payload[0] & 0x80) {
|
|
77
|
+
const flags = payload[offset++];
|
|
78
|
+
if (flags & 0x80) offset += payload[offset] & 0x80 ? 2 : 1;
|
|
79
|
+
if (flags & 0x40) offset++;
|
|
80
|
+
if (flags & 0x30) offset++;
|
|
81
|
+
}
|
|
82
|
+
if (offset >= payload.length) throw new RangeError('Truncated VP8 descriptor');
|
|
83
|
+
parts.push(payload.subarray(offset));
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const h265 = codec === 'H265';
|
|
87
|
+
const type = h265 ? (payload[0] >> 1) & 63 : payload[0] & 31;
|
|
88
|
+
const fuType = h265 ? 49 : 28;
|
|
89
|
+
const aggregateType = h265 ? 48 : 24;
|
|
90
|
+
if (type === aggregateType) {
|
|
91
|
+
if (fragment) throw new RangeError('Incomplete fragmented NAL');
|
|
92
|
+
let offset = h265 ? 2 : 1;
|
|
93
|
+
while (offset < payload.length) {
|
|
94
|
+
if (offset + 2 > payload.length) throw new RangeError('Truncated aggregation packet');
|
|
95
|
+
const size = payload.readUInt16BE(offset);
|
|
96
|
+
offset += 2;
|
|
97
|
+
if (!size || offset + size > payload.length) throw new RangeError('Invalid aggregation length');
|
|
98
|
+
parts.push(START, payload.subarray(offset, offset + size));
|
|
99
|
+
offset += size;
|
|
100
|
+
}
|
|
101
|
+
} else if (type === fuType) {
|
|
102
|
+
const size = h265 ? 3 : 2;
|
|
103
|
+
if (payload.length <= size) throw new RangeError('Truncated fragmentation unit');
|
|
104
|
+
const flags = payload[size - 1];
|
|
105
|
+
if (flags & 0x80) {
|
|
106
|
+
if (fragment) throw new RangeError('Incomplete fragmented NAL');
|
|
107
|
+
fragment = true;
|
|
108
|
+
parts.push(
|
|
109
|
+
START,
|
|
110
|
+
h265
|
|
111
|
+
? Buffer.from([(payload[0] & 0x81) | ((flags & 63) << 1), payload[1]])
|
|
112
|
+
: Buffer.from([(payload[0] & 0xe0) | (flags & 31)]),
|
|
113
|
+
);
|
|
114
|
+
} else if (!fragment) {
|
|
115
|
+
throw new RangeError('Missing first fragmentation unit');
|
|
116
|
+
}
|
|
117
|
+
parts.push(payload.subarray(size));
|
|
118
|
+
if (flags & 0x40) fragment = false;
|
|
119
|
+
} else {
|
|
120
|
+
if (fragment) throw new RangeError('Incomplete fragmented NAL');
|
|
121
|
+
if (type >= aggregateType || (!h265 && type === 0)) throw new RangeError('Unsupported NAL packet');
|
|
122
|
+
parts.push(START, payload);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (fragment) throw new RangeError('Incomplete fragmented NAL');
|
|
126
|
+
return Buffer.concat(parts);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
class VideoFrames {
|
|
130
|
+
constructor() {
|
|
131
|
+
this.frames = new Map();
|
|
132
|
+
this.completed = new Map();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
push(packet, codec) {
|
|
136
|
+
const { ssrc, timestamp, sequenceNumber: seq, marker } = packet.header;
|
|
137
|
+
const now = Date.now();
|
|
138
|
+
const key = `${ssrc}:${timestamp}:${codec}`;
|
|
139
|
+
for (const [id, time] of this.completed) {
|
|
140
|
+
if (now - time > 2000) this.completed.delete(id);
|
|
141
|
+
}
|
|
142
|
+
if (this.completed.has(key)) return null;
|
|
143
|
+
for (const [id, frame] of this.frames) {
|
|
144
|
+
if (now - frame.time > 2000) this.frames.delete(id);
|
|
145
|
+
}
|
|
146
|
+
let frame = this.frames.get(key);
|
|
147
|
+
if (!frame || frame.timestamp !== timestamp || frame.codec !== codec) {
|
|
148
|
+
if (this.frames.size >= 32) this.frames.delete(this.frames.keys().next().value);
|
|
149
|
+
frame = { timestamp, codec, time: now, packets: new Map(), bytes: 0, first: seq, last: null };
|
|
150
|
+
this.frames.set(key, frame);
|
|
151
|
+
}
|
|
152
|
+
if (frame.packets.has(seq)) return null;
|
|
153
|
+
if (((frame.first - seq) & 65535) < 4096) frame.first = seq;
|
|
154
|
+
frame.bytes += packet.payload.length;
|
|
155
|
+
if (frame.bytes > MAX_FRAME || frame.packets.size >= 4096) {
|
|
156
|
+
this.frames.delete(key);
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
frame.packets.set(seq, packet.payload);
|
|
160
|
+
if (marker) frame.last = seq;
|
|
161
|
+
if (frame.last === null) return null;
|
|
162
|
+
const count = ((frame.last - frame.first) & 65535) + 1;
|
|
163
|
+
if (count > 4096 || count !== frame.packets.size) return null;
|
|
164
|
+
const payloads = [];
|
|
165
|
+
for (let i = 0; i < count; i++) {
|
|
166
|
+
const payload = frame.packets.get((frame.first + i) & 65535);
|
|
167
|
+
if (!payload) return null;
|
|
168
|
+
payloads.push(payload);
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
const complete = depacketize(payloads, codec);
|
|
172
|
+
this.frames.delete(key);
|
|
173
|
+
if (this.completed.size >= 128) this.completed.delete(this.completed.keys().next().value);
|
|
174
|
+
this.completed.set(key, now);
|
|
175
|
+
return complete;
|
|
176
|
+
} catch {
|
|
177
|
+
// The marker can arrive before the first fragment. Retain bounded state for late packets.
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
module.exports = { VideoFrames, packetize, depacketize, lengthPrefixedToAnnexB, annexBNalus };
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const EventEmitter = require('events');
|
|
4
|
+
const { Buffer } = require('node:buffer');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* An interface class for volume transformation.
|
|
8
|
+
* @extends {EventEmitter}
|
|
9
|
+
*/
|
|
10
|
+
class VolumeInterface extends EventEmitter {
|
|
11
|
+
constructor({ volume = 1 } = {}) {
|
|
12
|
+
super();
|
|
13
|
+
this.setVolume(volume);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Whether or not the volume of this stream is editable
|
|
18
|
+
* @type {boolean}
|
|
19
|
+
* @readonly
|
|
20
|
+
*/
|
|
21
|
+
get volumeEditable() {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The current volume of the stream
|
|
27
|
+
* @type {number}
|
|
28
|
+
* @readonly
|
|
29
|
+
*/
|
|
30
|
+
get volume() {
|
|
31
|
+
return this._volume;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The current volume of the stream in decibels
|
|
36
|
+
* @type {number}
|
|
37
|
+
* @readonly
|
|
38
|
+
*/
|
|
39
|
+
get volumeDecibels() {
|
|
40
|
+
return Math.log10(this.volume) * 20;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The current volume of the stream from a logarithmic scale
|
|
45
|
+
* @type {number}
|
|
46
|
+
* @readonly
|
|
47
|
+
*/
|
|
48
|
+
get volumeLogarithmic() {
|
|
49
|
+
return Math.pow(this.volume, 1 / 1.660964);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
applyVolume(buffer, volume) {
|
|
53
|
+
volume = volume || this._volume;
|
|
54
|
+
if (volume === 1) return buffer;
|
|
55
|
+
|
|
56
|
+
const out = Buffer.alloc(buffer.length);
|
|
57
|
+
for (let i = 0; i < buffer.length; i += 2) {
|
|
58
|
+
if (i >= buffer.length - 1) break;
|
|
59
|
+
const uint = Math.min(32767, Math.max(-32767, Math.floor(volume * buffer.readInt16LE(i))));
|
|
60
|
+
out.writeInt16LE(uint, i);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Sets the volume relative to the input stream - i.e. 1 is normal, 0.5 is half, 2 is double.
|
|
68
|
+
* @param {number} volume The volume that you want to set
|
|
69
|
+
*/
|
|
70
|
+
setVolume(volume) {
|
|
71
|
+
/**
|
|
72
|
+
* Emitted when the volume of this interface changes.
|
|
73
|
+
* @event VolumeInterface#volumeChange
|
|
74
|
+
* @param {number} oldVolume The old volume of this interface
|
|
75
|
+
* @param {number} newVolume The new volume of this interface
|
|
76
|
+
*/
|
|
77
|
+
this.emit('volumeChange', this._volume, volume);
|
|
78
|
+
this._volume = volume;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Sets the volume in decibels.
|
|
83
|
+
* @param {number} db The decibels
|
|
84
|
+
*/
|
|
85
|
+
setVolumeDecibels(db) {
|
|
86
|
+
this.setVolume(Math.pow(10, db / 20));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Sets the volume so that a perceived value of 0.5 is half the perceived volume etc.
|
|
91
|
+
* @param {number} value The value for the volume
|
|
92
|
+
*/
|
|
93
|
+
setVolumeLogarithmic(value) {
|
|
94
|
+
this.setVolume(Math.pow(value, 1.660964));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const props = ['volumeDecibels', 'volumeLogarithmic', 'setVolumeDecibels', 'setVolumeLogarithmic'];
|
|
99
|
+
exports.VolumeInterface = VolumeInterface;
|
|
100
|
+
|
|
101
|
+
exports.applyToClass = function applyToClass(structure) {
|
|
102
|
+
for (const prop of props) {
|
|
103
|
+
Object.defineProperty(structure.prototype, prop, Object.getOwnPropertyDescriptor(VolumeInterface.prototype, prop));
|
|
104
|
+
}
|
|
105
|
+
};
|