safeness-sb-new 0.0.1
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/README.md +115 -0
- package/package.json +96 -0
- package/src/WebSocket.js +39 -0
- package/src/client/BaseClient.js +86 -0
- package/src/client/Client.js +836 -0
- package/src/client/WebhookClient.js +61 -0
- package/src/client/actions/Action.js +120 -0
- package/src/client/actions/ActionsManager.js +78 -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 +46 -0
- package/src/client/actions/MessageDelete.js +32 -0
- package/src/client/actions/MessageDeleteBulk.js +46 -0
- package/src/client/actions/MessageReactionAdd.js +56 -0
- package/src/client/actions/MessageReactionRemove.js +45 -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 +46 -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 +57 -0
- package/src/client/actions/WebhooksUpdate.js +20 -0
- package/src/client/voice/ClientVoiceManager.js +150 -0
- package/src/client/voice/VoiceConnection.js +849 -0
- package/src/client/voice/dispatcher/AnnexBDispatcher.js +120 -0
- package/src/client/voice/dispatcher/AudioDispatcher.js +115 -0
- package/src/client/voice/dispatcher/BaseDispatcher.js +405 -0
- package/src/client/voice/dispatcher/VPxDispatcher.js +52 -0
- package/src/client/voice/dispatcher/VideoDispatcher.js +31 -0
- package/src/client/voice/networking/VoiceUDPClient.js +188 -0
- package/src/client/voice/networking/VoiceWebSocket.js +280 -0
- package/src/client/voice/player/MediaPlayer.js +294 -0
- package/src/client/voice/player/processing/AnnexBNalSplitter.js +244 -0
- package/src/client/voice/player/processing/IvfSplitter.js +106 -0
- package/src/client/voice/receiver/PacketHandler.js +170 -0
- package/src/client/voice/receiver/Receiver.js +82 -0
- package/src/client/voice/receiver/video/IvfJoinner.js +106 -0
- package/src/client/voice/util/Function.js +14 -0
- package/src/client/voice/util/PlayInterface.js +122 -0
- package/src/client/voice/util/Secretbox.js +42 -0
- package/src/client/voice/util/Silence.js +16 -0
- package/src/client/voice/util/Socket.js +62 -0
- package/src/client/voice/util/VolumeInterface.js +104 -0
- package/src/client/websocket/WebSocketManager.js +392 -0
- package/src/client/websocket/WebSocketShard.js +906 -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 +22 -0
- package/src/client/websocket/handlers/MESSAGE_POLL_VOTE_REMOVE.js +12 -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 +120 -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_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 +83 -0
- package/src/errors/DJSError.js +61 -0
- package/src/errors/Messages.js +208 -0
- package/src/errors/index.js +4 -0
- package/src/index.js +159 -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 +138 -0
- package/src/managers/ClientUserSettingManager.js +372 -0
- package/src/managers/DataManager.js +61 -0
- package/src/managers/GuildBanManager.js +204 -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 +108 -0
- package/src/managers/GuildInviteManager.js +213 -0
- package/src/managers/GuildManager.js +304 -0
- package/src/managers/GuildMemberManager.js +597 -0
- package/src/managers/GuildMemberRoleManager.js +191 -0
- package/src/managers/GuildScheduledEventManager.js +296 -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 +391 -0
- package/src/managers/PermissionOverwriteManager.js +166 -0
- package/src/managers/PresenceManager.js +58 -0
- package/src/managers/ReactionManager.js +67 -0
- package/src/managers/ReactionUserManager.js +71 -0
- package/src/managers/RelationshipManager.js +265 -0
- package/src/managers/RoleManager.js +352 -0
- package/src/managers/StageInstanceManager.js +162 -0
- package/src/managers/ThreadManager.js +174 -0
- package/src/managers/ThreadMemberManager.js +186 -0
- package/src/managers/UserManager.js +146 -0
- package/src/managers/UserNoteManager.js +53 -0
- package/src/managers/VoiceStateManager.js +37 -0
- package/src/rest/APIRequest.js +159 -0
- package/src/rest/APIRouter.js +53 -0
- package/src/rest/DiscordAPIError.js +104 -0
- package/src/rest/HTTPError.js +62 -0
- package/src/rest/RESTManager.js +62 -0
- package/src/rest/RateLimitError.js +55 -0
- package/src/rest/RequestHandler.js +444 -0
- package/src/sharding/Shard.js +443 -0
- package/src/sharding/ShardClientUtil.js +275 -0
- package/src/sharding/ShardingManager.js +318 -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 +114 -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 +270 -0
- package/src/structures/ClientPresence.js +77 -0
- package/src/structures/ClientUser.js +450 -0
- package/src/structures/CommandInteraction.js +41 -0
- package/src/structures/CommandInteractionOptionResolver.js +276 -0
- package/src/structures/ContextMenuInteraction.js +65 -0
- package/src/structures/DMChannel.js +217 -0
- package/src/structures/DirectoryChannel.js +20 -0
- package/src/structures/Emoji.js +148 -0
- package/src/structures/ForumChannel.js +261 -0
- package/src/structures/GroupDMChannel.js +387 -0
- package/src/structures/Guild.js +1608 -0
- package/src/structures/GuildAuditLogs.js +729 -0
- package/src/structures/GuildBan.js +59 -0
- package/src/structures/GuildBoost.js +108 -0
- package/src/structures/GuildChannel.js +468 -0
- package/src/structures/GuildEmoji.js +161 -0
- package/src/structures/GuildMember.js +568 -0
- package/src/structures/GuildPreview.js +191 -0
- package/src/structures/GuildPreviewEmoji.js +27 -0
- package/src/structures/GuildScheduledEvent.js +441 -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/Message.js +1227 -0
- package/src/structures/MessageActionRow.js +103 -0
- package/src/structures/MessageAttachment.js +204 -0
- package/src/structures/MessageButton.js +165 -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 +586 -0
- package/src/structures/MessageMentions.js +273 -0
- package/src/structures/MessagePayload.js +318 -0
- package/src/structures/MessagePoll.js +238 -0
- package/src/structures/MessageReaction.js +171 -0
- package/src/structures/MessageSelectMenu.js +140 -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 +196 -0
- package/src/structures/Presence.js +1131 -0
- package/src/structures/ReactionCollector.js +229 -0
- package/src/structures/ReactionEmoji.js +31 -0
- package/src/structures/Role.js +531 -0
- package/src/structures/SelectMenuInteraction.js +21 -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 +71 -0
- package/src/structures/TextChannel.js +33 -0
- package/src/structures/TextInputComponent.js +131 -0
- package/src/structures/ThreadChannel.js +607 -0
- package/src/structures/ThreadMember.js +105 -0
- package/src/structures/Typing.js +74 -0
- package/src/structures/User.js +543 -0
- package/src/structures/UserContextMenuInteraction.js +29 -0
- package/src/structures/VoiceChannel.js +110 -0
- package/src/structures/VoiceRegion.js +53 -0
- package/src/structures/VoiceState.js +345 -0
- package/src/structures/WebEmbed.js +373 -0
- package/src/structures/Webhook.js +467 -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 +313 -0
- package/src/structures/interfaces/Collector.js +300 -0
- package/src/structures/interfaces/InteractionResponses.js +313 -0
- package/src/structures/interfaces/TextBasedChannel.js +719 -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 +170 -0
- package/src/util/ChannelFlags.js +45 -0
- package/src/util/Constants.js +1815 -0
- package/src/util/DataResolver.js +145 -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 +29 -0
- package/src/util/LimitedCollection.js +131 -0
- package/src/util/MessageFlags.js +54 -0
- package/src/util/Options.js +336 -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/RemoteAuth.js +382 -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 +889 -0
- package/typings/enums.d.ts +297 -0
- package/typings/index.d.ts +7670 -0
- package/typings/index.test-d.ts +0 -0
- package/typings/rawDataTypes.d.ts +342 -0
|
@@ -0,0 +1,120 @@
|
|
|
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 { Buffer } = require('buffer');
|
|
21
|
+
const VideoDispatcher = require('./VideoDispatcher');
|
|
22
|
+
const { H264Helpers, H265Helpers } = require('../player/processing/AnnexBNalSplitter');
|
|
23
|
+
|
|
24
|
+
class AnnexBDispatcher extends VideoDispatcher {
|
|
25
|
+
constructor(player, highWaterMark = 12, streams, fps, nalFunctions) {
|
|
26
|
+
super(player, highWaterMark, streams, fps);
|
|
27
|
+
this._nalFunctions = nalFunctions;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
codecCallback(frame) {
|
|
31
|
+
let accessUnit = frame;
|
|
32
|
+
let offset = 0;
|
|
33
|
+
|
|
34
|
+
// Extract NALUs from the access unit
|
|
35
|
+
while (offset < accessUnit.length) {
|
|
36
|
+
const naluSize = accessUnit.readUInt32BE(offset);
|
|
37
|
+
offset += 4;
|
|
38
|
+
const nalu = accessUnit.subarray(offset, offset + naluSize);
|
|
39
|
+
const isLastNal = offset + naluSize >= accessUnit.length;
|
|
40
|
+
if (nalu.length <= this.mtu) {
|
|
41
|
+
// If NALU size is within MTU, send it directly
|
|
42
|
+
this._playChunk(Buffer.concat([this.createHeaderExtension(), nalu]), isLastNal);
|
|
43
|
+
} else {
|
|
44
|
+
// If NALU size exceeds MTU, fragment it
|
|
45
|
+
const [naluHeader, naluData] = this._nalFunctions.splitHeader(nalu);
|
|
46
|
+
const dataFragments = this.partitionVideoData(naluData);
|
|
47
|
+
|
|
48
|
+
for (let fragmentIndex = 0; fragmentIndex < dataFragments.length; fragmentIndex++) {
|
|
49
|
+
const data = dataFragments[fragmentIndex];
|
|
50
|
+
const isFirstPacket = fragmentIndex === 0;
|
|
51
|
+
const isFinalPacket = fragmentIndex === dataFragments.length - 1;
|
|
52
|
+
|
|
53
|
+
this._playChunk(
|
|
54
|
+
Buffer.concat([
|
|
55
|
+
this.createHeaderExtension(),
|
|
56
|
+
this.makeFragmentationUnitHeader(isFirstPacket, isFinalPacket, naluHeader),
|
|
57
|
+
data,
|
|
58
|
+
]),
|
|
59
|
+
isLastNal && isFinalPacket,
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
offset += naluSize;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
class H264Dispatcher extends AnnexBDispatcher {
|
|
69
|
+
constructor(player, highWaterMark = 12, streams, fps) {
|
|
70
|
+
super(player, highWaterMark, streams, fps, H264Helpers);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
makeFragmentationUnitHeader(isFirstPacket, isLastPacket, naluHeader) {
|
|
74
|
+
const nal0 = naluHeader[0];
|
|
75
|
+
const fuPayloadHeader = Buffer.alloc(2);
|
|
76
|
+
const nalType = H264Helpers.getUnitType(naluHeader);
|
|
77
|
+
const fnri = nal0 & 0xe0;
|
|
78
|
+
|
|
79
|
+
fuPayloadHeader[0] = 0x1c | fnri;
|
|
80
|
+
|
|
81
|
+
if (isFirstPacket) {
|
|
82
|
+
fuPayloadHeader[1] = 0x80 | nalType;
|
|
83
|
+
} else if (isLastPacket) {
|
|
84
|
+
fuPayloadHeader[1] = 0x40 | nalType;
|
|
85
|
+
} else {
|
|
86
|
+
fuPayloadHeader[1] = nalType;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return fuPayloadHeader;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
class H265Dispatcher extends AnnexBDispatcher {
|
|
94
|
+
constructor(player, highWaterMark = 12, streams, fps) {
|
|
95
|
+
super(player, highWaterMark, streams, fps, H265Helpers);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
makeFragmentationUnitHeader(isFirstPacket, isLastPacket, naluHeader) {
|
|
99
|
+
const fuIndicatorHeader = Buffer.allocUnsafe(3);
|
|
100
|
+
naluHeader.copy(fuIndicatorHeader);
|
|
101
|
+
const nalType = H265Helpers.getUnitType(naluHeader);
|
|
102
|
+
|
|
103
|
+
fuIndicatorHeader[0] = (fuIndicatorHeader[0] & 0b10000001) | (49 << 1);
|
|
104
|
+
|
|
105
|
+
if (isFirstPacket) {
|
|
106
|
+
fuIndicatorHeader[2] = 0x80 | nalType;
|
|
107
|
+
} else if (isLastPacket) {
|
|
108
|
+
fuIndicatorHeader[2] = 0x40 | nalType;
|
|
109
|
+
} else {
|
|
110
|
+
fuIndicatorHeader[2] = nalType;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return fuIndicatorHeader;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
module.exports = {
|
|
118
|
+
H264Dispatcher,
|
|
119
|
+
H265Dispatcher,
|
|
120
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const BaseDispatcher = require('./BaseDispatcher');
|
|
4
|
+
const Silence = require('../util/Silence');
|
|
5
|
+
const VolumeInterface = require('../util/VolumeInterface');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @external WritableStream
|
|
9
|
+
* @see {@link https://nodejs.org/api/stream.html#stream_class_stream_writable}
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The class that sends voice packet data to the voice connection.
|
|
14
|
+
* ```js
|
|
15
|
+
* // Obtained using:
|
|
16
|
+
* client.voice.joinChannel(channel).then(connection => {
|
|
17
|
+
* // You can play a file or a stream here:
|
|
18
|
+
* const dispatcher = connection.playAudio('/home/hydrabolt/audio.mp3');
|
|
19
|
+
* });
|
|
20
|
+
* ```
|
|
21
|
+
* @implements {VolumeInterface}
|
|
22
|
+
* @extends {BaseDispatcher}
|
|
23
|
+
*/
|
|
24
|
+
class AudioDispatcher extends BaseDispatcher {
|
|
25
|
+
constructor(player, { seek = 0, volume = 1, fec, plp, bitrate = 96, highWaterMark = 12 } = {}, streams) {
|
|
26
|
+
const streamOptions = { seek, volume, fec, plp, bitrate, highWaterMark };
|
|
27
|
+
super(player, highWaterMark, 120, false, streams);
|
|
28
|
+
|
|
29
|
+
this.streamOptions = streamOptions;
|
|
30
|
+
|
|
31
|
+
this.streams.silence = new Silence();
|
|
32
|
+
|
|
33
|
+
this.setVolume(volume);
|
|
34
|
+
this.setBitrate(bitrate);
|
|
35
|
+
if (typeof fec !== 'undefined') this.setFEC(fec);
|
|
36
|
+
if (typeof plp !== 'undefined') this.setPLP(plp);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Set the bitrate of the current Opus encoder if using a compatible Opus stream.
|
|
41
|
+
* @param {number} value New bitrate, in kbps
|
|
42
|
+
* If set to 'auto', the voice channel's bitrate will be used
|
|
43
|
+
* @returns {boolean} true if the bitrate has been successfully changed.
|
|
44
|
+
*/
|
|
45
|
+
setBitrate(value) {
|
|
46
|
+
if (!value || !this.bitrateEditable) return false;
|
|
47
|
+
const bitrate = value === 'auto' ? this.player.voiceConnection.channel.bitrate : value;
|
|
48
|
+
this.streams.opus.setBitrate(bitrate * 1000);
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Sets the expected packet loss percentage if using a compatible Opus stream.
|
|
54
|
+
* @param {number} value between 0 and 1
|
|
55
|
+
* @returns {boolean} Returns true if it was successfully set.
|
|
56
|
+
*/
|
|
57
|
+
setPLP(value) {
|
|
58
|
+
if (!this.bitrateEditable) return false;
|
|
59
|
+
this.streams.opus.setPLP(value);
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Enables or disables forward error correction if using a compatible Opus stream.
|
|
65
|
+
* @param {boolean} enabled true to enable
|
|
66
|
+
* @returns {boolean} Returns true if it was successfully set.
|
|
67
|
+
*/
|
|
68
|
+
setFEC(enabled) {
|
|
69
|
+
if (!this.bitrateEditable) return false;
|
|
70
|
+
this.streams.opus.setFEC(enabled);
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
get volumeEditable() {
|
|
75
|
+
return Boolean(this.streams.volume);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Whether or not the Opus bitrate of this stream is editable
|
|
80
|
+
* @type {boolean}
|
|
81
|
+
* @readonly
|
|
82
|
+
*/
|
|
83
|
+
get bitrateEditable() {
|
|
84
|
+
return this.streams.opus && this.streams.opus.setBitrate;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Volume
|
|
88
|
+
get volume() {
|
|
89
|
+
return this.streams.volume ? this.streams.volume.volume : 1;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
setVolume(value) {
|
|
93
|
+
if (!this.streams.volume) return false;
|
|
94
|
+
/**
|
|
95
|
+
* Emitted when the volume of this dispatcher changes.
|
|
96
|
+
* @event AudioDispatcher#volumeChange
|
|
97
|
+
* @param {number} oldVolume The old volume of this dispatcher
|
|
98
|
+
* @param {number} newVolume The new volume of this dispatcher
|
|
99
|
+
*/
|
|
100
|
+
this.emit('volumeChange', this.volume, value);
|
|
101
|
+
this.streams.volume.setVolume(value);
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Volume stubs for docs
|
|
106
|
+
/* eslint-disable no-empty-function*/
|
|
107
|
+
get volumeDecibels() {}
|
|
108
|
+
get volumeLogarithmic() {}
|
|
109
|
+
setVolumeDecibels() {}
|
|
110
|
+
setVolumeLogarithmic() {}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
VolumeInterface.applyToClass(AudioDispatcher);
|
|
114
|
+
|
|
115
|
+
module.exports = AudioDispatcher;
|
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { Buffer } = require('node:buffer');
|
|
4
|
+
const { setTimeout } = require('node:timers');
|
|
5
|
+
const { Writable } = require('stream');
|
|
6
|
+
const find = require('find-process');
|
|
7
|
+
const kill = require('tree-kill');
|
|
8
|
+
const secretbox = require('../util/Secretbox');
|
|
9
|
+
|
|
10
|
+
const CHANNELS = 2;
|
|
11
|
+
|
|
12
|
+
const MAX_NONCE_SIZE = 2 ** 32 - 1;
|
|
13
|
+
const nonce = Buffer.alloc(24);
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @external WritableStream
|
|
17
|
+
* @see {@link https://nodejs.org/api/stream.html#stream_class_stream_writable}
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @extends {Writable}
|
|
22
|
+
*/
|
|
23
|
+
class BaseDispatcher extends Writable {
|
|
24
|
+
constructor(player, highWaterMark = 12, payloadType, extensionEnabled, streams = {}) {
|
|
25
|
+
super({
|
|
26
|
+
highWaterMark,
|
|
27
|
+
});
|
|
28
|
+
this.streams = streams;
|
|
29
|
+
/**
|
|
30
|
+
* The Audio Player that controls this dispatcher
|
|
31
|
+
* @type {MediaPlayer}
|
|
32
|
+
*/
|
|
33
|
+
this.player = player;
|
|
34
|
+
this.payloadType = payloadType;
|
|
35
|
+
this.extensionEnabled = extensionEnabled;
|
|
36
|
+
|
|
37
|
+
this._nonce = 0;
|
|
38
|
+
this._nonceBuffer = Buffer.alloc(24);
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The time that the stream was paused at (null if not paused)
|
|
42
|
+
* @type {?number}
|
|
43
|
+
*/
|
|
44
|
+
this.pausedSince = null;
|
|
45
|
+
this._writeCallback = null;
|
|
46
|
+
|
|
47
|
+
this._pausedTime = 0;
|
|
48
|
+
this._silentPausedTime = 0;
|
|
49
|
+
|
|
50
|
+
this.count = 0;
|
|
51
|
+
this.sequence = 0;
|
|
52
|
+
this.timestamp = 0;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Video FPS
|
|
56
|
+
* @type {number}
|
|
57
|
+
*/
|
|
58
|
+
this.fps = 0;
|
|
59
|
+
|
|
60
|
+
this.mtu = 1200;
|
|
61
|
+
|
|
62
|
+
const streamError = (type, err) => {
|
|
63
|
+
/**
|
|
64
|
+
* Emitted when the dispatcher encounters an error.
|
|
65
|
+
* @event BaseDispatcher#error
|
|
66
|
+
*/
|
|
67
|
+
if (type && err) {
|
|
68
|
+
err.message = `${type} stream: ${err.message}`;
|
|
69
|
+
this.emit(this.player.dispatcher === this ? 'error' : 'debug', err);
|
|
70
|
+
}
|
|
71
|
+
this.destroy();
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
this.on('error', () => streamError());
|
|
75
|
+
if (this.streams.input) this.streams.input.on('error', err => streamError('input', err));
|
|
76
|
+
if (this.streams.ffmpeg) this.streams.ffmpeg.on('error', err => streamError('ffmpeg', err));
|
|
77
|
+
if (this.streams.opus) this.streams.opus.on('error', err => streamError('opus', err));
|
|
78
|
+
if (this.streams.volume) this.streams.volume.on('error', err => streamError('volume', err));
|
|
79
|
+
|
|
80
|
+
this.on('finish', () => {
|
|
81
|
+
this._cleanup();
|
|
82
|
+
this._setSpeaking(0);
|
|
83
|
+
this._setVideoStatus(false);
|
|
84
|
+
this._setStreamStatus(true);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
get TIMESTAMP_INC() {
|
|
89
|
+
return this.extensionEnabled ? 90000 / this.fps : 480 * CHANNELS;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
get FRAME_LENGTH() {
|
|
93
|
+
return this.extensionEnabled ? 1000 / this.fps : 20;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
partitionVideoData(data) {
|
|
97
|
+
const out = [];
|
|
98
|
+
const dataLength = data.length;
|
|
99
|
+
|
|
100
|
+
for (let i = 0; i < dataLength; i += this.mtu) {
|
|
101
|
+
out.push(data.slice(i, i + this.mtu));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return out;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
getNewSequence() {
|
|
108
|
+
const currentSeq = this.sequence;
|
|
109
|
+
this.sequence++;
|
|
110
|
+
if (this.sequence >= 2 ** 16) this.sequence = 0;
|
|
111
|
+
return currentSeq;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
_write(chunk, enc, done) {
|
|
115
|
+
if (!this.startTime) {
|
|
116
|
+
/**
|
|
117
|
+
* Emitted once the stream has started to play.
|
|
118
|
+
* @event BaseDispatcher#start
|
|
119
|
+
*/
|
|
120
|
+
this.emit('start');
|
|
121
|
+
this.startTime = performance.now();
|
|
122
|
+
}
|
|
123
|
+
if (this.extensionEnabled) {
|
|
124
|
+
this.codecCallback(chunk);
|
|
125
|
+
} else {
|
|
126
|
+
this._playChunk(chunk);
|
|
127
|
+
}
|
|
128
|
+
this._step(done);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
_destroy(err, cb) {
|
|
132
|
+
this._cleanup();
|
|
133
|
+
super._destroy(err, cb);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
_cleanup() {
|
|
137
|
+
if (this.player.dispatcher === this) this.player.dispatcher = null;
|
|
138
|
+
const { streams } = this;
|
|
139
|
+
if (streams.opus) streams.opus.destroy();
|
|
140
|
+
if (streams.ffmpeg?.process) {
|
|
141
|
+
const ffmpegPid = streams.ffmpeg.process.pid; // But it is ppid ;-;
|
|
142
|
+
const args = streams.ffmpeg.process.spawnargs.slice(1).join(' '); // Skip ffmpeg
|
|
143
|
+
find('name', 'ffmpeg', true).then(list => {
|
|
144
|
+
let process = list.find(o => o.pid === ffmpegPid || o.ppid === ffmpegPid || o.cmd.includes(args));
|
|
145
|
+
if (process) {
|
|
146
|
+
kill(process.pid);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
streams.ffmpeg.destroy();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Pauses playback
|
|
155
|
+
* @param {boolean} [silence=false] Whether to play silence while paused to prevent audio glitches
|
|
156
|
+
*/
|
|
157
|
+
pause(silence = false) {
|
|
158
|
+
if (this.paused) return;
|
|
159
|
+
if (this.streams.opus) this.streams.opus.unpipe(this); // Audio
|
|
160
|
+
if (this.streams.video) {
|
|
161
|
+
this.streams.ffmpeg.pause();
|
|
162
|
+
this.streams.video.unpipe(this);
|
|
163
|
+
}
|
|
164
|
+
if (!this.extensionEnabled) {
|
|
165
|
+
// Audio
|
|
166
|
+
if (silence) {
|
|
167
|
+
this.streams.silence.pipe(this);
|
|
168
|
+
this._silence = true;
|
|
169
|
+
} else {
|
|
170
|
+
this._setSpeaking(0);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
this.pausedSince = performance.now();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Whether or not playback is paused
|
|
178
|
+
* @type {boolean}
|
|
179
|
+
* @readonly
|
|
180
|
+
*/
|
|
181
|
+
get paused() {
|
|
182
|
+
return Boolean(this.pausedSince);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Total time that this dispatcher has been paused in milliseconds
|
|
187
|
+
* @type {number}
|
|
188
|
+
* @readonly
|
|
189
|
+
*/
|
|
190
|
+
get pausedTime() {
|
|
191
|
+
return this._silentPausedTime + this._pausedTime + (this.paused ? performance.now() - this.pausedSince : 0);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Resumes playback
|
|
196
|
+
*/
|
|
197
|
+
resume() {
|
|
198
|
+
if (!this.pausedSince) return;
|
|
199
|
+
if (!this.extensionEnabled) this.streams.silence.unpipe(this);
|
|
200
|
+
if (this.streams.opus) this.streams.opus.pipe(this);
|
|
201
|
+
if (this.streams.video) {
|
|
202
|
+
this.streams.ffmpeg.resume();
|
|
203
|
+
this.streams.video.pipe(this);
|
|
204
|
+
}
|
|
205
|
+
if (this._silence) {
|
|
206
|
+
this._silentPausedTime += performance.now() - this.pausedSince;
|
|
207
|
+
this._silence = false;
|
|
208
|
+
} else {
|
|
209
|
+
this._pausedTime += performance.now() - this.pausedSince;
|
|
210
|
+
}
|
|
211
|
+
this.pausedSince = null;
|
|
212
|
+
if (typeof this._writeCallback === 'function') this._writeCallback();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* The time (in milliseconds) that the dispatcher has been playing audio for, taking into account skips and pauses
|
|
217
|
+
* @type {number}
|
|
218
|
+
* @readonly
|
|
219
|
+
*/
|
|
220
|
+
get totalStreamTime() {
|
|
221
|
+
return performance.now() - this.startTime;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
_step(done) {
|
|
225
|
+
this._writeCallback = () => {
|
|
226
|
+
this._writeCallback = null;
|
|
227
|
+
done();
|
|
228
|
+
};
|
|
229
|
+
const next = (this.count + 1) * this.FRAME_LENGTH - (performance.now() - this.startTime - this._pausedTime);
|
|
230
|
+
setTimeout(() => {
|
|
231
|
+
if ((!this.pausedSince || this._silence) && this._writeCallback) this._writeCallback();
|
|
232
|
+
}, next).unref();
|
|
233
|
+
this.timestamp += this.TIMESTAMP_INC;
|
|
234
|
+
if (this.timestamp >= 2 ** 32) this.timestamp = 0;
|
|
235
|
+
this.count++;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
_final(callback) {
|
|
239
|
+
this._writeCallback = null;
|
|
240
|
+
callback();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
_playChunk(chunk, isLastPacket) {
|
|
244
|
+
if (
|
|
245
|
+
(this.player.dispatcher !== this && this.player.videoDispatcher !== this) ||
|
|
246
|
+
!this.player.voiceConnection.authentication.secret_key
|
|
247
|
+
) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
this[this.extensionEnabled ? '_sendVideoPacket' : '_sendPacket'](this._createPacket(chunk, isLastPacket));
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Creates a single extension of type playout-delay
|
|
255
|
+
* Discord seems to send this extension on every video packet
|
|
256
|
+
* @see https://webrtc.googlesource.com/src/+/refs/heads/main/docs/native-code/rtp-hdrext/playout-delay
|
|
257
|
+
* @returns {Buffer} playout-delay extension
|
|
258
|
+
* Buffer <be de 00 01 51 00 00 00>
|
|
259
|
+
* @private
|
|
260
|
+
*/
|
|
261
|
+
createHeaderExtension() {
|
|
262
|
+
const extensions = [{ id: 5, len: 2, val: 0 }];
|
|
263
|
+
/**
|
|
264
|
+
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
|
265
|
+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
266
|
+
| defined by profile | length |
|
|
267
|
+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
268
|
+
*/
|
|
269
|
+
const profile = Buffer.alloc(4);
|
|
270
|
+
profile[0] = 0xbe;
|
|
271
|
+
profile[1] = 0xde;
|
|
272
|
+
profile.writeInt16BE(extensions.length, 2); // Extension count
|
|
273
|
+
const extensionsData = [];
|
|
274
|
+
for (let ext of extensions) {
|
|
275
|
+
/**
|
|
276
|
+
* EXTENSION DATA - each extension payload is 32 bits
|
|
277
|
+
*/
|
|
278
|
+
const data = Buffer.alloc(4);
|
|
279
|
+
/**
|
|
280
|
+
* 0 1 2 3 4 5 6 7
|
|
281
|
+
+-+-+-+-+-+-+-+-+
|
|
282
|
+
| ID | len |
|
|
283
|
+
+-+-+-+-+-+-+-+-+
|
|
284
|
+
|
|
285
|
+
where len = actual length - 1
|
|
286
|
+
*/
|
|
287
|
+
data[0] = (ext.id & 0b00001111) << 4;
|
|
288
|
+
data[0] |= (ext.len - 1) & 0b00001111;
|
|
289
|
+
/** Specific to type playout-delay
|
|
290
|
+
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4
|
|
291
|
+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
292
|
+
| MIN delay | MAX delay |
|
|
293
|
+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
294
|
+
*/
|
|
295
|
+
data.writeUIntBE(ext.val, 1, 2); // Not quite but its 0 anyway
|
|
296
|
+
extensionsData.push(data);
|
|
297
|
+
}
|
|
298
|
+
return Buffer.concat([profile, ...extensionsData]);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
_encrypt(buffer) {
|
|
302
|
+
const { secret_key, mode } = this.player.voiceConnection.authentication;
|
|
303
|
+
if (mode === 'xsalsa20_poly1305_lite') {
|
|
304
|
+
this._nonce++;
|
|
305
|
+
if (this._nonce > MAX_NONCE_SIZE) this._nonce = 0;
|
|
306
|
+
this._nonceBuffer.writeUInt32BE(this._nonce, 0);
|
|
307
|
+
return [secretbox.methods.close(buffer, this._nonceBuffer, secret_key), this._nonceBuffer.slice(0, 4)];
|
|
308
|
+
} else if (mode === 'xsalsa20_poly1305_suffix') {
|
|
309
|
+
const random = secretbox.methods.random(24);
|
|
310
|
+
return [secretbox.methods.close(buffer, random, secret_key), random];
|
|
311
|
+
} else {
|
|
312
|
+
return [secretbox.methods.close(buffer, nonce, secret_key)];
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
_createPacket(buffer, isLastPacket = false) {
|
|
317
|
+
// Header
|
|
318
|
+
const packetBuffer = Buffer.alloc(12);
|
|
319
|
+
packetBuffer[0] = this.extensionEnabled ? 0x90 : 0x80; // 0b10000000 | ((this.extensionEnabled ? 1 : 0) << 4);
|
|
320
|
+
packetBuffer[1] = this.payloadType;
|
|
321
|
+
|
|
322
|
+
if (this.extensionEnabled) {
|
|
323
|
+
if (isLastPacket) {
|
|
324
|
+
packetBuffer[1] |= 0x80;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
packetBuffer.writeUIntBE(this.getNewSequence(), 2, 2);
|
|
329
|
+
packetBuffer.writeUIntBE(this.timestamp, 4, 4);
|
|
330
|
+
packetBuffer.writeUIntBE(this.player.voiceConnection.authentication.ssrc + this.extensionEnabled, 8, 4);
|
|
331
|
+
|
|
332
|
+
packetBuffer.copy(nonce, 0, 0, 12);
|
|
333
|
+
|
|
334
|
+
return Buffer.concat([packetBuffer, ...this._encrypt(buffer)]);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
_sendPacket(packet) {
|
|
338
|
+
/**
|
|
339
|
+
* Emitted whenever the dispatcher has debug information.
|
|
340
|
+
* @event BaseDispatcher#debug
|
|
341
|
+
* @param {string} info The debug info
|
|
342
|
+
*/
|
|
343
|
+
this._setSpeaking(1);
|
|
344
|
+
if (!this.player.voiceConnection.sockets.udp) {
|
|
345
|
+
this.emit('debug', 'Failed to send a packet - no UDP socket');
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
this.player.voiceConnection.sockets.udp.send(packet).catch(e => {
|
|
349
|
+
this._setSpeaking(0);
|
|
350
|
+
this.emit('debug', `Failed to send a packet - ${e}`);
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
_sendVideoPacket(packet) {
|
|
355
|
+
this._setVideoStatus(true);
|
|
356
|
+
this._setStreamStatus(false);
|
|
357
|
+
if (!this.player.voiceConnection.sockets.udp) {
|
|
358
|
+
this.emit('debug', 'Failed to send a video packet - no UDP socket');
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
this.player.voiceConnection.sockets.udp.send(packet).catch(e => {
|
|
362
|
+
this._setVideoStatus(false);
|
|
363
|
+
this._setStreamStatus(true);
|
|
364
|
+
this.emit('debug', `Failed to send a video packet - ${e}`);
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
_setSpeaking(value) {
|
|
369
|
+
if (typeof this.player.voiceConnection !== 'undefined') {
|
|
370
|
+
this.player.voiceConnection.setSpeaking(value);
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Emitted when the dispatcher starts/stops speaking.
|
|
374
|
+
* @event AudioDispatcher#speaking
|
|
375
|
+
* @param {boolean} value Whether or not the dispatcher is speaking
|
|
376
|
+
*/
|
|
377
|
+
this.emit('speaking', value);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
_setVideoStatus(value) {
|
|
381
|
+
if (typeof this.player.voiceConnection !== 'undefined') {
|
|
382
|
+
this.player.voiceConnection.setVideoStatus(value);
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Emitted when the dispatcher starts/stops video.
|
|
386
|
+
* @event VideoDispatcher#videoStatus
|
|
387
|
+
* @param {boolean} value Whether or not the dispatcher is enable video
|
|
388
|
+
*/
|
|
389
|
+
this.emit('videoStatus', value);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
_setStreamStatus(value) {
|
|
393
|
+
if (typeof this.player.voiceConnection?.sendScreenshareState !== 'undefined') {
|
|
394
|
+
this.player.voiceConnection.sendScreenshareState(value);
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Emitted when the dispatcher starts/stops video.
|
|
398
|
+
* @event VideoDispatcher#streamStatus
|
|
399
|
+
* @param {boolean} isPaused Whether or not the dispatcher is pause video
|
|
400
|
+
*/
|
|
401
|
+
this.emit('streamStatus', value);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
module.exports = BaseDispatcher;
|
|
@@ -0,0 +1,52 @@
|
|
|
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 { Buffer } = require('node:buffer');
|
|
21
|
+
const VideoDispatcher = require('./VideoDispatcher');
|
|
22
|
+
|
|
23
|
+
class VP8Dispatcher extends VideoDispatcher {
|
|
24
|
+
constructor(player, highWaterMark = 12, streams, fps) {
|
|
25
|
+
super(player, highWaterMark, streams, fps);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
makeChunk(buffer, isFirstFrame) {
|
|
29
|
+
// Make frame
|
|
30
|
+
const headerExtensionBuf = this.createHeaderExtension();
|
|
31
|
+
// Vp8 payload descriptor
|
|
32
|
+
const payloadDescriptorBuf = Buffer.alloc(2);
|
|
33
|
+
payloadDescriptorBuf[0] = isFirstFrame ? 0x90 : 0x80; // Mark S bit, indicates start of frame: payloadDescriptorBuf[0] |= 0b00010000;
|
|
34
|
+
payloadDescriptorBuf[1] = 0x80;
|
|
35
|
+
// Vp8 pictureid payload extension
|
|
36
|
+
const pictureIdBuf = Buffer.alloc(2);
|
|
37
|
+
pictureIdBuf.writeUintBE(this.count, 0, 2);
|
|
38
|
+
pictureIdBuf[0] |= 0x80;
|
|
39
|
+
return Buffer.concat([headerExtensionBuf, payloadDescriptorBuf, pictureIdBuf, buffer]);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
codecCallback(chunk) {
|
|
43
|
+
const chunkSplit = this.partitionVideoData(chunk);
|
|
44
|
+
for (let i = 0; i < chunkSplit.length; i++) {
|
|
45
|
+
this._playChunk(this.makeChunk(chunkSplit[i], i == 0), i + 1 === chunkSplit.length);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
module.exports = {
|
|
51
|
+
VP8Dispatcher,
|
|
52
|
+
};
|