first commit to transcode branch for supporting both nosteam celt and steam opus
This commit is contained in:
@@ -36,10 +36,12 @@ for sdk_name in SM.sdks:
|
|||||||
|
|
||||||
binary.compiler.cxxincludes += [
|
binary.compiler.cxxincludes += [
|
||||||
os.path.join(SM.sm_root, 'public', 'extensions'),
|
os.path.join(SM.sm_root, 'public', 'extensions'),
|
||||||
os.path.join(builder.sourcePath, 'opus')
|
os.path.join(builder.sourcePath, 'opus'),
|
||||||
|
os.path.join(builder.sourcePath, 'celt')
|
||||||
]
|
]
|
||||||
binary.compiler.linkflags += [
|
binary.compiler.linkflags += [
|
||||||
os.path.join(builder.sourcePath, 'opus', 'libopus.a')
|
os.path.join(builder.sourcePath, 'opus', 'libopus.a'),
|
||||||
|
os.path.join(builder.sourcePath, 'celt', 'libcelt0_patched.a')
|
||||||
]
|
]
|
||||||
|
|
||||||
SM.extensions += builder.Add(project)
|
SM.extensions += builder.Add(project)
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
This new branch is used for allowing both nosteamers and linux users to communicate at the same time.
|
||||||
|
|
||||||
|
Nosteamers are stuck with CELT codec as they dont support steam/opus.
|
||||||
|
|
||||||
|
Linux users are stuck with steam as they dont support celt without manually fixing the .so file.
|
||||||
|
|
||||||
|
This branch discards BroadCastVoiceData for instead creating our own SendNetMsg using SVC_VoiceData
|
||||||
|
|
||||||
|
that enables us to take opus packets, decode to raw pcm and encode with celt.
|
||||||
|
|
||||||
|
that enables us to take celt packets, decode to raw pcm and encode with opus.
|
||||||
|
|
||||||
|
currently does the support for nosteamers to hear steam voice chat still have to be improved, but steam users can hear nosteam users very fine.
|
||||||
|
|
||||||
|
the sourcemod plugin used for stubbing nosteamers with SVC_VoiceInit: https://git.unloze.com/UNLOZE/sm-plugins/src/branch/master/CELT_VOICE
|
||||||
+325
@@ -0,0 +1,325 @@
|
|||||||
|
/* Copyright (c) 2007-2008 CSIRO
|
||||||
|
Copyright (c) 2007-2009 Xiph.Org Foundation
|
||||||
|
Copyright (c) 2008 Gregory Maxwell
|
||||||
|
Written by Jean-Marc Valin and Gregory Maxwell */
|
||||||
|
/**
|
||||||
|
@file celt.h
|
||||||
|
@brief Contains all the functions for encoding and decoding audio
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
- Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
- Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
|
||||||
|
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CELT_H
|
||||||
|
#define CELT_H
|
||||||
|
|
||||||
|
#include "celt_types.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__GNUC__) && defined(CELT_BUILD)
|
||||||
|
#define EXPORT __attribute__ ((visibility ("default")))
|
||||||
|
#elif defined(WIN32)
|
||||||
|
#define EXPORT __declspec(dllexport)
|
||||||
|
#else
|
||||||
|
#define EXPORT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define _celt_check_int(x) (((void)((x) == (celt_int32)0)), (celt_int32)(x))
|
||||||
|
#define _celt_check_mode_ptr_ptr(ptr) ((ptr) + ((ptr) - (const CELTMode**)(ptr)))
|
||||||
|
#define _celt_check_int_ptr(ptr) ((ptr) + ((ptr) - (int*)(ptr)))
|
||||||
|
|
||||||
|
/* Error codes */
|
||||||
|
/** No error */
|
||||||
|
#define CELT_OK 0
|
||||||
|
/** An (or more) invalid argument (e.g. out of range) */
|
||||||
|
#define CELT_BAD_ARG -1
|
||||||
|
/** The mode struct passed is invalid */
|
||||||
|
#define CELT_BUFFER_TOO_SMALL -2
|
||||||
|
/** An internal error was detected */
|
||||||
|
#define CELT_INTERNAL_ERROR -3
|
||||||
|
/** The data passed (e.g. compressed data to decoder) is corrupted */
|
||||||
|
#define CELT_CORRUPTED_DATA -4
|
||||||
|
/** Invalid/unsupported request number */
|
||||||
|
#define CELT_UNIMPLEMENTED -5
|
||||||
|
/** An encoder or decoder structure is invalid or already freed */
|
||||||
|
#define CELT_INVALID_STATE -6
|
||||||
|
/** Memory allocation has failed */
|
||||||
|
#define CELT_ALLOC_FAIL -7
|
||||||
|
|
||||||
|
|
||||||
|
/* Encoder/decoder Requests */
|
||||||
|
|
||||||
|
#define CELT_SET_COMPLEXITY_REQUEST 2
|
||||||
|
/** Controls the complexity from 0-10 (int) */
|
||||||
|
#define CELT_SET_COMPLEXITY(x) CELT_SET_COMPLEXITY_REQUEST, _celt_check_int(x)
|
||||||
|
|
||||||
|
#define CELT_SET_PREDICTION_REQUEST 4
|
||||||
|
/** Controls the use of interframe prediction.
|
||||||
|
0=Independent frames
|
||||||
|
1=Short term interframe prediction allowed
|
||||||
|
2=Long term prediction allowed
|
||||||
|
*/
|
||||||
|
#define CELT_SET_PREDICTION(x) CELT_SET_PREDICTION_REQUEST, _celt_check_int(x)
|
||||||
|
|
||||||
|
#define CELT_SET_BITRATE_REQUEST 6
|
||||||
|
/** Set the target VBR rate in bits per second(int); 0=CBR (default) */
|
||||||
|
#define CELT_SET_BITRATE(x) CELT_SET_BITRATE_REQUEST, _celt_check_int(x)
|
||||||
|
|
||||||
|
#define CELT_RESET_STATE_REQUEST 8
|
||||||
|
/** Reset the encoder/decoder memories to zero*/
|
||||||
|
#define CELT_RESET_STATE CELT_RESET_STATE_REQUEST
|
||||||
|
|
||||||
|
#define CELT_SET_VBR_CONSTRAINT_REQUEST 10
|
||||||
|
#define CELT_SET_VBR_CONSTRAINT(x) CELT_SET_VBR_CONSTRAINT_REQUEST, _celt_check_int(x)
|
||||||
|
|
||||||
|
#define CELT_SET_VBR_REQUEST 12
|
||||||
|
#define CELT_SET_VBR(x) CELT_SET_VBR_REQUEST, _celt_check_int(x)
|
||||||
|
|
||||||
|
#define CELT_SET_INPUT_CLIPPING_REQUEST 14
|
||||||
|
#define CELT_SET_INPUT_CLIPPING(x) CELT_SET_INPUT_CLIPPING_REQUEST, _celt_check_int(x)
|
||||||
|
|
||||||
|
#define CELT_GET_AND_CLEAR_ERROR_REQUEST 15
|
||||||
|
#define CELT_GET_AND_CLEAR_ERROR(x) CELT_GET_AND_CLEAR_ERROR_REQUEST, _celt_check_int_ptr(x)
|
||||||
|
|
||||||
|
#define CELT_GET_LOOKAHEAD_REQUEST 17
|
||||||
|
#define CELT_GET_LOOKAHEAD(x) CELT_GET_LOOKAHEAD_REQUEST, _celt_check_int_ptr(x)
|
||||||
|
|
||||||
|
#define CELT_SET_CHANNELS_REQUEST 18
|
||||||
|
#define CELT_SET_CHANNELS(x) CELT_SET_CHANNELS_REQUEST, _celt_check_int(x)
|
||||||
|
|
||||||
|
#define CELT_SET_LOSS_PERC_REQUEST 20
|
||||||
|
#define CELT_SET_LOSS_PERC(x) CELT_SET_LOSS_PERC_REQUEST, _celt_check_int(x)
|
||||||
|
|
||||||
|
/* Internal */
|
||||||
|
#define CELT_SET_START_BAND_REQUEST 10000
|
||||||
|
#define CELT_SET_START_BAND(x) CELT_SET_START_BAND_REQUEST, _celt_check_int(x)
|
||||||
|
|
||||||
|
#define CELT_SET_END_BAND_REQUEST 10001
|
||||||
|
#define CELT_SET_END_BAND(x) CELT_SET_END_BAND_REQUEST, _celt_check_int(x)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** Contains the state of an encoder. One encoder state is needed
|
||||||
|
for each stream. It is initialised once at the beginning of the
|
||||||
|
stream. Do *not* re-initialise the state for every frame.
|
||||||
|
@brief Encoder state
|
||||||
|
*/
|
||||||
|
typedef struct CELTEncoder CELTEncoder;
|
||||||
|
|
||||||
|
/** State of the decoder. One decoder state is needed for each stream.
|
||||||
|
It is initialised once at the beginning of the stream. Do *not*
|
||||||
|
re-initialise the state for every frame */
|
||||||
|
typedef struct CELTDecoder CELTDecoder;
|
||||||
|
|
||||||
|
/** The mode contains all the information necessary to create an
|
||||||
|
encoder. Both the encoder and decoder need to be initialised
|
||||||
|
with exactly the same mode, otherwise the quality will be very
|
||||||
|
bad */
|
||||||
|
typedef struct CELTMode CELTMode;
|
||||||
|
|
||||||
|
|
||||||
|
/** \defgroup codec Encoding and decoding */
|
||||||
|
/* @{ */
|
||||||
|
|
||||||
|
/* Mode calls */
|
||||||
|
|
||||||
|
/** Creates a new mode struct. This will be passed to an encoder or
|
||||||
|
decoder. The mode MUST NOT BE DESTROYED until the encoders and
|
||||||
|
decoders that use it are destroyed as well.
|
||||||
|
@param Fs Sampling rate (32000 to 96000 Hz)
|
||||||
|
@param frame_size Number of samples (per channel) to encode in each
|
||||||
|
packet (even values; 64 - 512)
|
||||||
|
@param error Returned error code (if NULL, no error will be returned)
|
||||||
|
@return A newly created mode
|
||||||
|
*/
|
||||||
|
EXPORT CELTMode *celt_mode_create(celt_int32 Fs, int frame_size, int *error);
|
||||||
|
|
||||||
|
/** Destroys a mode struct. Only call this after all encoders and
|
||||||
|
decoders using this mode are destroyed as well.
|
||||||
|
@param mode Mode to be destroyed
|
||||||
|
*/
|
||||||
|
EXPORT void celt_mode_destroy(CELTMode *mode);
|
||||||
|
|
||||||
|
/* Encoder stuff */
|
||||||
|
|
||||||
|
EXPORT int celt_encoder_get_size(int channels);
|
||||||
|
|
||||||
|
EXPORT int celt_encoder_get_size_custom(const CELTMode *mode, int channels);
|
||||||
|
|
||||||
|
/** Creates a new encoder state. Each stream needs its own encoder
|
||||||
|
state (can't be shared across simultaneous streams).
|
||||||
|
@param channels Number of channels
|
||||||
|
@param error Returns an error code
|
||||||
|
@return Newly created encoder state.
|
||||||
|
*/
|
||||||
|
EXPORT CELTEncoder *celt_encoder_create(int sampling_rate, int channels, int *error);
|
||||||
|
|
||||||
|
/** Creates a new encoder state. Each stream needs its own encoder
|
||||||
|
state (can't be shared across simultaneous streams).
|
||||||
|
@param mode Contains all the information about the characteristics of
|
||||||
|
* the stream (must be the same characteristics as used for the
|
||||||
|
* decoder)
|
||||||
|
@param channels Number of channels
|
||||||
|
@param error Returns an error code
|
||||||
|
@return Newly created encoder state.
|
||||||
|
*/
|
||||||
|
EXPORT CELTEncoder *celt_encoder_create_custom(const CELTMode *mode, int channels, int *error);
|
||||||
|
|
||||||
|
EXPORT CELTEncoder *celt_encoder_init(CELTEncoder *st, int sampling_rate, int channels, int *error);
|
||||||
|
|
||||||
|
EXPORT CELTEncoder *celt_encoder_init_custom(CELTEncoder *st, const CELTMode *mode, int channels, int *error);
|
||||||
|
|
||||||
|
/** Destroys a an encoder state.
|
||||||
|
@param st Encoder state to be destroyed
|
||||||
|
*/
|
||||||
|
EXPORT void celt_encoder_destroy(CELTEncoder *st);
|
||||||
|
|
||||||
|
/** Encodes a frame of audio.
|
||||||
|
@param st Encoder state
|
||||||
|
@param pcm PCM audio in float format, with a normal range of ±1.0.
|
||||||
|
* Samples with a range beyond ±1.0 are supported but will
|
||||||
|
* be clipped by decoders using the integer API and should
|
||||||
|
* only be used if it is known that the far end supports
|
||||||
|
* extended dynmaic range. There must be exactly
|
||||||
|
* frame_size samples per channel.
|
||||||
|
@param compressed The compressed data is written here. This may not alias pcm or
|
||||||
|
* optional_synthesis.
|
||||||
|
@param nbCompressedBytes Maximum number of bytes to use for compressing the frame
|
||||||
|
* (can change from one frame to another)
|
||||||
|
@return Number of bytes written to "compressed". Will be the same as
|
||||||
|
* "nbCompressedBytes" unless the stream is VBR and will never be larger.
|
||||||
|
* If negative, an error has occurred (see error codes). It is IMPORTANT that
|
||||||
|
* the length returned be somehow transmitted to the decoder. Otherwise, no
|
||||||
|
* decoding is possible.
|
||||||
|
*/
|
||||||
|
EXPORT int celt_encode_float(CELTEncoder *st, const float *pcm, int frame_size, unsigned char *compressed, int maxCompressedBytes);
|
||||||
|
|
||||||
|
/** Encodes a frame of audio.
|
||||||
|
@param st Encoder state
|
||||||
|
@param pcm PCM audio in signed 16-bit format (native endian). There must be
|
||||||
|
* exactly frame_size samples per channel.
|
||||||
|
@param compressed The compressed data is written here. This may not alias pcm or
|
||||||
|
* optional_synthesis.
|
||||||
|
@param nbCompressedBytes Maximum number of bytes to use for compressing the frame
|
||||||
|
* (can change from one frame to another)
|
||||||
|
@return Number of bytes written to "compressed". Will be the same as
|
||||||
|
* "nbCompressedBytes" unless the stream is VBR and will never be larger.
|
||||||
|
* If negative, an error has occurred (see error codes). It is IMPORTANT that
|
||||||
|
* the length returned be somehow transmitted to the decoder. Otherwise, no
|
||||||
|
* decoding is possible.
|
||||||
|
*/
|
||||||
|
EXPORT int celt_encode(CELTEncoder *st, const celt_int16 *pcm, int frame_size, unsigned char *compressed, int maxCompressedBytes);
|
||||||
|
|
||||||
|
/** Query and set encoder parameters
|
||||||
|
@param st Encoder state
|
||||||
|
@param request Parameter to change or query
|
||||||
|
@param value Pointer to a 32-bit int value
|
||||||
|
@return Error code
|
||||||
|
*/
|
||||||
|
EXPORT int celt_encoder_ctl(CELTEncoder * st, int request, ...);
|
||||||
|
|
||||||
|
/* Decoder stuff */
|
||||||
|
|
||||||
|
EXPORT int celt_decoder_get_size(int channels);
|
||||||
|
|
||||||
|
EXPORT int celt_decoder_get_size_custom(const CELTMode *mode, int channels);
|
||||||
|
|
||||||
|
/** Creates a new decoder state. Each stream needs its own decoder state (can't
|
||||||
|
be shared across simultaneous streams).
|
||||||
|
@param mode Contains all the information about the characteristics of the
|
||||||
|
stream (must be the same characteristics as used for the encoder)
|
||||||
|
@param channels Number of channels
|
||||||
|
@param error Returns an error code
|
||||||
|
@return Newly created decoder state.
|
||||||
|
*/
|
||||||
|
EXPORT CELTDecoder *celt_decoder_create(int sampling_rate, int channels, int *error);
|
||||||
|
|
||||||
|
/** Creates a new decoder state. Each stream needs its own decoder state (can't
|
||||||
|
be shared across simultaneous streams).
|
||||||
|
@param mode Contains all the information about the characteristics of the
|
||||||
|
stream (must be the same characteristics as used for the encoder)
|
||||||
|
@param channels Number of channels
|
||||||
|
@param error Returns an error code
|
||||||
|
@return Newly created decoder state.
|
||||||
|
*/
|
||||||
|
EXPORT CELTDecoder *celt_decoder_create_custom(const CELTMode *mode, int channels, int *error);
|
||||||
|
|
||||||
|
EXPORT CELTDecoder *celt_decoder_init(CELTDecoder *st, int sampling_rate, int channels, int *error);
|
||||||
|
|
||||||
|
EXPORT CELTDecoder *celt_decoder_init_custom(CELTDecoder *st, const CELTMode *mode, int channels, int *error);
|
||||||
|
|
||||||
|
/** Destroys a a decoder state.
|
||||||
|
@param st Decoder state to be destroyed
|
||||||
|
*/
|
||||||
|
EXPORT void celt_decoder_destroy(CELTDecoder *st);
|
||||||
|
|
||||||
|
/** Decodes a frame of audio.
|
||||||
|
@param st Decoder state
|
||||||
|
@param data Compressed data produced by an encoder
|
||||||
|
@param len Number of bytes to read from "data". This MUST be exactly the number
|
||||||
|
of bytes returned by the encoder. Using a larger value WILL NOT WORK.
|
||||||
|
@param pcm One frame (frame_size samples per channel) of decoded PCM will be
|
||||||
|
returned here in float format.
|
||||||
|
@return Error code.
|
||||||
|
*/
|
||||||
|
EXPORT int celt_decode_float(CELTDecoder *st, const unsigned char *data, int len, float *pcm, int frame_size);
|
||||||
|
|
||||||
|
/** Decodes a frame of audio.
|
||||||
|
@param st Decoder state
|
||||||
|
@param data Compressed data produced by an encoder
|
||||||
|
@param len Number of bytes to read from "data". This MUST be exactly the number
|
||||||
|
of bytes returned by the encoder. Using a larger value WILL NOT WORK.
|
||||||
|
@param pcm One frame (frame_size samples per channel) of decoded PCM will be
|
||||||
|
returned here in 16-bit PCM format (native endian).
|
||||||
|
@return Error code.
|
||||||
|
*/
|
||||||
|
EXPORT int celt_decode(CELTDecoder *st, const unsigned char *data, int len, celt_int16 *pcm, int frame_size);
|
||||||
|
|
||||||
|
/** Query and set decoder parameters
|
||||||
|
@param st Decoder state
|
||||||
|
@param request Parameter to change or query
|
||||||
|
@param value Pointer to a 32-bit int value
|
||||||
|
@return Error code
|
||||||
|
*/
|
||||||
|
EXPORT int celt_decoder_ctl(CELTDecoder * st, int request, ...);
|
||||||
|
|
||||||
|
|
||||||
|
/** Returns the English string that corresponds to an error code
|
||||||
|
* @param error Error code (negative for an error, 0 for success
|
||||||
|
* @return Constant string (must NOT be freed)
|
||||||
|
*/
|
||||||
|
EXPORT const char *celt_strerror(int error);
|
||||||
|
|
||||||
|
/* @} */
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /*CELT_H */
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/* Copyright (c) 2007 CSIRO
|
||||||
|
Copyright (c) 2007-2008 Xiph.Org Foundation
|
||||||
|
Written by Jean-Marc Valin */
|
||||||
|
/*
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
- Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
- Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
|
||||||
|
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CELT_HEADER_H
|
||||||
|
#define CELT_HEADER_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "celt.h"
|
||||||
|
#include "celt_types.h"
|
||||||
|
|
||||||
|
/** Header data to be used for Ogg files (or possibly other encapsulation)
|
||||||
|
@brief Header data
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
char codec_id[8]; /**< MUST be "CELT " (four spaces) */
|
||||||
|
char codec_version[20]; /**< Version used (as string) */
|
||||||
|
celt_int32 version_id; /**< Version id (negative for until stream is frozen) */
|
||||||
|
celt_int32 header_size; /**< Size of this header */
|
||||||
|
celt_int32 sample_rate; /**< Sampling rate of the original audio */
|
||||||
|
celt_int32 nb_channels; /**< Number of channels */
|
||||||
|
celt_int32 frame_size; /**< Samples per frame (per channel) */
|
||||||
|
celt_int32 overlap; /**< Overlapping samples (per channel) */
|
||||||
|
celt_int32 bytes_per_packet; /**< Number of bytes per compressed packet (0 if unknown) */
|
||||||
|
celt_int32 extra_headers; /**< Number of additional headers that follow this header */
|
||||||
|
} CELTHeader;
|
||||||
|
|
||||||
|
/** Creates a basic header struct */
|
||||||
|
EXPORT int celt_header_init(CELTHeader *header, const CELTMode *m, int frame_size, int channels);
|
||||||
|
|
||||||
|
EXPORT int celt_header_to_packet(const CELTHeader *header, unsigned char *packet, celt_uint32 size);
|
||||||
|
|
||||||
|
EXPORT int celt_header_from_packet(const unsigned char *packet, celt_uint32 size, CELTHeader *header);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* CELT_HEADER_H */
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
/* (C) COPYRIGHT 1994-2002 Xiph.Org Foundation */
|
||||||
|
/* Modified by Jean-Marc Valin */
|
||||||
|
/*
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
- Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
- Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
|
||||||
|
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
/* celt_types.h taken from libogg */
|
||||||
|
|
||||||
|
/**
|
||||||
|
@file celt_types.h
|
||||||
|
@brief CELT types
|
||||||
|
*/
|
||||||
|
#ifndef _CELT_TYPES_H
|
||||||
|
#define _CELT_TYPES_H
|
||||||
|
|
||||||
|
/* Use the real stdint.h if it's there (taken from Paul Hsieh's pstdint.h) */
|
||||||
|
#if (defined(__STDC__) && __STDC__ && __STDC_VERSION__ >= 199901L) || (defined(__GNUC__) && (defined(_STDINT_H) || defined(_STDINT_H_)) || defined (HAVE_STDINT_H))
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef int16_t celt_int16;
|
||||||
|
typedef uint16_t celt_uint16;
|
||||||
|
typedef int32_t celt_int32;
|
||||||
|
typedef uint32_t celt_uint32;
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
|
||||||
|
# if defined(__CYGWIN__)
|
||||||
|
# include <_G_config.h>
|
||||||
|
typedef _G_int32_t celt_int32;
|
||||||
|
typedef _G_uint32_t celt_uint32;
|
||||||
|
typedef _G_int16 celt_int16;
|
||||||
|
typedef _G_uint16 celt_uint16;
|
||||||
|
# elif defined(__MINGW32__)
|
||||||
|
typedef short celt_int16;
|
||||||
|
typedef unsigned short celt_uint16;
|
||||||
|
typedef int celt_int32;
|
||||||
|
typedef unsigned int celt_uint32;
|
||||||
|
# elif defined(__MWERKS__)
|
||||||
|
typedef int celt_int32;
|
||||||
|
typedef unsigned int celt_uint32;
|
||||||
|
typedef short celt_int16;
|
||||||
|
typedef unsigned short celt_uint16;
|
||||||
|
# else
|
||||||
|
/* MSVC/Borland */
|
||||||
|
typedef __int32 celt_int32;
|
||||||
|
typedef unsigned __int32 celt_uint32;
|
||||||
|
typedef __int16 celt_int16;
|
||||||
|
typedef unsigned __int16 celt_uint16;
|
||||||
|
# endif
|
||||||
|
|
||||||
|
#elif defined(__MACOS__)
|
||||||
|
|
||||||
|
# include <sys/types.h>
|
||||||
|
typedef SInt16 celt_int16;
|
||||||
|
typedef UInt16 celt_uint16;
|
||||||
|
typedef SInt32 celt_int32;
|
||||||
|
typedef UInt32 celt_uint32;
|
||||||
|
|
||||||
|
#elif (defined(__APPLE__) && defined(__MACH__)) /* MacOS X Framework build */
|
||||||
|
|
||||||
|
# include <sys/types.h>
|
||||||
|
typedef int16_t celt_int16;
|
||||||
|
typedef u_int16_t celt_uint16;
|
||||||
|
typedef int32_t celt_int32;
|
||||||
|
typedef u_int32_t celt_uint32;
|
||||||
|
|
||||||
|
#elif defined(__BEOS__)
|
||||||
|
|
||||||
|
/* Be */
|
||||||
|
# include <inttypes.h>
|
||||||
|
typedef int16 celt_int16;
|
||||||
|
typedef u_int16 celt_uint16;
|
||||||
|
typedef int32_t celt_int32;
|
||||||
|
typedef u_int32_t celt_uint32;
|
||||||
|
|
||||||
|
#elif defined (__EMX__)
|
||||||
|
|
||||||
|
/* OS/2 GCC */
|
||||||
|
typedef short celt_int16;
|
||||||
|
typedef unsigned short celt_uint16;
|
||||||
|
typedef int celt_int32;
|
||||||
|
typedef unsigned int celt_uint32;
|
||||||
|
|
||||||
|
#elif defined (DJGPP)
|
||||||
|
|
||||||
|
/* DJGPP */
|
||||||
|
typedef short celt_int16;
|
||||||
|
typedef unsigned short celt_uint16;
|
||||||
|
typedef int celt_int32;
|
||||||
|
typedef unsigned int celt_uint32;
|
||||||
|
|
||||||
|
#elif defined(R5900)
|
||||||
|
|
||||||
|
/* PS2 EE */
|
||||||
|
typedef int celt_int32;
|
||||||
|
typedef unsigned celt_uint32;
|
||||||
|
typedef short celt_int16;
|
||||||
|
typedef unsigned short celt_uint16;
|
||||||
|
|
||||||
|
#elif defined(__SYMBIAN32__)
|
||||||
|
|
||||||
|
/* Symbian GCC */
|
||||||
|
typedef signed short celt_int16;
|
||||||
|
typedef unsigned short celt_uint16;
|
||||||
|
typedef signed int celt_int32;
|
||||||
|
typedef unsigned int celt_uint32;
|
||||||
|
|
||||||
|
#elif defined(CONFIG_TI_C54X) || defined (CONFIG_TI_C55X)
|
||||||
|
|
||||||
|
typedef short celt_int16;
|
||||||
|
typedef unsigned short celt_uint16;
|
||||||
|
typedef long celt_int32;
|
||||||
|
typedef unsigned long celt_uint32;
|
||||||
|
|
||||||
|
#elif defined(CONFIG_TI_C6X)
|
||||||
|
|
||||||
|
typedef short celt_int16;
|
||||||
|
typedef unsigned short celt_uint16;
|
||||||
|
typedef int celt_int32;
|
||||||
|
typedef unsigned int celt_uint32;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/* Give up, take a reasonable guess */
|
||||||
|
typedef short celt_int16;
|
||||||
|
typedef unsigned short celt_uint16;
|
||||||
|
typedef int celt_int32;
|
||||||
|
typedef unsigned int celt_uint32;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _CELT_TYPES_H */
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+830
-11
@@ -44,6 +44,8 @@
|
|||||||
#include <iserver.h>
|
#include <iserver.h>
|
||||||
#include <ISDKTools.h>
|
#include <ISDKTools.h>
|
||||||
|
|
||||||
|
#include "inetmessage.h"
|
||||||
|
|
||||||
#include "CDetour/detours.h"
|
#include "CDetour/detours.h"
|
||||||
#include "extension.h"
|
#include "extension.h"
|
||||||
|
|
||||||
@@ -136,11 +138,133 @@ IServer *iserver = NULL;
|
|||||||
|
|
||||||
double g_fLastVoiceData[SM_MAXPLAYERS + 1];
|
double g_fLastVoiceData[SM_MAXPLAYERS + 1];
|
||||||
int g_aFrameVoiceBytes[SM_MAXPLAYERS + 1];
|
int g_aFrameVoiceBytes[SM_MAXPLAYERS + 1];
|
||||||
|
bool g_bIsNoSteam[SM_MAXPLAYERS + 1];
|
||||||
|
bool g_bClientMuted[SM_MAXPLAYERS + 1][SM_MAXPLAYERS + 1]; // [muter][mutee]
|
||||||
|
|
||||||
|
static void SendVoiceDataMsg(int fromClientSlot, IClient *pToClient, unsigned char *data, int nBytes, int64 xuid)
|
||||||
|
{
|
||||||
|
SVC_VoiceData msg;
|
||||||
|
msg.m_bProximity = false;
|
||||||
|
msg.m_nLength = nBytes * 8; // length in bits
|
||||||
|
msg.m_xuid = xuid;
|
||||||
|
msg.m_nFromClient = fromClientSlot;
|
||||||
|
msg.m_DataOut = data;
|
||||||
|
pToClient->SendNetMsg(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define Bits2Bytes(b) ((b+7)>>3)
|
||||||
|
|
||||||
|
/*
|
||||||
|
objdump -d /home/gameservers/css_dev/bin/engine_srv.so | grep -A 10 "_ZNK11CBaseClient13GetPlayerSlotEv"
|
||||||
|
00081a90 <_ZNK11CBaseClient13GetPlayerSlotEv>:
|
||||||
|
81a90: 55 push %ebp
|
||||||
|
81a91: 89 e5 mov %esp,%ebp
|
||||||
|
81a93: 8b 45 08 mov 0x8(%ebp),%eax
|
||||||
|
81a96: 5d pop %ebp
|
||||||
|
81a97: 8b 40 0c mov 0xc(%eax),%eax
|
||||||
|
81a9a: c3 ret
|
||||||
|
81a9b: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
|
||||||
|
81a9f: 90 nop
|
||||||
|
|
||||||
|
00081aa0 <_ZThn4_NK11CBaseClient13GetPlayerSlotEv>:
|
||||||
|
|
||||||
|
mov 0xc(%eax),%eax -> offset is 12 bytes (0x0c)
|
||||||
|
*/
|
||||||
|
|
||||||
|
DETOUR_DECL_MEMBER1(ProcessVoiceData, bool, void *, msg)
|
||||||
|
{
|
||||||
|
//in member class we use "this" to obtain the client.
|
||||||
|
int playerSlot = *(int*)((char*)this + 12); //m_nClientSlot member CBaseClient which CGameClient inherits from.
|
||||||
|
int clientIndex = playerSlot + 1;
|
||||||
|
|
||||||
|
CLC_VoiceData *voiceMsg = (CLC_VoiceData *)msg;
|
||||||
|
|
||||||
|
char voiceDataBuffer[4096];
|
||||||
|
int bitsRead = voiceMsg->m_DataIn.ReadBitsClamped(voiceDataBuffer, voiceMsg->m_nLength);
|
||||||
|
int nBytes = Bits2Bytes(bitsRead);
|
||||||
|
|
||||||
|
//still need this super important check for preventing empty voice packets spam.
|
||||||
|
if (nBytes < 1)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (clientIndex < 1 || clientIndex > SM_MAXPLAYERS)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Reject voice packet if we'd send more than NET_MAX_VOICE_BYTES_FRAME voice bytes from this client in the current frame.
|
||||||
|
// 5 = SVC_VoiceData header/overhead
|
||||||
|
g_aFrameVoiceBytes[clientIndex] += 5 + nBytes;
|
||||||
|
if (g_aFrameVoiceBytes[clientIndex] > NET_MAX_VOICE_BYTES_FRAME)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
g_fLastVoiceData[clientIndex] = gpGlobals->curtime;
|
||||||
|
|
||||||
|
// Get IClient for sending
|
||||||
|
IClient *pClient = iserver->GetClient(playerSlot);
|
||||||
|
if (!pClient)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Detect NoSteam
|
||||||
|
if (g_bIsNoSteam[clientIndex])
|
||||||
|
{
|
||||||
|
//convert nosteam CELT packet to OPUS for steam clients
|
||||||
|
g_Interface.TranscodeNoSteamToSteam(playerSlot, nBytes, voiceDataBuffer);
|
||||||
|
//send nosteam CELT packet to no steam clients
|
||||||
|
int maxClients = iserver->GetClientCount();
|
||||||
|
for (int i = 0; i < maxClients; i++)
|
||||||
|
{
|
||||||
|
IClient *pToClient = iserver->GetClient(i);
|
||||||
|
if (!pToClient || !pToClient->IsConnected() || !pToClient->IsActive() || pToClient->IsFakeClient())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bool bSelf = (i == playerSlot);
|
||||||
|
|
||||||
|
if (bSelf)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!g_bIsNoSteam[i + 1])
|
||||||
|
continue;
|
||||||
|
if (g_bClientMuted[i + 1][clientIndex])
|
||||||
|
continue;
|
||||||
|
//smutils->LogMessage(myself, "in ProcessVoiceData before SendVoiceDataMsg");
|
||||||
|
SendVoiceDataMsg(playerSlot, pToClient, (unsigned char *)voiceDataBuffer, nBytes, voiceMsg->m_xuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//convert steam OPUS packet to CELT for no steam clients
|
||||||
|
g_Interface.PushPlayerVoiceData(playerSlot, nBytes, voiceDataBuffer);
|
||||||
|
// Send steam Opus packet to Steam clients
|
||||||
|
int maxClients = iserver->GetClientCount();
|
||||||
|
for (int i = 0; i < maxClients; i++)
|
||||||
|
{
|
||||||
|
IClient *pToClient = iserver->GetClient(i);
|
||||||
|
if (!pToClient || !pToClient->IsConnected() || !pToClient->IsActive() || pToClient->IsFakeClient())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bool bSelf = (i == playerSlot);
|
||||||
|
|
||||||
|
if (bSelf)
|
||||||
|
continue;
|
||||||
|
if (g_bIsNoSteam[i + 1])
|
||||||
|
continue;
|
||||||
|
if (g_bClientMuted[i + 1][clientIndex])
|
||||||
|
continue;
|
||||||
|
//smutils->LogMessage(myself, "in ProcessVoiceData before SendVoiceDataMsg");
|
||||||
|
SendVoiceDataMsg(playerSlot, pToClient, (unsigned char *)voiceDataBuffer, nBytes, voiceMsg->m_xuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
DETOUR_DECL_STATIC4(SV_BroadcastVoiceData, void, IClient *, pClient, int, nBytes, char *, data, int64, xuid)
|
DETOUR_DECL_STATIC4(SV_BroadcastVoiceData, void, IClient *, pClient, int, nBytes, char *, data, int64, xuid)
|
||||||
{
|
{
|
||||||
if(g_Interface.OnBroadcastVoiceData(pClient, nBytes, data))
|
//if(g_Interface.OnBroadcastVoiceData(pClient, nBytes, data))
|
||||||
|
/*
|
||||||
|
if (1 == 2) //we dont do dis no more
|
||||||
|
{
|
||||||
DETOUR_STATIC_CALL(SV_BroadcastVoiceData)(pClient, nBytes, data, xuid);
|
DETOUR_STATIC_CALL(SV_BroadcastVoiceData)(pClient, nBytes, data, xuid);
|
||||||
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@@ -191,7 +315,40 @@ CVoice::CVoice()
|
|||||||
m_AvailableTime = 0.0;
|
m_AvailableTime = 0.0;
|
||||||
|
|
||||||
m_VoiceDetour = NULL;
|
m_VoiceDetour = NULL;
|
||||||
|
m_ProcessVoiceDataDetour = NULL;
|
||||||
m_SV_BroadcastVoiceData = NULL;
|
m_SV_BroadcastVoiceData = NULL;
|
||||||
|
|
||||||
|
m_TorchlightOpusDecoder = NULL;
|
||||||
|
for (int i = 0; i <= SM_MAXPLAYERS; i++)
|
||||||
|
{
|
||||||
|
m_PlayerOpusDecoder[i] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i <= SM_MAXPLAYERS; i++)
|
||||||
|
{
|
||||||
|
m_pCeltDecoder[i] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pCeltMode = NULL;
|
||||||
|
m_pCeltCodec = NULL;
|
||||||
|
m_pCeltModePlayer = NULL;
|
||||||
|
m_torchMonoAccumLen = 0;
|
||||||
|
m_torchResampleAccum = 0;
|
||||||
|
memset(m_playerResampleAccum, 0, sizeof(m_playerResampleAccum));
|
||||||
|
memset(m_torchMonoAccum, 0, sizeof(m_torchMonoAccum));
|
||||||
|
memset(m_nosteamSeqNum, 0, sizeof(m_nosteamSeqNum));
|
||||||
|
|
||||||
|
memset(g_bIsNoSteam, 0, sizeof(g_bIsNoSteam));
|
||||||
|
|
||||||
|
memset(m_playerAvailableTime, 0, sizeof(m_playerAvailableTime));
|
||||||
|
for (int i = 0; i <= SM_MAXPLAYERS; i++)
|
||||||
|
m_pCeltCodecPlayer[i] = NULL;
|
||||||
|
|
||||||
|
memset(g_bClientMuted, 0, sizeof(g_bClientMuted));
|
||||||
|
memset(m_nosteamResampleAccum, 0, sizeof(m_nosteamResampleAccum));
|
||||||
|
memset(m_nosteamAvailableTime, 0, sizeof(m_nosteamAvailableTime));
|
||||||
|
for (int i = 0; i <= SM_MAXPLAYERS; i++)
|
||||||
|
m_nosteamOpusEncoder[i] = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CVoice::SDK_OnLoad(char *error, size_t maxlength, bool late)
|
bool CVoice::SDK_OnLoad(char *error, size_t maxlength, bool late)
|
||||||
@@ -266,6 +423,8 @@ bool CVoice::SDK_OnLoad(char *error, size_t maxlength, bool late)
|
|||||||
dlclose(pEngineSo);
|
dlclose(pEngineSo);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
//2026 signature of CGameClient::ProcessVoiceData
|
||||||
|
void *adrProcessVoiceData = memutils->ResolveSymbol(pEngineSo, "_ZN11CGameClient16ProcessVoiceDataEP13CLC_VoiceData");
|
||||||
dlclose(pEngineSo);
|
dlclose(pEngineSo);
|
||||||
|
|
||||||
m_SV_BroadcastVoiceData = (t_SV_BroadcastVoiceData)adrVoiceData;
|
m_SV_BroadcastVoiceData = (t_SV_BroadcastVoiceData)adrVoiceData;
|
||||||
@@ -299,6 +458,15 @@ bool CVoice::SDK_OnLoad(char *error, size_t maxlength, bool late)
|
|||||||
|
|
||||||
m_VoiceDetour->EnableDetour();
|
m_VoiceDetour->EnableDetour();
|
||||||
|
|
||||||
|
//2026 detouring CGameClient::ProcessVoiceData
|
||||||
|
m_ProcessVoiceDataDetour = DETOUR_CREATE_MEMBER(ProcessVoiceData, adrProcessVoiceData);
|
||||||
|
if (!m_ProcessVoiceDataDetour)
|
||||||
|
{
|
||||||
|
g_SMAPI->Format(error, maxlength, "ProcessVoiceData detour failed.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_ProcessVoiceDataDetour->EnableDetour();
|
||||||
|
|
||||||
//opus edit
|
//opus edit
|
||||||
int err;
|
int err;
|
||||||
//m_OpusEncoder = opus_encoder_create(24000, 2, OPUS_APPLICATION_AUDIO, &err);
|
//m_OpusEncoder = opus_encoder_create(24000, 2, OPUS_APPLICATION_AUDIO, &err);
|
||||||
@@ -322,6 +490,56 @@ bool CVoice::SDK_OnLoad(char *error, size_t maxlength, bool late)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Opus decoder for torchlight (48000 Hz stereo)
|
||||||
|
int err2;
|
||||||
|
m_TorchlightOpusDecoder = opus_decoder_create(48000, 2, &err2);
|
||||||
|
if (err2 < 0)
|
||||||
|
{
|
||||||
|
smutils->LogError(myself, "failed to create torchlight opus decoder: %s", opus_strerror(err2));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CELT encoder (22050 Hz mono, 512 samples/frame, 64 byte packets)
|
||||||
|
m_CeltEncoderSettings.SampleRate_Hz = 22050;
|
||||||
|
m_CeltEncoderSettings.TargetBitRate_Kbps = 64;
|
||||||
|
m_CeltEncoderSettings.FrameSize = 512;
|
||||||
|
m_CeltEncoderSettings.PacketSize = 64;
|
||||||
|
m_CeltEncoderSettings.Complexity = 10;
|
||||||
|
m_CeltEncoderSettings.FrameTime = (double)m_CeltEncoderSettings.FrameSize
|
||||||
|
/ (double)m_CeltEncoderSettings.SampleRate_Hz;
|
||||||
|
|
||||||
|
int theError;
|
||||||
|
//torchlight celt
|
||||||
|
m_pCeltMode = celt_mode_create(m_CeltEncoderSettings.SampleRate_Hz,
|
||||||
|
m_CeltEncoderSettings.FrameSize, &theError);
|
||||||
|
if (!m_pCeltMode)
|
||||||
|
{
|
||||||
|
g_SMAPI->Format(error, maxlength, "celt_mode_create error: %d", theError);
|
||||||
|
SDK_OnUnload();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pCeltCodec = celt_encoder_create_custom(m_pCeltMode, 1, &theError);
|
||||||
|
if (!m_pCeltCodec)
|
||||||
|
{
|
||||||
|
g_SMAPI->Format(error, maxlength, "celt_encoder_create_custom error: %d", theError);
|
||||||
|
SDK_OnUnload();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
celt_encoder_ctl(m_pCeltCodec, CELT_RESET_STATE_REQUEST, NULL);
|
||||||
|
celt_encoder_ctl(m_pCeltCodec, CELT_SET_BITRATE(m_CeltEncoderSettings.TargetBitRate_Kbps * 1000));
|
||||||
|
celt_encoder_ctl(m_pCeltCodec, CELT_SET_COMPLEXITY(m_CeltEncoderSettings.Complexity));
|
||||||
|
|
||||||
|
//Player celt
|
||||||
|
m_pCeltModePlayer = celt_mode_create(m_CeltEncoderSettings.SampleRate_Hz,
|
||||||
|
m_CeltEncoderSettings.FrameSize, &theError);
|
||||||
|
if (!m_pCeltModePlayer)
|
||||||
|
{
|
||||||
|
g_SMAPI->Format(error, maxlength, "celt_mode_create 2 error: %d", theError);
|
||||||
|
SDK_OnUnload();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,9 +578,54 @@ cell_t IsClientTalking(IPluginContext *pContext, const cell_t *params)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cell_t Native_SetClientNoSteam(IPluginContext *pContext, const cell_t *params)
|
||||||
|
{
|
||||||
|
int client = params[1];
|
||||||
|
bool IsSteam = params[2];
|
||||||
|
|
||||||
|
if (client < 1 || client > SM_MAXPLAYERS)
|
||||||
|
return pContext->ThrowNativeError("Invalid client index %d", client);
|
||||||
|
|
||||||
|
g_bIsNoSteam[client] = IsSteam;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cell_t Native_SendCeltVoiceInit(IPluginContext *pContext, const cell_t *params)
|
||||||
|
{
|
||||||
|
int client = params[1];
|
||||||
|
if (client < 1 || client > SM_MAXPLAYERS)
|
||||||
|
return pContext->ThrowNativeError("Invalid client index %d", client);
|
||||||
|
|
||||||
|
IClient *pClient = iserver->GetClient(client - 1);
|
||||||
|
if (!pClient || !pClient->IsConnected())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
SVC_VoiceInit msg("vaudio_celt", 22050);
|
||||||
|
pClient->SendNetMsg(msg);
|
||||||
|
smutils->LogMessage(myself, "Sent SVC_VoiceInit vaudio_celt to client %d", client);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cell_t Native_ClientMutedOtherClient(IPluginContext *pContext, const cell_t *params)
|
||||||
|
{
|
||||||
|
int clientmuter = params[1];
|
||||||
|
int clientmutee = params[2];
|
||||||
|
if (clientmuter < 1 || clientmuter > SM_MAXPLAYERS)
|
||||||
|
return pContext->ThrowNativeError("Invalid clientmuter index %d", clientmuter);
|
||||||
|
|
||||||
|
if (clientmutee < 0 || clientmutee > SM_MAXPLAYERS) //we allow muting the SourceTV
|
||||||
|
return pContext->ThrowNativeError("Invalid clientmutee index %d", clientmutee);
|
||||||
|
|
||||||
|
g_bClientMuted[clientmuter][clientmutee] = params[3]; //true or false
|
||||||
|
//smutils->LogMessage(myself, "native g_bClientMuted: %i. clientmuter: %i. clientmutee: %i", g_bClientMuted[clientmuter][clientmutee], clientmuter, clientmutee);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
const sp_nativeinfo_t MyNatives[] =
|
const sp_nativeinfo_t MyNatives[] =
|
||||||
{
|
{
|
||||||
{ "IsClientTalking", IsClientTalking },
|
{ "IsClientTalking", IsClientTalking },
|
||||||
|
{ "SetClientNoSteam", Native_SetClientNoSteam },
|
||||||
|
{ "SendCeltVoiceInit", Native_SendCeltVoiceInit },
|
||||||
|
{ "ClientMutedOtherClient", Native_ClientMutedOtherClient },
|
||||||
{ NULL, NULL }
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -462,6 +725,12 @@ void CVoice::SDK_OnUnload()
|
|||||||
m_VoiceDetour = NULL;
|
m_VoiceDetour = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_ProcessVoiceDataDetour)
|
||||||
|
{
|
||||||
|
m_ProcessVoiceDataDetour->Destroy();
|
||||||
|
m_ProcessVoiceDataDetour = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if(m_ListenSocket != -1)
|
if(m_ListenSocket != -1)
|
||||||
{
|
{
|
||||||
close(m_ListenSocket);
|
close(m_ListenSocket);
|
||||||
@@ -478,12 +747,74 @@ void CVoice::SDK_OnUnload()
|
|||||||
}
|
}
|
||||||
|
|
||||||
opus_encoder_destroy(m_OpusEncoder);
|
opus_encoder_destroy(m_OpusEncoder);
|
||||||
|
|
||||||
|
if (m_TorchlightOpusDecoder)
|
||||||
|
{
|
||||||
|
opus_decoder_destroy(m_TorchlightOpusDecoder);
|
||||||
|
m_TorchlightOpusDecoder = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i <= SM_MAXPLAYERS; i++)
|
||||||
|
{
|
||||||
|
if (m_PlayerOpusDecoder[i])
|
||||||
|
{
|
||||||
|
opus_decoder_destroy(m_PlayerOpusDecoder[i]);
|
||||||
|
m_PlayerOpusDecoder[i] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_pCeltCodec)
|
||||||
|
{
|
||||||
|
celt_encoder_destroy(m_pCeltCodec);
|
||||||
|
m_pCeltCodec = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_pCeltMode)
|
||||||
|
{
|
||||||
|
celt_mode_destroy(m_pCeltMode);
|
||||||
|
m_pCeltMode = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i <= SM_MAXPLAYERS; i++)
|
||||||
|
{
|
||||||
|
if (m_pCeltCodecPlayer[i])
|
||||||
|
{
|
||||||
|
celt_encoder_destroy(m_pCeltCodecPlayer[i]);
|
||||||
|
m_pCeltCodecPlayer[i] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_pCeltModePlayer)
|
||||||
|
{
|
||||||
|
celt_mode_destroy(m_pCeltModePlayer);
|
||||||
|
m_pCeltModePlayer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i <= SM_MAXPLAYERS; i++)
|
||||||
|
{
|
||||||
|
if (m_pCeltDecoder[i])
|
||||||
|
{
|
||||||
|
celt_decoder_destroy(m_pCeltDecoder[i]);
|
||||||
|
m_pCeltDecoder[i] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i <= SM_MAXPLAYERS; i++)
|
||||||
|
{
|
||||||
|
if (m_nosteamOpusEncoder[i])
|
||||||
|
{
|
||||||
|
opus_encoder_destroy(m_nosteamOpusEncoder[i]);
|
||||||
|
m_nosteamOpusEncoder[i] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVoice::OnGameFrame(bool simulating)
|
void CVoice::OnGameFrame(bool simulating)
|
||||||
{
|
{
|
||||||
HandleNetwork();
|
HandleNetwork();
|
||||||
HandleVoiceData();
|
HandleVoiceData();
|
||||||
|
HandlePlayerVoiceData(); //send celt packets to nosteamers.
|
||||||
|
HandleNoSteamVoiceData(); //send opus packets to steamers.
|
||||||
|
|
||||||
// Reset per-client voice byte counter to 0 every frame.
|
// Reset per-client voice byte counter to 0 every frame.
|
||||||
memset(g_aFrameVoiceBytes, 0, sizeof(g_aFrameVoiceBytes));
|
memset(g_aFrameVoiceBytes, 0, sizeof(g_aFrameVoiceBytes));
|
||||||
@@ -497,6 +828,11 @@ bool CVoice::OnBroadcastVoiceData(IClient *pClient, int nBytes, char *data)
|
|||||||
|
|
||||||
int client = pClient->GetPlayerSlot() + 1;
|
int client = pClient->GetPlayerSlot() + 1;
|
||||||
|
|
||||||
|
if (client < 1 || client > SM_MAXPLAYERS)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Reject voice packet if we'd send more than NET_MAX_VOICE_BYTES_FRAME voice bytes from this client in the current frame.
|
// Reject voice packet if we'd send more than NET_MAX_VOICE_BYTES_FRAME voice bytes from this client in the current frame.
|
||||||
// 5 = SVC_VoiceData header/overhead
|
// 5 = SVC_VoiceData header/overhead
|
||||||
g_aFrameVoiceBytes[client] += 5 + nBytes;
|
g_aFrameVoiceBytes[client] += 5 + nBytes;
|
||||||
@@ -690,9 +1026,9 @@ void CVoice::OnDataReceived(CClient *pClient, int16_t *pData, size_t Samples)
|
|||||||
pClient->m_LastValidData = getTime();
|
pClient->m_LastValidData = getTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CVoice::HandleVoiceData()
|
void CVoice::HandleVoiceData()
|
||||||
{
|
{
|
||||||
//uint32_t sampleRate = 24000;
|
|
||||||
uint32_t sampleRate = 48000;
|
uint32_t sampleRate = 48000;
|
||||||
const int SamplesPerChannel = 480;
|
const int SamplesPerChannel = 480;
|
||||||
const int Channels = 2;
|
const int Channels = 2;
|
||||||
@@ -784,6 +1120,7 @@ void CVoice::HandleVoiceData()
|
|||||||
smutils->LogError(myself, "Opus encode failed: %s", opus_strerror(nbBytes));
|
smutils->LogError(myself, "Opus encode failed: %s", opus_strerror(nbBytes));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
BroadcastVoiceDataCelt(pClient, aBuffer, SamplesPerChannel);
|
||||||
|
|
||||||
// Write frame size
|
// Write frame size
|
||||||
*pFrameSize = (uint16_t)nbBytes;
|
*pFrameSize = (uint16_t)nbBytes;
|
||||||
@@ -813,7 +1150,20 @@ void CVoice::HandleVoiceData()
|
|||||||
memcpy(&aFinal[FinalSize], &crc32_value, sizeof(uint32_t));
|
memcpy(&aFinal[FinalSize], &crc32_value, sizeof(uint32_t));
|
||||||
FinalSize += sizeof(uint32_t);
|
FinalSize += sizeof(uint32_t);
|
||||||
|
|
||||||
BroadcastVoiceData(pClient, FinalSize, aFinal);
|
int maxClients = iserver->GetClientCount();
|
||||||
|
for (int i = 0; i < maxClients; i++)
|
||||||
|
{
|
||||||
|
IClient *pToClient = iserver->GetClient(i);
|
||||||
|
if (!pToClient || !pToClient->IsConnected() || !pToClient->IsActive() || pToClient->IsFakeClient())
|
||||||
|
continue;
|
||||||
|
if (g_bClientMuted[i + 1][pClient->GetPlayerSlot()])
|
||||||
|
continue;
|
||||||
|
if (!g_bIsNoSteam[i + 1])
|
||||||
|
{
|
||||||
|
//sending opus packets to steam users.
|
||||||
|
SendVoiceDataMsg(pClient->GetPlayerSlot(), pToClient, aFinal, FinalSize, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (m_AvailableTime < getTime())
|
if (m_AvailableTime < getTime())
|
||||||
m_AvailableTime = getTime();
|
m_AvailableTime = getTime();
|
||||||
@@ -825,14 +1175,483 @@ void CVoice::HandleVoiceData()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVoice::BroadcastVoiceData(IClient *pClient, int nBytes, unsigned char *pData)
|
void CVoice::BroadcastVoiceDataCelt(IClient *pClient, int16_t *pPCM, int nSamples)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
// pPCM is stereo interleaved at 48000 Hz (SamplesPerChannel * 2 total samples)
|
||||||
__asm mov ecx, pClient;
|
// Downsample to mono 22050 Hz and accumulate into m_torchMonoAccum
|
||||||
__asm mov edx, nBytes;
|
const int STEP = m_CeltEncoderSettings.SampleRate_Hz; // 22050
|
||||||
|
const int DIV = 48000;
|
||||||
|
const int CELT_FRAME_SIZE = m_CeltEncoderSettings.FrameSize; // 512
|
||||||
|
const int CELT_PACKET_SIZE = m_CeltEncoderSettings.PacketSize; // 64
|
||||||
|
|
||||||
DETOUR_STATIC_CALL(SV_BroadcastVoiceData_LTCG)((char *)pData, 0);
|
int fromSlot = pClient->GetPlayerSlot();
|
||||||
#else
|
int maxClients = iserver->GetClientCount();
|
||||||
DETOUR_STATIC_CALL(SV_BroadcastVoiceData)(pClient, nBytes, (char *)pData, 0);
|
|
||||||
#endif
|
for (int i = 0; i < nSamples; i++)
|
||||||
|
{
|
||||||
|
if (m_torchMonoAccumLen >= 4096)
|
||||||
|
break;
|
||||||
|
|
||||||
|
int32_t mono = ((int32_t)pPCM[i * 2] + (int32_t)pPCM[i * 2 + 1]) / 2;
|
||||||
|
if (mono > 32767) mono = 32767;
|
||||||
|
if (mono < -32768) mono = -32768;
|
||||||
|
|
||||||
|
m_torchResampleAccum += STEP;
|
||||||
|
while (m_torchResampleAccum >= DIV)
|
||||||
|
{
|
||||||
|
m_torchResampleAccum -= DIV;
|
||||||
|
if (m_torchMonoAccumLen < 4096)
|
||||||
|
m_torchMonoAccum[m_torchMonoAccumLen++] = (int16_t)mono;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (m_torchMonoAccumLen >= CELT_FRAME_SIZE)
|
||||||
|
{
|
||||||
|
unsigned char celtPacket[CELT_PACKET_SIZE];
|
||||||
|
int celtBytes = celt_encode(m_pCeltCodec, m_torchMonoAccum, CELT_FRAME_SIZE,
|
||||||
|
celtPacket, CELT_PACKET_SIZE);
|
||||||
|
if (celtBytes > 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < maxClients; i++)
|
||||||
|
{
|
||||||
|
IClient *pToClient = iserver->GetClient(i);
|
||||||
|
if (!pToClient || !pToClient->IsConnected() || !pToClient->IsActive() || pToClient->IsFakeClient())
|
||||||
|
continue;
|
||||||
|
if (!g_bIsNoSteam[i + 1]) //i dont actually understand why the index is one off here.
|
||||||
|
continue;
|
||||||
|
if (g_bClientMuted[i + 1][fromSlot])
|
||||||
|
continue;
|
||||||
|
//smutils->LogMessage(myself, "right before SendVoiceDataMsg");
|
||||||
|
SendVoiceDataMsg(fromSlot, pToClient, celtPacket, celtBytes, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
smutils->LogError(myself, "BroadcastVoiceDataCelt: celt_encode failed: %d", celtBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_torchMonoAccumLen -= CELT_FRAME_SIZE;
|
||||||
|
if (m_torchMonoAccumLen > 0)
|
||||||
|
memmove(m_torchMonoAccum, m_torchMonoAccum + CELT_FRAME_SIZE,
|
||||||
|
m_torchMonoAccumLen * sizeof(int16_t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CVoice::PushPlayerVoiceData(int playerSlot, int nBytes, char *data)
|
||||||
|
{
|
||||||
|
if (playerSlot < 0 || playerSlot >= SM_MAXPLAYERS)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!m_pCeltCodecPlayer[playerSlot])
|
||||||
|
{
|
||||||
|
// First time this player speaks - create encoder
|
||||||
|
int theError;
|
||||||
|
m_pCeltCodecPlayer[playerSlot] = celt_encoder_create_custom(
|
||||||
|
m_pCeltModePlayer, 1, &theError);
|
||||||
|
if (!m_pCeltCodecPlayer[playerSlot])
|
||||||
|
{
|
||||||
|
smutils->LogError(myself, "PushPlayerVoiceData: celt_encoder_create_custom failed: %d", theError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
celt_encoder_ctl(m_pCeltCodecPlayer[playerSlot], CELT_RESET_STATE_REQUEST, NULL);
|
||||||
|
celt_encoder_ctl(m_pCeltCodecPlayer[playerSlot], CELT_SET_BITRATE(
|
||||||
|
m_CeltEncoderSettings.TargetBitRate_Kbps * 1000));
|
||||||
|
celt_encoder_ctl(m_pCeltCodecPlayer[playerSlot], CELT_SET_COMPLEXITY(
|
||||||
|
m_CeltEncoderSettings.Complexity));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_PlayerOpusDecoder[playerSlot])
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
m_PlayerOpusDecoder[playerSlot] = opus_decoder_create(24000, 1, &err);
|
||||||
|
if (err < 0)
|
||||||
|
{
|
||||||
|
smutils->LogError(myself, "PushPlayerVoiceData: opus_decoder_create failed: %s",
|
||||||
|
opus_strerror(err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const int OPUS_SAMPLE_RATE = 24000;
|
||||||
|
const unsigned char *p = (const unsigned char *)data;
|
||||||
|
|
||||||
|
/*
|
||||||
|
// debugging real steam clients voice packets.
|
||||||
|
int dumpLen = nBytes < 32 ? nBytes : 32;
|
||||||
|
char hexBuf[32 * 3 + 1];
|
||||||
|
int pos = 0;
|
||||||
|
for (int i = 0; i < dumpLen; i++)
|
||||||
|
{
|
||||||
|
static const char hex[] = "0123456789ABCDEF";
|
||||||
|
hexBuf[pos++] = hex[(p[i] >> 4) & 0xF];
|
||||||
|
hexBuf[pos++] = hex[p[i] & 0xF];
|
||||||
|
hexBuf[pos++] = ' ';
|
||||||
|
}
|
||||||
|
hexBuf[pos] = '\0';
|
||||||
|
smutils->LogMessage(myself, "Steam packet in PushPlayerVoiceData: nBytes=%d first %d bytes: %s", nBytes, dumpLen, hexBuf);
|
||||||
|
*/
|
||||||
|
|
||||||
|
//if its not opus packets we dont process it
|
||||||
|
if (nBytes < 18 || p[8] != 0x0B || (p[11] != 0x05 && p[11] != 0x06))
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint16_t totalDataLength;
|
||||||
|
memcpy(&totalDataLength, p + 12, sizeof(uint16_t));
|
||||||
|
if ((int)(14 + totalDataLength + 4) != nBytes)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint32_t expectedCRC;
|
||||||
|
memcpy(&expectedCRC, p + nBytes - 4, sizeof(uint32_t));
|
||||||
|
if (UTIL_CRC32(p, nBytes - 4) != expectedCRC)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int offset = 14;
|
||||||
|
int end = 14 + (int)totalDataLength;
|
||||||
|
|
||||||
|
if (offset + 4 <= end)
|
||||||
|
{
|
||||||
|
uint16_t frameLen;
|
||||||
|
memcpy(&frameLen, p + offset, sizeof(uint16_t));
|
||||||
|
offset += 2;
|
||||||
|
offset += 2;
|
||||||
|
|
||||||
|
if (frameLen <= 2 || offset + (int)(frameLen - 2) > end)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const unsigned char *opusFrame = p + offset;
|
||||||
|
|
||||||
|
int16_t pcmBuf[5760];
|
||||||
|
int decoded = opus_decode(m_PlayerOpusDecoder[playerSlot],
|
||||||
|
opusFrame, frameLen - 2,
|
||||||
|
pcmBuf, 5760, 0);
|
||||||
|
if (decoded <= 0)
|
||||||
|
{
|
||||||
|
smutils->LogError(myself, "PushPlayerVoiceData: opus_decode failed: %s",
|
||||||
|
opus_strerror(decoded));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int STEP = m_CeltEncoderSettings.SampleRate_Hz;
|
||||||
|
const int DIV = OPUS_SAMPLE_RATE;
|
||||||
|
|
||||||
|
for (int i = 0; i < decoded; i++)
|
||||||
|
{
|
||||||
|
m_playerResampleAccum[playerSlot] += STEP;
|
||||||
|
while (m_playerResampleAccum[playerSlot] >= DIV)
|
||||||
|
{
|
||||||
|
m_playerResampleAccum[playerSlot] -= DIV;
|
||||||
|
int16_t sample = pcmBuf[i];
|
||||||
|
if (m_playerVoiceBuffer[playerSlot].CurrentFree() > 0)
|
||||||
|
m_playerVoiceBuffer[playerSlot].Push(&sample, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If buffer still doesn't have enough for one CELT frame, use Opus PLC to fill
|
||||||
|
if (m_playerVoiceBuffer[playerSlot].TotalLength() < (size_t)m_CeltEncoderSettings.FrameSize)
|
||||||
|
{
|
||||||
|
int16_t plcBuf[5760];
|
||||||
|
int plcDecoded = opus_decode(m_PlayerOpusDecoder[playerSlot],
|
||||||
|
NULL, 0, // NULL = PLC mode
|
||||||
|
plcBuf, 480, 0);
|
||||||
|
if (plcDecoded > 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < plcDecoded; i++)
|
||||||
|
{
|
||||||
|
m_playerResampleAccum[playerSlot] += m_CeltEncoderSettings.SampleRate_Hz;
|
||||||
|
while (m_playerResampleAccum[playerSlot] >= 24000)
|
||||||
|
{
|
||||||
|
m_playerResampleAccum[playerSlot] -= 24000;
|
||||||
|
int16_t sample = plcBuf[i];
|
||||||
|
if (m_playerVoiceBuffer[playerSlot].CurrentFree() > 0)
|
||||||
|
m_playerVoiceBuffer[playerSlot].Push(&sample, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//smutils->LogMessage(myself, "PushPlayerVoiceData: pushed, bufLen=%d", (int)m_playerVoiceBuffer[playerSlot].TotalLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CVoice::HandlePlayerVoiceData()
|
||||||
|
{
|
||||||
|
const int CELT_FRAME_SIZE = m_CeltEncoderSettings.FrameSize;
|
||||||
|
const int CELT_PACKET_SIZE = m_CeltEncoderSettings.PacketSize;
|
||||||
|
int maxClients = iserver->GetClientCount();
|
||||||
|
|
||||||
|
for (int playerSlot = 0; playerSlot < maxClients; playerSlot++)
|
||||||
|
{
|
||||||
|
if (!m_pCeltCodecPlayer[playerSlot])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (m_playerVoiceBuffer[playerSlot].TotalLength() < (size_t)CELT_FRAME_SIZE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float timeAvailable = (float)m_playerVoiceBuffer[playerSlot].TotalLength()
|
||||||
|
/ (float)m_CeltEncoderSettings.SampleRate_Hz;
|
||||||
|
|
||||||
|
|
||||||
|
//warmup encoder with 5 frames
|
||||||
|
if (m_playerAvailableTime[playerSlot] == 0.0 &&
|
||||||
|
m_playerVoiceBuffer[playerSlot].TotalLength() >= (size_t)CELT_FRAME_SIZE * 11)
|
||||||
|
{
|
||||||
|
for (int w = 0; w < 10; w++)
|
||||||
|
{
|
||||||
|
int16_t warmupInput[CELT_FRAME_SIZE];
|
||||||
|
if (!m_playerVoiceBuffer[playerSlot].Pop(warmupInput, CELT_FRAME_SIZE))
|
||||||
|
break;
|
||||||
|
unsigned char discard[64];
|
||||||
|
celt_encode(m_pCeltCodecPlayer[playerSlot], warmupInput, CELT_FRAME_SIZE, discard, 64);
|
||||||
|
}
|
||||||
|
m_playerAvailableTime[playerSlot] = getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
//if (m_playerAvailableTime[playerSlot] < getTime() && timeAvailable < 0.2)
|
||||||
|
if (m_playerAvailableTime[playerSlot] < getTime() && timeAvailable < 0.05)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (m_playerAvailableTime[playerSlot] > getTime() + 1.0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int framesEmitted = 0;
|
||||||
|
while (m_playerVoiceBuffer[playerSlot].TotalLength() >= (size_t)CELT_FRAME_SIZE)
|
||||||
|
{
|
||||||
|
//smutils->LogMessage(myself, "framesEmitted: %d", framesEmitted);
|
||||||
|
if (framesEmitted >= 1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
int16_t celtInput[CELT_FRAME_SIZE];
|
||||||
|
if (!m_playerVoiceBuffer[playerSlot].Pop(celtInput, CELT_FRAME_SIZE))
|
||||||
|
break;
|
||||||
|
|
||||||
|
unsigned char celtPacket[CELT_PACKET_SIZE];
|
||||||
|
int celtBytes = celt_encode(m_pCeltCodecPlayer[playerSlot], celtInput,
|
||||||
|
CELT_FRAME_SIZE, celtPacket, CELT_PACKET_SIZE);
|
||||||
|
if (celtBytes > 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < maxClients; i++)
|
||||||
|
{
|
||||||
|
IClient *pToClient = iserver->GetClient(i);
|
||||||
|
if (!pToClient || !pToClient->IsConnected() || !pToClient->IsActive())
|
||||||
|
continue;
|
||||||
|
if (!g_bIsNoSteam[i + 1])
|
||||||
|
continue;
|
||||||
|
if (g_bClientMuted[i + 1][playerSlot + 1])
|
||||||
|
continue;
|
||||||
|
SendVoiceDataMsg(playerSlot, pToClient, celtPacket, celtBytes, 0);
|
||||||
|
}
|
||||||
|
framesEmitted++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
smutils->LogError(myself, "HandlePlayerVoiceData: celt_encode failed: %d", celtBytes);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (framesEmitted > 0)
|
||||||
|
{
|
||||||
|
if (m_playerAvailableTime[playerSlot] < getTime())
|
||||||
|
m_playerAvailableTime[playerSlot] = getTime();
|
||||||
|
m_playerAvailableTime[playerSlot] += (double)framesEmitted * m_CeltEncoderSettings.FrameTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CVoice::TranscodeNoSteamToSteam(int playerSlot, int nBytes, char *data)
|
||||||
|
{
|
||||||
|
if (playerSlot < 0 || playerSlot >= SM_MAXPLAYERS)
|
||||||
|
return;
|
||||||
|
if (!m_pCeltDecoder[playerSlot])
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
m_pCeltDecoder[playerSlot] = celt_decoder_create_custom(m_pCeltModePlayer, 1, &err);
|
||||||
|
if (!m_pCeltDecoder[playerSlot])
|
||||||
|
{
|
||||||
|
smutils->LogError(myself, "TranscodeNoSteamToSteam: celt_decoder_create_custom failed: %d", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_nosteamOpusEncoder[playerSlot])
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
m_nosteamOpusEncoder[playerSlot] = opus_encoder_create(24000, 1, OPUS_APPLICATION_AUDIO, &err);
|
||||||
|
if (err < 0)
|
||||||
|
{
|
||||||
|
smutils->LogError(myself, "TranscodeNoSteamToSteam: opus_encoder_create failed: %s", opus_strerror(err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
0x68 TOC -> 01101000 -> c bits (6-7): 00 -> s bit (5): 0 (Mono) -> config bits (0-4): 01101 (13)
|
||||||
|
Mode: Hybrid (SILK + CELT)
|
||||||
|
Bandwidth: SWB
|
||||||
|
Frame Size: 20 ms
|
||||||
|
BITRATE 640000 changes TOC to 0xD8
|
||||||
|
*/
|
||||||
|
opus_encoder_ctl(m_nosteamOpusEncoder[playerSlot], OPUS_SET_BITRATE(32000));
|
||||||
|
opus_encoder_ctl(m_nosteamOpusEncoder[playerSlot], OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE));
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t pcmBuf[512];
|
||||||
|
int decoded = celt_decode(m_pCeltDecoder[playerSlot],
|
||||||
|
(const unsigned char *)data, nBytes,
|
||||||
|
pcmBuf, m_CeltEncoderSettings.FrameSize);
|
||||||
|
if (decoded < 0)
|
||||||
|
{
|
||||||
|
smutils->LogError(myself, "TranscodeNoSteamToSteam: celt_decode failed: %d", decoded);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resample 22050 -> 24000 and push into ring buffer
|
||||||
|
const int STEP = 24000;
|
||||||
|
const int DIV = 22050;
|
||||||
|
|
||||||
|
for (int i = 0; i < decoded; i++)
|
||||||
|
{
|
||||||
|
m_nosteamResampleAccum[playerSlot] += STEP;
|
||||||
|
while (m_nosteamResampleAccum[playerSlot] >= DIV)
|
||||||
|
{
|
||||||
|
m_nosteamResampleAccum[playerSlot] -= DIV;
|
||||||
|
int16_t sample = pcmBuf[i];
|
||||||
|
if (m_nosteamOpusPCMBuffer[playerSlot].CurrentFree() > 0)
|
||||||
|
m_nosteamOpusPCMBuffer[playerSlot].Push(&sample, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CVoice::HandleNoSteamVoiceData()
|
||||||
|
{
|
||||||
|
int maxClients = iserver->GetClientCount();
|
||||||
|
|
||||||
|
for (int playerSlot = 0; playerSlot < maxClients; playerSlot++)
|
||||||
|
{
|
||||||
|
if (!m_nosteamOpusEncoder[playerSlot])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (m_nosteamOpusPCMBuffer[playerSlot].TotalLength() < 480)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float timeAvailable = (float)m_nosteamOpusPCMBuffer[playerSlot].TotalLength() / 24000.0f;
|
||||||
|
|
||||||
|
if (m_nosteamAvailableTime[playerSlot] < getTime() && timeAvailable < 0.1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (m_nosteamAvailableTime[playerSlot] > getTime() + 1.0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int framesEmitted = 0;
|
||||||
|
while (m_nosteamOpusPCMBuffer[playerSlot].TotalLength() >= 480)
|
||||||
|
{
|
||||||
|
if (framesEmitted >= 1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
int16_t opusInput[480];
|
||||||
|
if (!m_nosteamOpusPCMBuffer[playerSlot].Pop(opusInput, 480))
|
||||||
|
break;
|
||||||
|
|
||||||
|
unsigned char opusBuf[512];
|
||||||
|
int opusBytes = opus_encode(m_nosteamOpusEncoder[playerSlot], opusInput, 480, opusBuf, sizeof(opusBuf));
|
||||||
|
if (opusBytes < 0)
|
||||||
|
{
|
||||||
|
smutils->LogError(myself, "HandleNoSteamVoiceData: opus_encode failed: %s",
|
||||||
|
opus_strerror(opusBytes));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
IClient *pNoSteamClient = iserver->GetClient(playerSlot);
|
||||||
|
if (!pNoSteamClient)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const char *networkID = pNoSteamClient->GetNetworkIDString();
|
||||||
|
uint32_t accountID = 0;
|
||||||
|
// Parse [U:1:XXXXXXXX] format
|
||||||
|
const char *numStart = strrchr(networkID, ':');
|
||||||
|
if (numStart)
|
||||||
|
accountID = (uint32_t)atoi(numStart + 1);
|
||||||
|
|
||||||
|
// Build modern Steam voice packet with working routing blocks
|
||||||
|
unsigned char aFinal[8192];
|
||||||
|
size_t FinalSize = 0;
|
||||||
|
|
||||||
|
// Build Steam voice packet header
|
||||||
|
uint32_t steamID = accountID;
|
||||||
|
uint32_t steamCommunity = 0x01100001;
|
||||||
|
memcpy(&aFinal[FinalSize], &steamID, sizeof(uint32_t));
|
||||||
|
FinalSize += sizeof(uint32_t);
|
||||||
|
memcpy(&aFinal[FinalSize], &steamCommunity, sizeof(uint32_t));
|
||||||
|
FinalSize += sizeof(uint32_t);
|
||||||
|
|
||||||
|
// 2. Payload Type (1 byte)
|
||||||
|
aFinal[FinalSize++] = 0x0B;
|
||||||
|
|
||||||
|
// 3. Sample Rate (2 bytes little-endian)
|
||||||
|
uint16_t sampleRate = 24000;
|
||||||
|
memcpy(&aFinal[FinalSize], &sampleRate, sizeof(uint16_t));
|
||||||
|
FinalSize += sizeof(uint16_t);
|
||||||
|
|
||||||
|
// 4. Codec Type (1 byte) - 0x06 for Opus PLC
|
||||||
|
aFinal[FinalSize++] = 0x06;
|
||||||
|
|
||||||
|
// 5. Track where Size 1 lives
|
||||||
|
size_t totalDataLengthOffset = FinalSize;
|
||||||
|
FinalSize += sizeof(uint16_t); // Reserve 2 bytes
|
||||||
|
|
||||||
|
// 6. Sub-Frame Header size (2 bytes) - MUST be exactly raw opusBytes count
|
||||||
|
uint16_t frameSize = (uint16_t)opusBytes;
|
||||||
|
memcpy(&aFinal[FinalSize], &frameSize, sizeof(uint16_t));
|
||||||
|
FinalSize += sizeof(uint16_t);
|
||||||
|
|
||||||
|
// 7. Sequence Number (2 bytes)
|
||||||
|
uint16_t seq = m_nosteamSeqNum[playerSlot]++;
|
||||||
|
memcpy(&aFinal[FinalSize], &seq, sizeof(uint16_t));
|
||||||
|
FinalSize += sizeof(uint16_t);
|
||||||
|
|
||||||
|
// 8. Raw Opus Audio Payload
|
||||||
|
memcpy(&aFinal[FinalSize], opusBuf, opusBytes);
|
||||||
|
FinalSize += opusBytes;
|
||||||
|
|
||||||
|
uint16_t totalRemainingBytes = (uint16_t)(sizeof(uint16_t) + sizeof(uint16_t) + opusBytes);
|
||||||
|
memcpy(&aFinal[totalDataLengthOffset], &totalRemainingBytes, sizeof(uint16_t));
|
||||||
|
|
||||||
|
// 10. Compute and append CRC32 checksum over the complete array built so far
|
||||||
|
uint32_t crc = UTIL_CRC32(aFinal, FinalSize);
|
||||||
|
memcpy(&aFinal[FinalSize], &crc, sizeof(uint32_t));
|
||||||
|
FinalSize += sizeof(uint32_t);
|
||||||
|
|
||||||
|
for (int i = 0; i < maxClients; i++)
|
||||||
|
{
|
||||||
|
IClient *pToClient = iserver->GetClient(i);
|
||||||
|
if (!pToClient || !pToClient->IsConnected() || !pToClient->IsActive() || pToClient->IsFakeClient())
|
||||||
|
continue;
|
||||||
|
if (g_bIsNoSteam[i + 1])
|
||||||
|
continue;
|
||||||
|
if (g_bClientMuted[i + 1][playerSlot + 1])
|
||||||
|
continue;
|
||||||
|
// debugging voice packets
|
||||||
|
/*
|
||||||
|
int dumpLen = (int)FinalSize < 32 ? (int)FinalSize : 32;
|
||||||
|
char hexBuf[32 * 3 + 1];
|
||||||
|
int pos = 0;
|
||||||
|
for (int i = 0; i < dumpLen; i++)
|
||||||
|
{
|
||||||
|
static const char hex[] = "0123456789ABCDEF";
|
||||||
|
hexBuf[pos++] = hex[(aFinal[i] >> 4) & 0xF];
|
||||||
|
hexBuf[pos++] = hex[aFinal[i] & 0xF];
|
||||||
|
hexBuf[pos++] = ' ';
|
||||||
|
}
|
||||||
|
hexBuf[pos] = '\0';
|
||||||
|
smutils->LogMessage(myself, "CVoice::HandleNoSteamVoiceData packet: FinalSize=%d first %d bytes: %s", (int)FinalSize, dumpLen, hexBuf);
|
||||||
|
*/
|
||||||
|
SendVoiceDataMsg(playerSlot, pToClient, aFinal, (int)FinalSize, 0);
|
||||||
|
}
|
||||||
|
framesEmitted++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (framesEmitted > 0)
|
||||||
|
{
|
||||||
|
if (m_nosteamAvailableTime[playerSlot] < getTime())
|
||||||
|
m_nosteamAvailableTime[playerSlot] = getTime();
|
||||||
|
m_nosteamAvailableTime[playerSlot] += (double)framesEmitted * (480.0 / 24000.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+48
-2
@@ -34,6 +34,7 @@
|
|||||||
|
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include "smsdk_ext.h"
|
#include "smsdk_ext.h"
|
||||||
|
#include "celt_header.h"
|
||||||
#include "ringbuffer.h"
|
#include "ringbuffer.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -139,6 +140,9 @@ public:
|
|||||||
bool OnBroadcastVoiceData(IClient *pClient, int nBytes, char *data);
|
bool OnBroadcastVoiceData(IClient *pClient, int nBytes, char *data);
|
||||||
|
|
||||||
void ListenSocket();
|
void ListenSocket();
|
||||||
|
void PushPlayerVoiceData(int playerSlot, int nBytes, char *data);
|
||||||
|
void BroadcastVoiceDataCelt(IClient *pClient, int16_t *pPCM, int nSamples);
|
||||||
|
void TranscodeNoSteamToSteam(int playerSlot, int nBytes, char *data);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_ListenSocket;
|
int m_ListenSocket;
|
||||||
@@ -161,16 +165,58 @@ private:
|
|||||||
|
|
||||||
OpusEncoder *m_OpusEncoder;
|
OpusEncoder *m_OpusEncoder;
|
||||||
|
|
||||||
double m_AvailableTime;
|
// Torchlight transcode state (48000 Hz stereo -> 22050 Hz mono -> CELT)
|
||||||
|
OpusDecoder *m_TorchlightOpusDecoder;
|
||||||
|
int16_t m_torchMonoAccum[4096];
|
||||||
|
int m_torchMonoAccumLen;
|
||||||
|
int m_torchResampleAccum;
|
||||||
|
|
||||||
|
// Player transcode state (24000 Hz mono -> 22050 Hz mono -> CELT)
|
||||||
|
OpusDecoder *m_PlayerOpusDecoder[SM_MAXPLAYERS + 1];
|
||||||
|
int m_playerResampleAccum[SM_MAXPLAYERS + 1];
|
||||||
|
|
||||||
|
double m_AvailableTime;
|
||||||
|
double m_playerAvailableTime[SM_MAXPLAYERS + 1];
|
||||||
|
|
||||||
|
// NoSteam->Steam transcode state per player
|
||||||
|
CRingBuffer m_nosteamOpusPCMBuffer[SM_MAXPLAYERS + 1];
|
||||||
|
int m_nosteamResampleAccum[SM_MAXPLAYERS + 1];
|
||||||
|
OpusEncoder *m_nosteamOpusEncoder[SM_MAXPLAYERS + 1];
|
||||||
|
|
||||||
|
// CELT encoder (shared by both paths, same output format)
|
||||||
|
struct CEncoderSettings
|
||||||
|
{
|
||||||
|
int32_t SampleRate_Hz;
|
||||||
|
int32_t TargetBitRate_Kbps;
|
||||||
|
int32_t FrameSize;
|
||||||
|
int32_t PacketSize;
|
||||||
|
int32_t Complexity;
|
||||||
|
double FrameTime;
|
||||||
|
} m_CeltEncoderSettings;
|
||||||
|
|
||||||
|
CELTMode *m_pCeltMode;
|
||||||
|
CELTEncoder *m_pCeltCodec;
|
||||||
|
|
||||||
|
CELTMode *m_pCeltModePlayer;
|
||||||
|
CELTEncoder *m_pCeltCodecPlayer[SM_MAXPLAYERS + 1];
|
||||||
|
|
||||||
|
CELTDecoder *m_pCeltDecoder[SM_MAXPLAYERS + 1];
|
||||||
|
|
||||||
|
CRingBuffer m_playerVoiceBuffer[SM_MAXPLAYERS + 1];
|
||||||
|
|
||||||
t_SV_BroadcastVoiceData m_SV_BroadcastVoiceData;
|
t_SV_BroadcastVoiceData m_SV_BroadcastVoiceData;
|
||||||
CDetour *m_VoiceDetour;
|
CDetour *m_VoiceDetour;
|
||||||
|
CDetour *m_ProcessVoiceDataDetour;
|
||||||
|
|
||||||
|
uint16_t m_nosteamSeqNum[SM_MAXPLAYERS + 1];
|
||||||
|
|
||||||
|
double m_nosteamAvailableTime[SM_MAXPLAYERS + 1];
|
||||||
|
|
||||||
void HandleNetwork();
|
void HandleNetwork();
|
||||||
void OnDataReceived(CClient *pClient, int16_t *pData, size_t Samples);
|
void OnDataReceived(CClient *pClient, int16_t *pData, size_t Samples);
|
||||||
void HandleVoiceData();
|
void HandleVoiceData();
|
||||||
void BroadcastVoiceData(IClient *pClient, int nBytes, unsigned char *pData);
|
void HandlePlayerVoiceData();
|
||||||
|
void HandleNoSteamVoiceData();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
|
#endif // _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
|
||||||
|
|||||||
+195
@@ -0,0 +1,195 @@
|
|||||||
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||||
|
//
|
||||||
|
// Purpose: INetMessage interface
|
||||||
|
//
|
||||||
|
// $NoKeywords: $
|
||||||
|
//=============================================================================//
|
||||||
|
|
||||||
|
#ifndef INETMESSAGE_FRAEVEN_H
|
||||||
|
#define INETMESSAGE_FRAEVEN_H
|
||||||
|
|
||||||
|
#include "smsdk_ext.h"
|
||||||
|
#include "bitbuf.h"
|
||||||
|
#include "inetchannelinfo.h"
|
||||||
|
|
||||||
|
class INetMsgHandler;
|
||||||
|
class INetMessage;
|
||||||
|
class INetChannel;
|
||||||
|
|
||||||
|
// typedef bool (INetMsgHandler::*PROCESSFUNCPTR)(INetMessage*);
|
||||||
|
// #define CASTPROCPTR( fn ) static_cast <bool (INetMsgHandler::*)(INetMessage*)> (fn)
|
||||||
|
|
||||||
|
class INetMessage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~INetMessage() {};
|
||||||
|
|
||||||
|
// Use these to setup who can hear whose voice.
|
||||||
|
// Pass in client indices (which are their ent indices - 1).
|
||||||
|
|
||||||
|
virtual void SetNetChannel(INetChannel * netchan) = 0; // netchannel this message is from/for
|
||||||
|
virtual void SetReliable( bool state ) = 0; // set to true if it's a reliable message
|
||||||
|
|
||||||
|
virtual bool Process( void ) = 0; // calles the recently set handler to process this message
|
||||||
|
|
||||||
|
virtual bool ReadFromBuffer( bf_read &buffer ) = 0; // returns true if parsing was OK
|
||||||
|
virtual bool WriteToBuffer( bf_write &buffer ) = 0; // returns true if writing was OK
|
||||||
|
|
||||||
|
virtual bool IsReliable( void ) const = 0; // true, if message needs reliable handling
|
||||||
|
|
||||||
|
virtual int GetType( void ) const = 0; // returns module specific header tag eg svc_serverinfo
|
||||||
|
virtual int GetGroup( void ) const = 0; // returns net message group of this message
|
||||||
|
virtual const char *GetName( void ) const = 0; // returns network message name, eg "svc_serverinfo"
|
||||||
|
virtual INetChannel *GetNetChannel( void ) const = 0;
|
||||||
|
virtual const char *ToString( void ) const = 0; // returns a human readable string about message content
|
||||||
|
};
|
||||||
|
|
||||||
|
class CNetMessage : public INetMessage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CNetMessage() { m_bReliable = true;
|
||||||
|
m_NetChannel = NULL; }
|
||||||
|
|
||||||
|
virtual ~CNetMessage() {};
|
||||||
|
|
||||||
|
virtual int GetGroup() const { return INetChannelInfo::GENERIC; }
|
||||||
|
INetChannel *GetNetChannel() const { return m_NetChannel; }
|
||||||
|
|
||||||
|
virtual void SetReliable( bool state) {m_bReliable = state;};
|
||||||
|
virtual bool IsReliable() const { return m_bReliable; };
|
||||||
|
virtual void SetNetChannel(INetChannel * netchan) { m_NetChannel = netchan; }
|
||||||
|
virtual bool Process() { return false; }; // no handler set
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool m_bReliable; // true if message should be send reliable
|
||||||
|
INetChannel *m_NetChannel; // netchannel this message is from/for
|
||||||
|
};
|
||||||
|
|
||||||
|
#define svc_VoiceData 15
|
||||||
|
|
||||||
|
#define DECLARE_BASE_MESSAGE( msgtype ) \
|
||||||
|
public: \
|
||||||
|
bool ReadFromBuffer( bf_read &buffer ); \
|
||||||
|
bool WriteToBuffer( bf_write &buffer ); \
|
||||||
|
const char *ToString() const; \
|
||||||
|
int GetType() const { return msgtype; } \
|
||||||
|
const char *GetName() const { return #msgtype;}\
|
||||||
|
|
||||||
|
#define DECLARE_SVC_MESSAGE( name ) \
|
||||||
|
DECLARE_BASE_MESSAGE( svc_##name ); \
|
||||||
|
IServerMessageHandler *m_pMessageHandler;\
|
||||||
|
bool Process() { return m_pMessageHandler->Process##name( this ); }\
|
||||||
|
|
||||||
|
class SVC_VoiceData : public CNetMessage
|
||||||
|
{
|
||||||
|
DECLARE_SVC_MESSAGE( VoiceData );
|
||||||
|
|
||||||
|
int GetGroup() const { return INetChannelInfo::VOICE; }
|
||||||
|
|
||||||
|
SVC_VoiceData() { m_bReliable = false; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
int m_nFromClient; // client who has spoken
|
||||||
|
bool m_bProximity;
|
||||||
|
int m_nLength; // data length in bits
|
||||||
|
uint64 m_xuid; // X360 player ID
|
||||||
|
|
||||||
|
bf_read m_DataIn;
|
||||||
|
void *m_DataOut;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NETMSG_TYPE_BITS 6
|
||||||
|
|
||||||
|
bool SVC_VoiceData::WriteToBuffer( bf_write &buffer )
|
||||||
|
{
|
||||||
|
buffer.WriteUBitLong( GetType(), NETMSG_TYPE_BITS );
|
||||||
|
buffer.WriteByte( m_nFromClient );
|
||||||
|
buffer.WriteByte( m_bProximity );
|
||||||
|
buffer.WriteWord( m_nLength );
|
||||||
|
|
||||||
|
return buffer.WriteBits( m_DataOut, m_nLength );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SVC_VoiceData::ReadFromBuffer( bf_read &buffer )
|
||||||
|
{
|
||||||
|
// VPROF( "SVC_VoiceData::ReadFromBuffer" );
|
||||||
|
|
||||||
|
m_nFromClient = buffer.ReadByte();
|
||||||
|
m_bProximity = !!buffer.ReadByte();
|
||||||
|
m_nLength = buffer.ReadWord();
|
||||||
|
|
||||||
|
// if ( IsX360() )
|
||||||
|
// {
|
||||||
|
// m_xuid = buffer.ReadLongLong();
|
||||||
|
// }
|
||||||
|
|
||||||
|
m_DataIn = buffer;
|
||||||
|
return buffer.SeekRelative( m_nLength );
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *SVC_VoiceData::ToString(void) const
|
||||||
|
{
|
||||||
|
// Q_snprintf(s_text, sizeof(s_text), "%s: client %i, bytes %i", GetName(), m_nFromClient, Bits2Bytes(m_nLength) );
|
||||||
|
// return s_text;
|
||||||
|
return "idc";
|
||||||
|
}
|
||||||
|
|
||||||
|
#define svc_VoiceInit 14
|
||||||
|
|
||||||
|
class SVC_VoiceInit : public CNetMessage
|
||||||
|
{
|
||||||
|
DECLARE_SVC_MESSAGE( VoiceInit );
|
||||||
|
|
||||||
|
int GetGroup() const { return INetChannelInfo::SIGNON; }
|
||||||
|
|
||||||
|
SVC_VoiceInit() : m_nSampleRate(0)
|
||||||
|
{
|
||||||
|
m_bReliable = true;
|
||||||
|
memset(m_szVoiceCodec, 0, sizeof(m_szVoiceCodec));
|
||||||
|
}
|
||||||
|
|
||||||
|
SVC_VoiceInit(const char *pCodec, int nSampleRate) : m_nSampleRate(nSampleRate)
|
||||||
|
{
|
||||||
|
m_bReliable = true;
|
||||||
|
Q_strncpy(m_szVoiceCodec, pCodec ? pCodec : "", sizeof(m_szVoiceCodec));
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
char m_szVoiceCodec[MAX_PATH];
|
||||||
|
int m_nSampleRate;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool SVC_VoiceInit::WriteToBuffer(bf_write &buffer)
|
||||||
|
{
|
||||||
|
buffer.WriteUBitLong(GetType(), NETMSG_TYPE_BITS);
|
||||||
|
buffer.WriteString(m_szVoiceCodec);
|
||||||
|
buffer.WriteShort(m_nSampleRate);
|
||||||
|
return !buffer.IsOverflowed();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SVC_VoiceInit::ReadFromBuffer(bf_read &buffer)
|
||||||
|
{
|
||||||
|
buffer.ReadString(m_szVoiceCodec, sizeof(m_szVoiceCodec));
|
||||||
|
m_nSampleRate = buffer.ReadShort();
|
||||||
|
return !buffer.IsOverflowed();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *SVC_VoiceInit::ToString() const
|
||||||
|
{
|
||||||
|
return "SVC_VoiceInit";
|
||||||
|
}
|
||||||
|
|
||||||
|
#define clc_VoiceData 10
|
||||||
|
|
||||||
|
//objdump -d /home/gameservers/css_dev/bin/engine_srv.so | grep -A 15 "_ZN11CGameClient16ProcessVoiceDataEP13CLC_VoiceData"
|
||||||
|
class CLC_VoiceData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
uint8_t _pad[20]; // vtable + CNetMessage base fields + padding up to m_nLength (20 offset)
|
||||||
|
int m_nLength;
|
||||||
|
bf_read m_DataIn;
|
||||||
|
bf_write m_DataOut;
|
||||||
|
uint64 m_xuid;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user