mirror of https://github.com/NekoX-Dev/NekoX.git
Update to 5.7.1
This commit is contained in:
parent
96247ca2bc
commit
9ffc7787d6
|
@ -19,7 +19,7 @@ dependencies {
|
||||||
implementation 'androidx.palette:palette:1.0.0'
|
implementation 'androidx.palette:palette:1.0.0'
|
||||||
implementation 'androidx.exifinterface:exifinterface:1.0.0'
|
implementation 'androidx.exifinterface:exifinterface:1.0.0'
|
||||||
|
|
||||||
implementation 'com.airbnb.android:lottie:3.0.1'
|
implementation 'com.airbnb.android:lottie:3.0.3'
|
||||||
|
|
||||||
compileOnly 'org.checkerframework:checker-qual:2.5.2'
|
compileOnly 'org.checkerframework:checker-qual:2.5.2'
|
||||||
compileOnly 'org.checkerframework:checker-compat-qual:2.5.0'
|
compileOnly 'org.checkerframework:checker-compat-qual:2.5.0'
|
||||||
|
@ -243,7 +243,7 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig.versionCode = 1591
|
defaultConfig.versionCode = 1608
|
||||||
|
|
||||||
applicationVariants.all { variant ->
|
applicationVariants.all { variant ->
|
||||||
variant.outputs.all { output ->
|
variant.outputs.all { output ->
|
||||||
|
@ -277,7 +277,7 @@ android {
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 27
|
targetSdkVersion 27
|
||||||
versionName "5.6.1"
|
versionName "5.7.1"
|
||||||
|
|
||||||
vectorDrawables.generatedDensities = ['mdpi', 'hdpi', 'xhdpi', 'xxhdpi']
|
vectorDrawables.generatedDensities = ['mdpi', 'hdpi', 'xhdpi', 'xxhdpi']
|
||||||
|
|
||||||
|
|
|
@ -101,7 +101,8 @@ LOCAL_PATH := $(MY_LOCAL_PATH) # restore local path after include
|
||||||
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
LOCAL_CPPFLAGS := -Wall -std=c++11 -DANDROID -frtti -DHAVE_PTHREAD -finline-functions -ffast-math -O0
|
LOCAL_CPPFLAGS := -Wall -std=c++11 -DANDROID -frtti -DHAVE_PTHREAD -finline-functions -ffast-math -Os
|
||||||
|
|
||||||
LOCAL_C_INCLUDES += ./jni/boringssl/include/
|
LOCAL_C_INCLUDES += ./jni/boringssl/include/
|
||||||
LOCAL_ARM_MODE := arm
|
LOCAL_ARM_MODE := arm
|
||||||
LOCAL_MODULE := tgnet
|
LOCAL_MODULE := tgnet
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
APP_PLATFORM := android-14
|
APP_PLATFORM := android-14
|
||||||
NDK_TOOLCHAIN_VERSION := 4.9
|
NDK_TOOLCHAIN_VERSION := clang
|
||||||
APP_STL := gnustl_static
|
APP_STL := gnustl_static
|
|
@ -0,0 +1,350 @@
|
||||||
|
LOCAL_PATH := $(call my-dir)
|
||||||
|
|
||||||
|
LOCAL_MODULE := voip
|
||||||
|
LOCAL_CPPFLAGS := -Wall -std=c++11 -DANDROID -finline-functions -ffast-math -Os -fno-strict-aliasing -O3 -frtti -D__STDC_LIMIT_MACROS -Wno-unknown-pragmas
|
||||||
|
LOCAL_CFLAGS := -O3 -DUSE_KISS_FFT -fexceptions -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_POSIX -DWEBRTC_ANDROID -D__STDC_LIMIT_MACROS -DFIXED_POINT -DWEBRTC_NS_FLOAT
|
||||||
|
LOCAL_EXPORT_LDLIBS := -llog -lOpenSLES
|
||||||
|
|
||||||
|
MY_DIR := libtgvoip
|
||||||
|
|
||||||
|
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../opus/include $(LOCAL_PATH)/../boringssl/include/ $(LOCAL_PATH)/webrtc_dsp/
|
||||||
|
|
||||||
|
ifeq ($(TARGET_ARCH_ABI),$(filter $(TARGET_ARCH_ABI),armeabi-v7a arm64-v8a))
|
||||||
|
CC_NEON := cc.neon
|
||||||
|
LOCAL_CFLAGS += -DWEBRTC_HAS_NEON
|
||||||
|
else
|
||||||
|
CC_NEON := cc
|
||||||
|
endif
|
||||||
|
|
||||||
|
LOCAL_CFLAGS += $(TGVOIP_ADDITIONAL_CFLAGS)
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES := \
|
||||||
|
./logging.cpp \
|
||||||
|
./VoIPController.cpp \
|
||||||
|
./VoIPGroupController.cpp \
|
||||||
|
./Buffers.cpp \
|
||||||
|
./BlockingQueue.cpp \
|
||||||
|
./audio/AudioInput.cpp \
|
||||||
|
./os/android/AudioInputOpenSLES.cpp \
|
||||||
|
./MediaStreamItf.cpp \
|
||||||
|
./audio/AudioOutput.cpp \
|
||||||
|
./OpusEncoder.cpp \
|
||||||
|
./os/android/AudioOutputOpenSLES.cpp \
|
||||||
|
./JitterBuffer.cpp \
|
||||||
|
./OpusDecoder.cpp \
|
||||||
|
./os/android/OpenSLEngineWrapper.cpp \
|
||||||
|
./os/android/AudioInputAndroid.cpp \
|
||||||
|
./os/android/AudioOutputAndroid.cpp \
|
||||||
|
./EchoCanceller.cpp \
|
||||||
|
./CongestionControl.cpp \
|
||||||
|
./VoIPServerConfig.cpp \
|
||||||
|
./audio/Resampler.cpp \
|
||||||
|
./NetworkSocket.cpp \
|
||||||
|
./os/posix/NetworkSocketPosix.cpp \
|
||||||
|
./PacketReassembler.cpp \
|
||||||
|
./MessageThread.cpp \
|
||||||
|
./json11.cpp \
|
||||||
|
./audio/AudioIO.cpp \
|
||||||
|
./video/VideoRenderer.cpp \
|
||||||
|
./video/VideoSource.cpp \
|
||||||
|
./video/ScreamCongestionController.cpp \
|
||||||
|
./os/android/VideoSourceAndroid.cpp \
|
||||||
|
./os/android/VideoRendererAndroid.cpp \
|
||||||
|
./client/android/tg_voip_jni.cpp
|
||||||
|
|
||||||
|
# WebRTC signal processing
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES += \
|
||||||
|
./webrtc_dsp/system_wrappers/source/field_trial.cc \
|
||||||
|
./webrtc_dsp/system_wrappers/source/metrics.cc \
|
||||||
|
./webrtc_dsp/system_wrappers/source/cpu_features.cc \
|
||||||
|
./webrtc_dsp/absl/strings/internal/memutil.cc \
|
||||||
|
./webrtc_dsp/absl/strings/string_view.cc \
|
||||||
|
./webrtc_dsp/absl/strings/ascii.cc \
|
||||||
|
./webrtc_dsp/absl/types/bad_optional_access.cc \
|
||||||
|
./webrtc_dsp/absl/types/optional.cc \
|
||||||
|
./webrtc_dsp/absl/base/internal/raw_logging.cc \
|
||||||
|
./webrtc_dsp/absl/base/internal/throw_delegate.cc \
|
||||||
|
./webrtc_dsp/rtc_base/race_checker.cc \
|
||||||
|
./webrtc_dsp/rtc_base/strings/string_builder.cc \
|
||||||
|
./webrtc_dsp/rtc_base/memory/aligned_malloc.cc \
|
||||||
|
./webrtc_dsp/rtc_base/timeutils.cc \
|
||||||
|
./webrtc_dsp/rtc_base/platform_file.cc \
|
||||||
|
./webrtc_dsp/rtc_base/string_to_number.cc \
|
||||||
|
./webrtc_dsp/rtc_base/thread_checker_impl.cc \
|
||||||
|
./webrtc_dsp/rtc_base/stringencode.cc \
|
||||||
|
./webrtc_dsp/rtc_base/stringutils.cc \
|
||||||
|
./webrtc_dsp/rtc_base/checks.cc \
|
||||||
|
./webrtc_dsp/rtc_base/platform_thread.cc \
|
||||||
|
./webrtc_dsp/rtc_base/criticalsection.cc \
|
||||||
|
./webrtc_dsp/rtc_base/platform_thread_types.cc \
|
||||||
|
./webrtc_dsp/rtc_base/event.cc \
|
||||||
|
./webrtc_dsp/rtc_base/event_tracer.cc \
|
||||||
|
./webrtc_dsp/rtc_base/logging_webrtc.cc \
|
||||||
|
./webrtc_dsp/third_party/rnnoise/src/rnn_vad_weights.cc \
|
||||||
|
./webrtc_dsp/third_party/rnnoise/src/kiss_fft.cc \
|
||||||
|
./webrtc_dsp/api/audio/audio_frame.cc \
|
||||||
|
./webrtc_dsp/api/audio/echo_canceller3_config.cc \
|
||||||
|
./webrtc_dsp/api/audio/echo_canceller3_factory.cc \
|
||||||
|
./webrtc_dsp/modules/third_party/fft/fft.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_estimator.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb16_tables.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_gain_tables.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/arith_routines_logist.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/filterbanks.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/transform.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_filter.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/encode_lpc_swb.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/filter_functions.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/decode.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lattice.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/intialize.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_tables.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_gain_swb_tables.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/bandwidth_estimator.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/encode.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_analysis.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/arith_routines_hist.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/entropy_coding.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/isac_vad.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/arith_routines.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/crc.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb12_tables.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/decode_bwe.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/spectrum_ar_model_tables.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_lag_tables.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/isac.c \
|
||||||
|
./webrtc_dsp/modules/audio_processing/rms_level.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/echo_detector/normalized_covariance_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/echo_detector/moving_max.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/echo_detector/circular_buffer.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/echo_detector/mean_variance_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/splitting_filter.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/gain_control_impl.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/ns/nsx_core.c \
|
||||||
|
./webrtc_dsp/modules/audio_processing/ns/noise_suppression_x.c \
|
||||||
|
./webrtc_dsp/modules/audio_processing/ns/nsx_core_c.c \
|
||||||
|
./webrtc_dsp/modules/audio_processing/ns/ns_core.c \
|
||||||
|
./webrtc_dsp/modules/audio_processing/ns/noise_suppression.c \
|
||||||
|
./webrtc_dsp/modules/audio_processing/audio_buffer.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/typing_detection.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/include/audio_processing_statistics.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/include/audio_generator_factory.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/include/aec_dump.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/include/audio_processing.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/include/config.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/interpolated_gain_curve.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/agc2_common.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/gain_applier.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/adaptive_agc.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/adaptive_digital_gain_applier.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/limiter.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/saturation_protector.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/rnn_vad/rnn.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/rnn_vad/spectral_features.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/rnn_vad/pitch_search.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/rnn_vad/features_extraction.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/rnn_vad/fft_util.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/rnn_vad/lp_residual.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/adaptive_mode_level_estimator_agc.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/vector_float_frame.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/noise_level_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/agc2_testing_common.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/fixed_digital_level_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/fixed_gain_controller.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/vad_with_level.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/limiter_db_gain_curve.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/down_sampler.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/signal_classifier.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/noise_spectrum_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/compute_interpolated_gain_curve.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/biquad_filter.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/adaptive_mode_level_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/transient/moving_moments.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/transient/wpd_tree.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/transient/wpd_node.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/transient/transient_suppressor.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/transient/transient_detector.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/low_cut_filter.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/level_estimator_impl.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/three_band_filter_bank.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec/echo_cancellation.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec/aec_resampler.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec/aec_core.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/voice_detection_impl.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/echo_cancellation_impl.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/gain_control_for_experimental_agc.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc/agc.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc/loudness_histogram.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc/agc_manager_direct.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc/legacy/analog_agc.c \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc/legacy/digital_agc.c \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc/utility.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/audio_processing_impl.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/audio_generator/file_audio_generator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/gain_controller2.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/residual_echo_detector.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/noise_suppression_impl.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aecm/aecm_core.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aecm/aecm_core_c.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aecm/echo_control_mobile.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/render_reverb_model.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/reverb_model_fallback.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/echo_remover_metrics.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/matched_filter_lag_aggregator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/render_delay_buffer2.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/echo_path_variability.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/frame_blocker.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/subtractor.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/aec3_fft.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/fullband_erle_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/suppression_filter.$(CC_NEON) \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/block_processor.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/subband_erle_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/render_delay_controller_metrics.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/render_delay_buffer.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/vector_buffer.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/erl_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/aec_state.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/adaptive_fir_filter.$(CC_NEON) \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/render_delay_controller.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/skew_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/echo_path_delay_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/block_framer.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/erle_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/reverb_model.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/cascaded_biquad_filter.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/render_buffer.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/subtractor_output.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/stationarity_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/render_signal_analyzer.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/subtractor_output_analyzer.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/suppression_gain.$(CC_NEON) \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/echo_audibility.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/block_processor_metrics.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/moving_average.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/reverb_model_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/aec3_common.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/residual_echo_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/matched_filter.$(CC_NEON) \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/reverb_decay_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/render_delay_controller2.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/suppression_gain_limiter.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/main_filter_update_gain.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/echo_remover.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/downsampled_render_buffer.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/matrix_buffer.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/block_processor2.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/echo_canceller3.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/block_delay_buffer.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/fft_buffer.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/comfort_noise_generator.$(CC_NEON) \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/shadow_filter_update_gain.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/filter_analyzer.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/reverb_frequency_response.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/decimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/echo_control_mobile_impl.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/logging/apm_data_dumper.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/vad/voice_activity_detector.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/vad/standalone_vad.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/vad/pitch_internal.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/vad/vad_circular_buffer.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/vad/vad_audio_proc.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/vad/pole_zero_filter.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/vad/pitch_based_vad.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/vad/gmm.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/utility/ooura_fft.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/utility/delay_estimator_wrapper.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/utility/delay_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/utility/block_mean_calculator.cc \
|
||||||
|
./webrtc_dsp/common_audio/window_generator.cc \
|
||||||
|
./webrtc_dsp/common_audio/channel_buffer.cc \
|
||||||
|
./webrtc_dsp/common_audio/fir_filter_factory.cc \
|
||||||
|
./webrtc_dsp/common_audio/wav_header.cc \
|
||||||
|
./webrtc_dsp/common_audio/real_fourier_ooura.cc \
|
||||||
|
./webrtc_dsp/common_audio/audio_util.cc \
|
||||||
|
./webrtc_dsp/common_audio/resampler/push_sinc_resampler.cc \
|
||||||
|
./webrtc_dsp/common_audio/resampler/resampler.cc \
|
||||||
|
./webrtc_dsp/common_audio/resampler/push_resampler.cc \
|
||||||
|
./webrtc_dsp/common_audio/resampler/sinc_resampler.cc \
|
||||||
|
./webrtc_dsp/common_audio/resampler/sinusoidal_linear_chirp_source.cc \
|
||||||
|
./webrtc_dsp/common_audio/wav_file.cc \
|
||||||
|
./webrtc_dsp/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.c \
|
||||||
|
./webrtc_dsp/common_audio/third_party/fft4g/fft4g.c \
|
||||||
|
./webrtc_dsp/common_audio/audio_converter.cc \
|
||||||
|
./webrtc_dsp/common_audio/real_fourier.cc \
|
||||||
|
./webrtc_dsp/common_audio/sparse_fir_filter.cc \
|
||||||
|
./webrtc_dsp/common_audio/smoothing_filter.cc \
|
||||||
|
./webrtc_dsp/common_audio/fir_filter_c.cc \
|
||||||
|
./webrtc_dsp/common_audio/ring_buffer.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/complex_fft.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/filter_ma_fast_q12.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/levinson_durbin.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/dot_product_with_scale.cc \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/auto_corr_to_refl_coef.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/resample_by_2_internal.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/energy.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/sqrt_of_one_minus_x_squared.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/downsample_fast.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/splitting_filter1.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/filter_ar_fast_q12.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/spl_init.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/lpc_to_refl_coef.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/cross_correlation.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/division_operations.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/auto_correlation.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/get_scaling_square.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/resample.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/min_max_operations.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/refl_coef_to_lpc.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/filter_ar.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/vector_scaling_operations.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/resample_fractional.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/real_fft.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/ilbc_specific_functions.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/complex_bit_reverse.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/randomization_functions.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/copy_set_operations.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/resample_by_2.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/get_hanning_window.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/resample_48khz.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/spl_inl.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/spl_sqrt.c \
|
||||||
|
./webrtc_dsp/common_audio/vad/vad_sp.c \
|
||||||
|
./webrtc_dsp/common_audio/vad/vad.cc \
|
||||||
|
./webrtc_dsp/common_audio/vad/webrtc_vad.c \
|
||||||
|
./webrtc_dsp/common_audio/vad/vad_filterbank.c \
|
||||||
|
./webrtc_dsp/common_audio/vad/vad_core.c \
|
||||||
|
./webrtc_dsp/common_audio/vad/vad_gmm.c
|
||||||
|
|
||||||
|
ifeq ($(TARGET_ARCH_ABI),$(filter $(TARGET_ARCH_ABI),armeabi-v7a arm64-v8a))
|
||||||
|
LOCAL_SRC_FILES += \
|
||||||
|
./webrtc_dsp/modules/audio_processing/ns/nsx_core_neon.c.neon \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec/aec_core_neon.cc.neon \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aecm/aecm_core_neon.cc.neon \
|
||||||
|
./webrtc_dsp/modules/audio_processing/utility/ooura_fft_neon.cc.neon \
|
||||||
|
./webrtc_dsp/common_audio/fir_filter_neon.cc.neon \
|
||||||
|
./webrtc_dsp/common_audio/resampler/sinc_resampler_neon.cc.neon \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/downsample_fast_neon.c.neon \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/min_max_operations_neon.c.neon \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/cross_correlation_neon.c.neon
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
|
||||||
|
LOCAL_SRC_FILES += \
|
||||||
|
./webrtc_dsp/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor_arm.S.neon \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/complex_bit_reverse_arm.S.neon \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/filter_ar_fast_q12_armv7.S.neon
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(TARGET_ARCH_ABI),$(filter $(TARGET_ARCH_ABI),x86 x86_64))
|
||||||
|
LOCAL_SRC_FILES += \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec/aec_core_sse2.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/utility/ooura_fft_sse2.cc \
|
||||||
|
./webrtc_dsp/common_audio/fir_filter_sse.cc \
|
||||||
|
./webrtc_dsp/common_audio/resampler/sinc_resampler_sse.cc
|
||||||
|
endif
|
||||||
|
|
||||||
|
include $(BUILD_STATIC_LIBRARY)
|
|
@ -0,0 +1,10 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "BlockingQueue.h"
|
||||||
|
|
||||||
|
using namespace tgvoip;
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LIBTGVOIP_BLOCKINGQUEUE_H
|
||||||
|
#define LIBTGVOIP_BLOCKINGQUEUE_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <list>
|
||||||
|
#include "threading.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace tgvoip{
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class BlockingQueue{
|
||||||
|
public:
|
||||||
|
TGVOIP_DISALLOW_COPY_AND_ASSIGN(BlockingQueue);
|
||||||
|
BlockingQueue(size_t capacity) : semaphore(capacity, 0){
|
||||||
|
this->capacity=capacity;
|
||||||
|
overflowCallback=NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
~BlockingQueue(){
|
||||||
|
semaphore.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Put(T thing){
|
||||||
|
MutexGuard sync(mutex);
|
||||||
|
queue.push_back(std::move(thing));
|
||||||
|
bool didOverflow=false;
|
||||||
|
while(queue.size()>capacity){
|
||||||
|
didOverflow=true;
|
||||||
|
if(overflowCallback){
|
||||||
|
overflowCallback(std::move(queue.front()));
|
||||||
|
queue.pop_front();
|
||||||
|
}else{
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!didOverflow)
|
||||||
|
semaphore.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
T GetBlocking(){
|
||||||
|
semaphore.Acquire();
|
||||||
|
MutexGuard sync(mutex);
|
||||||
|
return GetInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
T Get(){
|
||||||
|
MutexGuard sync(mutex);
|
||||||
|
if(queue.size()>0)
|
||||||
|
semaphore.Acquire();
|
||||||
|
return GetInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int Size(){
|
||||||
|
return queue.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrepareDealloc(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetOverflowCallback(void (*overflowCallback)(T)){
|
||||||
|
this->overflowCallback=overflowCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T GetInternal(){
|
||||||
|
//if(queue.size()==0)
|
||||||
|
// return NULL;
|
||||||
|
T r=std::move(queue.front());
|
||||||
|
queue.pop_front();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
list<T> queue;
|
||||||
|
size_t capacity;
|
||||||
|
//tgvoip_lock_t lock;
|
||||||
|
Semaphore semaphore;
|
||||||
|
Mutex mutex;
|
||||||
|
void (*overflowCallback)(T);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //LIBTGVOIP_BLOCKINGQUEUE_H
|
|
@ -0,0 +1,290 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "Buffers.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <exception>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "logging.h"
|
||||||
|
|
||||||
|
using namespace tgvoip;
|
||||||
|
|
||||||
|
#pragma mark - BufferInputStream
|
||||||
|
|
||||||
|
BufferInputStream::BufferInputStream(const unsigned char* data, size_t length){
|
||||||
|
this->buffer=data;
|
||||||
|
this->length=length;
|
||||||
|
offset=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferInputStream::BufferInputStream(const Buffer &buffer){
|
||||||
|
this->buffer=*buffer;
|
||||||
|
this->length=buffer.Length();
|
||||||
|
offset=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferInputStream::~BufferInputStream(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BufferInputStream::Seek(size_t offset){
|
||||||
|
if(offset>length){
|
||||||
|
throw std::out_of_range("Not enough bytes in buffer");
|
||||||
|
}
|
||||||
|
this->offset=offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t BufferInputStream::GetLength(){
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t BufferInputStream::GetOffset(){
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t BufferInputStream::Remaining(){
|
||||||
|
return length-offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char BufferInputStream::ReadByte(){
|
||||||
|
EnsureEnoughRemaining(1);
|
||||||
|
return (unsigned char)buffer[offset++];
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t BufferInputStream::ReadInt32(){
|
||||||
|
EnsureEnoughRemaining(4);
|
||||||
|
int32_t res=((int32_t)buffer[offset] & 0xFF) |
|
||||||
|
(((int32_t)buffer[offset+1] & 0xFF) << 8) |
|
||||||
|
(((int32_t)buffer[offset+2] & 0xFF) << 16) |
|
||||||
|
(((int32_t)buffer[offset+3] & 0xFF) << 24);
|
||||||
|
offset+=4;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t BufferInputStream::ReadInt64(){
|
||||||
|
EnsureEnoughRemaining(8);
|
||||||
|
int64_t res=((int64_t)buffer[offset] & 0xFF) |
|
||||||
|
(((int64_t)buffer[offset+1] & 0xFF) << 8) |
|
||||||
|
(((int64_t)buffer[offset+2] & 0xFF) << 16) |
|
||||||
|
(((int64_t)buffer[offset+3] & 0xFF) << 24) |
|
||||||
|
(((int64_t)buffer[offset+4] & 0xFF) << 32) |
|
||||||
|
(((int64_t)buffer[offset+5] & 0xFF) << 40) |
|
||||||
|
(((int64_t)buffer[offset+6] & 0xFF) << 48) |
|
||||||
|
(((int64_t)buffer[offset+7] & 0xFF) << 56);
|
||||||
|
offset+=8;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t BufferInputStream::ReadInt16(){
|
||||||
|
EnsureEnoughRemaining(2);
|
||||||
|
int16_t res=(uint16_t)buffer[offset] | ((uint16_t)buffer[offset+1] << 8);
|
||||||
|
offset+=2;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int32_t BufferInputStream::ReadTlLength(){
|
||||||
|
unsigned char l=ReadByte();
|
||||||
|
if(l<254)
|
||||||
|
return l;
|
||||||
|
assert(length-offset>=3);
|
||||||
|
EnsureEnoughRemaining(3);
|
||||||
|
int32_t res=((int32_t)buffer[offset] & 0xFF) |
|
||||||
|
(((int32_t)buffer[offset+1] & 0xFF) << 8) |
|
||||||
|
(((int32_t)buffer[offset+2] & 0xFF) << 16);
|
||||||
|
offset+=3;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BufferInputStream::ReadBytes(unsigned char *to, size_t count){
|
||||||
|
EnsureEnoughRemaining(count);
|
||||||
|
memcpy(to, buffer+offset, count);
|
||||||
|
offset+=count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BufferInputStream::ReadBytes(Buffer &to){
|
||||||
|
ReadBytes(*to, to.Length());
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferInputStream BufferInputStream::GetPartBuffer(size_t length, bool advance){
|
||||||
|
EnsureEnoughRemaining(length);
|
||||||
|
BufferInputStream s=BufferInputStream(buffer+offset, length);
|
||||||
|
if(advance)
|
||||||
|
offset+=length;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BufferInputStream::EnsureEnoughRemaining(size_t need){
|
||||||
|
if(length-offset<need){
|
||||||
|
throw std::out_of_range("Not enough bytes in buffer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - BufferOutputStream
|
||||||
|
|
||||||
|
BufferOutputStream::BufferOutputStream(size_t size){
|
||||||
|
buffer=(unsigned char*) malloc(size);
|
||||||
|
if(!buffer)
|
||||||
|
throw std::bad_alloc();
|
||||||
|
offset=0;
|
||||||
|
this->size=size;
|
||||||
|
bufferProvided=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferOutputStream::BufferOutputStream(unsigned char *buffer, size_t size){
|
||||||
|
this->buffer=buffer;
|
||||||
|
this->size=size;
|
||||||
|
offset=0;
|
||||||
|
bufferProvided=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferOutputStream::~BufferOutputStream(){
|
||||||
|
if(!bufferProvided && buffer)
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BufferOutputStream::WriteByte(unsigned char byte){
|
||||||
|
this->ExpandBufferIfNeeded(1);
|
||||||
|
buffer[offset++]=byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BufferOutputStream::WriteInt32(int32_t i){
|
||||||
|
this->ExpandBufferIfNeeded(4);
|
||||||
|
buffer[offset+3]=(unsigned char)((i >> 24) & 0xFF);
|
||||||
|
buffer[offset+2]=(unsigned char)((i >> 16) & 0xFF);
|
||||||
|
buffer[offset+1]=(unsigned char)((i >> 8) & 0xFF);
|
||||||
|
buffer[offset]=(unsigned char)(i & 0xFF);
|
||||||
|
offset+=4;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BufferOutputStream::WriteInt64(int64_t i){
|
||||||
|
this->ExpandBufferIfNeeded(8);
|
||||||
|
buffer[offset+7]=(unsigned char)((i >> 56) & 0xFF);
|
||||||
|
buffer[offset+6]=(unsigned char)((i >> 48) & 0xFF);
|
||||||
|
buffer[offset+5]=(unsigned char)((i >> 40) & 0xFF);
|
||||||
|
buffer[offset+4]=(unsigned char)((i >> 32) & 0xFF);
|
||||||
|
buffer[offset+3]=(unsigned char)((i >> 24) & 0xFF);
|
||||||
|
buffer[offset+2]=(unsigned char)((i >> 16) & 0xFF);
|
||||||
|
buffer[offset+1]=(unsigned char)((i >> 8) & 0xFF);
|
||||||
|
buffer[offset]=(unsigned char)(i & 0xFF);
|
||||||
|
offset+=8;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BufferOutputStream::WriteInt16(int16_t i){
|
||||||
|
this->ExpandBufferIfNeeded(2);
|
||||||
|
buffer[offset+1]=(unsigned char)((i >> 8) & 0xFF);
|
||||||
|
buffer[offset]=(unsigned char)(i & 0xFF);
|
||||||
|
offset+=2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BufferOutputStream::WriteBytes(const unsigned char *bytes, size_t count){
|
||||||
|
this->ExpandBufferIfNeeded(count);
|
||||||
|
memcpy(buffer+offset, bytes, count);
|
||||||
|
offset+=count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BufferOutputStream::WriteBytes(const Buffer &buffer){
|
||||||
|
WriteBytes(*buffer, buffer.Length());
|
||||||
|
}
|
||||||
|
|
||||||
|
void BufferOutputStream::WriteBytes(const Buffer &buffer, size_t offset, size_t count){
|
||||||
|
if(offset+count>buffer.Length())
|
||||||
|
throw std::out_of_range("offset out of buffer bounds");
|
||||||
|
WriteBytes(*buffer+offset, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *BufferOutputStream::GetBuffer(){
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t BufferOutputStream::GetLength(){
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BufferOutputStream::ExpandBufferIfNeeded(size_t need){
|
||||||
|
if(offset+need>size){
|
||||||
|
if(bufferProvided){
|
||||||
|
throw std::out_of_range("buffer overflow");
|
||||||
|
}
|
||||||
|
if(need<1024){
|
||||||
|
buffer=(unsigned char *) realloc(buffer, size+1024);
|
||||||
|
size+=1024;
|
||||||
|
}else{
|
||||||
|
buffer=(unsigned char *) realloc(buffer, size+need);
|
||||||
|
size+=need;
|
||||||
|
}
|
||||||
|
if(!buffer)
|
||||||
|
throw std::bad_alloc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BufferOutputStream::Reset(){
|
||||||
|
offset=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BufferOutputStream::Rewind(size_t numBytes){
|
||||||
|
if(numBytes>offset)
|
||||||
|
throw std::out_of_range("buffer underflow");
|
||||||
|
offset-=numBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - BufferPool
|
||||||
|
|
||||||
|
BufferPool::BufferPool(unsigned int size, unsigned int count){
|
||||||
|
assert(count<=64);
|
||||||
|
buffers[0]=(unsigned char*) malloc(size*count);
|
||||||
|
bufferCount=count;
|
||||||
|
unsigned int i;
|
||||||
|
for(i=1;i<count;i++){
|
||||||
|
buffers[i]=buffers[0]+i*size;
|
||||||
|
}
|
||||||
|
usedBuffers=0;
|
||||||
|
this->size=size;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferPool::~BufferPool(){
|
||||||
|
free(buffers[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char* BufferPool::Get(){
|
||||||
|
MutexGuard m(mutex);
|
||||||
|
int i;
|
||||||
|
for(i=0;i<bufferCount;i++){
|
||||||
|
if(!((usedBuffers >> i) & 1)){
|
||||||
|
usedBuffers|=(1LL << i);
|
||||||
|
return buffers[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BufferPool::Reuse(unsigned char* buffer){
|
||||||
|
MutexGuard m(mutex);
|
||||||
|
int i;
|
||||||
|
for(i=0;i<bufferCount;i++){
|
||||||
|
if(buffers[i]==buffer){
|
||||||
|
usedBuffers&= ~(1LL << i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOGE("pointer passed isn't a valid buffer from this pool");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t BufferPool::GetSingleBufferSize(){
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t BufferPool::GetBufferCount(){
|
||||||
|
return (size_t) bufferCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Buffer
|
||||||
|
|
|
@ -0,0 +1,278 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LIBTGVOIP_BUFFERINPUTSTREAM_H
|
||||||
|
#define LIBTGVOIP_BUFFERINPUTSTREAM_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <array>
|
||||||
|
#include <limits>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "threading.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
namespace tgvoip{
|
||||||
|
class Buffer;
|
||||||
|
|
||||||
|
class BufferInputStream{
|
||||||
|
|
||||||
|
public:
|
||||||
|
BufferInputStream(const unsigned char* data, size_t length);
|
||||||
|
BufferInputStream(const Buffer& buffer);
|
||||||
|
~BufferInputStream();
|
||||||
|
void Seek(size_t offset);
|
||||||
|
size_t GetLength();
|
||||||
|
size_t GetOffset();
|
||||||
|
size_t Remaining();
|
||||||
|
unsigned char ReadByte();
|
||||||
|
int64_t ReadInt64();
|
||||||
|
int32_t ReadInt32();
|
||||||
|
int16_t ReadInt16();
|
||||||
|
int32_t ReadTlLength();
|
||||||
|
void ReadBytes(unsigned char* to, size_t count);
|
||||||
|
void ReadBytes(Buffer& to);
|
||||||
|
BufferInputStream GetPartBuffer(size_t length, bool advance);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void EnsureEnoughRemaining(size_t need);
|
||||||
|
const unsigned char* buffer;
|
||||||
|
size_t length;
|
||||||
|
size_t offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BufferOutputStream{
|
||||||
|
friend class Buffer;
|
||||||
|
public:
|
||||||
|
TGVOIP_DISALLOW_COPY_AND_ASSIGN(BufferOutputStream);
|
||||||
|
BufferOutputStream(size_t size);
|
||||||
|
BufferOutputStream(unsigned char* buffer, size_t size);
|
||||||
|
~BufferOutputStream();
|
||||||
|
void WriteByte(unsigned char byte);
|
||||||
|
void WriteInt64(int64_t i);
|
||||||
|
void WriteInt32(int32_t i);
|
||||||
|
void WriteInt16(int16_t i);
|
||||||
|
void WriteBytes(const unsigned char* bytes, size_t count);
|
||||||
|
void WriteBytes(const Buffer& buffer);
|
||||||
|
void WriteBytes(const Buffer& buffer, size_t offset, size_t count);
|
||||||
|
unsigned char* GetBuffer();
|
||||||
|
size_t GetLength();
|
||||||
|
void Reset();
|
||||||
|
void Rewind(size_t numBytes);
|
||||||
|
|
||||||
|
BufferOutputStream& operator=(BufferOutputStream&& other){
|
||||||
|
if(this!=&other){
|
||||||
|
if(!bufferProvided && buffer)
|
||||||
|
free(buffer);
|
||||||
|
buffer=other.buffer;
|
||||||
|
offset=other.offset;
|
||||||
|
size=other.size;
|
||||||
|
bufferProvided=other.bufferProvided;
|
||||||
|
other.buffer=NULL;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void ExpandBufferIfNeeded(size_t need);
|
||||||
|
unsigned char* buffer=NULL;
|
||||||
|
size_t size;
|
||||||
|
size_t offset;
|
||||||
|
bool bufferProvided;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BufferPool{
|
||||||
|
public:
|
||||||
|
TGVOIP_DISALLOW_COPY_AND_ASSIGN(BufferPool);
|
||||||
|
BufferPool(unsigned int size, unsigned int count);
|
||||||
|
~BufferPool();
|
||||||
|
unsigned char* Get();
|
||||||
|
void Reuse(unsigned char* buffer);
|
||||||
|
size_t GetSingleBufferSize();
|
||||||
|
size_t GetBufferCount();
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint64_t usedBuffers;
|
||||||
|
int bufferCount;
|
||||||
|
size_t size;
|
||||||
|
unsigned char* buffers[64];
|
||||||
|
Mutex mutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Buffer{
|
||||||
|
public:
|
||||||
|
Buffer(size_t capacity){
|
||||||
|
if(capacity>0)
|
||||||
|
data=(unsigned char *) malloc(capacity);
|
||||||
|
else
|
||||||
|
data=NULL;
|
||||||
|
length=capacity;
|
||||||
|
};
|
||||||
|
TGVOIP_DISALLOW_COPY_AND_ASSIGN(Buffer); // use Buffer::CopyOf to copy contents explicitly
|
||||||
|
Buffer(Buffer&& other) noexcept {
|
||||||
|
data=other.data;
|
||||||
|
length=other.length;
|
||||||
|
other.data=NULL;
|
||||||
|
};
|
||||||
|
Buffer(BufferOutputStream&& stream){
|
||||||
|
data=stream.buffer;
|
||||||
|
length=stream.offset;
|
||||||
|
stream.buffer=NULL;
|
||||||
|
}
|
||||||
|
Buffer(){
|
||||||
|
data=NULL;
|
||||||
|
length=0;
|
||||||
|
}
|
||||||
|
~Buffer(){
|
||||||
|
if(data)
|
||||||
|
free(data);
|
||||||
|
data=NULL;
|
||||||
|
};
|
||||||
|
Buffer& operator=(Buffer&& other){
|
||||||
|
if(this!=&other){
|
||||||
|
if(data)
|
||||||
|
free(data);
|
||||||
|
data=other.data;
|
||||||
|
length=other.length;
|
||||||
|
other.data=NULL;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
unsigned char& operator[](size_t i){
|
||||||
|
if(i>=length)
|
||||||
|
throw std::out_of_range("");
|
||||||
|
return data[i];
|
||||||
|
}
|
||||||
|
const unsigned char& operator[](size_t i) const{
|
||||||
|
if(i>=length)
|
||||||
|
throw std::out_of_range("");
|
||||||
|
return data[i];
|
||||||
|
}
|
||||||
|
unsigned char* operator*(){
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
const unsigned char* operator*() const{
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
void CopyFrom(const Buffer& other, size_t count, size_t srcOffset=0, size_t dstOffset=0){
|
||||||
|
if(!other.data)
|
||||||
|
throw std::invalid_argument("CopyFrom can't copy from NULL");
|
||||||
|
if(other.length<srcOffset+count || length<dstOffset+count)
|
||||||
|
throw std::out_of_range("Out of offset+count bounds of either buffer");
|
||||||
|
memcpy(data+dstOffset, other.data+srcOffset, count);
|
||||||
|
}
|
||||||
|
void CopyFrom(const void* ptr, size_t dstOffset, size_t count){
|
||||||
|
if(length<dstOffset+count)
|
||||||
|
throw std::out_of_range("Offset+count is out of bounds");
|
||||||
|
memcpy(data+dstOffset, ptr, count);
|
||||||
|
}
|
||||||
|
void Resize(size_t newSize){
|
||||||
|
data=(unsigned char *) realloc(data, newSize);
|
||||||
|
length=newSize;
|
||||||
|
}
|
||||||
|
size_t Length() const{
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
bool IsEmpty() const{
|
||||||
|
return length==0;
|
||||||
|
}
|
||||||
|
static Buffer CopyOf(const Buffer& other){
|
||||||
|
Buffer buf(other.length);
|
||||||
|
buf.CopyFrom(other, other.length);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
unsigned char* data;
|
||||||
|
size_t length;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, size_t size, typename AVG_T=T> class HistoricBuffer{
|
||||||
|
public:
|
||||||
|
HistoricBuffer(){
|
||||||
|
std::fill(data.begin(), data.end(), (T)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
AVG_T Average(){
|
||||||
|
AVG_T avg=(AVG_T)0;
|
||||||
|
for(T& i:data){
|
||||||
|
avg+=i;
|
||||||
|
}
|
||||||
|
return avg/(AVG_T)size;
|
||||||
|
}
|
||||||
|
|
||||||
|
AVG_T Average(size_t firstN){
|
||||||
|
AVG_T avg=(AVG_T)0;
|
||||||
|
for(size_t i=0;i<firstN;i++){
|
||||||
|
avg+=(*this)[i];
|
||||||
|
}
|
||||||
|
return avg/(AVG_T)firstN;
|
||||||
|
}
|
||||||
|
|
||||||
|
AVG_T NonZeroAverage(){
|
||||||
|
AVG_T avg=(AVG_T)0;
|
||||||
|
int nonZeroCount=0;
|
||||||
|
for(T& i:data){
|
||||||
|
if(i!=0){
|
||||||
|
nonZeroCount++;
|
||||||
|
avg+=i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(nonZeroCount==0)
|
||||||
|
return (AVG_T)0;
|
||||||
|
return avg/(AVG_T)nonZeroCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Add(T el){
|
||||||
|
data[offset]=el;
|
||||||
|
offset=(offset+1)%size;
|
||||||
|
}
|
||||||
|
|
||||||
|
T Min(){
|
||||||
|
T min=std::numeric_limits<T>::max();
|
||||||
|
for(T& i:data){
|
||||||
|
if(i<min)
|
||||||
|
min=i;
|
||||||
|
}
|
||||||
|
return min;
|
||||||
|
}
|
||||||
|
|
||||||
|
T Max(){
|
||||||
|
T max=std::numeric_limits<T>::min();
|
||||||
|
for(T& i:data){
|
||||||
|
if(i>max)
|
||||||
|
max=i;
|
||||||
|
}
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reset(){
|
||||||
|
std::fill(data.begin(), data.end(), (T)0);
|
||||||
|
offset=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
T& operator[](size_t i){
|
||||||
|
assert(i<size);
|
||||||
|
// [0] should return the most recent entry, [1] the one before it, and so on
|
||||||
|
ptrdiff_t _i=offset-i-1;
|
||||||
|
if(_i<0)
|
||||||
|
_i=size+_i;
|
||||||
|
return data[_i];
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Size(){
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::array<T, size> data;
|
||||||
|
ptrdiff_t offset=0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //LIBTGVOIP_BUFFERINPUTSTREAM_H
|
|
@ -0,0 +1,142 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "CongestionControl.h"
|
||||||
|
#include "VoIPController.h"
|
||||||
|
#include "logging.h"
|
||||||
|
#include "VoIPServerConfig.h"
|
||||||
|
#include "PrivateDefines.h"
|
||||||
|
#include <math.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
using namespace tgvoip;
|
||||||
|
|
||||||
|
CongestionControl::CongestionControl(){
|
||||||
|
memset(inflightPackets, 0, sizeof(inflightPackets));
|
||||||
|
tmpRtt=0;
|
||||||
|
tmpRttCount=0;
|
||||||
|
lastSentSeq=0;
|
||||||
|
lastActionTime=0;
|
||||||
|
lastActionRtt=0;
|
||||||
|
stateTransitionTime=0;
|
||||||
|
inflightDataSize=0;
|
||||||
|
lossCount=0;
|
||||||
|
cwnd=(size_t) ServerConfig::GetSharedInstance()->GetInt("audio_congestion_window", 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
CongestionControl::~CongestionControl(){
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t CongestionControl::GetAcknowledgedDataSize(){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double CongestionControl::GetAverageRTT(){
|
||||||
|
return rttHistory.NonZeroAverage();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t CongestionControl::GetInflightDataSize(){
|
||||||
|
return inflightHistory.Average();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t CongestionControl::GetCongestionWindow(){
|
||||||
|
return cwnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
double CongestionControl::GetMinimumRTT(){
|
||||||
|
return rttHistory.Min();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CongestionControl::PacketAcknowledged(uint32_t seq){
|
||||||
|
MutexGuard sync(mutex);
|
||||||
|
int i;
|
||||||
|
for(i=0;i<100;i++){
|
||||||
|
if(inflightPackets[i].seq==seq && inflightPackets[i].sendTime>0){
|
||||||
|
tmpRtt+=(VoIPController::GetCurrentTime()-inflightPackets[i].sendTime);
|
||||||
|
tmpRttCount++;
|
||||||
|
inflightPackets[i].sendTime=0;
|
||||||
|
inflightDataSize-=inflightPackets[i].size;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CongestionControl::PacketSent(uint32_t seq, size_t size){
|
||||||
|
if(!seqgt(seq, lastSentSeq) || seq==lastSentSeq){
|
||||||
|
LOGW("Duplicate outgoing seq %u", seq);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lastSentSeq=seq;
|
||||||
|
MutexGuard sync(mutex);
|
||||||
|
double smallestSendTime=INFINITY;
|
||||||
|
tgvoip_congestionctl_packet_t* slot=NULL;
|
||||||
|
int i;
|
||||||
|
for(i=0;i<100;i++){
|
||||||
|
if(inflightPackets[i].sendTime==0){
|
||||||
|
slot=&inflightPackets[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(smallestSendTime>inflightPackets[i].sendTime){
|
||||||
|
slot=&inflightPackets[i];
|
||||||
|
smallestSendTime=slot->sendTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(slot!=NULL);
|
||||||
|
if(slot->sendTime>0){
|
||||||
|
inflightDataSize-=slot->size;
|
||||||
|
lossCount++;
|
||||||
|
LOGD("Packet with seq %u was not acknowledged", slot->seq);
|
||||||
|
}
|
||||||
|
slot->seq=seq;
|
||||||
|
slot->size=size;
|
||||||
|
slot->sendTime=VoIPController::GetCurrentTime();
|
||||||
|
inflightDataSize+=size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CongestionControl::Tick(){
|
||||||
|
tickCount++;
|
||||||
|
MutexGuard sync(mutex);
|
||||||
|
if(tmpRttCount>0){
|
||||||
|
rttHistory.Add(tmpRtt/tmpRttCount);
|
||||||
|
tmpRtt=0;
|
||||||
|
tmpRttCount=0;
|
||||||
|
}
|
||||||
|
int i;
|
||||||
|
for(i=0;i<100;i++){
|
||||||
|
if(inflightPackets[i].sendTime!=0 && VoIPController::GetCurrentTime()-inflightPackets[i].sendTime>2){
|
||||||
|
inflightPackets[i].sendTime=0;
|
||||||
|
inflightDataSize-=inflightPackets[i].size;
|
||||||
|
lossCount++;
|
||||||
|
LOGD("Packet with seq %u was not acknowledged", inflightPackets[i].seq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inflightHistory.Add(inflightDataSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int CongestionControl::GetBandwidthControlAction(){
|
||||||
|
if(VoIPController::GetCurrentTime()-lastActionTime<1)
|
||||||
|
return TGVOIP_CONCTL_ACT_NONE;
|
||||||
|
size_t inflightAvg=GetInflightDataSize();
|
||||||
|
size_t max=cwnd+cwnd/10;
|
||||||
|
size_t min=cwnd-cwnd/10;
|
||||||
|
if(inflightAvg<min){
|
||||||
|
lastActionTime=VoIPController::GetCurrentTime();
|
||||||
|
return TGVOIP_CONCTL_ACT_INCREASE;
|
||||||
|
}
|
||||||
|
if(inflightAvg>max){
|
||||||
|
lastActionTime=VoIPController::GetCurrentTime();
|
||||||
|
return TGVOIP_CONCTL_ACT_DECREASE;
|
||||||
|
}
|
||||||
|
return TGVOIP_CONCTL_ACT_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t CongestionControl::GetSendLossCount(){
|
||||||
|
return lossCount;
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LIBTGVOIP_CONGESTIONCONTROL_H
|
||||||
|
#define LIBTGVOIP_CONGESTIONCONTROL_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "threading.h"
|
||||||
|
#include "Buffers.h"
|
||||||
|
|
||||||
|
#define TGVOIP_CONCTL_ACT_INCREASE 1
|
||||||
|
#define TGVOIP_CONCTL_ACT_DECREASE 2
|
||||||
|
#define TGVOIP_CONCTL_ACT_NONE 0
|
||||||
|
|
||||||
|
namespace tgvoip{
|
||||||
|
|
||||||
|
struct tgvoip_congestionctl_packet_t{
|
||||||
|
uint32_t seq;
|
||||||
|
double sendTime;
|
||||||
|
size_t size;
|
||||||
|
};
|
||||||
|
typedef struct tgvoip_congestionctl_packet_t tgvoip_congestionctl_packet_t;
|
||||||
|
|
||||||
|
class CongestionControl{
|
||||||
|
public:
|
||||||
|
CongestionControl();
|
||||||
|
~CongestionControl();
|
||||||
|
|
||||||
|
void PacketSent(uint32_t seq, size_t size);
|
||||||
|
void PacketAcknowledged(uint32_t seq);
|
||||||
|
|
||||||
|
double GetAverageRTT();
|
||||||
|
double GetMinimumRTT();
|
||||||
|
size_t GetInflightDataSize();
|
||||||
|
size_t GetCongestionWindow();
|
||||||
|
size_t GetAcknowledgedDataSize();
|
||||||
|
void Tick();
|
||||||
|
int GetBandwidthControlAction();
|
||||||
|
uint32_t GetSendLossCount();
|
||||||
|
|
||||||
|
private:
|
||||||
|
HistoricBuffer<double, 100> rttHistory;
|
||||||
|
HistoricBuffer<size_t, 30> inflightHistory;
|
||||||
|
tgvoip_congestionctl_packet_t inflightPackets[100];
|
||||||
|
uint32_t lossCount;
|
||||||
|
double tmpRtt;
|
||||||
|
double lastActionTime;
|
||||||
|
double lastActionRtt;
|
||||||
|
double stateTransitionTime;
|
||||||
|
int tmpRttCount;
|
||||||
|
uint32_t lastSentSeq;
|
||||||
|
uint32_t tickCount;
|
||||||
|
size_t inflightDataSize;
|
||||||
|
size_t cwnd;
|
||||||
|
Mutex mutex;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //LIBTGVOIP_CONGESTIONCONTROL_H
|
|
@ -0,0 +1,240 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef TGVOIP_NO_DSP
|
||||||
|
#include "webrtc_dsp/modules/audio_processing/include/audio_processing.h"
|
||||||
|
#include "webrtc_dsp/api/audio/audio_frame.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "EchoCanceller.h"
|
||||||
|
#include "audio/AudioOutput.h"
|
||||||
|
#include "audio/AudioInput.h"
|
||||||
|
#include "logging.h"
|
||||||
|
#include "VoIPServerConfig.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
using namespace tgvoip;
|
||||||
|
|
||||||
|
EchoCanceller::EchoCanceller(bool enableAEC, bool enableNS, bool enableAGC){
|
||||||
|
#ifndef TGVOIP_NO_DSP
|
||||||
|
this->enableAEC=enableAEC;
|
||||||
|
this->enableAGC=enableAGC;
|
||||||
|
this->enableNS=enableNS;
|
||||||
|
isOn=true;
|
||||||
|
|
||||||
|
webrtc::Config extraConfig;
|
||||||
|
#ifdef TGVOIP_USE_DESKTOP_DSP
|
||||||
|
extraConfig.Set(new webrtc::DelayAgnostic(true));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
apm=webrtc::AudioProcessingBuilder().Create(extraConfig);
|
||||||
|
|
||||||
|
webrtc::AudioProcessing::Config config;
|
||||||
|
config.echo_canceller.enabled = enableAEC;
|
||||||
|
#ifndef TGVOIP_USE_DESKTOP_DSP
|
||||||
|
config.echo_canceller.mobile_mode = true;
|
||||||
|
#else
|
||||||
|
config.echo_canceller.mobile_mode = false;
|
||||||
|
#endif
|
||||||
|
config.high_pass_filter.enabled = enableAEC;
|
||||||
|
config.gain_controller2.enabled = enableAGC;
|
||||||
|
apm->ApplyConfig(config);
|
||||||
|
|
||||||
|
webrtc::NoiseSuppression::Level nsLevel;
|
||||||
|
#ifdef __APPLE__
|
||||||
|
switch(ServerConfig::GetSharedInstance()->GetInt("webrtc_ns_level_vpio", 0)){
|
||||||
|
#else
|
||||||
|
switch(ServerConfig::GetSharedInstance()->GetInt("webrtc_ns_level", 2)){
|
||||||
|
#endif
|
||||||
|
case 0:
|
||||||
|
nsLevel=webrtc::NoiseSuppression::Level::kLow;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
nsLevel=webrtc::NoiseSuppression::Level::kModerate;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
nsLevel=webrtc::NoiseSuppression::Level::kVeryHigh;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
default:
|
||||||
|
nsLevel=webrtc::NoiseSuppression::Level::kHigh;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
apm->noise_suppression()->set_level(nsLevel);
|
||||||
|
apm->noise_suppression()->Enable(enableNS);
|
||||||
|
if(enableAGC){
|
||||||
|
apm->gain_control()->set_mode(webrtc::GainControl::Mode::kAdaptiveDigital);
|
||||||
|
apm->gain_control()->set_target_level_dbfs(ServerConfig::GetSharedInstance()->GetInt("webrtc_agc_target_level", 9));
|
||||||
|
apm->gain_control()->enable_limiter(ServerConfig::GetSharedInstance()->GetBoolean("webrtc_agc_enable_limiter", true));
|
||||||
|
apm->gain_control()->set_compression_gain_db(ServerConfig::GetSharedInstance()->GetInt("webrtc_agc_compression_gain", 20));
|
||||||
|
}
|
||||||
|
apm->voice_detection()->set_likelihood(webrtc::VoiceDetection::Likelihood::kVeryLowLikelihood);
|
||||||
|
|
||||||
|
audioFrame=new webrtc::AudioFrame();
|
||||||
|
audioFrame->samples_per_channel_=480;
|
||||||
|
audioFrame->sample_rate_hz_=48000;
|
||||||
|
audioFrame->num_channels_=1;
|
||||||
|
|
||||||
|
farendQueue=new BlockingQueue<int16_t*>(11);
|
||||||
|
farendBufferPool=new BufferPool(960*2, 10);
|
||||||
|
running=true;
|
||||||
|
bufferFarendThread=new Thread(std::bind(&EchoCanceller::RunBufferFarendThread, this));
|
||||||
|
bufferFarendThread->Start();
|
||||||
|
|
||||||
|
#else
|
||||||
|
this->enableAEC=this->enableAGC=enableAGC=this->enableNS=enableNS=false;
|
||||||
|
isOn=true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
EchoCanceller::~EchoCanceller(){
|
||||||
|
#ifndef TGVOIP_NO_DSP
|
||||||
|
delete apm;
|
||||||
|
delete audioFrame;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void EchoCanceller::Start(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void EchoCanceller::Stop(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void EchoCanceller::SpeakerOutCallback(unsigned char* data, size_t len){
|
||||||
|
if(len!=960*2 || !enableAEC || !isOn)
|
||||||
|
return;
|
||||||
|
#ifndef TGVOIP_NO_DSP
|
||||||
|
int16_t* buf=(int16_t*)farendBufferPool->Get();
|
||||||
|
if(buf){
|
||||||
|
memcpy(buf, data, 960*2);
|
||||||
|
farendQueue->Put(buf);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef TGVOIP_NO_DSP
|
||||||
|
void EchoCanceller::RunBufferFarendThread(){
|
||||||
|
webrtc::AudioFrame frame;
|
||||||
|
frame.num_channels_=1;
|
||||||
|
frame.sample_rate_hz_=48000;
|
||||||
|
frame.samples_per_channel_=480;
|
||||||
|
while(running){
|
||||||
|
int16_t* samplesIn=farendQueue->GetBlocking();
|
||||||
|
if(samplesIn){
|
||||||
|
memcpy(frame.mutable_data(), samplesIn, 480*2);
|
||||||
|
apm->ProcessReverseStream(&frame);
|
||||||
|
memcpy(frame.mutable_data(), samplesIn+480, 480*2);
|
||||||
|
apm->ProcessReverseStream(&frame);
|
||||||
|
didBufferFarend=true;
|
||||||
|
farendBufferPool->Reuse(reinterpret_cast<unsigned char*>(samplesIn));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void EchoCanceller::Enable(bool enabled){
|
||||||
|
isOn=enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EchoCanceller::ProcessInput(int16_t* inOut, size_t numSamples, bool& hasVoice){
|
||||||
|
#ifndef TGVOIP_NO_DSP
|
||||||
|
if(!isOn || (!enableAEC && !enableAGC && !enableNS)){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int delay=audio::AudioInput::GetEstimatedDelay()+audio::AudioOutput::GetEstimatedDelay();
|
||||||
|
assert(numSamples==960);
|
||||||
|
|
||||||
|
memcpy(audioFrame->mutable_data(), inOut, 480*2);
|
||||||
|
if(enableAEC)
|
||||||
|
apm->set_stream_delay_ms(delay);
|
||||||
|
apm->ProcessStream(audioFrame);
|
||||||
|
if(enableVAD)
|
||||||
|
hasVoice=apm->voice_detection()->stream_has_voice();
|
||||||
|
memcpy(inOut, audioFrame->data(), 480*2);
|
||||||
|
memcpy(audioFrame->mutable_data(), inOut+480, 480*2);
|
||||||
|
if(enableAEC)
|
||||||
|
apm->set_stream_delay_ms(delay);
|
||||||
|
apm->ProcessStream(audioFrame);
|
||||||
|
if(enableVAD){
|
||||||
|
hasVoice=hasVoice || apm->voice_detection()->stream_has_voice();
|
||||||
|
}
|
||||||
|
memcpy(inOut+480, audioFrame->data(), 480*2);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void EchoCanceller::SetAECStrength(int strength){
|
||||||
|
#ifndef TGVOIP_NO_DSP
|
||||||
|
/*if(aec){
|
||||||
|
#ifndef TGVOIP_USE_DESKTOP_DSP
|
||||||
|
AecmConfig cfg;
|
||||||
|
cfg.cngMode=AecmFalse;
|
||||||
|
cfg.echoMode=(int16_t) strength;
|
||||||
|
WebRtcAecm_set_config(aec, cfg);
|
||||||
|
#endif
|
||||||
|
}*/
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void EchoCanceller::SetVoiceDetectionEnabled(bool enabled){
|
||||||
|
enableVAD=enabled;
|
||||||
|
#ifndef TGVOIP_NO_DSP
|
||||||
|
apm->voice_detection()->Enable(enabled);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace tgvoip::effects;
|
||||||
|
|
||||||
|
AudioEffect::~AudioEffect(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioEffect::SetPassThrough(bool passThrough){
|
||||||
|
this->passThrough=passThrough;
|
||||||
|
}
|
||||||
|
|
||||||
|
Volume::Volume(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Volume::~Volume(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Volume::Process(int16_t* inOut, size_t numSamples){
|
||||||
|
if(level==1.0f || passThrough){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for(size_t i=0;i<numSamples;i++){
|
||||||
|
float sample=(float)inOut[i]*multiplier;
|
||||||
|
if(sample>32767.0f)
|
||||||
|
inOut[i]=INT16_MAX;
|
||||||
|
else if(sample<-32768.0f)
|
||||||
|
inOut[i]=INT16_MIN;
|
||||||
|
else
|
||||||
|
inOut[i]=(int16_t)sample;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Volume::SetLevel(float level){
|
||||||
|
this->level=level;
|
||||||
|
float db;
|
||||||
|
if(level<1.0f)
|
||||||
|
db=-50.0f*(1.0f-level);
|
||||||
|
else if(level>1.0f && level<=2.0f)
|
||||||
|
db=10.0f*(level-1.0f);
|
||||||
|
else
|
||||||
|
db=0.0f;
|
||||||
|
multiplier=expf(db/20.0f * logf(10.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
float Volume::GetLevel(){
|
||||||
|
return level;
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LIBTGVOIP_ECHOCANCELLER_H
|
||||||
|
#define LIBTGVOIP_ECHOCANCELLER_H
|
||||||
|
|
||||||
|
#include "threading.h"
|
||||||
|
#include "Buffers.h"
|
||||||
|
#include "BlockingQueue.h"
|
||||||
|
#include "MediaStreamItf.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
namespace webrtc{
|
||||||
|
class AudioProcessing;
|
||||||
|
class AudioFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace tgvoip{
|
||||||
|
class EchoCanceller{
|
||||||
|
|
||||||
|
public:
|
||||||
|
TGVOIP_DISALLOW_COPY_AND_ASSIGN(EchoCanceller);
|
||||||
|
EchoCanceller(bool enableAEC, bool enableNS, bool enableAGC);
|
||||||
|
virtual ~EchoCanceller();
|
||||||
|
virtual void Start();
|
||||||
|
virtual void Stop();
|
||||||
|
void SpeakerOutCallback(unsigned char* data, size_t len);
|
||||||
|
void Enable(bool enabled);
|
||||||
|
void ProcessInput(int16_t* inOut, size_t numSamples, bool& hasVoice);
|
||||||
|
void SetAECStrength(int strength);
|
||||||
|
void SetVoiceDetectionEnabled(bool enabled);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool enableAEC;
|
||||||
|
bool enableAGC;
|
||||||
|
bool enableNS;
|
||||||
|
bool enableVAD=false;
|
||||||
|
bool isOn;
|
||||||
|
#ifndef TGVOIP_NO_DSP
|
||||||
|
webrtc::AudioProcessing* apm=NULL;
|
||||||
|
webrtc::AudioFrame* audioFrame=NULL;
|
||||||
|
void RunBufferFarendThread();
|
||||||
|
bool didBufferFarend;
|
||||||
|
Thread* bufferFarendThread;
|
||||||
|
BlockingQueue<int16_t*>* farendQueue;
|
||||||
|
BufferPool* farendBufferPool;
|
||||||
|
bool running;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace effects{
|
||||||
|
|
||||||
|
class AudioEffect{
|
||||||
|
public:
|
||||||
|
virtual ~AudioEffect()=0;
|
||||||
|
virtual void Process(int16_t* inOut, size_t numSamples)=0;
|
||||||
|
virtual void SetPassThrough(bool passThrough);
|
||||||
|
protected:
|
||||||
|
bool passThrough=false;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Volume : public AudioEffect{
|
||||||
|
public:
|
||||||
|
Volume();
|
||||||
|
virtual ~Volume();
|
||||||
|
virtual void Process(int16_t* inOut, size_t numSamples);
|
||||||
|
/**
|
||||||
|
* Level is (0.0, 2.0]
|
||||||
|
*/
|
||||||
|
void SetLevel(float level);
|
||||||
|
float GetLevel();
|
||||||
|
private:
|
||||||
|
float level=1.0f;
|
||||||
|
float multiplier=1.0f;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //LIBTGVOIP_ECHOCANCELLER_H
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>FMWK</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
|
<key>NSPrincipalClass</key>
|
||||||
|
<string></string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -0,0 +1,463 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "VoIPController.h"
|
||||||
|
#include "JitterBuffer.h"
|
||||||
|
#include "logging.h"
|
||||||
|
#include "VoIPServerConfig.h"
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
using namespace tgvoip;
|
||||||
|
|
||||||
|
JitterBuffer::JitterBuffer(MediaStreamItf *out, uint32_t step):bufferPool(JITTER_SLOT_SIZE, JITTER_SLOT_COUNT){
|
||||||
|
if(out)
|
||||||
|
out->SetCallback(JitterBuffer::CallbackOut, this);
|
||||||
|
this->step=step;
|
||||||
|
memset(slots, 0, sizeof(jitter_packet_t)*JITTER_SLOT_COUNT);
|
||||||
|
if(step<30){
|
||||||
|
minMinDelay=(uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_min_delay_20", 6);
|
||||||
|
maxMinDelay=(uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_max_delay_20", 25);
|
||||||
|
maxUsedSlots=(uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_max_slots_20", 50);
|
||||||
|
}else if(step<50){
|
||||||
|
minMinDelay=(uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_min_delay_40", 4);
|
||||||
|
maxMinDelay=(uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_max_delay_40", 15);
|
||||||
|
maxUsedSlots=(uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_max_slots_40", 30);
|
||||||
|
}else{
|
||||||
|
minMinDelay=(uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_min_delay_60", 2);
|
||||||
|
maxMinDelay=(uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_max_delay_60", 10);
|
||||||
|
maxUsedSlots=(uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_max_slots_60", 20);
|
||||||
|
}
|
||||||
|
lossesToReset=(uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_losses_to_reset", 20);
|
||||||
|
resyncThreshold=ServerConfig::GetSharedInstance()->GetDouble("jitter_resync_threshold", 1.0);
|
||||||
|
#ifdef TGVOIP_DUMP_JITTER_STATS
|
||||||
|
#ifdef TGVOIP_JITTER_DUMP_FILE
|
||||||
|
dump=fopen(TGVOIP_JITTER_DUMP_FILE, "w");
|
||||||
|
#elif defined(__ANDROID__)
|
||||||
|
dump=fopen("/sdcard/tgvoip_jitter_dump.txt", "w");
|
||||||
|
#else
|
||||||
|
dump=fopen("tgvoip_jitter_dump.txt", "w");
|
||||||
|
#endif
|
||||||
|
tgvoip_log_file_write_header(dump);
|
||||||
|
fprintf(dump, "PTS\tRTS\tNumInBuf\tAJitter\tADelay\tTDelay\n");
|
||||||
|
#endif
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
JitterBuffer::~JitterBuffer(){
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void JitterBuffer::SetMinPacketCount(uint32_t count){
|
||||||
|
LOGI("jitter: set min packet count %u", count);
|
||||||
|
minDelay=count;
|
||||||
|
minMinDelay=count;
|
||||||
|
//Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
int JitterBuffer::GetMinPacketCount(){
|
||||||
|
return (int)minDelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t JitterBuffer::CallbackIn(unsigned char *data, size_t len, void *param){
|
||||||
|
//((JitterBuffer*)param)->HandleInput(data, len);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t JitterBuffer::CallbackOut(unsigned char *data, size_t len, void *param){
|
||||||
|
return 0; //((JitterBuffer*)param)->HandleOutput(data, len, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JitterBuffer::HandleInput(unsigned char *data, size_t len, uint32_t timestamp, bool isEC){
|
||||||
|
MutexGuard m(mutex);
|
||||||
|
jitter_packet_t pkt;
|
||||||
|
pkt.size=len;
|
||||||
|
pkt.buffer=data;
|
||||||
|
pkt.timestamp=timestamp;
|
||||||
|
pkt.isEC=isEC;
|
||||||
|
PutInternal(&pkt, !isEC);
|
||||||
|
//LOGV("in, ts=%d, ec=%d", timestamp, isEC);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void JitterBuffer::Reset(){
|
||||||
|
wasReset=true;
|
||||||
|
needBuffering=true;
|
||||||
|
lastPutTimestamp=0;
|
||||||
|
int i;
|
||||||
|
for(i=0;i<JITTER_SLOT_COUNT;i++){
|
||||||
|
if(slots[i].buffer){
|
||||||
|
bufferPool.Reuse(slots[i].buffer);
|
||||||
|
slots[i].buffer=NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delayHistory.Reset();
|
||||||
|
lateHistory.Reset();
|
||||||
|
adjustingDelay=false;
|
||||||
|
lostSinceReset=0;
|
||||||
|
gotSinceReset=0;
|
||||||
|
expectNextAtTime=0;
|
||||||
|
deviationHistory.Reset();
|
||||||
|
outstandingDelayChange=0;
|
||||||
|
dontChangeDelay=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t JitterBuffer::HandleOutput(unsigned char *buffer, size_t len, int offsetInSteps, bool advance, int& playbackScaledDuration, bool& isEC){
|
||||||
|
jitter_packet_t pkt;
|
||||||
|
pkt.buffer=buffer;
|
||||||
|
pkt.size=len;
|
||||||
|
MutexGuard m(mutex);
|
||||||
|
if(first){
|
||||||
|
first=false;
|
||||||
|
unsigned int delay=GetCurrentDelay();
|
||||||
|
if(GetCurrentDelay()>5){
|
||||||
|
LOGW("jitter: delay too big upon start (%u), dropping packets", delay);
|
||||||
|
while(delay>GetMinPacketCount()){
|
||||||
|
for(int i=0;i<JITTER_SLOT_COUNT;i++){
|
||||||
|
if(slots[i].timestamp==nextTimestamp){
|
||||||
|
if(slots[i].buffer){
|
||||||
|
bufferPool.Reuse(slots[i].buffer);
|
||||||
|
slots[i].buffer=NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Advance();
|
||||||
|
delay--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int result=GetInternal(&pkt, offsetInSteps, advance);
|
||||||
|
if(outstandingDelayChange!=0){
|
||||||
|
if(outstandingDelayChange<0){
|
||||||
|
playbackScaledDuration=40;
|
||||||
|
outstandingDelayChange+=20;
|
||||||
|
}else{
|
||||||
|
playbackScaledDuration=80;
|
||||||
|
outstandingDelayChange-=20;
|
||||||
|
}
|
||||||
|
//LOGV("outstanding delay change: %d", outstandingDelayChange);
|
||||||
|
}else if(advance && GetCurrentDelay()==0){
|
||||||
|
//LOGV("stretching packet because the next one is late");
|
||||||
|
playbackScaledDuration=80;
|
||||||
|
}else{
|
||||||
|
playbackScaledDuration=60;
|
||||||
|
}
|
||||||
|
if(result==JR_OK){
|
||||||
|
isEC=pkt.isEC;
|
||||||
|
return pkt.size;
|
||||||
|
}else{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int JitterBuffer::GetInternal(jitter_packet_t* pkt, int offset, bool advance){
|
||||||
|
/*if(needBuffering && lastPutTimestamp<nextTimestamp){
|
||||||
|
LOGV("jitter: don't have timestamp %lld, buffering", (long long int)nextTimestamp);
|
||||||
|
Advance();
|
||||||
|
return JR_BUFFERING;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
//needBuffering=false;
|
||||||
|
|
||||||
|
int64_t timestampToGet=nextTimestamp+offset*(int32_t)step;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for(i=0;i<JITTER_SLOT_COUNT;i++){
|
||||||
|
if(slots[i].buffer!=NULL && slots[i].timestamp==timestampToGet){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(i<JITTER_SLOT_COUNT){
|
||||||
|
if(pkt && pkt->size<slots[i].size){
|
||||||
|
LOGE("jitter: packet won't fit into provided buffer of %d (need %d)", int(slots[i].size), int(pkt->size));
|
||||||
|
}else{
|
||||||
|
if(pkt) {
|
||||||
|
pkt->size = slots[i].size;
|
||||||
|
pkt->timestamp = slots[i].timestamp;
|
||||||
|
memcpy(pkt->buffer, slots[i].buffer, slots[i].size);
|
||||||
|
pkt->isEC=slots[i].isEC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bufferPool.Reuse(slots[i].buffer);
|
||||||
|
slots[i].buffer=NULL;
|
||||||
|
if(offset==0)
|
||||||
|
Advance();
|
||||||
|
lostCount=0;
|
||||||
|
needBuffering=false;
|
||||||
|
return JR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGV("jitter: found no packet for timestamp %lld (last put = %d, lost = %d)", (long long int)timestampToGet, lastPutTimestamp, lostCount);
|
||||||
|
|
||||||
|
if(advance)
|
||||||
|
Advance();
|
||||||
|
|
||||||
|
if(!needBuffering){
|
||||||
|
lostCount++;
|
||||||
|
if(offset==0){
|
||||||
|
lostPackets++;
|
||||||
|
lostSinceReset++;
|
||||||
|
}
|
||||||
|
if(lostCount>=lossesToReset || (gotSinceReset>minDelay*25 && lostSinceReset>gotSinceReset/2)){
|
||||||
|
LOGW("jitter: lost %d packets in a row, resetting", lostCount);
|
||||||
|
//minDelay++;
|
||||||
|
dontIncMinDelay=16;
|
||||||
|
dontDecMinDelay+=128;
|
||||||
|
if(GetCurrentDelay()<minDelay)
|
||||||
|
nextTimestamp-=(int64_t)(minDelay-GetCurrentDelay());
|
||||||
|
lostCount=0;
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
return JR_MISSING;
|
||||||
|
}
|
||||||
|
return JR_BUFFERING;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JitterBuffer::PutInternal(jitter_packet_t* pkt, bool overwriteExisting){
|
||||||
|
if(pkt->size>JITTER_SLOT_SIZE){
|
||||||
|
LOGE("The packet is too big to fit into the jitter buffer");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for(i=0;i<JITTER_SLOT_COUNT;i++){
|
||||||
|
if(slots[i].buffer && slots[i].timestamp==pkt->timestamp){
|
||||||
|
//LOGV("Found existing packet for timestamp %u, overwrite %d", pkt->timestamp, overwriteExisting);
|
||||||
|
if(overwriteExisting){
|
||||||
|
memcpy(slots[i].buffer, pkt->buffer, pkt->size);
|
||||||
|
slots[i].size=pkt->size;
|
||||||
|
slots[i].isEC=pkt->isEC;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gotSinceReset++;
|
||||||
|
if(wasReset){
|
||||||
|
wasReset=false;
|
||||||
|
outstandingDelayChange=0;
|
||||||
|
nextTimestamp=(int64_t)(((int64_t)pkt->timestamp)-step*minDelay);
|
||||||
|
first=true;
|
||||||
|
LOGI("jitter: resyncing, next timestamp = %lld (step=%d, minDelay=%f)", (long long int)nextTimestamp, step, minDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i=0;i<JITTER_SLOT_COUNT;i++){
|
||||||
|
if(slots[i].buffer!=NULL){
|
||||||
|
if(slots[i].timestamp<nextTimestamp-1){
|
||||||
|
bufferPool.Reuse(slots[i].buffer);
|
||||||
|
slots[i].buffer=NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*double prevTime=0;
|
||||||
|
uint32_t closestTime=0;
|
||||||
|
for(i=0;i<JITTER_SLOT_COUNT;i++){
|
||||||
|
if(slots[i].buffer!=NULL && pkt->timestamp-slots[i].timestamp<pkt->timestamp-closestTime){
|
||||||
|
closestTime=slots[i].timestamp;
|
||||||
|
prevTime=slots[i].recvTime;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
double time=VoIPController::GetCurrentTime();
|
||||||
|
if(expectNextAtTime!=0){
|
||||||
|
double dev=expectNextAtTime-time;
|
||||||
|
//LOGV("packet dev %f", dev);
|
||||||
|
deviationHistory.Add(dev);
|
||||||
|
expectNextAtTime+=step/1000.0;
|
||||||
|
}else{
|
||||||
|
expectNextAtTime=time+step/1000.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pkt->timestamp<nextTimestamp){
|
||||||
|
//LOGW("jitter: would drop packet with timestamp %d because it is late but not hopelessly", pkt->timestamp);
|
||||||
|
latePacketCount++;
|
||||||
|
lostPackets--;
|
||||||
|
}else if(pkt->timestamp<nextTimestamp-1){
|
||||||
|
//LOGW("jitter: dropping packet with timestamp %d because it is too late", pkt->timestamp);
|
||||||
|
latePacketCount++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pkt->timestamp>lastPutTimestamp)
|
||||||
|
lastPutTimestamp=pkt->timestamp;
|
||||||
|
|
||||||
|
for(i=0;i<JITTER_SLOT_COUNT;i++){
|
||||||
|
if(slots[i].buffer==NULL)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(i==JITTER_SLOT_COUNT || GetCurrentDelay()>=maxUsedSlots){
|
||||||
|
int toRemove=JITTER_SLOT_COUNT;
|
||||||
|
uint32_t bestTimestamp=0xFFFFFFFF;
|
||||||
|
for(i=0;i<JITTER_SLOT_COUNT;i++){
|
||||||
|
if(slots[i].buffer!=NULL && slots[i].timestamp<bestTimestamp){
|
||||||
|
toRemove=i;
|
||||||
|
bestTimestamp=slots[i].timestamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Advance();
|
||||||
|
bufferPool.Reuse(slots[toRemove].buffer);
|
||||||
|
slots[toRemove].buffer=NULL;
|
||||||
|
i=toRemove;
|
||||||
|
}
|
||||||
|
slots[i].timestamp=pkt->timestamp;
|
||||||
|
slots[i].size=pkt->size;
|
||||||
|
slots[i].buffer=bufferPool.Get();
|
||||||
|
slots[i].recvTimeDiff=time-prevRecvTime;
|
||||||
|
slots[i].isEC=pkt->isEC;
|
||||||
|
if(slots[i].buffer)
|
||||||
|
memcpy(slots[i].buffer, pkt->buffer, pkt->size);
|
||||||
|
else
|
||||||
|
LOGE("WTF!!");
|
||||||
|
#ifdef TGVOIP_DUMP_JITTER_STATS
|
||||||
|
fprintf(dump, "%u\t%.03f\t%d\t%.03f\t%.03f\t%.03f\n", pkt->timestamp, time, GetCurrentDelay(), lastMeasuredJitter, lastMeasuredDelay, minDelay);
|
||||||
|
#endif
|
||||||
|
prevRecvTime=time;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void JitterBuffer::Advance(){
|
||||||
|
nextTimestamp+=step;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned int JitterBuffer::GetCurrentDelay(){
|
||||||
|
unsigned int delay=0;
|
||||||
|
int i;
|
||||||
|
for(i=0;i<JITTER_SLOT_COUNT;i++){
|
||||||
|
if(slots[i].buffer!=NULL)
|
||||||
|
delay++;
|
||||||
|
}
|
||||||
|
return delay;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JitterBuffer::Tick(){
|
||||||
|
MutexGuard m(mutex);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
lateHistory.Add(latePacketCount);
|
||||||
|
latePacketCount=0;
|
||||||
|
bool absolutelyNoLatePackets=lateHistory.Max()==0;
|
||||||
|
|
||||||
|
double avgLate16=lateHistory.Average(16);
|
||||||
|
//LOGV("jitter: avg late=%.1f, %.1f, %.1f", avgLate16, avgLate32, avgLate64);
|
||||||
|
if(avgLate16>=resyncThreshold){
|
||||||
|
LOGV("resyncing: avgLate16=%f, resyncThreshold=%f", avgLate16, resyncThreshold);
|
||||||
|
wasReset=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(absolutelyNoLatePackets){
|
||||||
|
if(dontDecMinDelay>0)
|
||||||
|
dontDecMinDelay--;
|
||||||
|
}
|
||||||
|
|
||||||
|
delayHistory.Add(GetCurrentDelay());
|
||||||
|
avgDelay=delayHistory.Average(32);
|
||||||
|
|
||||||
|
double stddev=0;
|
||||||
|
double avgdev=deviationHistory.Average();
|
||||||
|
for(i=0;i<64;i++){
|
||||||
|
double d=(deviationHistory[i]-avgdev);
|
||||||
|
stddev+=(d*d);
|
||||||
|
}
|
||||||
|
stddev=sqrt(stddev/64);
|
||||||
|
uint32_t stddevDelay=(uint32_t)ceil(stddev*2*1000/step);
|
||||||
|
if(stddevDelay<minMinDelay)
|
||||||
|
stddevDelay=minMinDelay;
|
||||||
|
if(stddevDelay>maxMinDelay)
|
||||||
|
stddevDelay=maxMinDelay;
|
||||||
|
if(stddevDelay!=minDelay){
|
||||||
|
int32_t diff=(int32_t)(stddevDelay-minDelay);
|
||||||
|
if(diff>0){
|
||||||
|
dontDecMinDelay=100;
|
||||||
|
}
|
||||||
|
if(diff<-1)
|
||||||
|
diff=-1;
|
||||||
|
if(diff>1)
|
||||||
|
diff=1;
|
||||||
|
if((diff>0 && dontIncMinDelay==0) || (diff<0 && dontDecMinDelay==0)){
|
||||||
|
//nextTimestamp+=diff*(int32_t)step;
|
||||||
|
minDelay+=diff;
|
||||||
|
outstandingDelayChange+=diff*60;
|
||||||
|
dontChangeDelay+=32;
|
||||||
|
//LOGD("new delay from stddev %f", minDelay);
|
||||||
|
if(diff<0){
|
||||||
|
dontDecMinDelay+=25;
|
||||||
|
}
|
||||||
|
if(diff>0){
|
||||||
|
dontIncMinDelay=25;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastMeasuredJitter=stddev;
|
||||||
|
lastMeasuredDelay=stddevDelay;
|
||||||
|
//LOGV("stddev=%.3f, avg=%.3f, ndelay=%d, dontDec=%u", stddev, avgdev, stddevDelay, dontDecMinDelay);
|
||||||
|
if(dontChangeDelay==0){
|
||||||
|
if(avgDelay>minDelay+0.5){
|
||||||
|
outstandingDelayChange-=avgDelay>minDelay+2 ? 60 : 20;
|
||||||
|
dontChangeDelay+=10;
|
||||||
|
}else if(avgDelay<minDelay-0.3){
|
||||||
|
outstandingDelayChange+=20;
|
||||||
|
dontChangeDelay+=10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(dontChangeDelay>0)
|
||||||
|
dontChangeDelay--;
|
||||||
|
|
||||||
|
//LOGV("jitter: avg delay=%d, delay=%d, late16=%.1f, dontDecMinDelay=%d", avgDelay, delayHistory[0], avgLate16, dontDecMinDelay);
|
||||||
|
/*if(!adjustingDelay) {
|
||||||
|
if (((minDelay==1 ? (avgDelay>=3) : (avgDelay>=minDelay/2)) && delayHistory[0]>minDelay && avgLate16<=0.1 && absolutelyNoLatePackets && dontDecMinDelay<32 && min>minDelay)) {
|
||||||
|
LOGI("jitter: need adjust");
|
||||||
|
adjustingDelay=true;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
if(!absolutelyNoLatePackets){
|
||||||
|
LOGI("jitter: done adjusting because we're losing packets");
|
||||||
|
adjustingDelay=false;
|
||||||
|
}else if(tickCount%5==0){
|
||||||
|
LOGD("jitter: removing a packet to reduce delay");
|
||||||
|
GetInternal(NULL, 0);
|
||||||
|
expectNextAtTime=0;
|
||||||
|
if(GetCurrentDelay()<=minDelay || min<=minDelay){
|
||||||
|
adjustingDelay = false;
|
||||||
|
LOGI("jitter: done adjusting");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
tickCount++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void JitterBuffer::GetAverageLateCount(double *out){
|
||||||
|
double avgLate64=lateHistory.Average(), avgLate32=lateHistory.Average(32), avgLate16=lateHistory.Average(16);
|
||||||
|
out[0]=avgLate16;
|
||||||
|
out[1]=avgLate32;
|
||||||
|
out[2]=avgLate64;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int JitterBuffer::GetAndResetLostPacketCount(){
|
||||||
|
MutexGuard m(mutex);
|
||||||
|
int r=lostPackets;
|
||||||
|
lostPackets=0;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
double JitterBuffer::GetLastMeasuredJitter(){
|
||||||
|
return lastMeasuredJitter;
|
||||||
|
}
|
||||||
|
|
||||||
|
double JitterBuffer::GetLastMeasuredDelay(){
|
||||||
|
return lastMeasuredDelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
double JitterBuffer::GetAverageDelay(){
|
||||||
|
return avgDelay;
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LIBTGVOIP_JITTERBUFFER_H
|
||||||
|
#define LIBTGVOIP_JITTERBUFFER_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "MediaStreamItf.h"
|
||||||
|
#include "BlockingQueue.h"
|
||||||
|
#include "Buffers.h"
|
||||||
|
#include "threading.h"
|
||||||
|
|
||||||
|
#define JITTER_SLOT_COUNT 64
|
||||||
|
#define JITTER_SLOT_SIZE 1024
|
||||||
|
#define JR_OK 1
|
||||||
|
#define JR_MISSING 2
|
||||||
|
#define JR_BUFFERING 3
|
||||||
|
|
||||||
|
|
||||||
|
namespace tgvoip{
|
||||||
|
class JitterBuffer{
|
||||||
|
public:
|
||||||
|
JitterBuffer(MediaStreamItf* out, uint32_t step);
|
||||||
|
~JitterBuffer();
|
||||||
|
void SetMinPacketCount(uint32_t count);
|
||||||
|
int GetMinPacketCount();
|
||||||
|
unsigned int GetCurrentDelay();
|
||||||
|
double GetAverageDelay();
|
||||||
|
void Reset();
|
||||||
|
void HandleInput(unsigned char* data, size_t len, uint32_t timestamp, bool isEC);
|
||||||
|
size_t HandleOutput(unsigned char* buffer, size_t len, int offsetInSteps, bool advance, int& playbackScaledDuration, bool& isEC);
|
||||||
|
void Tick();
|
||||||
|
void GetAverageLateCount(double* out);
|
||||||
|
int GetAndResetLostPacketCount();
|
||||||
|
double GetLastMeasuredJitter();
|
||||||
|
double GetLastMeasuredDelay();
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct jitter_packet_t{
|
||||||
|
unsigned char* buffer=NULL;
|
||||||
|
size_t size;
|
||||||
|
uint32_t timestamp;
|
||||||
|
bool isEC;
|
||||||
|
double recvTimeDiff;
|
||||||
|
};
|
||||||
|
static size_t CallbackIn(unsigned char* data, size_t len, void* param);
|
||||||
|
static size_t CallbackOut(unsigned char* data, size_t len, void* param);
|
||||||
|
void PutInternal(jitter_packet_t* pkt, bool overwriteExisting);
|
||||||
|
int GetInternal(jitter_packet_t* pkt, int offset, bool advance);
|
||||||
|
void Advance();
|
||||||
|
|
||||||
|
BufferPool bufferPool;
|
||||||
|
Mutex mutex;
|
||||||
|
jitter_packet_t slots[JITTER_SLOT_COUNT];
|
||||||
|
int64_t nextTimestamp=0;
|
||||||
|
uint32_t step;
|
||||||
|
double minDelay=6;
|
||||||
|
uint32_t minMinDelay;
|
||||||
|
uint32_t maxMinDelay;
|
||||||
|
uint32_t maxUsedSlots;
|
||||||
|
uint32_t lastPutTimestamp;
|
||||||
|
uint32_t lossesToReset;
|
||||||
|
double resyncThreshold;
|
||||||
|
unsigned int lostCount=0;
|
||||||
|
unsigned int lostSinceReset=0;
|
||||||
|
unsigned int gotSinceReset=0;
|
||||||
|
bool wasReset=true;
|
||||||
|
bool needBuffering=true;
|
||||||
|
HistoricBuffer<int, 64, double> delayHistory;
|
||||||
|
HistoricBuffer<int, 64, double> lateHistory;
|
||||||
|
bool adjustingDelay=false;
|
||||||
|
unsigned int tickCount=0;
|
||||||
|
unsigned int latePacketCount=0;
|
||||||
|
unsigned int dontIncMinDelay=0;
|
||||||
|
unsigned int dontDecMinDelay=0;
|
||||||
|
int lostPackets=0;
|
||||||
|
double prevRecvTime=0;
|
||||||
|
double expectNextAtTime=0;
|
||||||
|
HistoricBuffer<double, 64> deviationHistory;
|
||||||
|
double lastMeasuredJitter=0;
|
||||||
|
double lastMeasuredDelay=0;
|
||||||
|
int outstandingDelayChange=0;
|
||||||
|
unsigned int dontChangeDelay=0;
|
||||||
|
double avgDelay=0;
|
||||||
|
bool first=true;
|
||||||
|
#ifdef TGVOIP_DUMP_JITTER_STATS
|
||||||
|
FILE* dump;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //LIBTGVOIP_JITTERBUFFER_H
|
|
@ -0,0 +1,760 @@
|
||||||
|
AUTOMAKE_OPTIONS = foreign
|
||||||
|
|
||||||
|
CFLAGS = -Wall -DHAVE_CONFIG_H -Wno-unknown-pragmas
|
||||||
|
|
||||||
|
lib_LTLIBRARIES = libtgvoip.la
|
||||||
|
|
||||||
|
SRC = VoIPController.cpp \
|
||||||
|
Buffers.cpp \
|
||||||
|
CongestionControl.cpp \
|
||||||
|
EchoCanceller.cpp \
|
||||||
|
JitterBuffer.cpp \
|
||||||
|
logging.cpp \
|
||||||
|
MediaStreamItf.cpp \
|
||||||
|
MessageThread.cpp \
|
||||||
|
NetworkSocket.cpp \
|
||||||
|
OpusDecoder.cpp \
|
||||||
|
OpusEncoder.cpp \
|
||||||
|
PacketReassembler.cpp \
|
||||||
|
VoIPGroupController.cpp \
|
||||||
|
VoIPServerConfig.cpp \
|
||||||
|
audio/AudioIO.cpp \
|
||||||
|
audio/AudioInput.cpp \
|
||||||
|
audio/AudioOutput.cpp \
|
||||||
|
audio/Resampler.cpp \
|
||||||
|
os/posix/NetworkSocketPosix.cpp \
|
||||||
|
video/VideoSource.cpp \
|
||||||
|
video/VideoRenderer.cpp \
|
||||||
|
video/ScreamCongestionController.cpp \
|
||||||
|
json11.cpp
|
||||||
|
|
||||||
|
TGVOIP_HDRS = \
|
||||||
|
VoIPController.h \
|
||||||
|
Buffers.h \
|
||||||
|
BlockingQueue.h \
|
||||||
|
PrivateDefines.h \
|
||||||
|
CongestionControl.h \
|
||||||
|
EchoCanceller.h \
|
||||||
|
JitterBuffer.h \
|
||||||
|
logging.h \
|
||||||
|
threading.h \
|
||||||
|
MediaStreamItf.h \
|
||||||
|
MessageThread.h \
|
||||||
|
NetworkSocket.h \
|
||||||
|
OpusDecoder.h \
|
||||||
|
OpusEncoder.h \
|
||||||
|
PacketReassembler.h \
|
||||||
|
VoIPServerConfig.h \
|
||||||
|
audio/AudioIO.h \
|
||||||
|
audio/AudioInput.h \
|
||||||
|
audio/AudioOutput.h \
|
||||||
|
audio/Resampler.h \
|
||||||
|
os/posix/NetworkSocketPosix.h \
|
||||||
|
video/VideoSource.h \
|
||||||
|
video/VideoRenderer.h \
|
||||||
|
video/ScreamCongestionController.h \
|
||||||
|
json11.hpp \
|
||||||
|
utils.h
|
||||||
|
|
||||||
|
if TARGET_OS_OSX
|
||||||
|
|
||||||
|
SRC += \
|
||||||
|
os/darwin/AudioInputAudioUnit.cpp \
|
||||||
|
os/darwin/AudioOutputAudioUnit.cpp \
|
||||||
|
os/darwin/AudioUnitIO.cpp \
|
||||||
|
os/darwin/AudioInputAudioUnitOSX.cpp \
|
||||||
|
os/darwin/AudioOutputAudioUnitOSX.cpp \
|
||||||
|
os/darwin/DarwinSpecific.mm \
|
||||||
|
os/darwin/SampleBufferDisplayLayerRenderer.mm \
|
||||||
|
os/darwin/TGVVideoRenderer.mm \
|
||||||
|
os/darwin/TGVVideoSource.mm \
|
||||||
|
os/darwin/VideoToolboxEncoderSource.mm
|
||||||
|
|
||||||
|
TGVOIP_HDRS += \
|
||||||
|
os/darwin/AudioInputAudioUnit.h \
|
||||||
|
os/darwin/AudioOutputAudioUnit.h \
|
||||||
|
os/darwin/AudioUnitIO.h \
|
||||||
|
os/darwin/AudioInputAudioUnitOSX.h \
|
||||||
|
os/darwin/AudioOutputAudioUnitOSX.h \
|
||||||
|
os/darwin/DarwinSpecific.h \
|
||||||
|
os/darwin/SampleBufferDisplayLayerRenderer.h \
|
||||||
|
os/darwin/TGVVideoRenderer.h \
|
||||||
|
os/darwin/TGVVideoSource.h \
|
||||||
|
os/darwin/VideoToolboxEncoderSource.h
|
||||||
|
|
||||||
|
LDFLAGS += -framework Foundation -framework CoreFoundation -framework CoreAudio -framework AudioToolbox -framework VideoToolbox -framework CoreMedia -framework CoreVideo
|
||||||
|
|
||||||
|
else
|
||||||
|
# Linux-specific
|
||||||
|
|
||||||
|
if WITH_ALSA
|
||||||
|
SRC += \
|
||||||
|
os/linux/AudioInputALSA.cpp \
|
||||||
|
os/linux/AudioOutputALSA.cpp
|
||||||
|
TGVOIP_HDRS += \
|
||||||
|
os/linux/AudioInputALSA.h \
|
||||||
|
os/linux/AudioOutputALSA.h
|
||||||
|
endif
|
||||||
|
|
||||||
|
if WITH_PULSE
|
||||||
|
SRC += \
|
||||||
|
os/linux/AudioOutputPulse.cpp \
|
||||||
|
os/linux/AudioInputPulse.cpp \
|
||||||
|
os/linux/AudioPulse.cpp
|
||||||
|
TGVOIP_HDRS += \
|
||||||
|
os/linux/AudioOutputPulse.h \
|
||||||
|
os/linux/AudioInputPulse.h \
|
||||||
|
os/linux/AudioPulse.h \
|
||||||
|
os/linux/PulseFunctions.h
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
if ENABLE_DSP
|
||||||
|
CFLAGS += -DWEBRTC_POSIX -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_NS_FLOAT -I$(top_srcdir)/webrtc_dsp
|
||||||
|
CCASFLAGS += -I$(top_srcdir)/webrtc_dsp
|
||||||
|
|
||||||
|
SRC += \
|
||||||
|
./webrtc_dsp/system_wrappers/source/field_trial.cc \
|
||||||
|
./webrtc_dsp/system_wrappers/source/metrics.cc \
|
||||||
|
./webrtc_dsp/system_wrappers/source/cpu_features.cc \
|
||||||
|
./webrtc_dsp/absl/strings/internal/memutil.cc \
|
||||||
|
./webrtc_dsp/absl/strings/string_view.cc \
|
||||||
|
./webrtc_dsp/absl/strings/ascii.cc \
|
||||||
|
./webrtc_dsp/absl/types/bad_optional_access.cc \
|
||||||
|
./webrtc_dsp/absl/types/optional.cc \
|
||||||
|
./webrtc_dsp/absl/base/internal/raw_logging.cc \
|
||||||
|
./webrtc_dsp/absl/base/internal/throw_delegate.cc \
|
||||||
|
./webrtc_dsp/rtc_base/race_checker.cc \
|
||||||
|
./webrtc_dsp/rtc_base/strings/string_builder.cc \
|
||||||
|
./webrtc_dsp/rtc_base/memory/aligned_malloc.cc \
|
||||||
|
./webrtc_dsp/rtc_base/timeutils.cc \
|
||||||
|
./webrtc_dsp/rtc_base/platform_file.cc \
|
||||||
|
./webrtc_dsp/rtc_base/string_to_number.cc \
|
||||||
|
./webrtc_dsp/rtc_base/thread_checker_impl.cc \
|
||||||
|
./webrtc_dsp/rtc_base/stringencode.cc \
|
||||||
|
./webrtc_dsp/rtc_base/stringutils.cc \
|
||||||
|
./webrtc_dsp/rtc_base/checks.cc \
|
||||||
|
./webrtc_dsp/rtc_base/platform_thread.cc \
|
||||||
|
./webrtc_dsp/rtc_base/logging_webrtc.cc \
|
||||||
|
./webrtc_dsp/rtc_base/criticalsection.cc \
|
||||||
|
./webrtc_dsp/rtc_base/platform_thread_types.cc \
|
||||||
|
./webrtc_dsp/rtc_base/event.cc \
|
||||||
|
./webrtc_dsp/rtc_base/event_tracer.cc \
|
||||||
|
./webrtc_dsp/third_party/rnnoise/src/rnn_vad_weights.cc \
|
||||||
|
./webrtc_dsp/third_party/rnnoise/src/kiss_fft.cc \
|
||||||
|
./webrtc_dsp/api/audio/audio_frame.cc \
|
||||||
|
./webrtc_dsp/api/audio/echo_canceller3_config.cc \
|
||||||
|
./webrtc_dsp/api/audio/echo_canceller3_factory.cc \
|
||||||
|
./webrtc_dsp/modules/third_party/fft/fft.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_estimator.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb16_tables.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_gain_tables.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/arith_routines_logist.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/filterbanks.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/transform.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_filter.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/encode_lpc_swb.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/filter_functions.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/decode.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lattice.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/intialize.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_tables.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_gain_swb_tables.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/bandwidth_estimator.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/encode.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_analysis.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/arith_routines_hist.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/entropy_coding.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/isac_vad.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/arith_routines.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/crc.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb12_tables.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/decode_bwe.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/spectrum_ar_model_tables.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_lag_tables.c \
|
||||||
|
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/isac.c \
|
||||||
|
./webrtc_dsp/modules/audio_processing/rms_level.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/echo_detector/normalized_covariance_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/echo_detector/moving_max.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/echo_detector/circular_buffer.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/echo_detector/mean_variance_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/splitting_filter.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/gain_control_impl.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/ns/nsx_core.c \
|
||||||
|
./webrtc_dsp/modules/audio_processing/ns/noise_suppression_x.c \
|
||||||
|
./webrtc_dsp/modules/audio_processing/ns/nsx_core_c.c \
|
||||||
|
./webrtc_dsp/modules/audio_processing/ns/ns_core.c \
|
||||||
|
./webrtc_dsp/modules/audio_processing/ns/noise_suppression.c \
|
||||||
|
./webrtc_dsp/modules/audio_processing/audio_buffer.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/typing_detection.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/include/audio_processing_statistics.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/include/audio_generator_factory.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/include/aec_dump.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/include/audio_processing.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/include/config.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/interpolated_gain_curve.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/agc2_common.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/gain_applier.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/adaptive_agc.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/adaptive_digital_gain_applier.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/limiter.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/saturation_protector.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/rnn_vad/rnn.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/rnn_vad/spectral_features.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/rnn_vad/pitch_search.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/rnn_vad/features_extraction.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/rnn_vad/fft_util.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/rnn_vad/lp_residual.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/adaptive_mode_level_estimator_agc.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/vector_float_frame.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/noise_level_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/agc2_testing_common.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/fixed_digital_level_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/fixed_gain_controller.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/vad_with_level.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/limiter_db_gain_curve.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/down_sampler.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/signal_classifier.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/noise_spectrum_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/compute_interpolated_gain_curve.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/biquad_filter.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc2/adaptive_mode_level_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/transient/moving_moments.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/transient/wpd_tree.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/transient/wpd_node.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/transient/transient_suppressor.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/transient/transient_detector.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/low_cut_filter.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/level_estimator_impl.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/three_band_filter_bank.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec/echo_cancellation.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec/aec_resampler.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec/aec_core.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/voice_detection_impl.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/echo_cancellation_impl.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/gain_control_for_experimental_agc.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc/agc.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc/loudness_histogram.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc/agc_manager_direct.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc/legacy/analog_agc.c \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc/legacy/digital_agc.c \
|
||||||
|
./webrtc_dsp/modules/audio_processing/agc/utility.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/audio_processing_impl.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/audio_generator/file_audio_generator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/gain_controller2.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/residual_echo_detector.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/noise_suppression_impl.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aecm/aecm_core.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aecm/aecm_core_c.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aecm/echo_control_mobile.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/render_reverb_model.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/reverb_model_fallback.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/echo_remover_metrics.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/matched_filter_lag_aggregator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/render_delay_buffer2.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/echo_path_variability.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/frame_blocker.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/subtractor.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/aec3_fft.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/fullband_erle_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/suppression_filter.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/block_processor.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/subband_erle_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/render_delay_controller_metrics.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/render_delay_buffer.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/vector_buffer.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/erl_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/aec_state.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/adaptive_fir_filter.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/render_delay_controller.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/skew_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/echo_path_delay_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/block_framer.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/erle_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/reverb_model.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/cascaded_biquad_filter.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/render_buffer.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/subtractor_output.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/stationarity_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/render_signal_analyzer.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/subtractor_output_analyzer.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/suppression_gain.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/echo_audibility.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/block_processor_metrics.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/moving_average.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/reverb_model_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/aec3_common.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/residual_echo_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/matched_filter.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/reverb_decay_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/render_delay_controller2.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/suppression_gain_limiter.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/main_filter_update_gain.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/echo_remover.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/downsampled_render_buffer.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/matrix_buffer.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/block_processor2.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/echo_canceller3.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/block_delay_buffer.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/fft_buffer.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/comfort_noise_generator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/shadow_filter_update_gain.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/filter_analyzer.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/reverb_frequency_response.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/aec3/decimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/echo_control_mobile_impl.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/logging/apm_data_dumper.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/vad/voice_activity_detector.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/vad/standalone_vad.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/vad/pitch_internal.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/vad/vad_circular_buffer.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/vad/vad_audio_proc.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/vad/pole_zero_filter.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/vad/pitch_based_vad.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/vad/gmm.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/utility/ooura_fft.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/utility/delay_estimator_wrapper.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/utility/delay_estimator.cc \
|
||||||
|
./webrtc_dsp/modules/audio_processing/utility/block_mean_calculator.cc \
|
||||||
|
./webrtc_dsp/common_audio/window_generator.cc \
|
||||||
|
./webrtc_dsp/common_audio/channel_buffer.cc \
|
||||||
|
./webrtc_dsp/common_audio/fir_filter_factory.cc \
|
||||||
|
./webrtc_dsp/common_audio/wav_header.cc \
|
||||||
|
./webrtc_dsp/common_audio/real_fourier_ooura.cc \
|
||||||
|
./webrtc_dsp/common_audio/audio_util.cc \
|
||||||
|
./webrtc_dsp/common_audio/fir_filter_sse.cc \
|
||||||
|
./webrtc_dsp/common_audio/resampler/push_sinc_resampler.cc \
|
||||||
|
./webrtc_dsp/common_audio/resampler/resampler.cc \
|
||||||
|
./webrtc_dsp/common_audio/resampler/sinc_resampler_sse.cc \
|
||||||
|
./webrtc_dsp/common_audio/resampler/push_resampler.cc \
|
||||||
|
./webrtc_dsp/common_audio/resampler/sinc_resampler.cc \
|
||||||
|
./webrtc_dsp/common_audio/resampler/sinusoidal_linear_chirp_source.cc \
|
||||||
|
./webrtc_dsp/common_audio/wav_file.cc \
|
||||||
|
./webrtc_dsp/common_audio/third_party/fft4g/fft4g.c \
|
||||||
|
./webrtc_dsp/common_audio/audio_converter.cc \
|
||||||
|
./webrtc_dsp/common_audio/real_fourier.cc \
|
||||||
|
./webrtc_dsp/common_audio/sparse_fir_filter.cc \
|
||||||
|
./webrtc_dsp/common_audio/smoothing_filter.cc \
|
||||||
|
./webrtc_dsp/common_audio/fir_filter_c.cc \
|
||||||
|
./webrtc_dsp/common_audio/ring_buffer.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/complex_fft.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/filter_ma_fast_q12.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/splitting_filter1.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/levinson_durbin.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/dot_product_with_scale.cc \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/auto_corr_to_refl_coef.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/resample_by_2_internal.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/energy.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/sqrt_of_one_minus_x_squared.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/downsample_fast.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/filter_ar_fast_q12.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/spl_init.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/lpc_to_refl_coef.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/cross_correlation.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/division_operations.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/auto_correlation.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/get_scaling_square.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/resample.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/min_max_operations.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/refl_coef_to_lpc.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/filter_ar.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/vector_scaling_operations.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/resample_fractional.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/real_fft.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/ilbc_specific_functions.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/randomization_functions.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/copy_set_operations.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/resample_by_2.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/get_hanning_window.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/resample_48khz.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/spl_inl.c \
|
||||||
|
./webrtc_dsp/common_audio/signal_processing/spl_sqrt.c \
|
||||||
|
./webrtc_dsp/common_audio/vad/vad_sp.c \
|
||||||
|
./webrtc_dsp/common_audio/vad/vad.cc \
|
||||||
|
./webrtc_dsp/common_audio/vad/webrtc_vad.c \
|
||||||
|
./webrtc_dsp/common_audio/vad/vad_filterbank.c \
|
||||||
|
./webrtc_dsp/common_audio/vad/vad_core.c \
|
||||||
|
./webrtc_dsp/common_audio/vad/vad_gmm.c
|
||||||
|
|
||||||
|
if TARGET_OS_OSX
|
||||||
|
CFLAGS += -DWEBRTC_MAC
|
||||||
|
SRC += \
|
||||||
|
webrtc_dsp/rtc_base/logging_mac.mm \
|
||||||
|
webrtc_dsp/rtc_base/logging_mac.h
|
||||||
|
else
|
||||||
|
CFLAGS += -DWEBRTC_LINUX
|
||||||
|
endif
|
||||||
|
|
||||||
|
if TARGET_CPU_X86
|
||||||
|
SRC += \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec/aec_core_sse2.cc \
|
||||||
|
webrtc_dsp/modules/audio_processing/utility/ooura_fft_sse2.cc
|
||||||
|
endif
|
||||||
|
|
||||||
|
if ENABLE_AUDIO_CALLBACK
|
||||||
|
CFLAGS += -DTGVOIP_USE_CALLBACK_AUDIO_IO
|
||||||
|
SRC += \
|
||||||
|
audio/AudioIOCallback.cpp
|
||||||
|
TGVOIP_HDRS += \
|
||||||
|
audio/AudioIOCallback.h
|
||||||
|
endif
|
||||||
|
|
||||||
|
if TARGET_CPU_ARM
|
||||||
|
SRC += \
|
||||||
|
webrtc_dsp/common_audio/signal_processing/complex_bit_reverse_arm.S \
|
||||||
|
webrtc_dsp/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor_arm.S
|
||||||
|
if TARGET_CPU_ARMV7
|
||||||
|
CFLAGS += -mfpu=neon -mfloat-abi=hard
|
||||||
|
CCASFLAGS += -mfpu=neon -mfloat-abi=hard
|
||||||
|
SRC += \
|
||||||
|
webrtc_dsp/common_audio/signal_processing/cross_correlation_neon.c \
|
||||||
|
webrtc_dsp/common_audio/signal_processing/downsample_fast_neon.c \
|
||||||
|
webrtc_dsp/common_audio/signal_processing/min_max_operations_neon.c \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec/aec_core_neon.cc \
|
||||||
|
webrtc_dsp/modules/audio_processing/aecm/aecm_core_neon.cc \
|
||||||
|
webrtc_dsp/modules/audio_processing/ns/nsx_core_neon.c \
|
||||||
|
webrtc_dsp/modules/audio_processing/utility/ooura_fft_neon.cc
|
||||||
|
# webrtc_dsp/common_audio/signal_processing/filter_ar_fast_q12_armv7.S
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
SRC += \
|
||||||
|
webrtc_dsp/common_audio/signal_processing/complex_bit_reverse.c \
|
||||||
|
webrtc_dsp/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.c
|
||||||
|
endif
|
||||||
|
|
||||||
|
# headers
|
||||||
|
SRC += \
|
||||||
|
webrtc_dsp/system_wrappers/include/field_trial.h \
|
||||||
|
webrtc_dsp/system_wrappers/include/cpu_features_wrapper.h \
|
||||||
|
webrtc_dsp/system_wrappers/include/asm_defines.h \
|
||||||
|
webrtc_dsp/system_wrappers/include/metrics.h \
|
||||||
|
webrtc_dsp/system_wrappers/include/compile_assert_c.h \
|
||||||
|
webrtc_dsp/typedefs.h \
|
||||||
|
webrtc_dsp/absl/strings/internal/memutil.h \
|
||||||
|
webrtc_dsp/absl/strings/ascii.h \
|
||||||
|
webrtc_dsp/absl/strings/string_view.h \
|
||||||
|
webrtc_dsp/absl/types/optional.h \
|
||||||
|
webrtc_dsp/absl/types/bad_optional_access.h \
|
||||||
|
webrtc_dsp/absl/memory/memory.h \
|
||||||
|
webrtc_dsp/absl/meta/type_traits.h \
|
||||||
|
webrtc_dsp/absl/algorithm/algorithm.h \
|
||||||
|
webrtc_dsp/absl/container/inlined_vector.h \
|
||||||
|
webrtc_dsp/absl/base/policy_checks.h \
|
||||||
|
webrtc_dsp/absl/base/port.h \
|
||||||
|
webrtc_dsp/absl/base/config.h \
|
||||||
|
webrtc_dsp/absl/base/internal/invoke.h \
|
||||||
|
webrtc_dsp/absl/base/internal/inline_variable.h \
|
||||||
|
webrtc_dsp/absl/base/internal/atomic_hook.h \
|
||||||
|
webrtc_dsp/absl/base/internal/identity.h \
|
||||||
|
webrtc_dsp/absl/base/internal/raw_logging.h \
|
||||||
|
webrtc_dsp/absl/base/internal/throw_delegate.h \
|
||||||
|
webrtc_dsp/absl/base/attributes.h \
|
||||||
|
webrtc_dsp/absl/base/macros.h \
|
||||||
|
webrtc_dsp/absl/base/optimization.h \
|
||||||
|
webrtc_dsp/absl/base/log_severity.h \
|
||||||
|
webrtc_dsp/absl/utility/utility.h \
|
||||||
|
webrtc_dsp/rtc_base/string_to_number.h \
|
||||||
|
webrtc_dsp/rtc_base/constructormagic.h \
|
||||||
|
webrtc_dsp/rtc_base/strings/string_builder.h \
|
||||||
|
webrtc_dsp/rtc_base/event_tracer.h \
|
||||||
|
webrtc_dsp/rtc_base/stringencode.h \
|
||||||
|
webrtc_dsp/rtc_base/memory/aligned_malloc.h \
|
||||||
|
webrtc_dsp/rtc_base/event.h \
|
||||||
|
webrtc_dsp/rtc_base/ignore_wundef.h \
|
||||||
|
webrtc_dsp/rtc_base/stringutils.h \
|
||||||
|
webrtc_dsp/rtc_base/arraysize.h \
|
||||||
|
webrtc_dsp/rtc_base/swap_queue.h \
|
||||||
|
webrtc_dsp/rtc_base/trace_event.h \
|
||||||
|
webrtc_dsp/rtc_base/checks.h \
|
||||||
|
webrtc_dsp/rtc_base/deprecation.h \
|
||||||
|
webrtc_dsp/rtc_base/sanitizer.h \
|
||||||
|
webrtc_dsp/rtc_base/scoped_ref_ptr.h \
|
||||||
|
webrtc_dsp/rtc_base/logging.h \
|
||||||
|
webrtc_dsp/rtc_base/timeutils.h \
|
||||||
|
webrtc_dsp/rtc_base/atomicops.h \
|
||||||
|
webrtc_dsp/rtc_base/numerics/safe_minmax.h \
|
||||||
|
webrtc_dsp/rtc_base/numerics/safe_conversions.h \
|
||||||
|
webrtc_dsp/rtc_base/numerics/safe_conversions_impl.h \
|
||||||
|
webrtc_dsp/rtc_base/numerics/safe_compare.h \
|
||||||
|
webrtc_dsp/rtc_base/system/unused.h \
|
||||||
|
webrtc_dsp/rtc_base/system/inline.h \
|
||||||
|
webrtc_dsp/rtc_base/system/ignore_warnings.h \
|
||||||
|
webrtc_dsp/rtc_base/system/asm_defines.h \
|
||||||
|
webrtc_dsp/rtc_base/system/rtc_export.h \
|
||||||
|
webrtc_dsp/rtc_base/system/arch.h \
|
||||||
|
webrtc_dsp/rtc_base/platform_thread.h \
|
||||||
|
webrtc_dsp/rtc_base/platform_thread_types.h \
|
||||||
|
webrtc_dsp/rtc_base/protobuf_utils.h \
|
||||||
|
webrtc_dsp/rtc_base/thread_annotations.h \
|
||||||
|
webrtc_dsp/rtc_base/gtest_prod_util.h \
|
||||||
|
webrtc_dsp/rtc_base/function_view.h \
|
||||||
|
webrtc_dsp/rtc_base/criticalsection.h \
|
||||||
|
webrtc_dsp/rtc_base/refcount.h \
|
||||||
|
webrtc_dsp/rtc_base/thread_checker_impl.h \
|
||||||
|
webrtc_dsp/rtc_base/compile_assert_c.h \
|
||||||
|
webrtc_dsp/rtc_base/type_traits.h \
|
||||||
|
webrtc_dsp/rtc_base/platform_file.h \
|
||||||
|
webrtc_dsp/rtc_base/refcounter.h \
|
||||||
|
webrtc_dsp/rtc_base/thread_checker.h \
|
||||||
|
webrtc_dsp/rtc_base/race_checker.h \
|
||||||
|
webrtc_dsp/rtc_base/refcountedobject.h \
|
||||||
|
webrtc_dsp/third_party/rnnoise/src/rnn_activations.h \
|
||||||
|
webrtc_dsp/third_party/rnnoise/src/kiss_fft.h \
|
||||||
|
webrtc_dsp/third_party/rnnoise/src/rnn_vad_weights.h \
|
||||||
|
webrtc_dsp/api/audio/echo_canceller3_config.h \
|
||||||
|
webrtc_dsp/api/audio/echo_control.h \
|
||||||
|
webrtc_dsp/api/audio/audio_frame.h \
|
||||||
|
webrtc_dsp/api/audio/echo_canceller3_factory.h \
|
||||||
|
webrtc_dsp/api/array_view.h \
|
||||||
|
webrtc_dsp/modules/third_party/fft/fft.h \
|
||||||
|
webrtc_dsp/modules/audio_coding/codecs/isac/bandwidth_info.h \
|
||||||
|
webrtc_dsp/modules/audio_coding/codecs/isac/main/include/isac.h \
|
||||||
|
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/os_specific_inline.h \
|
||||||
|
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/entropy_coding.h \
|
||||||
|
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/isac_vad.h \
|
||||||
|
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/settings.h \
|
||||||
|
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb12_tables.h \
|
||||||
|
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/arith_routines.h \
|
||||||
|
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/crc.h \
|
||||||
|
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/isac_float_type.h \
|
||||||
|
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_lag_tables.h \
|
||||||
|
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/spectrum_ar_model_tables.h \
|
||||||
|
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/codec.h \
|
||||||
|
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_gain_tables.h \
|
||||||
|
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb16_tables.h \
|
||||||
|
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_estimator.h \
|
||||||
|
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/structs.h \
|
||||||
|
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/filter_functions.h \
|
||||||
|
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/encode_lpc_swb.h \
|
||||||
|
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_filter.h \
|
||||||
|
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_analysis.h \
|
||||||
|
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/bandwidth_estimator.h \
|
||||||
|
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_gain_swb_tables.h \
|
||||||
|
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_tables.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/echo_detector/moving_max.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/echo_detector/circular_buffer.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/echo_detector/normalized_covariance_estimator.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/echo_detector/mean_variance_estimator.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/gain_control_for_experimental_agc.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/rms_level.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/ns/ns_core.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/ns/defines.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/ns/noise_suppression.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/ns/nsx_core.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/ns/windows_private.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/ns/noise_suppression_x.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/ns/nsx_defines.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/residual_echo_detector.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/audio_processing_impl.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/render_queue_item_verifier.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/include/audio_generator.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/include/config.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/include/audio_frame_view.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/include/mock_audio_processing.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/include/gain_control.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/include/audio_generator_factory.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/include/aec_dump.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/include/audio_processing_statistics.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/include/audio_processing.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/interpolated_gain_curve.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/biquad_filter.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/agc2_testing_common.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/adaptive_mode_level_estimator.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/signal_classifier.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/vector_float_frame.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/rnn_vad/sequence_buffer.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/rnn_vad/rnn.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/rnn_vad/test_utils.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/rnn_vad/pitch_info.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/rnn_vad/lp_residual.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/rnn_vad/ring_buffer.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/rnn_vad/symmetric_matrix_buffer.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/rnn_vad/spectral_features.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/rnn_vad/features_extraction.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/rnn_vad/common.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/rnn_vad/fft_util.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/rnn_vad/pitch_search.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/fixed_gain_controller.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/down_sampler.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/saturation_protector.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/agc2_common.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/adaptive_mode_level_estimator_agc.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/adaptive_digital_gain_applier.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/vad_with_level.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/limiter_db_gain_curve.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/fixed_digital_level_estimator.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/adaptive_agc.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/gain_applier.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/noise_level_estimator.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/compute_interpolated_gain_curve.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/noise_spectrum_estimator.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc2/limiter.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/transient/transient_detector.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/transient/transient_suppressor.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/transient/daubechies_8_wavelet_coeffs.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/transient/common.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/transient/wpd_node.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/transient/moving_moments.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/transient/wpd_tree.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/transient/dyadic_decimator.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/noise_suppression_impl.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec/aec_resampler.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec/echo_cancellation.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec/aec_core.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec/aec_core_optimized_methods.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec/aec_common.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/voice_detection_impl.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc/legacy/analog_agc.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc/legacy/gain_control.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc/legacy/digital_agc.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc/mock_agc.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc/loudness_histogram.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc/gain_map_internal.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc/utility.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc/agc_manager_direct.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/agc/agc.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/common.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/audio_buffer.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/echo_control_mobile_impl.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/splitting_filter.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/low_cut_filter.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/audio_generator/file_audio_generator.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/three_band_filter_bank.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/echo_cancellation_impl.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/level_estimator_impl.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/gain_controller2.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aecm/aecm_core.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aecm/aecm_defines.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aecm/echo_control_mobile.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/downsampled_render_buffer.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/subtractor_output_analyzer.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/residual_echo_estimator.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/shadow_filter_update_gain.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/aec_state.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/suppression_filter.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/block_delay_buffer.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/adaptive_fir_filter.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/cascaded_biquad_filter.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/matched_filter.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/subtractor_output.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/render_signal_analyzer.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/aec3_fft.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/echo_remover_metrics.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/filter_analyzer.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/subtractor.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/echo_path_delay_estimator.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/block_processor_metrics.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/fft_data.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/render_delay_controller_metrics.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/comfort_noise_generator.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/erl_estimator.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/echo_remover.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/matrix_buffer.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/reverb_model_estimator.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/echo_path_variability.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/moving_average.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/render_reverb_model.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/render_delay_controller.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/suppression_gain.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/erle_estimator.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/subband_erle_estimator.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/block_processor.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/fullband_erle_estimator.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/stationarity_estimator.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/echo_canceller3.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/skew_estimator.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/render_buffer.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/reverb_model_fallback.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/vector_buffer.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/reverb_frequency_response.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/echo_audibility.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/fft_buffer.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/aec3_common.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/vector_math.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/decimator.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/frame_blocker.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/block_framer.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/suppression_gain_limiter.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/delay_estimate.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/reverb_model.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/main_filter_update_gain.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/matched_filter_lag_aggregator.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/reverb_decay_estimator.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/aec3/render_delay_buffer.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/gain_control_impl.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/typing_detection.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/logging/apm_data_dumper.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/vad/vad_audio_proc_internal.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/vad/vad_circular_buffer.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/vad/pitch_based_vad.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/vad/pole_zero_filter.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/vad/gmm.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/vad/common.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/vad/vad_audio_proc.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/vad/voice_gmm_tables.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/vad/noise_gmm_tables.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/vad/pitch_internal.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/vad/standalone_vad.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/vad/voice_activity_detector.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/utility/ooura_fft_tables_neon_sse2.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/utility/delay_estimator_internal.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/utility/ooura_fft.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/utility/block_mean_calculator.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/utility/delay_estimator.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/utility/ooura_fft_tables_common.h \
|
||||||
|
webrtc_dsp/modules/audio_processing/utility/delay_estimator_wrapper.h \
|
||||||
|
webrtc_dsp/common_audio/mocks/mock_smoothing_filter.h \
|
||||||
|
webrtc_dsp/common_audio/wav_file.h \
|
||||||
|
webrtc_dsp/common_audio/sparse_fir_filter.h \
|
||||||
|
webrtc_dsp/common_audio/fir_filter_sse.h \
|
||||||
|
webrtc_dsp/common_audio/window_generator.h \
|
||||||
|
webrtc_dsp/common_audio/ring_buffer.h \
|
||||||
|
webrtc_dsp/common_audio/fir_filter.h \
|
||||||
|
webrtc_dsp/common_audio/include/audio_util.h \
|
||||||
|
webrtc_dsp/common_audio/real_fourier_ooura.h \
|
||||||
|
webrtc_dsp/common_audio/smoothing_filter.h \
|
||||||
|
webrtc_dsp/common_audio/resampler/sinc_resampler.h \
|
||||||
|
webrtc_dsp/common_audio/resampler/include/push_resampler.h \
|
||||||
|
webrtc_dsp/common_audio/resampler/include/resampler.h \
|
||||||
|
webrtc_dsp/common_audio/resampler/push_sinc_resampler.h \
|
||||||
|
webrtc_dsp/common_audio/resampler/sinusoidal_linear_chirp_source.h \
|
||||||
|
webrtc_dsp/common_audio/fir_filter_factory.h \
|
||||||
|
webrtc_dsp/common_audio/audio_converter.h \
|
||||||
|
webrtc_dsp/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.h \
|
||||||
|
webrtc_dsp/common_audio/third_party/fft4g/fft4g.h \
|
||||||
|
webrtc_dsp/common_audio/channel_buffer.h \
|
||||||
|
webrtc_dsp/common_audio/real_fourier.h \
|
||||||
|
webrtc_dsp/common_audio/fir_filter_neon.h \
|
||||||
|
webrtc_dsp/common_audio/fir_filter_c.h \
|
||||||
|
webrtc_dsp/common_audio/signal_processing/complex_fft_tables.h \
|
||||||
|
webrtc_dsp/common_audio/signal_processing/include/signal_processing_library.h \
|
||||||
|
webrtc_dsp/common_audio/signal_processing/include/real_fft.h \
|
||||||
|
webrtc_dsp/common_audio/signal_processing/include/spl_inl.h \
|
||||||
|
webrtc_dsp/common_audio/signal_processing/include/spl_inl_armv7.h \
|
||||||
|
webrtc_dsp/common_audio/signal_processing/dot_product_with_scale.h \
|
||||||
|
webrtc_dsp/common_audio/signal_processing/resample_by_2_internal.h \
|
||||||
|
webrtc_dsp/common_audio/wav_header.h \
|
||||||
|
webrtc_dsp/common_audio/vad/vad_core.h \
|
||||||
|
webrtc_dsp/common_audio/vad/include/vad.h \
|
||||||
|
webrtc_dsp/common_audio/vad/include/webrtc_vad.h \
|
||||||
|
webrtc_dsp/common_audio/vad/vad_gmm.h \
|
||||||
|
webrtc_dsp/common_audio/vad/vad_sp.h \
|
||||||
|
webrtc_dsp/common_audio/vad/vad_filterbank.h
|
||||||
|
|
||||||
|
else
|
||||||
|
CFLAGS += -DTGVOIP_NO_DSP
|
||||||
|
endif
|
||||||
|
|
||||||
|
libtgvoip_la_SOURCES = $(SRC) $(TGVOIP_HDRS)
|
||||||
|
tgvoipincludedir = $(includedir)/tgvoip
|
||||||
|
nobase_tgvoipinclude_HEADERS = $(TGVOIP_HDRS)
|
||||||
|
|
||||||
|
CXXFLAGS += -std=gnu++0x $(CFLAGS)
|
||||||
|
if TARGET_OS_OSX
|
||||||
|
OBJCFLAGS = $(CFLAGS)
|
||||||
|
OBJCXXFLAGS += -std=gnu++0x $(CFLAGS)
|
||||||
|
endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,209 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "logging.h"
|
||||||
|
#include "MediaStreamItf.h"
|
||||||
|
#include "EchoCanceller.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <math.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
using namespace tgvoip;
|
||||||
|
|
||||||
|
void MediaStreamItf::SetCallback(size_t (*f)(unsigned char *, size_t, void*), void* param){
|
||||||
|
callback=f;
|
||||||
|
callbackParam=param;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MediaStreamItf::InvokeCallback(unsigned char *data, size_t length){
|
||||||
|
if(callback)
|
||||||
|
return (*callback)(data, length, callbackParam);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioMixer::AudioMixer() : bufferPool(960*2, 16), processedQueue(16), semaphore(16, 0){
|
||||||
|
running=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioMixer::~AudioMixer(){
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioMixer::SetOutput(MediaStreamItf* output){
|
||||||
|
output->SetCallback(OutputCallback, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioMixer::Start(){
|
||||||
|
assert(!running);
|
||||||
|
running=true;
|
||||||
|
thread=new Thread(std::bind(&AudioMixer::RunThread, this));
|
||||||
|
thread->SetName("AudioMixer");
|
||||||
|
thread->Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioMixer::Stop(){
|
||||||
|
if(!running){
|
||||||
|
LOGE("Tried to stop AudioMixer that wasn't started");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
running=false;
|
||||||
|
semaphore.Release();
|
||||||
|
thread->Join();
|
||||||
|
delete thread;
|
||||||
|
thread=NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioMixer::DoCallback(unsigned char *data, size_t length){
|
||||||
|
//memset(data, 0, 960*2);
|
||||||
|
//LOGD("audio mixer callback, %d inputs", inputs.size());
|
||||||
|
if(processedQueue.Size()==0)
|
||||||
|
semaphore.Release(2);
|
||||||
|
else
|
||||||
|
semaphore.Release();
|
||||||
|
unsigned char* buf=processedQueue.GetBlocking();
|
||||||
|
memcpy(data, buf, 960*2);
|
||||||
|
bufferPool.Reuse(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AudioMixer::OutputCallback(unsigned char *data, size_t length, void *arg){
|
||||||
|
((AudioMixer*)arg)->DoCallback(data, length);
|
||||||
|
return 960*2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioMixer::AddInput(std::shared_ptr<MediaStreamItf> input){
|
||||||
|
MutexGuard m(inputsMutex);
|
||||||
|
MixerInput in;
|
||||||
|
in.multiplier=1;
|
||||||
|
in.source=input;
|
||||||
|
inputs.push_back(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioMixer::RemoveInput(std::shared_ptr<MediaStreamItf> input){
|
||||||
|
MutexGuard m(inputsMutex);
|
||||||
|
for(std::vector<MixerInput>::iterator i=inputs.begin();i!=inputs.end();++i){
|
||||||
|
if(i->source==input){
|
||||||
|
inputs.erase(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioMixer::SetInputVolume(std::shared_ptr<MediaStreamItf> input, float volumeDB){
|
||||||
|
MutexGuard m(inputsMutex);
|
||||||
|
for(std::vector<MixerInput>::iterator i=inputs.begin();i!=inputs.end();++i){
|
||||||
|
if(i->source==input){
|
||||||
|
if(volumeDB==-INFINITY)
|
||||||
|
i->multiplier=0;
|
||||||
|
else
|
||||||
|
i->multiplier=expf(volumeDB/20.0f * logf(10.0f));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioMixer::RunThread(){
|
||||||
|
LOGV("AudioMixer thread started");
|
||||||
|
while(running){
|
||||||
|
semaphore.Acquire();
|
||||||
|
if(!running)
|
||||||
|
break;
|
||||||
|
|
||||||
|
unsigned char* data=bufferPool.Get();
|
||||||
|
//LOGV("Audio mixer processing a frame");
|
||||||
|
if(!data){
|
||||||
|
LOGE("AudioMixer: no buffers left");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
MutexGuard m(inputsMutex);
|
||||||
|
int16_t* buf=reinterpret_cast<int16_t*>(data);
|
||||||
|
int16_t input[960];
|
||||||
|
float out[960];
|
||||||
|
memset(out, 0, 960*4);
|
||||||
|
int usedInputs=0;
|
||||||
|
for(std::vector<MixerInput>::iterator in=inputs.begin();in!=inputs.end();++in){
|
||||||
|
size_t res=in->source->InvokeCallback(reinterpret_cast<unsigned char*>(input), 960*2);
|
||||||
|
if(!res || in->multiplier==0){
|
||||||
|
//LOGV("AudioMixer: skipping silent packet");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
usedInputs++;
|
||||||
|
float k=in->multiplier;
|
||||||
|
if(k!=1){
|
||||||
|
for(size_t i=0; i<960; i++){
|
||||||
|
out[i]+=(float)input[i]*k;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
for(size_t i=0;i<960;i++){
|
||||||
|
out[i]+=(float)input[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(usedInputs>0){
|
||||||
|
for(size_t i=0; i<960; i++){
|
||||||
|
if(out[i]>32767.0f)
|
||||||
|
buf[i]=INT16_MAX;
|
||||||
|
else if(out[i]<-32768.0f)
|
||||||
|
buf[i]=INT16_MIN;
|
||||||
|
else
|
||||||
|
buf[i]=(int16_t)out[i];
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
memset(data, 0, 960*2);
|
||||||
|
}
|
||||||
|
if(echoCanceller)
|
||||||
|
echoCanceller->SpeakerOutCallback(data, 960*2);
|
||||||
|
processedQueue.Put(data);
|
||||||
|
}
|
||||||
|
LOGI("======== audio mixer thread exiting =========");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioMixer::SetEchoCanceller(EchoCanceller *aec){
|
||||||
|
echoCanceller=aec;
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioLevelMeter::AudioLevelMeter(){
|
||||||
|
absMax=0;
|
||||||
|
count=0;
|
||||||
|
currentLevel=0;
|
||||||
|
currentLevelFullRange=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
float AudioLevelMeter::GetLevel(){
|
||||||
|
return currentLevel/9.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioLevelMeter::Update(int16_t *samples, size_t count){
|
||||||
|
// Number of bars on the indicator.
|
||||||
|
// Note that the number of elements is specified because we are indexing it
|
||||||
|
// in the range of 0-32
|
||||||
|
const int8_t permutation[33]={0,1,2,3,4,4,5,5,5,5,6,6,6,6,6,7,7,7,7,8,8,8,9,9,9,9,9,9,9,9,9,9,9};
|
||||||
|
int16_t absValue=0;
|
||||||
|
for(unsigned int k=0;k<count;k++){
|
||||||
|
int16_t absolute=(int16_t)abs(samples[k]);
|
||||||
|
if (absolute>absValue)
|
||||||
|
absValue=absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(absValue>absMax)
|
||||||
|
absMax = absValue;
|
||||||
|
// Update level approximately 10 times per second
|
||||||
|
if (this->count++==10){
|
||||||
|
currentLevelFullRange=absMax;
|
||||||
|
this->count=0;
|
||||||
|
// Highest value for a int16_t is 0x7fff = 32767
|
||||||
|
// Divide with 1000 to get in the range of 0-32 which is the range of
|
||||||
|
// the permutation vector
|
||||||
|
int32_t position=absMax/1000;
|
||||||
|
// Make it less likely that the bar stays at position 0. I.e. only if
|
||||||
|
// its in the range 0-250 (instead of 0-1000)
|
||||||
|
/*if ((position==0) && (absMax>250)){
|
||||||
|
position=1;
|
||||||
|
}*/
|
||||||
|
currentLevel=permutation[position];
|
||||||
|
// Decay the absolute maximum (divide by 4)
|
||||||
|
absMax >>= 2;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LIBTGVOIP_MEDIASTREAMINPUT_H
|
||||||
|
#define LIBTGVOIP_MEDIASTREAMINPUT_H
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "threading.h"
|
||||||
|
#include "BlockingQueue.h"
|
||||||
|
#include "Buffers.h"
|
||||||
|
|
||||||
|
namespace tgvoip{
|
||||||
|
|
||||||
|
class EchoCanceller;
|
||||||
|
|
||||||
|
class MediaStreamItf{
|
||||||
|
public:
|
||||||
|
virtual void Start()=0;
|
||||||
|
virtual void Stop()=0;
|
||||||
|
void SetCallback(size_t (*f)(unsigned char*, size_t, void*), void* param);
|
||||||
|
|
||||||
|
//protected:
|
||||||
|
size_t InvokeCallback(unsigned char* data, size_t length);
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t (*callback)(unsigned char*, size_t, void*)=NULL;
|
||||||
|
void* callbackParam;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AudioMixer : public MediaStreamItf{
|
||||||
|
public:
|
||||||
|
AudioMixer();
|
||||||
|
virtual ~AudioMixer();
|
||||||
|
void SetOutput(MediaStreamItf* output);
|
||||||
|
virtual void Start();
|
||||||
|
virtual void Stop();
|
||||||
|
void AddInput(std::shared_ptr<MediaStreamItf> input);
|
||||||
|
void RemoveInput(std::shared_ptr<MediaStreamItf> input);
|
||||||
|
void SetInputVolume(std::shared_ptr<MediaStreamItf> input, float volumeDB);
|
||||||
|
void SetEchoCanceller(EchoCanceller* aec);
|
||||||
|
private:
|
||||||
|
void RunThread();
|
||||||
|
struct MixerInput{
|
||||||
|
std::shared_ptr<MediaStreamItf> source;
|
||||||
|
float multiplier;
|
||||||
|
};
|
||||||
|
Mutex inputsMutex;
|
||||||
|
void DoCallback(unsigned char* data, size_t length);
|
||||||
|
static size_t OutputCallback(unsigned char* data, size_t length, void* arg);
|
||||||
|
std::vector<MixerInput> inputs;
|
||||||
|
Thread* thread;
|
||||||
|
BufferPool bufferPool;
|
||||||
|
BlockingQueue<unsigned char*> processedQueue;
|
||||||
|
Semaphore semaphore;
|
||||||
|
EchoCanceller* echoCanceller;
|
||||||
|
bool running;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CallbackWrapper : public MediaStreamItf{
|
||||||
|
public:
|
||||||
|
CallbackWrapper(){};
|
||||||
|
virtual ~CallbackWrapper(){};
|
||||||
|
virtual void Start(){};
|
||||||
|
virtual void Stop(){};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class AudioLevelMeter{
|
||||||
|
public:
|
||||||
|
AudioLevelMeter();
|
||||||
|
float GetLevel();
|
||||||
|
void Update(int16_t* samples, size_t count);
|
||||||
|
private:
|
||||||
|
int16_t absMax;
|
||||||
|
int16_t count;
|
||||||
|
int8_t currentLevel;
|
||||||
|
int16_t currentLevelFullRange;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //LIBTGVOIP_MEDIASTREAMINPUT_H
|
|
@ -0,0 +1,183 @@
|
||||||
|
//
|
||||||
|
// Created by Grishka on 17.06.2018.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <float.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#include <sys/time.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "MessageThread.h"
|
||||||
|
#include "VoIPController.h"
|
||||||
|
#include "logging.h"
|
||||||
|
|
||||||
|
using namespace tgvoip;
|
||||||
|
|
||||||
|
MessageThread::MessageThread() : Thread(std::bind(&MessageThread::Run, this)){
|
||||||
|
|
||||||
|
SetName("MessageThread");
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY!=WINAPI_FAMILY_PHONE_APP
|
||||||
|
event=CreateEvent(NULL, false, false, NULL);
|
||||||
|
#else
|
||||||
|
event=CreateEventEx(NULL, NULL, 0, EVENT_ALL_ACCESS);
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
pthread_cond_init(&cond, NULL);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageThread::~MessageThread(){
|
||||||
|
Stop();
|
||||||
|
#ifdef _WIN32
|
||||||
|
CloseHandle(event);
|
||||||
|
#else
|
||||||
|
pthread_cond_destroy(&cond);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageThread::Stop(){
|
||||||
|
if(running){
|
||||||
|
running=false;
|
||||||
|
#ifdef _WIN32
|
||||||
|
SetEvent(event);
|
||||||
|
#else
|
||||||
|
pthread_cond_signal(&cond);
|
||||||
|
#endif
|
||||||
|
Join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageThread::Run(){
|
||||||
|
queueMutex.Lock();
|
||||||
|
while(running){
|
||||||
|
double currentTime=VoIPController::GetCurrentTime();
|
||||||
|
double waitTimeout=queue.empty() ? DBL_MAX : (queue[0].deliverAt-currentTime);
|
||||||
|
//LOGW("MessageThread wait timeout %f", waitTimeout);
|
||||||
|
if(waitTimeout>0.0){
|
||||||
|
#ifdef _WIN32
|
||||||
|
queueMutex.Unlock();
|
||||||
|
DWORD actualWaitTimeout=waitTimeout==DBL_MAX ? INFINITE : ((DWORD)round(waitTimeout*1000.0));
|
||||||
|
#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY!=WINAPI_FAMILY_PHONE_APP
|
||||||
|
WaitForSingleObject(event, actualWaitTimeout);
|
||||||
|
#else
|
||||||
|
WaitForSingleObjectEx(event, actualWaitTimeout, false);
|
||||||
|
#endif
|
||||||
|
// we don't really care if a context switch happens here and anything gets added to the queue by another thread
|
||||||
|
// since any new no-delay messages will get delivered on this iteration anyway
|
||||||
|
queueMutex.Lock();
|
||||||
|
#else
|
||||||
|
if(waitTimeout!=DBL_MAX){
|
||||||
|
struct timeval now;
|
||||||
|
struct timespec timeout;
|
||||||
|
gettimeofday(&now, NULL);
|
||||||
|
waitTimeout+=now.tv_sec;
|
||||||
|
waitTimeout+=(now.tv_usec/1000000.0);
|
||||||
|
timeout.tv_sec=(time_t)(floor(waitTimeout));
|
||||||
|
timeout.tv_nsec=(long)((waitTimeout-floor(waitTimeout))*1000000000.0);
|
||||||
|
pthread_cond_timedwait(&cond, queueMutex.NativeHandle(), &timeout);
|
||||||
|
}else{
|
||||||
|
pthread_cond_wait(&cond, queueMutex.NativeHandle());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if(!running){
|
||||||
|
queueMutex.Unlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
currentTime=VoIPController::GetCurrentTime();
|
||||||
|
std::vector<Message> msgsToDeliverNow;
|
||||||
|
for(std::vector<Message>::iterator m=queue.begin();m!=queue.end();){
|
||||||
|
if(m->deliverAt==0.0 || currentTime>=m->deliverAt){
|
||||||
|
msgsToDeliverNow.push_back(*m);
|
||||||
|
m=queue.erase(m);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
++m;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(Message& m:msgsToDeliverNow){
|
||||||
|
//LOGI("MessageThread delivering %u", m.msg);
|
||||||
|
cancelCurrent=false;
|
||||||
|
if(m.deliverAt==0.0)
|
||||||
|
m.deliverAt=VoIPController::GetCurrentTime();
|
||||||
|
if(m.func!=nullptr){
|
||||||
|
m.func();
|
||||||
|
}
|
||||||
|
if(!cancelCurrent && m.interval>0.0){
|
||||||
|
m.deliverAt+=m.interval;
|
||||||
|
InsertMessageInternal(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
queueMutex.Unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t MessageThread::Post(std::function<void()> func, double delay, double interval){
|
||||||
|
assert(delay>=0);
|
||||||
|
//LOGI("MessageThread post [function] delay %f", delay);
|
||||||
|
if(!IsCurrent()){
|
||||||
|
queueMutex.Lock();
|
||||||
|
}
|
||||||
|
double currentTime=VoIPController::GetCurrentTime();
|
||||||
|
Message m{lastMessageID++, delay==0.0 ? 0.0 : (currentTime+delay), interval, func};
|
||||||
|
InsertMessageInternal(m);
|
||||||
|
if(!IsCurrent()){
|
||||||
|
#ifdef _WIN32
|
||||||
|
SetEvent(event);
|
||||||
|
#else
|
||||||
|
pthread_cond_signal(&cond);
|
||||||
|
#endif
|
||||||
|
queueMutex.Unlock();
|
||||||
|
}
|
||||||
|
return m.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageThread::InsertMessageInternal(MessageThread::Message &m){
|
||||||
|
if(queue.empty()){
|
||||||
|
queue.push_back(m);
|
||||||
|
}else{
|
||||||
|
if(queue[0].deliverAt>m.deliverAt){
|
||||||
|
queue.insert(queue.begin(), m);
|
||||||
|
}else{
|
||||||
|
std::vector<Message>::iterator insertAfter=queue.begin();
|
||||||
|
for(; insertAfter!=queue.end(); ++insertAfter){
|
||||||
|
std::vector<Message>::iterator next=std::next(insertAfter);
|
||||||
|
if(next==queue.end() || (next->deliverAt>m.deliverAt && insertAfter->deliverAt<=m.deliverAt)){
|
||||||
|
queue.insert(next, m);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageThread::Cancel(uint32_t id){
|
||||||
|
if(!IsCurrent()){
|
||||||
|
queueMutex.Lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(std::vector<Message>::iterator m=queue.begin();m!=queue.end();){
|
||||||
|
if(m->id==id){
|
||||||
|
m=queue.erase(m);
|
||||||
|
}else{
|
||||||
|
++m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!IsCurrent()){
|
||||||
|
queueMutex.Unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageThread::CancelSelf(){
|
||||||
|
assert(IsCurrent());
|
||||||
|
cancelCurrent=true;
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
//
|
||||||
|
// Created by Grishka on 17.06.2018.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LIBTGVOIP_MESSAGETHREAD_H
|
||||||
|
#define LIBTGVOIP_MESSAGETHREAD_H
|
||||||
|
|
||||||
|
#include "threading.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include <vector>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace tgvoip{
|
||||||
|
class MessageThread : public Thread{
|
||||||
|
public:
|
||||||
|
TGVOIP_DISALLOW_COPY_AND_ASSIGN(MessageThread);
|
||||||
|
MessageThread();
|
||||||
|
virtual ~MessageThread();
|
||||||
|
uint32_t Post(std::function<void()> func, double delay=0, double interval=0);
|
||||||
|
void Cancel(uint32_t id);
|
||||||
|
void CancelSelf();
|
||||||
|
void Stop();
|
||||||
|
|
||||||
|
enum{
|
||||||
|
INVALID_ID=0
|
||||||
|
};
|
||||||
|
private:
|
||||||
|
struct Message{
|
||||||
|
uint32_t id;
|
||||||
|
double deliverAt;
|
||||||
|
double interval;
|
||||||
|
std::function<void()> func;
|
||||||
|
};
|
||||||
|
|
||||||
|
void Run();
|
||||||
|
void InsertMessageInternal(Message& m);
|
||||||
|
|
||||||
|
bool running=true;
|
||||||
|
std::vector<Message> queue;
|
||||||
|
Mutex queueMutex;
|
||||||
|
uint32_t lastMessageID=1;
|
||||||
|
bool cancelCurrent=false;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
HANDLE event;
|
||||||
|
#else
|
||||||
|
pthread_cond_t cond;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //LIBTGVOIP_MESSAGETHREAD_H
|
|
@ -0,0 +1,682 @@
|
||||||
|
//
|
||||||
|
// Created by Grishka on 29.03.17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "NetworkSocket.h"
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#include "os/windows/NetworkSocketWinsock.h"
|
||||||
|
#include <winsock2.h>
|
||||||
|
#else
|
||||||
|
#include "os/posix/NetworkSocketPosix.h"
|
||||||
|
#endif
|
||||||
|
#include "logging.h"
|
||||||
|
#include "VoIPServerConfig.h"
|
||||||
|
#include "VoIPController.h"
|
||||||
|
#include "Buffers.h"
|
||||||
|
|
||||||
|
#define MIN_UDP_PORT 16384
|
||||||
|
#define MAX_UDP_PORT 32768
|
||||||
|
|
||||||
|
using namespace tgvoip;
|
||||||
|
|
||||||
|
NetworkSocket::NetworkSocket(NetworkProtocol protocol) : protocol(protocol){
|
||||||
|
ipv6Timeout=ServerConfig::GetSharedInstance()->GetDouble("nat64_fallback_timeout", 3);
|
||||||
|
failed=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkSocket::~NetworkSocket(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string NetworkSocket::GetLocalInterfaceInfo(IPv4Address *inet4addr, IPv6Address *inet6addr){
|
||||||
|
std::string r="not implemented";
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t NetworkSocket::GenerateLocalPort(){
|
||||||
|
uint16_t rnd;
|
||||||
|
VoIPController::crypto.rand_bytes(reinterpret_cast<uint8_t*>(&rnd), 2);
|
||||||
|
return (uint16_t) ((rnd%(MAX_UDP_PORT-MIN_UDP_PORT))+MIN_UDP_PORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkSocket::SetMaxPriority(){
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NetworkSocket::IsFailed(){
|
||||||
|
return failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkSocket *NetworkSocket::Create(NetworkProtocol protocol){
|
||||||
|
#ifndef _WIN32
|
||||||
|
return new NetworkSocketPosix(protocol);
|
||||||
|
#else
|
||||||
|
return new NetworkSocketWinsock(protocol);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
IPv4Address *NetworkSocket::ResolveDomainName(std::string name){
|
||||||
|
#ifndef _WIN32
|
||||||
|
return NetworkSocketPosix::ResolveDomainName(name);
|
||||||
|
#else
|
||||||
|
return NetworkSocketWinsock::ResolveDomainName(name);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkSocket::GenerateTCPO2States(unsigned char* buffer, TCPO2State* recvState, TCPO2State* sendState){
|
||||||
|
memset(recvState, 0, sizeof(TCPO2State));
|
||||||
|
memset(sendState, 0, sizeof(TCPO2State));
|
||||||
|
unsigned char nonce[64];
|
||||||
|
uint32_t *first = reinterpret_cast<uint32_t*>(nonce), *second = first + 1;
|
||||||
|
uint32_t first1 = 0x44414548U, first2 = 0x54534f50U, first3 = 0x20544547U, first4 = 0x20544547U, first5 = 0xeeeeeeeeU;
|
||||||
|
uint32_t second1 = 0;
|
||||||
|
do {
|
||||||
|
VoIPController::crypto.rand_bytes(nonce, sizeof(nonce));
|
||||||
|
} while (*first == first1 || *first == first2 || *first == first3 || *first == first4 || *first == first5 || *second == second1 || *reinterpret_cast<unsigned char*>(nonce) == 0xef);
|
||||||
|
|
||||||
|
// prepare encryption key/iv
|
||||||
|
memcpy(sendState->key, nonce + 8, 32);
|
||||||
|
memcpy(sendState->iv, nonce + 8 + 32, 16);
|
||||||
|
|
||||||
|
// prepare decryption key/iv
|
||||||
|
char reversed[48];
|
||||||
|
memcpy(reversed, nonce + 8, sizeof(reversed));
|
||||||
|
std::reverse(reversed, reversed + sizeof(reversed));
|
||||||
|
memcpy(recvState->key, reversed, 32);
|
||||||
|
memcpy(recvState->iv, reversed + 32, 16);
|
||||||
|
|
||||||
|
// write protocol identifier
|
||||||
|
*reinterpret_cast<uint32_t*>(nonce + 56) = 0xefefefefU;
|
||||||
|
memcpy(buffer, nonce, 56);
|
||||||
|
EncryptForTCPO2(nonce, sizeof(nonce), sendState);
|
||||||
|
memcpy(buffer+56, nonce+56, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkSocket::EncryptForTCPO2(unsigned char *buffer, size_t len, TCPO2State *state){
|
||||||
|
VoIPController::crypto.aes_ctr_encrypt(buffer, len, state->key, state->iv, state->ecount, &state->num);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t NetworkSocket::Receive(unsigned char *buffer, size_t len){
|
||||||
|
NetworkPacket pkt={0};
|
||||||
|
pkt.data=buffer;
|
||||||
|
pkt.length=len;
|
||||||
|
Receive(&pkt);
|
||||||
|
return pkt.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t NetworkSocket::Send(unsigned char *buffer, size_t len){
|
||||||
|
NetworkPacket pkt={0};
|
||||||
|
pkt.data=buffer;
|
||||||
|
pkt.length=len;
|
||||||
|
Send(&pkt);
|
||||||
|
return pkt.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NetworkAddress::operator==(const NetworkAddress &other) const{
|
||||||
|
const IPv4Address* self4=dynamic_cast<const IPv4Address*>(this);
|
||||||
|
const IPv4Address* other4=dynamic_cast<const IPv4Address*>((NetworkAddress*)&other);
|
||||||
|
if(self4 && other4){
|
||||||
|
return self4->GetAddress()==other4->GetAddress();
|
||||||
|
}
|
||||||
|
const IPv6Address* self6=dynamic_cast<const IPv6Address*>(this);
|
||||||
|
const IPv6Address* other6=dynamic_cast<const IPv6Address*>((NetworkAddress*)&other);
|
||||||
|
if(self6 && other6){
|
||||||
|
return memcmp(self6->GetAddress(), other6->GetAddress(), 16)==0;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NetworkAddress::operator!=(const NetworkAddress &other) const{
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
IPv4Address::IPv4Address(std::string addr){
|
||||||
|
#ifndef _WIN32
|
||||||
|
this->address=NetworkSocketPosix::StringToV4Address(addr);
|
||||||
|
#else
|
||||||
|
this->address=NetworkSocketWinsock::StringToV4Address(addr);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
IPv4Address::IPv4Address(uint32_t addr){
|
||||||
|
this->address=addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPv4Address::IPv4Address(){
|
||||||
|
this->address=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string IPv4Address::ToString() const{
|
||||||
|
#ifndef _WIN32
|
||||||
|
return NetworkSocketPosix::V4AddressToString(address);
|
||||||
|
#else
|
||||||
|
return NetworkSocketWinsock::V4AddressToString(address);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPv4Address::PrefixMatches(const unsigned int prefix, const NetworkAddress &other) const{
|
||||||
|
const IPv4Address* v4=dynamic_cast<const IPv4Address*>(&other);
|
||||||
|
if(v4){
|
||||||
|
uint32_t mask=0xFFFFFFFF << (32-prefix);
|
||||||
|
return (address & mask) == (v4->address & mask);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*sockaddr &IPv4Address::ToSockAddr(uint16_t port){
|
||||||
|
sockaddr_in sa;
|
||||||
|
sa.sin_family=AF_INET;
|
||||||
|
sa.sin_addr=addr;
|
||||||
|
sa.sin_port=port;
|
||||||
|
return *((sockaddr *) &sa);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
uint32_t IPv4Address::GetAddress() const{
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPv4Address::IsEmpty() const{
|
||||||
|
return address==0;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPv6Address::IPv6Address(std::string addr){
|
||||||
|
#ifndef _WIN32
|
||||||
|
NetworkSocketPosix::StringToV6Address(addr, this->address);
|
||||||
|
#else
|
||||||
|
NetworkSocketWinsock::StringToV6Address(addr, this->address);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
IPv6Address::IPv6Address(const uint8_t* addr){
|
||||||
|
memcpy(address, addr, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
IPv6Address::IPv6Address(){
|
||||||
|
memset(address, 0, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string IPv6Address::ToString() const{
|
||||||
|
#ifndef _WIN32
|
||||||
|
return NetworkSocketPosix::V6AddressToString(address);
|
||||||
|
#else
|
||||||
|
return NetworkSocketWinsock::V6AddressToString(address);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPv6Address::PrefixMatches(const unsigned int prefix, const NetworkAddress &other) const{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPv6Address::IsEmpty() const{
|
||||||
|
const uint64_t* a=reinterpret_cast<const uint64_t*>(address);
|
||||||
|
return a[0]==0LL && a[1]==0LL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*sockaddr &IPv6Address::ToSockAddr(uint16_t port){
|
||||||
|
sockaddr_in6 sa;
|
||||||
|
sa.sin6_family=AF_INET6;
|
||||||
|
sa.sin6_addr=addr;
|
||||||
|
sa.sin6_port=port;
|
||||||
|
return *((sockaddr *) &sa);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
const uint8_t *IPv6Address::GetAddress() const{
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NetworkSocket::Select(std::vector<NetworkSocket *> &readFds, std::vector<NetworkSocket*> &writeFds, std::vector<NetworkSocket *> &errorFds, SocketSelectCanceller *canceller){
|
||||||
|
#ifndef _WIN32
|
||||||
|
return NetworkSocketPosix::Select(readFds, writeFds, errorFds, canceller);
|
||||||
|
#else
|
||||||
|
return NetworkSocketWinsock::Select(readFds, writeFds, errorFds, canceller);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
SocketSelectCanceller::~SocketSelectCanceller(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
SocketSelectCanceller *SocketSelectCanceller::Create(){
|
||||||
|
#ifndef _WIN32
|
||||||
|
return new SocketSelectCancellerPosix();
|
||||||
|
#else
|
||||||
|
return new SocketSelectCancellerWin32();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
NetworkSocketTCPObfuscated::NetworkSocketTCPObfuscated(NetworkSocket *wrapped) : NetworkSocketWrapper(PROTO_TCP){
|
||||||
|
this->wrapped=wrapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkSocketTCPObfuscated::~NetworkSocketTCPObfuscated(){
|
||||||
|
if(wrapped)
|
||||||
|
delete wrapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkSocket *NetworkSocketTCPObfuscated::GetWrapped(){
|
||||||
|
return wrapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkSocketTCPObfuscated::InitConnection(){
|
||||||
|
unsigned char buf[64];
|
||||||
|
GenerateTCPO2States(buf, &recvState, &sendState);
|
||||||
|
wrapped->Send(buf, 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkSocketTCPObfuscated::Send(NetworkPacket *packet){
|
||||||
|
BufferOutputStream os(packet->length+4);
|
||||||
|
size_t len=packet->length/4;
|
||||||
|
if(len<0x7F){
|
||||||
|
os.WriteByte((unsigned char)len);
|
||||||
|
}else{
|
||||||
|
os.WriteByte(0x7F);
|
||||||
|
os.WriteByte((unsigned char)(len & 0xFF));
|
||||||
|
os.WriteByte((unsigned char)((len >> 8) & 0xFF));
|
||||||
|
os.WriteByte((unsigned char)((len >> 16) & 0xFF));
|
||||||
|
}
|
||||||
|
os.WriteBytes(packet->data, packet->length);
|
||||||
|
EncryptForTCPO2(os.GetBuffer(), os.GetLength(), &sendState);
|
||||||
|
wrapped->Send(os.GetBuffer(), os.GetLength());
|
||||||
|
//LOGD("Sent %u bytes", os.GetLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NetworkSocketTCPObfuscated::OnReadyToSend(){
|
||||||
|
if(!initialized){
|
||||||
|
//LOGV("Initializing TCPO2 connection");
|
||||||
|
initialized=true;
|
||||||
|
InitConnection();
|
||||||
|
readyToSend=true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return wrapped->OnReadyToSend();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkSocketTCPObfuscated::Receive(NetworkPacket *packet){
|
||||||
|
unsigned char len1;
|
||||||
|
size_t packetLen=0;
|
||||||
|
size_t offset=0;
|
||||||
|
size_t len;
|
||||||
|
len=wrapped->Receive(&len1, 1);
|
||||||
|
if(len<=0){
|
||||||
|
packet->length=0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
EncryptForTCPO2(&len1, 1, &recvState);
|
||||||
|
|
||||||
|
if(len1<0x7F){
|
||||||
|
packetLen=(size_t)len1*4;
|
||||||
|
}else{
|
||||||
|
unsigned char len2[3];
|
||||||
|
len=wrapped->Receive(len2, 3);
|
||||||
|
if(len<=0){
|
||||||
|
packet->length=0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
EncryptForTCPO2(len2, 3, &recvState);
|
||||||
|
packetLen=((size_t)len2[0] | ((size_t)len2[1] << 8) | ((size_t)len2[2] << 16))*4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(packetLen>packet->length){
|
||||||
|
LOGW("packet too big to fit into buffer (%u vs %u)", (unsigned int)packetLen, (unsigned int)packet->length);
|
||||||
|
packet->length=0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(offset<packetLen){
|
||||||
|
len=wrapped->Receive(packet->data+offset, packetLen-offset);
|
||||||
|
if(len<=0){
|
||||||
|
packet->length=0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
offset+=len;
|
||||||
|
}
|
||||||
|
EncryptForTCPO2(packet->data, packetLen, &recvState);
|
||||||
|
//packet->address=&itr->address;
|
||||||
|
packet->length=packetLen;
|
||||||
|
//packet->port=itr->port;
|
||||||
|
packet->protocol=PROTO_TCP;
|
||||||
|
packet->address=wrapped->GetConnectedAddress();
|
||||||
|
packet->port=wrapped->GetConnectedPort();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkSocketTCPObfuscated::Open(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkSocketTCPObfuscated::Close(){
|
||||||
|
wrapped->Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkSocketTCPObfuscated::Connect(const NetworkAddress *address, uint16_t port){
|
||||||
|
wrapped->Connect(address, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NetworkSocketTCPObfuscated::IsFailed(){
|
||||||
|
return wrapped->IsFailed();
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkSocketSOCKS5Proxy::NetworkSocketSOCKS5Proxy(NetworkSocket *tcp, NetworkSocket *udp, std::string username, std::string password) : NetworkSocketWrapper(udp ? PROTO_UDP : PROTO_TCP){
|
||||||
|
this->tcp=tcp;
|
||||||
|
this->udp=udp;
|
||||||
|
this->username=username;
|
||||||
|
this->password=password;
|
||||||
|
connectedAddress=NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkSocketSOCKS5Proxy::~NetworkSocketSOCKS5Proxy(){
|
||||||
|
delete tcp;
|
||||||
|
if(connectedAddress)
|
||||||
|
delete connectedAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkSocketSOCKS5Proxy::Send(NetworkPacket *packet){
|
||||||
|
if(protocol==PROTO_TCP){
|
||||||
|
tcp->Send(packet);
|
||||||
|
}else if(protocol==PROTO_UDP){
|
||||||
|
unsigned char buf[1500];
|
||||||
|
BufferOutputStream out(buf, sizeof(buf));
|
||||||
|
out.WriteInt16(0); // RSV
|
||||||
|
out.WriteByte(0); // FRAG
|
||||||
|
const IPv4Address* v4=dynamic_cast<IPv4Address*>(packet->address);
|
||||||
|
const IPv6Address* v6=dynamic_cast<IPv6Address*>(packet->address);
|
||||||
|
if(v4){
|
||||||
|
out.WriteByte(1); // ATYP (IPv4)
|
||||||
|
out.WriteInt32(v4->GetAddress());
|
||||||
|
}else{
|
||||||
|
out.WriteByte(4); // ATYP (IPv6)
|
||||||
|
out.WriteBytes((unsigned char *) v6->GetAddress(), 16);
|
||||||
|
}
|
||||||
|
out.WriteInt16(htons(packet->port));
|
||||||
|
out.WriteBytes(packet->data, packet->length);
|
||||||
|
NetworkPacket p={0};
|
||||||
|
p.data=buf;
|
||||||
|
p.length=out.GetLength();
|
||||||
|
p.address=connectedAddress;
|
||||||
|
p.port=connectedPort;
|
||||||
|
p.protocol=PROTO_UDP;
|
||||||
|
udp->Send(&p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkSocketSOCKS5Proxy::Receive(NetworkPacket *packet){
|
||||||
|
if(protocol==PROTO_TCP){
|
||||||
|
tcp->Receive(packet);
|
||||||
|
packet->address=connectedAddress;
|
||||||
|
packet->port=connectedPort;
|
||||||
|
}else if(protocol==PROTO_UDP){
|
||||||
|
unsigned char buf[1500];
|
||||||
|
NetworkPacket p={0};
|
||||||
|
p.data=buf;
|
||||||
|
p.length=sizeof(buf);
|
||||||
|
udp->Receive(&p);
|
||||||
|
if(p.length && p.address && *p.address==*connectedAddress && p.port==connectedPort){
|
||||||
|
BufferInputStream in(buf, p.length);
|
||||||
|
in.ReadInt16(); // RSV
|
||||||
|
in.ReadByte(); // FRAG
|
||||||
|
unsigned char atyp=in.ReadByte();
|
||||||
|
if(atyp==1){ // IPv4
|
||||||
|
lastRecvdV4=IPv4Address((uint32_t) in.ReadInt32());
|
||||||
|
packet->address=&lastRecvdV4;
|
||||||
|
}else if(atyp==4){ // IPv6
|
||||||
|
unsigned char addr[16];
|
||||||
|
in.ReadBytes(addr, 16);
|
||||||
|
lastRecvdV6=IPv6Address(addr);
|
||||||
|
packet->address=&lastRecvdV6;
|
||||||
|
}
|
||||||
|
packet->port=ntohs(in.ReadInt16());
|
||||||
|
if(packet->length>=in.Remaining()){
|
||||||
|
packet->length=in.Remaining();
|
||||||
|
in.ReadBytes(packet->data, in.Remaining());
|
||||||
|
}else{
|
||||||
|
packet->length=0;
|
||||||
|
LOGW("socks5: received packet too big");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkSocketSOCKS5Proxy::Open(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkSocketSOCKS5Proxy::Close(){
|
||||||
|
tcp->Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkSocketSOCKS5Proxy::Connect(const NetworkAddress *address, uint16_t port){
|
||||||
|
const IPv4Address* v4=dynamic_cast<const IPv4Address*>(address);
|
||||||
|
const IPv6Address* v6=dynamic_cast<const IPv6Address*>(address);
|
||||||
|
connectedAddress=v4 ? (NetworkAddress*)new IPv4Address(*v4) : (NetworkAddress*)new IPv6Address(*v6);
|
||||||
|
connectedPort=port;
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkSocket *NetworkSocketSOCKS5Proxy::GetWrapped(){
|
||||||
|
return protocol==PROTO_TCP ? tcp : udp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkSocketSOCKS5Proxy::InitConnection(){
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NetworkSocketSOCKS5Proxy::IsFailed(){
|
||||||
|
return NetworkSocket::IsFailed() || tcp->IsFailed();
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkAddress *NetworkSocketSOCKS5Proxy::GetConnectedAddress(){
|
||||||
|
return connectedAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t NetworkSocketSOCKS5Proxy::GetConnectedPort(){
|
||||||
|
return connectedPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NetworkSocketSOCKS5Proxy::OnReadyToSend(){
|
||||||
|
//LOGV("on ready to send, state=%d", state);
|
||||||
|
unsigned char buf[1024];
|
||||||
|
if(state==ConnectionState::Initial){
|
||||||
|
BufferOutputStream p(buf, sizeof(buf));
|
||||||
|
p.WriteByte(5); // VER
|
||||||
|
if(!username.empty()){
|
||||||
|
p.WriteByte(2); // NMETHODS
|
||||||
|
p.WriteByte(0); // no auth
|
||||||
|
p.WriteByte(2); // user/pass
|
||||||
|
}else{
|
||||||
|
p.WriteByte(1); // NMETHODS
|
||||||
|
p.WriteByte(0); // no auth
|
||||||
|
}
|
||||||
|
tcp->Send(buf, p.GetLength());
|
||||||
|
state=ConnectionState::WaitingForAuthMethod;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return udp ? udp->OnReadyToSend() : tcp->OnReadyToSend();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NetworkSocketSOCKS5Proxy::OnReadyToReceive(){
|
||||||
|
//LOGV("on ready to receive state=%d", state);
|
||||||
|
unsigned char buf[1024];
|
||||||
|
if(state==ConnectionState::WaitingForAuthMethod){
|
||||||
|
size_t l=tcp->Receive(buf, sizeof(buf));
|
||||||
|
if(l<2 || tcp->IsFailed()){
|
||||||
|
failed=true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
BufferInputStream in(buf, l);
|
||||||
|
unsigned char ver=in.ReadByte();
|
||||||
|
unsigned char chosenMethod=in.ReadByte();
|
||||||
|
LOGV("socks5: VER=%02X, METHOD=%02X", ver, chosenMethod);
|
||||||
|
if(ver!=5){
|
||||||
|
LOGW("socks5: incorrect VER in response");
|
||||||
|
failed=true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(chosenMethod==0){
|
||||||
|
// connected, no further auth needed
|
||||||
|
SendConnectionCommand();
|
||||||
|
}else if(chosenMethod==2 && !username.empty()){
|
||||||
|
BufferOutputStream p(buf, sizeof(buf));
|
||||||
|
p.WriteByte(1); // VER
|
||||||
|
p.WriteByte((unsigned char)(username.length()>255 ? 255 : username.length())); // ULEN
|
||||||
|
p.WriteBytes((unsigned char*)username.c_str(), username.length()>255 ? 255 : username.length()); // UNAME
|
||||||
|
p.WriteByte((unsigned char)(password.length()>255 ? 255 : password.length())); // PLEN
|
||||||
|
p.WriteBytes((unsigned char*)password.c_str(), password.length()>255 ? 255 : password.length()); // PASSWD
|
||||||
|
tcp->Send(buf, p.GetLength());
|
||||||
|
state=ConnectionState::WaitingForAuthResult;
|
||||||
|
}else{
|
||||||
|
LOGW("socks5: unsupported auth method");
|
||||||
|
failed=true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}else if(state==ConnectionState::WaitingForAuthResult){
|
||||||
|
size_t l=tcp->Receive(buf, sizeof(buf));
|
||||||
|
if(l<2 || tcp->IsFailed()){
|
||||||
|
failed=true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
BufferInputStream in(buf, l);
|
||||||
|
uint8_t ver=in.ReadByte();
|
||||||
|
unsigned char status=in.ReadByte();
|
||||||
|
LOGV("socks5: auth response VER=%02X, STATUS=%02X", ver, status);
|
||||||
|
if(ver!=1){
|
||||||
|
LOGW("socks5: auth response VER is incorrect");
|
||||||
|
failed=true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(status!=0){
|
||||||
|
LOGW("socks5: username/password auth failed");
|
||||||
|
failed=true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LOGV("socks5: authentication succeeded");
|
||||||
|
SendConnectionCommand();
|
||||||
|
return false;
|
||||||
|
}else if(state==ConnectionState::WaitingForCommandResult){
|
||||||
|
size_t l=tcp->Receive(buf, sizeof(buf));
|
||||||
|
if(protocol==PROTO_TCP){
|
||||||
|
if(l<2 || tcp->IsFailed()){
|
||||||
|
LOGW("socks5: connect failed")
|
||||||
|
failed=true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
BufferInputStream in(buf, l);
|
||||||
|
unsigned char ver=in.ReadByte();
|
||||||
|
if(ver!=5){
|
||||||
|
LOGW("socks5: connect: wrong ver in response");
|
||||||
|
failed=true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
unsigned char rep=in.ReadByte();
|
||||||
|
if(rep!=0){
|
||||||
|
LOGW("socks5: connect: failed with error %02X", rep);
|
||||||
|
failed=true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LOGV("socks5: connect succeeded");
|
||||||
|
state=ConnectionState::Connected;
|
||||||
|
tcp=new NetworkSocketTCPObfuscated(tcp);
|
||||||
|
readyToSend=true;
|
||||||
|
return tcp->OnReadyToSend();
|
||||||
|
}else if(protocol==PROTO_UDP){
|
||||||
|
if(l<2 || tcp->IsFailed()){
|
||||||
|
LOGW("socks5: udp associate failed");
|
||||||
|
failed=true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try{
|
||||||
|
BufferInputStream in(buf, l);
|
||||||
|
unsigned char ver=in.ReadByte();
|
||||||
|
unsigned char rep=in.ReadByte();
|
||||||
|
if(ver!=5){
|
||||||
|
LOGW("socks5: udp associate: wrong ver in response");
|
||||||
|
failed=true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(rep!=0){
|
||||||
|
LOGW("socks5: udp associate failed with error %02X", rep);
|
||||||
|
failed=true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
in.ReadByte(); // RSV
|
||||||
|
unsigned char atyp=in.ReadByte();
|
||||||
|
if(atyp==1){
|
||||||
|
uint32_t addr=(uint32_t) in.ReadInt32();
|
||||||
|
connectedAddress=new IPv4Address(addr);
|
||||||
|
}else if(atyp==3){
|
||||||
|
unsigned char len=in.ReadByte();
|
||||||
|
char domain[256];
|
||||||
|
memset(domain, 0, sizeof(domain));
|
||||||
|
in.ReadBytes((unsigned char*)domain, len);
|
||||||
|
LOGD("address type is domain, address=%s", domain);
|
||||||
|
connectedAddress=ResolveDomainName(std::string(domain));
|
||||||
|
if(!connectedAddress){
|
||||||
|
LOGW("socks5: failed to resolve domain name '%s'", domain);
|
||||||
|
failed=true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}else if(atyp==4){
|
||||||
|
unsigned char addr[16];
|
||||||
|
in.ReadBytes(addr, 16);
|
||||||
|
connectedAddress=new IPv6Address(addr);
|
||||||
|
}else{
|
||||||
|
LOGW("socks5: unknown address type %d", atyp);
|
||||||
|
failed=true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
connectedPort=(uint16_t)ntohs(in.ReadInt16());
|
||||||
|
state=ConnectionState::Connected;
|
||||||
|
readyToSend=true;
|
||||||
|
LOGV("socks5: udp associate successful, given endpoint %s:%d", connectedAddress->ToString().c_str(), connectedPort);
|
||||||
|
}catch(std::out_of_range& x){
|
||||||
|
LOGW("socks5: udp associate response parse failed");
|
||||||
|
failed=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return udp ? udp->OnReadyToReceive() : tcp->OnReadyToReceive();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkSocketSOCKS5Proxy::SendConnectionCommand(){
|
||||||
|
unsigned char buf[1024];
|
||||||
|
if(protocol==PROTO_TCP){
|
||||||
|
BufferOutputStream out(buf, sizeof(buf));
|
||||||
|
out.WriteByte(5); // VER
|
||||||
|
out.WriteByte(1); // CMD (CONNECT)
|
||||||
|
out.WriteByte(0); // RSV
|
||||||
|
const IPv4Address* v4=dynamic_cast<const IPv4Address*>(connectedAddress);
|
||||||
|
const IPv6Address* v6=dynamic_cast<const IPv6Address*>(connectedAddress);
|
||||||
|
if(v4){
|
||||||
|
out.WriteByte(1); // ATYP (IPv4)
|
||||||
|
out.WriteInt32(v4->GetAddress());
|
||||||
|
}else if(v6){
|
||||||
|
out.WriteByte(4); // ATYP (IPv6)
|
||||||
|
out.WriteBytes((unsigned char*)v6->GetAddress(), 16);
|
||||||
|
}else{
|
||||||
|
LOGW("socks5: unknown address type");
|
||||||
|
failed=true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
out.WriteInt16(htons(connectedPort)); // DST.PORT
|
||||||
|
tcp->Send(buf, out.GetLength());
|
||||||
|
state=ConnectionState::WaitingForCommandResult;
|
||||||
|
}else if(protocol==PROTO_UDP){
|
||||||
|
LOGV("Sending udp associate");
|
||||||
|
BufferOutputStream out(buf, sizeof(buf));
|
||||||
|
out.WriteByte(5); // VER
|
||||||
|
out.WriteByte(3); // CMD (UDP ASSOCIATE)
|
||||||
|
out.WriteByte(0); // RSV
|
||||||
|
out.WriteByte(1); // ATYP (IPv4)
|
||||||
|
out.WriteInt32(0); // DST.ADDR
|
||||||
|
out.WriteInt16(0); // DST.PORT
|
||||||
|
tcp->Send(buf, out.GetLength());
|
||||||
|
state=ConnectionState::WaitingForCommandResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NetworkSocketSOCKS5Proxy::NeedSelectForSending(){
|
||||||
|
return state==ConnectionState::Initial || state==ConnectionState::Connected;
|
||||||
|
}
|
|
@ -0,0 +1,210 @@
|
||||||
|
//
|
||||||
|
// Created by Grishka on 29.03.17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LIBTGVOIP_NETWORKSOCKET_H
|
||||||
|
#define LIBTGVOIP_NETWORKSOCKET_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
namespace tgvoip {
|
||||||
|
|
||||||
|
enum NetworkProtocol{
|
||||||
|
PROTO_UDP=0,
|
||||||
|
PROTO_TCP
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TCPO2State{
|
||||||
|
unsigned char key[32];
|
||||||
|
unsigned char iv[16];
|
||||||
|
unsigned char ecount[16];
|
||||||
|
uint32_t num;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NetworkAddress{
|
||||||
|
public:
|
||||||
|
virtual std::string ToString() const =0;
|
||||||
|
bool operator==(const NetworkAddress& other) const;
|
||||||
|
bool operator!=(const NetworkAddress& other) const;
|
||||||
|
virtual ~NetworkAddress()=default;
|
||||||
|
virtual bool IsEmpty() const =0;
|
||||||
|
virtual bool PrefixMatches(const unsigned int prefix, const NetworkAddress& other) const =0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IPv4Address : public NetworkAddress{
|
||||||
|
public:
|
||||||
|
IPv4Address(std::string addr);
|
||||||
|
IPv4Address(uint32_t addr);
|
||||||
|
IPv4Address();
|
||||||
|
virtual std::string ToString() const override;
|
||||||
|
uint32_t GetAddress() const;
|
||||||
|
virtual bool IsEmpty() const override;
|
||||||
|
virtual bool PrefixMatches(const unsigned int prefix, const NetworkAddress& other) const override;
|
||||||
|
|
||||||
|
static const IPv4Address Broadcast(){
|
||||||
|
return IPv4Address(0xFFFFFFFF);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
uint32_t address;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IPv6Address : public NetworkAddress{
|
||||||
|
public:
|
||||||
|
IPv6Address(std::string addr);
|
||||||
|
IPv6Address(const uint8_t* addr);
|
||||||
|
IPv6Address();
|
||||||
|
virtual std::string ToString() const override;
|
||||||
|
const uint8_t* GetAddress() const;
|
||||||
|
virtual bool IsEmpty() const override;
|
||||||
|
virtual bool PrefixMatches(const unsigned int prefix, const NetworkAddress& other) const override;
|
||||||
|
private:
|
||||||
|
uint8_t address[16];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NetworkPacket{
|
||||||
|
unsigned char* data;
|
||||||
|
size_t length;
|
||||||
|
NetworkAddress* address;
|
||||||
|
uint16_t port;
|
||||||
|
NetworkProtocol protocol;
|
||||||
|
};
|
||||||
|
typedef struct NetworkPacket NetworkPacket;
|
||||||
|
|
||||||
|
class SocketSelectCanceller{
|
||||||
|
public:
|
||||||
|
virtual ~SocketSelectCanceller();
|
||||||
|
virtual void CancelSelect()=0;
|
||||||
|
static SocketSelectCanceller* Create();
|
||||||
|
};
|
||||||
|
|
||||||
|
class NetworkSocket{
|
||||||
|
public:
|
||||||
|
friend class NetworkSocketPosix;
|
||||||
|
friend class NetworkSocketWinsock;
|
||||||
|
|
||||||
|
TGVOIP_DISALLOW_COPY_AND_ASSIGN(NetworkSocket);
|
||||||
|
NetworkSocket(NetworkProtocol protocol);
|
||||||
|
virtual ~NetworkSocket();
|
||||||
|
virtual void Send(NetworkPacket* packet)=0;
|
||||||
|
virtual void Receive(NetworkPacket* packet)=0;
|
||||||
|
size_t Receive(unsigned char* buffer, size_t len);
|
||||||
|
size_t Send(unsigned char* buffer, size_t len);
|
||||||
|
virtual void Open()=0;
|
||||||
|
virtual void Close()=0;
|
||||||
|
virtual uint16_t GetLocalPort(){ return 0; };
|
||||||
|
virtual void Connect(const NetworkAddress* address, uint16_t port)=0;
|
||||||
|
virtual std::string GetLocalInterfaceInfo(IPv4Address* inet4addr, IPv6Address* inet6addr);
|
||||||
|
virtual void OnActiveInterfaceChanged(){};
|
||||||
|
virtual NetworkAddress* GetConnectedAddress(){ return NULL; };
|
||||||
|
virtual uint16_t GetConnectedPort(){ return 0; };
|
||||||
|
virtual void SetTimeouts(int sendTimeout, int recvTimeout){};
|
||||||
|
|
||||||
|
virtual bool IsFailed();
|
||||||
|
virtual bool IsReadyToSend(){
|
||||||
|
return readyToSend;
|
||||||
|
}
|
||||||
|
virtual bool OnReadyToSend(){ readyToSend=true; return true; };
|
||||||
|
virtual bool OnReadyToReceive(){ return true; };
|
||||||
|
void SetTimeout(double timeout){
|
||||||
|
this->timeout=timeout;
|
||||||
|
};
|
||||||
|
|
||||||
|
static NetworkSocket* Create(NetworkProtocol protocol);
|
||||||
|
static IPv4Address* ResolveDomainName(std::string name);
|
||||||
|
static bool Select(std::vector<NetworkSocket*>& readFds, std::vector<NetworkSocket*>& writeFds, std::vector<NetworkSocket*>& errorFds, SocketSelectCanceller* canceller);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual uint16_t GenerateLocalPort();
|
||||||
|
virtual void SetMaxPriority();
|
||||||
|
|
||||||
|
static void GenerateTCPO2States(unsigned char* buffer, TCPO2State* recvState, TCPO2State* sendState);
|
||||||
|
static void EncryptForTCPO2(unsigned char* buffer, size_t len, TCPO2State* state);
|
||||||
|
double ipv6Timeout;
|
||||||
|
unsigned char nat64Prefix[12];
|
||||||
|
bool failed;
|
||||||
|
bool readyToSend=false;
|
||||||
|
double lastSuccessfulOperationTime=0.0;
|
||||||
|
double timeout=0.0;
|
||||||
|
NetworkProtocol protocol;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NetworkSocketWrapper : public NetworkSocket{
|
||||||
|
public:
|
||||||
|
NetworkSocketWrapper(NetworkProtocol protocol) : NetworkSocket(protocol){};
|
||||||
|
virtual ~NetworkSocketWrapper(){};
|
||||||
|
virtual NetworkSocket* GetWrapped()=0;
|
||||||
|
virtual void InitConnection()=0;
|
||||||
|
virtual void SetNonBlocking(bool){};
|
||||||
|
};
|
||||||
|
|
||||||
|
class NetworkSocketTCPObfuscated : public NetworkSocketWrapper{
|
||||||
|
public:
|
||||||
|
NetworkSocketTCPObfuscated(NetworkSocket* wrapped);
|
||||||
|
virtual ~NetworkSocketTCPObfuscated();
|
||||||
|
virtual NetworkSocket* GetWrapped();
|
||||||
|
virtual void InitConnection();
|
||||||
|
virtual void Send(NetworkPacket *packet);
|
||||||
|
virtual void Receive(NetworkPacket *packet);
|
||||||
|
virtual void Open();
|
||||||
|
virtual void Close();
|
||||||
|
virtual void Connect(const NetworkAddress *address, uint16_t port);
|
||||||
|
virtual bool OnReadyToSend();
|
||||||
|
|
||||||
|
virtual bool IsFailed();
|
||||||
|
virtual bool IsReadyToSend(){
|
||||||
|
return readyToSend && wrapped->IsReadyToSend();
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
NetworkSocket* wrapped;
|
||||||
|
TCPO2State recvState;
|
||||||
|
TCPO2State sendState;
|
||||||
|
bool initialized=false;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NetworkSocketSOCKS5Proxy : public NetworkSocketWrapper{
|
||||||
|
public:
|
||||||
|
NetworkSocketSOCKS5Proxy(NetworkSocket* tcp, NetworkSocket* udp, std::string username, std::string password);
|
||||||
|
virtual ~NetworkSocketSOCKS5Proxy();
|
||||||
|
virtual void Send(NetworkPacket *packet);
|
||||||
|
virtual void Receive(NetworkPacket *packet);
|
||||||
|
virtual void Open();
|
||||||
|
virtual void Close();
|
||||||
|
virtual void Connect(const NetworkAddress *address, uint16_t port);
|
||||||
|
virtual NetworkSocket *GetWrapped();
|
||||||
|
virtual void InitConnection();
|
||||||
|
virtual bool IsFailed();
|
||||||
|
virtual NetworkAddress *GetConnectedAddress();
|
||||||
|
virtual uint16_t GetConnectedPort();
|
||||||
|
virtual bool OnReadyToSend();
|
||||||
|
virtual bool OnReadyToReceive();
|
||||||
|
|
||||||
|
bool NeedSelectForSending();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void SendConnectionCommand();
|
||||||
|
enum ConnectionState{
|
||||||
|
Initial,
|
||||||
|
WaitingForAuthMethod,
|
||||||
|
WaitingForAuthResult,
|
||||||
|
WaitingForCommandResult,
|
||||||
|
Connected
|
||||||
|
};
|
||||||
|
NetworkSocket* tcp;
|
||||||
|
NetworkSocket* udp;
|
||||||
|
std::string username;
|
||||||
|
std::string password;
|
||||||
|
NetworkAddress* connectedAddress;
|
||||||
|
uint16_t connectedPort;
|
||||||
|
ConnectionState state=ConnectionState::Initial;
|
||||||
|
|
||||||
|
IPv4Address lastRecvdV4;
|
||||||
|
IPv6Address lastRecvdV6;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //LIBTGVOIP_NETWORKSOCKET_H
|
|
@ -0,0 +1,290 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "OpusDecoder.h"
|
||||||
|
#include "audio/Resampler.h"
|
||||||
|
#include "logging.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <opus/opus.h>
|
||||||
|
#else
|
||||||
|
#include "opus.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "VoIPController.h"
|
||||||
|
|
||||||
|
#define PACKET_SIZE (960*2)
|
||||||
|
|
||||||
|
using namespace tgvoip;
|
||||||
|
|
||||||
|
tgvoip::OpusDecoder::OpusDecoder(const std::shared_ptr<MediaStreamItf>& dst, bool isAsync, bool needEC){
|
||||||
|
dst->SetCallback(OpusDecoder::Callback, this);
|
||||||
|
Initialize(isAsync, needEC);
|
||||||
|
}
|
||||||
|
|
||||||
|
tgvoip::OpusDecoder::OpusDecoder(const std::unique_ptr<MediaStreamItf>& dst, bool isAsync, bool needEC){
|
||||||
|
dst->SetCallback(OpusDecoder::Callback, this);
|
||||||
|
Initialize(isAsync, needEC);
|
||||||
|
}
|
||||||
|
|
||||||
|
tgvoip::OpusDecoder::OpusDecoder(MediaStreamItf* dst, bool isAsync, bool needEC){
|
||||||
|
dst->SetCallback(OpusDecoder::Callback, this);
|
||||||
|
Initialize(isAsync, needEC);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tgvoip::OpusDecoder::Initialize(bool isAsync, bool needEC){
|
||||||
|
async=isAsync;
|
||||||
|
if(async){
|
||||||
|
decodedQueue=new BlockingQueue<unsigned char*>(33);
|
||||||
|
bufferPool=new BufferPool(PACKET_SIZE, 32);
|
||||||
|
semaphore=new Semaphore(32, 0);
|
||||||
|
}else{
|
||||||
|
decodedQueue=NULL;
|
||||||
|
bufferPool=NULL;
|
||||||
|
semaphore=NULL;
|
||||||
|
}
|
||||||
|
dec=opus_decoder_create(48000, 1, NULL);
|
||||||
|
if(needEC)
|
||||||
|
ecDec=opus_decoder_create(48000, 1, NULL);
|
||||||
|
else
|
||||||
|
ecDec=NULL;
|
||||||
|
buffer=(unsigned char *) malloc(8192);
|
||||||
|
lastDecoded=NULL;
|
||||||
|
outputBufferSize=0;
|
||||||
|
echoCanceller=NULL;
|
||||||
|
frameDuration=20;
|
||||||
|
consecutiveLostPackets=0;
|
||||||
|
enableDTX=false;
|
||||||
|
silentPacketCount=0;
|
||||||
|
levelMeter=NULL;
|
||||||
|
nextLen=0;
|
||||||
|
running=false;
|
||||||
|
remainingDataLen=0;
|
||||||
|
processedBuffer=NULL;
|
||||||
|
prevWasEC=false;
|
||||||
|
prevLastSample=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
tgvoip::OpusDecoder::~OpusDecoder(){
|
||||||
|
opus_decoder_destroy(dec);
|
||||||
|
if(ecDec)
|
||||||
|
opus_decoder_destroy(ecDec);
|
||||||
|
free(buffer);
|
||||||
|
if(bufferPool)
|
||||||
|
delete bufferPool;
|
||||||
|
if(decodedQueue)
|
||||||
|
delete decodedQueue;
|
||||||
|
if(semaphore)
|
||||||
|
delete semaphore;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void tgvoip::OpusDecoder::SetEchoCanceller(EchoCanceller* canceller){
|
||||||
|
echoCanceller=canceller;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t tgvoip::OpusDecoder::Callback(unsigned char *data, size_t len, void *param){
|
||||||
|
return ((OpusDecoder*)param)->HandleCallback(data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t tgvoip::OpusDecoder::HandleCallback(unsigned char *data, size_t len){
|
||||||
|
if(async){
|
||||||
|
if(!running){
|
||||||
|
memset(data, 0, len);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if(outputBufferSize==0){
|
||||||
|
outputBufferSize=len;
|
||||||
|
int packetsNeeded;
|
||||||
|
if(len>PACKET_SIZE)
|
||||||
|
packetsNeeded=len/PACKET_SIZE;
|
||||||
|
else
|
||||||
|
packetsNeeded=1;
|
||||||
|
packetsNeeded*=2;
|
||||||
|
semaphore->Release(packetsNeeded);
|
||||||
|
}
|
||||||
|
assert(outputBufferSize==len && "output buffer size is supposed to be the same throughout callbacks");
|
||||||
|
if(len==PACKET_SIZE){
|
||||||
|
lastDecoded=(unsigned char *) decodedQueue->GetBlocking();
|
||||||
|
if(!lastDecoded)
|
||||||
|
return 0;
|
||||||
|
memcpy(data, lastDecoded, PACKET_SIZE);
|
||||||
|
bufferPool->Reuse(lastDecoded);
|
||||||
|
semaphore->Release();
|
||||||
|
if(silentPacketCount>0){
|
||||||
|
silentPacketCount--;
|
||||||
|
if(levelMeter)
|
||||||
|
levelMeter->Update(reinterpret_cast<int16_t *>(data), 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if(echoCanceller){
|
||||||
|
echoCanceller->SpeakerOutCallback(data, PACKET_SIZE);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
LOGE("Opus decoder buffer length != 960 samples");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
if(remainingDataLen==0 && silentPacketCount==0){
|
||||||
|
int duration=DecodeNextFrame();
|
||||||
|
remainingDataLen=(size_t) (duration/20*960*2);
|
||||||
|
}
|
||||||
|
if(silentPacketCount>0 || remainingDataLen==0 || !processedBuffer){
|
||||||
|
if(silentPacketCount>0)
|
||||||
|
silentPacketCount--;
|
||||||
|
memset(data, 0, 960*2);
|
||||||
|
if(levelMeter)
|
||||||
|
levelMeter->Update(reinterpret_cast<int16_t *>(data), 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
memcpy(data, processedBuffer, 960*2);
|
||||||
|
remainingDataLen-=960*2;
|
||||||
|
if(remainingDataLen>0){
|
||||||
|
memmove(processedBuffer, processedBuffer+960*2, remainingDataLen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(levelMeter)
|
||||||
|
levelMeter->Update(reinterpret_cast<int16_t *>(data), len/2);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void tgvoip::OpusDecoder::Start(){
|
||||||
|
if(!async)
|
||||||
|
return;
|
||||||
|
running=true;
|
||||||
|
thread=new Thread(std::bind(&tgvoip::OpusDecoder::RunThread, this));
|
||||||
|
thread->SetName("opus_decoder");
|
||||||
|
thread->SetMaxPriority();
|
||||||
|
thread->Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void tgvoip::OpusDecoder::Stop(){
|
||||||
|
if(!running || !async)
|
||||||
|
return;
|
||||||
|
running=false;
|
||||||
|
semaphore->Release();
|
||||||
|
thread->Join();
|
||||||
|
delete thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tgvoip::OpusDecoder::RunThread(){
|
||||||
|
int i;
|
||||||
|
LOGI("decoder: packets per frame %d", packetsPerFrame);
|
||||||
|
while(running){
|
||||||
|
int playbackDuration=DecodeNextFrame();
|
||||||
|
for(i=0;i<playbackDuration/20;i++){
|
||||||
|
semaphore->Acquire();
|
||||||
|
if(!running){
|
||||||
|
LOGI("==== decoder exiting ====");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsigned char *buf=bufferPool->Get();
|
||||||
|
if(buf){
|
||||||
|
if(remainingDataLen>0){
|
||||||
|
for(effects::AudioEffect*& effect:postProcEffects){
|
||||||
|
effect->Process(reinterpret_cast<int16_t*>(processedBuffer+(PACKET_SIZE*i)), 960);
|
||||||
|
}
|
||||||
|
memcpy(buf, processedBuffer+(PACKET_SIZE*i), PACKET_SIZE);
|
||||||
|
}else{
|
||||||
|
//LOGE("Error decoding, result=%d", size);
|
||||||
|
memset(buf, 0, PACKET_SIZE);
|
||||||
|
}
|
||||||
|
decodedQueue->Put(buf);
|
||||||
|
}else{
|
||||||
|
LOGW("decoder: no buffers left!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int tgvoip::OpusDecoder::DecodeNextFrame(){
|
||||||
|
int playbackDuration=0;
|
||||||
|
bool isEC=false;
|
||||||
|
size_t len=jitterBuffer->HandleOutput(buffer, 8192, 0, true, playbackDuration, isEC);
|
||||||
|
bool fec=false;
|
||||||
|
if(!len){
|
||||||
|
fec=true;
|
||||||
|
len=jitterBuffer->HandleOutput(buffer, 8192, 0, false, playbackDuration, isEC);
|
||||||
|
//if(len)
|
||||||
|
// LOGV("Trying FEC...");
|
||||||
|
}
|
||||||
|
int size;
|
||||||
|
if(len){
|
||||||
|
size=opus_decode(isEC ? ecDec : dec, buffer, len, (opus_int16 *) decodeBuffer, packetsPerFrame*960, fec ? 1 : 0);
|
||||||
|
consecutiveLostPackets=0;
|
||||||
|
if(prevWasEC!=isEC && size){
|
||||||
|
// It turns out the waveforms generated by the PLC feature are also great to help smooth out the
|
||||||
|
// otherwise audible transition between the frames from different decoders. Those are basically an extrapolation
|
||||||
|
// of the previous successfully decoded data -- which is exactly what we need here.
|
||||||
|
size=opus_decode(prevWasEC ? ecDec : dec, NULL, 0, (opus_int16*)nextBuffer, packetsPerFrame*960, 0);
|
||||||
|
if(size){
|
||||||
|
int16_t* plcSamples=reinterpret_cast<int16_t*>(nextBuffer);
|
||||||
|
int16_t* samples=reinterpret_cast<int16_t*>(decodeBuffer);
|
||||||
|
constexpr float coeffs[]={0.999802, 0.995062, 0.984031, 0.966778, 0.943413, 0.914084, 0.878975, 0.838309, 0.792344,
|
||||||
|
0.741368, 0.685706, 0.625708, 0.561754, 0.494249, 0.423619, 0.350311, 0.274788, 0.197527, 0.119018, 0.039757};
|
||||||
|
for(int i=0;i<20;i++){
|
||||||
|
samples[i]=(int16_t)round((plcSamples[i]*coeffs[i]+(float)samples[i]*(1.0-coeffs[i])));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prevWasEC=isEC;
|
||||||
|
prevLastSample=decodeBuffer[size-1];
|
||||||
|
}else{ // do packet loss concealment
|
||||||
|
consecutiveLostPackets++;
|
||||||
|
if(consecutiveLostPackets>2 && enableDTX){
|
||||||
|
silentPacketCount+=packetsPerFrame;
|
||||||
|
size=packetsPerFrame*960;
|
||||||
|
}else{
|
||||||
|
size=opus_decode(prevWasEC ? ecDec : dec, NULL, 0, (opus_int16 *) decodeBuffer, packetsPerFrame*960, 0);
|
||||||
|
//LOGV("PLC");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(size<0)
|
||||||
|
LOGW("decoder: opus_decode error %d", size);
|
||||||
|
remainingDataLen=size;
|
||||||
|
if(playbackDuration==80){
|
||||||
|
processedBuffer=buffer;
|
||||||
|
audio::Resampler::Rescale60To80((int16_t*) decodeBuffer, (int16_t*) processedBuffer);
|
||||||
|
}else if(playbackDuration==40){
|
||||||
|
processedBuffer=buffer;
|
||||||
|
audio::Resampler::Rescale60To40((int16_t*) decodeBuffer, (int16_t*) processedBuffer);
|
||||||
|
}else{
|
||||||
|
processedBuffer=decodeBuffer;
|
||||||
|
}
|
||||||
|
return playbackDuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void tgvoip::OpusDecoder::SetFrameDuration(uint32_t duration){
|
||||||
|
frameDuration=duration;
|
||||||
|
packetsPerFrame=frameDuration/20;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void tgvoip::OpusDecoder::SetJitterBuffer(std::shared_ptr<JitterBuffer> jitterBuffer){
|
||||||
|
this->jitterBuffer=jitterBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tgvoip::OpusDecoder::SetDTX(bool enable){
|
||||||
|
enableDTX=enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tgvoip::OpusDecoder::SetLevelMeter(AudioLevelMeter *levelMeter){
|
||||||
|
this->levelMeter=levelMeter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tgvoip::OpusDecoder::AddAudioEffect(effects::AudioEffect *effect){
|
||||||
|
postProcEffects.push_back(effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tgvoip::OpusDecoder::RemoveAudioEffect(effects::AudioEffect *effect){
|
||||||
|
std::vector<effects::AudioEffect*>::iterator i=std::find(postProcEffects.begin(), postProcEffects.end(), effect);
|
||||||
|
if(i!=postProcEffects.end())
|
||||||
|
postProcEffects.erase(i);
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LIBTGVOIP_OPUSDECODER_H
|
||||||
|
#define LIBTGVOIP_OPUSDECODER_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "MediaStreamItf.h"
|
||||||
|
#include "threading.h"
|
||||||
|
#include "BlockingQueue.h"
|
||||||
|
#include "Buffers.h"
|
||||||
|
#include "EchoCanceller.h"
|
||||||
|
#include "JitterBuffer.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
struct OpusDecoder;
|
||||||
|
|
||||||
|
namespace tgvoip{
|
||||||
|
class OpusDecoder {
|
||||||
|
public:
|
||||||
|
TGVOIP_DISALLOW_COPY_AND_ASSIGN(OpusDecoder);
|
||||||
|
virtual void Start();
|
||||||
|
|
||||||
|
virtual void Stop();
|
||||||
|
|
||||||
|
OpusDecoder(const std::shared_ptr<MediaStreamItf>& dst, bool isAsync, bool needEC);
|
||||||
|
OpusDecoder(const std::unique_ptr<MediaStreamItf>& dst, bool isAsync, bool needEC);
|
||||||
|
OpusDecoder(MediaStreamItf* dst, bool isAsync, bool needEC);
|
||||||
|
virtual ~OpusDecoder();
|
||||||
|
size_t HandleCallback(unsigned char* data, size_t len);
|
||||||
|
void SetEchoCanceller(EchoCanceller* canceller);
|
||||||
|
void SetFrameDuration(uint32_t duration);
|
||||||
|
void SetJitterBuffer(std::shared_ptr<JitterBuffer> jitterBuffer);
|
||||||
|
void SetDTX(bool enable);
|
||||||
|
void SetLevelMeter(AudioLevelMeter* levelMeter);
|
||||||
|
void AddAudioEffect(effects::AudioEffect* effect);
|
||||||
|
void RemoveAudioEffect(effects::AudioEffect* effect);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Initialize(bool isAsync, bool needEC);
|
||||||
|
static size_t Callback(unsigned char* data, size_t len, void* param);
|
||||||
|
void RunThread();
|
||||||
|
int DecodeNextFrame();
|
||||||
|
::OpusDecoder* dec;
|
||||||
|
::OpusDecoder* ecDec;
|
||||||
|
BlockingQueue<unsigned char*>* decodedQueue;
|
||||||
|
BufferPool* bufferPool;
|
||||||
|
unsigned char* buffer;
|
||||||
|
unsigned char* lastDecoded;
|
||||||
|
unsigned char* processedBuffer;
|
||||||
|
size_t outputBufferSize;
|
||||||
|
bool running;
|
||||||
|
Thread* thread;
|
||||||
|
Semaphore* semaphore;
|
||||||
|
uint32_t frameDuration;
|
||||||
|
EchoCanceller* echoCanceller;
|
||||||
|
std::shared_ptr<JitterBuffer> jitterBuffer;
|
||||||
|
AudioLevelMeter* levelMeter;
|
||||||
|
int consecutiveLostPackets;
|
||||||
|
bool enableDTX;
|
||||||
|
size_t silentPacketCount;
|
||||||
|
std::vector<effects::AudioEffect*> postProcEffects;
|
||||||
|
bool async;
|
||||||
|
unsigned char nextBuffer[8192];
|
||||||
|
unsigned char decodeBuffer[8192];
|
||||||
|
size_t nextLen;
|
||||||
|
unsigned int packetsPerFrame;
|
||||||
|
ptrdiff_t remainingDataLen;
|
||||||
|
bool prevWasEC;
|
||||||
|
int16_t prevLastSample;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //LIBTGVOIP_OPUSDECODER_H
|
|
@ -0,0 +1,270 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "OpusEncoder.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include "logging.h"
|
||||||
|
#include "VoIPServerConfig.h"
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <opus/opus.h>
|
||||||
|
#else
|
||||||
|
#include "opus.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace{
|
||||||
|
int serverConfigValueToBandwidth(int config){
|
||||||
|
switch(config){
|
||||||
|
case 0:
|
||||||
|
return OPUS_BANDWIDTH_NARROWBAND;
|
||||||
|
case 1:
|
||||||
|
return OPUS_BANDWIDTH_MEDIUMBAND;
|
||||||
|
case 2:
|
||||||
|
return OPUS_BANDWIDTH_WIDEBAND;
|
||||||
|
case 3:
|
||||||
|
return OPUS_BANDWIDTH_SUPERWIDEBAND;
|
||||||
|
case 4:
|
||||||
|
default:
|
||||||
|
return OPUS_BANDWIDTH_FULLBAND;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tgvoip::OpusEncoder::OpusEncoder(MediaStreamItf *source, bool needSecondary):queue(11), bufferPool(960*2, 10){
|
||||||
|
this->source=source;
|
||||||
|
source->SetCallback(tgvoip::OpusEncoder::Callback, this);
|
||||||
|
enc=opus_encoder_create(48000, 1, OPUS_APPLICATION_VOIP, NULL);
|
||||||
|
opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(10));
|
||||||
|
opus_encoder_ctl(enc, OPUS_SET_PACKET_LOSS_PERC(1));
|
||||||
|
opus_encoder_ctl(enc, OPUS_SET_INBAND_FEC(1));
|
||||||
|
opus_encoder_ctl(enc, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE));
|
||||||
|
opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_FULLBAND));
|
||||||
|
requestedBitrate=20000;
|
||||||
|
currentBitrate=0;
|
||||||
|
running=false;
|
||||||
|
echoCanceller=NULL;
|
||||||
|
complexity=10;
|
||||||
|
frameDuration=20;
|
||||||
|
levelMeter=NULL;
|
||||||
|
vadNoVoiceBitrate=static_cast<uint32_t>(ServerConfig::GetSharedInstance()->GetInt("audio_vad_no_voice_bitrate", 6000));
|
||||||
|
vadModeVoiceBandwidth=serverConfigValueToBandwidth(ServerConfig::GetSharedInstance()->GetInt("audio_vad_bandwidth", 3));
|
||||||
|
vadModeNoVoiceBandwidth=serverConfigValueToBandwidth(ServerConfig::GetSharedInstance()->GetInt("audio_vad_no_voice_bandwidth", 0));
|
||||||
|
secondaryEnabledBandwidth=serverConfigValueToBandwidth(ServerConfig::GetSharedInstance()->GetInt("audio_extra_ec_bandwidth", 2));
|
||||||
|
secondaryEncoderEnabled=false;
|
||||||
|
|
||||||
|
if(needSecondary){
|
||||||
|
secondaryEncoder=opus_encoder_create(48000, 1, OPUS_APPLICATION_VOIP, NULL);
|
||||||
|
opus_encoder_ctl(secondaryEncoder, OPUS_SET_COMPLEXITY(10));
|
||||||
|
opus_encoder_ctl(secondaryEncoder, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE));
|
||||||
|
//opus_encoder_ctl(secondaryEncoder, OPUS_SET_VBR(0));
|
||||||
|
opus_encoder_ctl(secondaryEncoder, OPUS_SET_BITRATE(8000));
|
||||||
|
opus_encoder_ctl(secondaryEncoder, OPUS_SET_BANDWIDTH(secondaryEnabledBandwidth));
|
||||||
|
}else{
|
||||||
|
secondaryEncoder=NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tgvoip::OpusEncoder::~OpusEncoder(){
|
||||||
|
opus_encoder_destroy(enc);
|
||||||
|
if(secondaryEncoder)
|
||||||
|
opus_encoder_destroy(secondaryEncoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tgvoip::OpusEncoder::Start(){
|
||||||
|
if(running)
|
||||||
|
return;
|
||||||
|
running=true;
|
||||||
|
thread=new Thread(std::bind(&tgvoip::OpusEncoder::RunThread, this));
|
||||||
|
thread->SetName("OpusEncoder");
|
||||||
|
thread->Start();
|
||||||
|
thread->SetMaxPriority();
|
||||||
|
}
|
||||||
|
|
||||||
|
void tgvoip::OpusEncoder::Stop(){
|
||||||
|
if(!running)
|
||||||
|
return;
|
||||||
|
running=false;
|
||||||
|
queue.Put(NULL);
|
||||||
|
thread->Join();
|
||||||
|
delete thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void tgvoip::OpusEncoder::SetBitrate(uint32_t bitrate){
|
||||||
|
requestedBitrate=bitrate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tgvoip::OpusEncoder::Encode(int16_t* data, size_t len){
|
||||||
|
if(requestedBitrate!=currentBitrate){
|
||||||
|
opus_encoder_ctl(enc, OPUS_SET_BITRATE(requestedBitrate));
|
||||||
|
currentBitrate=requestedBitrate;
|
||||||
|
LOGV("opus_encoder: setting bitrate to %u", currentBitrate);
|
||||||
|
}
|
||||||
|
if(levelMeter)
|
||||||
|
levelMeter->Update(data, len);
|
||||||
|
if(secondaryEncoderEnabled!=wasSecondaryEncoderEnabled){
|
||||||
|
wasSecondaryEncoderEnabled=secondaryEncoderEnabled;
|
||||||
|
opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(secondaryEncoderEnabled ? secondaryEnabledBandwidth : OPUS_BANDWIDTH_FULLBAND));
|
||||||
|
}
|
||||||
|
int32_t r=opus_encode(enc, data, static_cast<int>(len), buffer, 4096);
|
||||||
|
if(r<=0){
|
||||||
|
LOGE("Error encoding: %d", r);
|
||||||
|
}else if(r==1){
|
||||||
|
LOGW("DTX");
|
||||||
|
}else if(running){
|
||||||
|
//LOGV("Packet size = %d", r);
|
||||||
|
int32_t secondaryLen=0;
|
||||||
|
unsigned char secondaryBuffer[128];
|
||||||
|
if(secondaryEncoderEnabled && secondaryEncoder){
|
||||||
|
secondaryLen=opus_encode(secondaryEncoder, data, static_cast<int>(len), secondaryBuffer, sizeof(secondaryBuffer));
|
||||||
|
//LOGV("secondaryLen %d", secondaryLen);
|
||||||
|
}
|
||||||
|
InvokeCallback(buffer, (size_t)r, secondaryBuffer, (size_t)secondaryLen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t tgvoip::OpusEncoder::Callback(unsigned char *data, size_t len, void* param){
|
||||||
|
OpusEncoder* e=(OpusEncoder*)param;
|
||||||
|
unsigned char* buf=e->bufferPool.Get();
|
||||||
|
if(buf){
|
||||||
|
assert(len==960*2);
|
||||||
|
memcpy(buf, data, 960*2);
|
||||||
|
e->queue.Put(buf);
|
||||||
|
}else{
|
||||||
|
LOGW("opus_encoder: no buffer slots left");
|
||||||
|
if(e->complexity>1){
|
||||||
|
e->complexity--;
|
||||||
|
opus_encoder_ctl(e->enc, OPUS_SET_COMPLEXITY(e->complexity));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t tgvoip::OpusEncoder::GetBitrate(){
|
||||||
|
return requestedBitrate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tgvoip::OpusEncoder::SetEchoCanceller(EchoCanceller* aec){
|
||||||
|
echoCanceller=aec;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tgvoip::OpusEncoder::RunThread(){
|
||||||
|
uint32_t bufferedCount=0;
|
||||||
|
uint32_t packetsPerFrame=frameDuration/20;
|
||||||
|
LOGV("starting encoder, packets per frame=%d", packetsPerFrame);
|
||||||
|
int16_t* frame;
|
||||||
|
if(packetsPerFrame>1)
|
||||||
|
frame=(int16_t*) malloc(960*2*packetsPerFrame);
|
||||||
|
else
|
||||||
|
frame=NULL;
|
||||||
|
bool frameHasVoice=false;
|
||||||
|
bool wasVadMode=false;
|
||||||
|
while(running){
|
||||||
|
int16_t* packet=(int16_t*)queue.GetBlocking();
|
||||||
|
if(packet){
|
||||||
|
bool hasVoice=true;
|
||||||
|
if(echoCanceller)
|
||||||
|
echoCanceller->ProcessInput(packet, 960, hasVoice);
|
||||||
|
if(!postProcEffects.empty()){
|
||||||
|
for(effects::AudioEffect* effect:postProcEffects){
|
||||||
|
effect->Process(packet, 960);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(packetsPerFrame==1){
|
||||||
|
Encode(packet, 960);
|
||||||
|
}else{
|
||||||
|
memcpy(frame+(960*bufferedCount), packet, 960*2);
|
||||||
|
frameHasVoice=frameHasVoice || hasVoice;
|
||||||
|
bufferedCount++;
|
||||||
|
if(bufferedCount==packetsPerFrame){
|
||||||
|
if(vadMode){
|
||||||
|
if(frameHasVoice){
|
||||||
|
opus_encoder_ctl(enc, OPUS_SET_BITRATE(currentBitrate));
|
||||||
|
opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(vadModeVoiceBandwidth));
|
||||||
|
if(secondaryEncoder){
|
||||||
|
opus_encoder_ctl(secondaryEncoder, OPUS_SET_BITRATE(currentBitrate));
|
||||||
|
opus_encoder_ctl(secondaryEncoder, OPUS_SET_BANDWIDTH(vadModeVoiceBandwidth));
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
opus_encoder_ctl(enc, OPUS_SET_BITRATE(vadNoVoiceBitrate));
|
||||||
|
opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(vadModeNoVoiceBandwidth));
|
||||||
|
if(secondaryEncoder){
|
||||||
|
opus_encoder_ctl(secondaryEncoder, OPUS_SET_BITRATE(vadNoVoiceBitrate));
|
||||||
|
opus_encoder_ctl(secondaryEncoder, OPUS_SET_BANDWIDTH(vadModeNoVoiceBandwidth));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wasVadMode=true;
|
||||||
|
}else if(wasVadMode){
|
||||||
|
wasVadMode=false;
|
||||||
|
opus_encoder_ctl(enc, OPUS_SET_BITRATE(currentBitrate));
|
||||||
|
opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(secondaryEncoderEnabled ? secondaryEnabledBandwidth : OPUS_AUTO));
|
||||||
|
if(secondaryEncoder){
|
||||||
|
opus_encoder_ctl(secondaryEncoder, OPUS_SET_BITRATE(currentBitrate));
|
||||||
|
opus_encoder_ctl(secondaryEncoder, OPUS_SET_BANDWIDTH(secondaryEnabledBandwidth));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Encode(frame, 960*packetsPerFrame);
|
||||||
|
bufferedCount=0;
|
||||||
|
frameHasVoice=false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bufferPool.Reuse(reinterpret_cast<unsigned char *>(packet));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(frame)
|
||||||
|
free(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void tgvoip::OpusEncoder::SetOutputFrameDuration(uint32_t duration){
|
||||||
|
frameDuration=duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void tgvoip::OpusEncoder::SetPacketLoss(int percent){
|
||||||
|
packetLossPercent=std::min(20, percent);
|
||||||
|
opus_encoder_ctl(enc, OPUS_SET_PACKET_LOSS_PERC(packetLossPercent));
|
||||||
|
opus_encoder_ctl(enc, OPUS_SET_INBAND_FEC(percent>0 && !secondaryEncoderEnabled ? 1 : 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
int tgvoip::OpusEncoder::GetPacketLoss(){
|
||||||
|
return packetLossPercent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tgvoip::OpusEncoder::SetDTX(bool enable){
|
||||||
|
opus_encoder_ctl(enc, OPUS_SET_DTX(enable ? 1 : 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tgvoip::OpusEncoder::SetLevelMeter(tgvoip::AudioLevelMeter *levelMeter){
|
||||||
|
this->levelMeter=levelMeter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tgvoip::OpusEncoder::SetCallback(void (*f)(unsigned char *, size_t, unsigned char *, size_t, void *), void *param){
|
||||||
|
callback=f;
|
||||||
|
callbackParam=param;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tgvoip::OpusEncoder::InvokeCallback(unsigned char *data, size_t length, unsigned char *secondaryData, size_t secondaryLength){
|
||||||
|
callback(data, length, secondaryData, secondaryLength, callbackParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tgvoip::OpusEncoder::SetSecondaryEncoderEnabled(bool enabled){
|
||||||
|
secondaryEncoderEnabled=enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tgvoip::OpusEncoder::SetVadMode(bool vad){
|
||||||
|
vadMode=vad;
|
||||||
|
}
|
||||||
|
void tgvoip::OpusEncoder::AddAudioEffect(effects::AudioEffect *effect){
|
||||||
|
postProcEffects.push_back(effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tgvoip::OpusEncoder::RemoveAudioEffect(effects::AudioEffect *effect){
|
||||||
|
std::vector<effects::AudioEffect*>::iterator i=std::find(postProcEffects.begin(), postProcEffects.end(), effect);
|
||||||
|
if(i!=postProcEffects.end())
|
||||||
|
postProcEffects.erase(i);
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LIBTGVOIP_OPUSENCODER_H
|
||||||
|
#define LIBTGVOIP_OPUSENCODER_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "MediaStreamItf.h"
|
||||||
|
#include "threading.h"
|
||||||
|
#include "BlockingQueue.h"
|
||||||
|
#include "Buffers.h"
|
||||||
|
#include "EchoCanceller.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct OpusEncoder;
|
||||||
|
|
||||||
|
namespace tgvoip{
|
||||||
|
class OpusEncoder{
|
||||||
|
public:
|
||||||
|
TGVOIP_DISALLOW_COPY_AND_ASSIGN(OpusEncoder);
|
||||||
|
OpusEncoder(MediaStreamItf* source, bool needSecondary);
|
||||||
|
virtual ~OpusEncoder();
|
||||||
|
virtual void Start();
|
||||||
|
virtual void Stop();
|
||||||
|
void SetBitrate(uint32_t bitrate);
|
||||||
|
void SetEchoCanceller(EchoCanceller* aec);
|
||||||
|
void SetOutputFrameDuration(uint32_t duration);
|
||||||
|
void SetPacketLoss(int percent);
|
||||||
|
int GetPacketLoss();
|
||||||
|
uint32_t GetBitrate();
|
||||||
|
void SetDTX(bool enable);
|
||||||
|
void SetLevelMeter(AudioLevelMeter* levelMeter);
|
||||||
|
void SetCallback(void (*f)(unsigned char*, size_t, unsigned char*, size_t, void*), void* param);
|
||||||
|
void SetSecondaryEncoderEnabled(bool enabled);
|
||||||
|
void SetVadMode(bool vad);
|
||||||
|
void AddAudioEffect(effects::AudioEffect* effect);
|
||||||
|
void RemoveAudioEffect(effects::AudioEffect* effect);
|
||||||
|
int GetComplexity(){
|
||||||
|
return complexity;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static size_t Callback(unsigned char* data, size_t len, void* param);
|
||||||
|
void RunThread();
|
||||||
|
void Encode(int16_t* data, size_t len);
|
||||||
|
void InvokeCallback(unsigned char* data, size_t length, unsigned char* secondaryData, size_t secondaryLength);
|
||||||
|
MediaStreamItf* source;
|
||||||
|
::OpusEncoder* enc;
|
||||||
|
::OpusEncoder* secondaryEncoder;
|
||||||
|
unsigned char buffer[4096];
|
||||||
|
uint32_t requestedBitrate;
|
||||||
|
uint32_t currentBitrate;
|
||||||
|
Thread* thread;
|
||||||
|
BlockingQueue<unsigned char*> queue;
|
||||||
|
BufferPool bufferPool;
|
||||||
|
EchoCanceller* echoCanceller;
|
||||||
|
int complexity;
|
||||||
|
bool running;
|
||||||
|
uint32_t frameDuration;
|
||||||
|
int packetLossPercent;
|
||||||
|
AudioLevelMeter* levelMeter;
|
||||||
|
bool secondaryEncoderEnabled;
|
||||||
|
bool vadMode=false;
|
||||||
|
uint32_t vadNoVoiceBitrate;
|
||||||
|
std::vector<effects::AudioEffect*> postProcEffects;
|
||||||
|
int secondaryEnabledBandwidth;
|
||||||
|
int vadModeVoiceBandwidth;
|
||||||
|
int vadModeNoVoiceBandwidth;
|
||||||
|
|
||||||
|
bool wasSecondaryEncoderEnabled=false;
|
||||||
|
|
||||||
|
void (*callback)(unsigned char*, size_t, unsigned char*, size_t, void*);
|
||||||
|
void* callbackParam;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //LIBTGVOIP_OPUSENCODER_H
|
|
@ -0,0 +1,92 @@
|
||||||
|
//
|
||||||
|
// Created by Grishka on 19.03.2018.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "PacketReassembler.h"
|
||||||
|
#include "logging.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
using namespace tgvoip;
|
||||||
|
|
||||||
|
PacketReassembler::PacketReassembler(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
PacketReassembler::~PacketReassembler(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void PacketReassembler::Reset(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void PacketReassembler::AddFragment(Buffer pkt, unsigned int fragmentIndex, unsigned int fragmentCount, uint32_t pts, bool keyframe, uint16_t rotation){
|
||||||
|
for(Packet& packet:packets){
|
||||||
|
if(packet.timestamp==pts){
|
||||||
|
if(fragmentCount!=packet.partCount){
|
||||||
|
LOGE("Received fragment total count %u inconsistent with previous %u", fragmentCount, packet.partCount);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
packet.AddFragment(std::move(pkt), fragmentIndex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pts<maxTimestamp){
|
||||||
|
LOGW("Received fragment doesn't belong here (ts=%u < maxTs=%u)", pts, maxTimestamp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(fragmentIndex>=fragmentCount){
|
||||||
|
LOGE("Received fragment index %u is out of bounds %u", fragmentIndex, fragmentCount);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(fragmentCount>255){
|
||||||
|
LOGE("Received fragment total count too big %u", fragmentCount);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
maxTimestamp=std::max(maxTimestamp, pts);
|
||||||
|
|
||||||
|
Packet packet(fragmentCount);
|
||||||
|
packet.timestamp=pts;
|
||||||
|
packet.isKeyframe=keyframe;
|
||||||
|
packet.receivedPartCount=0;
|
||||||
|
packet.rotation=rotation;
|
||||||
|
packet.AddFragment(std::move(pkt), fragmentIndex);
|
||||||
|
|
||||||
|
packets.push_back(std::move(packet));
|
||||||
|
while(packets.size()>3){
|
||||||
|
Packet&& old=std::move(packets[0]);
|
||||||
|
packets.erase(packets.begin());
|
||||||
|
if(old.receivedPartCount==old.partCount){
|
||||||
|
Buffer buffer=old.Reassemble();
|
||||||
|
callback(std::move(buffer), old.timestamp, old.isKeyframe, old.rotation);
|
||||||
|
//LOGV("Packet %u reassembled", old.timestamp);
|
||||||
|
}else{
|
||||||
|
LOGW("Packet %u not reassembled (%u of %u)", old.timestamp, old.receivedPartCount, old.partCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PacketReassembler::SetCallback(std::function<void(Buffer packet, uint32_t pts, bool keyframe, uint16_t rotation)> callback){
|
||||||
|
this->callback=callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PacketReassembler::Packet::AddFragment(Buffer pkt, uint32_t fragmentIndex){
|
||||||
|
//LOGV("Add fragment %u/%u to packet %u", fragmentIndex, partCount, timestamp);
|
||||||
|
parts[fragmentIndex]=std::move(pkt);
|
||||||
|
receivedPartCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer PacketReassembler::Packet::Reassemble(){
|
||||||
|
if(partCount==1){
|
||||||
|
return std::move(parts[0]);
|
||||||
|
}
|
||||||
|
BufferOutputStream out(10240);
|
||||||
|
for(unsigned int i=0;i<partCount;i++){
|
||||||
|
out.WriteBytes(parts[i]);
|
||||||
|
parts[i]=Buffer();
|
||||||
|
}
|
||||||
|
return Buffer(std::move(out));
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
//
|
||||||
|
// Created by Grishka on 19.03.2018.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef TGVOIP_PACKETREASSEMBLER_H
|
||||||
|
#define TGVOIP_PACKETREASSEMBLER_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <functional>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "Buffers.h"
|
||||||
|
|
||||||
|
namespace tgvoip {
|
||||||
|
class PacketReassembler{
|
||||||
|
public:
|
||||||
|
PacketReassembler();
|
||||||
|
virtual ~PacketReassembler();
|
||||||
|
|
||||||
|
void Reset();
|
||||||
|
void AddFragment(Buffer pkt, unsigned int fragmentIndex, unsigned int fragmentCount, uint32_t pts, bool keyframe, uint16_t rotation);
|
||||||
|
void SetCallback(std::function<void(Buffer packet, uint32_t pts, bool keyframe, uint16_t rotation)> callback);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Packet{
|
||||||
|
uint32_t timestamp;
|
||||||
|
uint32_t partCount;
|
||||||
|
uint32_t receivedPartCount;
|
||||||
|
bool isKeyframe;
|
||||||
|
uint16_t rotation;
|
||||||
|
Buffer* parts;
|
||||||
|
|
||||||
|
TGVOIP_DISALLOW_COPY_AND_ASSIGN(Packet);
|
||||||
|
|
||||||
|
Packet(Packet&& other) : timestamp(other.timestamp), partCount(other.partCount), receivedPartCount(other.receivedPartCount), isKeyframe(other.isKeyframe), rotation(other.rotation){
|
||||||
|
parts=other.parts;
|
||||||
|
other.parts=NULL;
|
||||||
|
}
|
||||||
|
Packet& operator=(Packet&& other){
|
||||||
|
if(&other!=this){
|
||||||
|
if(parts)
|
||||||
|
delete[] parts;
|
||||||
|
parts=other.parts;
|
||||||
|
other.parts=NULL;
|
||||||
|
timestamp=other.timestamp;
|
||||||
|
partCount=other.partCount;
|
||||||
|
receivedPartCount=other.receivedPartCount;
|
||||||
|
isKeyframe=other.isKeyframe;
|
||||||
|
rotation=other.rotation;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Packet(uint32_t partCount) : partCount(partCount){
|
||||||
|
parts=new Buffer[partCount];
|
||||||
|
}
|
||||||
|
~Packet(){
|
||||||
|
if(parts)
|
||||||
|
delete[] parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AddFragment(Buffer pkt, uint32_t fragmentIndex);
|
||||||
|
Buffer Reassemble();
|
||||||
|
};
|
||||||
|
std::function<void(Buffer, uint32_t, bool, uint16_t)> callback;
|
||||||
|
std::vector<Packet> packets;
|
||||||
|
uint32_t maxTimestamp=0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //TGVOIP_PACKETREASSEMBLER_H
|
|
@ -0,0 +1,141 @@
|
||||||
|
//
|
||||||
|
// Created by Grishka on 20.04.2018.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef TGVOIP_PRIVATEDEFINES_H
|
||||||
|
#define TGVOIP_PRIVATEDEFINES_H
|
||||||
|
|
||||||
|
#define PKT_INIT 1
|
||||||
|
#define PKT_INIT_ACK 2
|
||||||
|
#define PKT_STREAM_STATE 3
|
||||||
|
#define PKT_STREAM_DATA 4
|
||||||
|
#define PKT_UPDATE_STREAMS 5
|
||||||
|
#define PKT_PING 6
|
||||||
|
#define PKT_PONG 7
|
||||||
|
#define PKT_STREAM_DATA_X2 8
|
||||||
|
#define PKT_STREAM_DATA_X3 9
|
||||||
|
#define PKT_LAN_ENDPOINT 10
|
||||||
|
#define PKT_NETWORK_CHANGED 11
|
||||||
|
#define PKT_SWITCH_PREF_RELAY 12
|
||||||
|
#define PKT_SWITCH_TO_P2P 13
|
||||||
|
#define PKT_NOP 14
|
||||||
|
//#define PKT_GROUP_CALL_KEY 15 // replaced with 'extra' in 2.1 (protocol v6)
|
||||||
|
//#define PKT_REQUEST_GROUP 16
|
||||||
|
#define PKT_STREAM_EC 17
|
||||||
|
|
||||||
|
#define IS_MOBILE_NETWORK(x) (x==NET_TYPE_GPRS || x==NET_TYPE_EDGE || x==NET_TYPE_3G || x==NET_TYPE_HSPA || x==NET_TYPE_LTE || x==NET_TYPE_OTHER_MOBILE)
|
||||||
|
|
||||||
|
#define PROTOCOL_NAME 0x50567247 // "GrVP" in little endian (reversed here)
|
||||||
|
#define PROTOCOL_VERSION 9
|
||||||
|
#define MIN_PROTOCOL_VERSION 3
|
||||||
|
|
||||||
|
#define STREAM_DATA_FLAG_LEN16 0x40
|
||||||
|
#define STREAM_DATA_FLAG_HAS_MORE_FLAGS 0x80
|
||||||
|
// Since the data can't be larger than the MTU anyway,
|
||||||
|
// 5 top bits of data length are allocated for these flags
|
||||||
|
#define STREAM_DATA_XFLAG_KEYFRAME (1 << 15)
|
||||||
|
#define STREAM_DATA_XFLAG_FRAGMENTED (1 << 14)
|
||||||
|
#define STREAM_DATA_XFLAG_EXTRA_FEC (1 << 13)
|
||||||
|
|
||||||
|
#define STREAM_TYPE_AUDIO 1
|
||||||
|
#define STREAM_TYPE_VIDEO 2
|
||||||
|
|
||||||
|
#define FOURCC(a,b,c,d) ((uint32_t)d | ((uint32_t)c << 8) | ((uint32_t)b << 16) | ((uint32_t)a << 24))
|
||||||
|
#define PRINT_FOURCC(x) (char)(x >> 24), (char)(x >> 16), (char)(x >> 8), (char)x
|
||||||
|
|
||||||
|
#define CODEC_OPUS_OLD 1
|
||||||
|
#define CODEC_OPUS FOURCC('O','P','U','S')
|
||||||
|
|
||||||
|
#define CODEC_AVC FOURCC('A','V','C',' ')
|
||||||
|
#define CODEC_HEVC FOURCC('H','E','V','C')
|
||||||
|
#define CODEC_VP8 FOURCC('V','P','8','0')
|
||||||
|
#define CODEC_VP9 FOURCC('V','P','9','0')
|
||||||
|
#define CODEC_AV1 FOURCC('A','V','0','1')
|
||||||
|
|
||||||
|
#define DEFAULT_MTU 1100
|
||||||
|
|
||||||
|
/*flags:# voice_call_id:flags.2?int128 in_seq_no:flags.4?int out_seq_no:flags.4?int
|
||||||
|
* recent_received_mask:flags.5?int proto:flags.3?int extra:flags.1?string raw_data:flags.0?string*/
|
||||||
|
#define PFLAG_HAS_DATA 1
|
||||||
|
#define PFLAG_HAS_EXTRA 2
|
||||||
|
#define PFLAG_HAS_CALL_ID 4
|
||||||
|
#define PFLAG_HAS_PROTO 8
|
||||||
|
#define PFLAG_HAS_SEQ 16
|
||||||
|
#define PFLAG_HAS_RECENT_RECV 32
|
||||||
|
#define PFLAG_HAS_SENDER_TAG_HASH 64
|
||||||
|
|
||||||
|
#define XPFLAG_HAS_EXTRA 1
|
||||||
|
#define XPFLAG_HAS_RECV_TS 2
|
||||||
|
|
||||||
|
#define EXTRA_TYPE_STREAM_FLAGS 1
|
||||||
|
#define EXTRA_TYPE_STREAM_CSD 2
|
||||||
|
#define EXTRA_TYPE_LAN_ENDPOINT 3
|
||||||
|
#define EXTRA_TYPE_NETWORK_CHANGED 4
|
||||||
|
#define EXTRA_TYPE_GROUP_CALL_KEY 5
|
||||||
|
#define EXTRA_TYPE_REQUEST_GROUP 6
|
||||||
|
#define EXTRA_TYPE_IPV6_ENDPOINT 7
|
||||||
|
|
||||||
|
#define STREAM_FLAG_ENABLED 1
|
||||||
|
#define STREAM_FLAG_DTX 2
|
||||||
|
#define STREAM_FLAG_EXTRA_EC 4
|
||||||
|
|
||||||
|
#define STREAM_RFLAG_SUPPORTED 1
|
||||||
|
|
||||||
|
#define INIT_FLAG_DATA_SAVING_ENABLED 1
|
||||||
|
#define INIT_FLAG_GROUP_CALLS_SUPPORTED 2
|
||||||
|
#define INIT_FLAG_VIDEO_SEND_SUPPORTED 4
|
||||||
|
#define INIT_FLAG_VIDEO_RECV_SUPPORTED 8
|
||||||
|
|
||||||
|
#define INIT_VIDEO_RES_NONE 0
|
||||||
|
#define INIT_VIDEO_RES_240 1
|
||||||
|
#define INIT_VIDEO_RES_360 2
|
||||||
|
#define INIT_VIDEO_RES_480 3
|
||||||
|
#define INIT_VIDEO_RES_720 4
|
||||||
|
#define INIT_VIDEO_RES_1080 5
|
||||||
|
#define INIT_VIDEO_RES_1440 6
|
||||||
|
#define INIT_VIDEO_RES_4K 7
|
||||||
|
|
||||||
|
#define TLID_DECRYPTED_AUDIO_BLOCK 0xDBF948C1
|
||||||
|
#define TLID_SIMPLE_AUDIO_BLOCK 0xCC0D0E76
|
||||||
|
#define TLID_UDP_REFLECTOR_PEER_INFO 0x27D9371C
|
||||||
|
#define TLID_UDP_REFLECTOR_PEER_INFO_IPV6 0x83fc73b1
|
||||||
|
#define TLID_UDP_REFLECTOR_SELF_INFO 0xc01572c7
|
||||||
|
#define TLID_UDP_REFLECTOR_REQUEST_PACKETS_INFO 0x1a06fc96
|
||||||
|
#define TLID_UDP_REFLECTOR_LAST_PACKETS_INFO 0x0e107305
|
||||||
|
#define TLID_VECTOR 0x1cb5c415
|
||||||
|
#define PAD4(x) (4-(x+(x<=253 ? 1 : 0))%4)
|
||||||
|
|
||||||
|
#define MAX_RECENT_PACKETS 128
|
||||||
|
|
||||||
|
#define MAX(a,b) (a>b ? a : b)
|
||||||
|
#define MIN(a,b) (a<b ? a : b)
|
||||||
|
|
||||||
|
#define SHA1_LENGTH 20
|
||||||
|
#define SHA256_LENGTH 32
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define MSC_STACK_FALLBACK(a, b) (b)
|
||||||
|
#else
|
||||||
|
#define MSC_STACK_FALLBACK(a, b) (a)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define SEQ_MAX 0xFFFFFFFF
|
||||||
|
|
||||||
|
inline bool seqgt(uint32_t s1, uint32_t s2){
|
||||||
|
return ((s1>s2) && (s1-s2<=SEQ_MAX/2)) || ((s1<s2) && (s2-s1>SEQ_MAX/2));
|
||||||
|
}
|
||||||
|
|
||||||
|
#define NEED_RATE_FLAG_SHITTY_INTERNET_MODE 1
|
||||||
|
#define NEED_RATE_FLAG_UDP_NA 2
|
||||||
|
#define NEED_RATE_FLAG_UDP_BAD 4
|
||||||
|
#define NEED_RATE_FLAG_RECONNECTING 8
|
||||||
|
|
||||||
|
#define VIDEO_FRAME_FLAG_KEYFRAME 1
|
||||||
|
|
||||||
|
#define VIDEO_ROTATION_MASK 3
|
||||||
|
#define VIDEO_ROTATION_0 0
|
||||||
|
#define VIDEO_ROTATION_90 1
|
||||||
|
#define VIDEO_ROTATION_180 2
|
||||||
|
#define VIDEO_ROTATION_270 3
|
||||||
|
|
||||||
|
#endif //TGVOIP_PRIVATEDEFINES_H
|
|
@ -0,0 +1,24 @@
|
||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
distribute this software, either in source code form or as a compiled
|
||||||
|
binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
means.
|
||||||
|
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors
|
||||||
|
of this software dedicate any and all copyright interest in the
|
||||||
|
software to the public domain. We make this dedication for the benefit
|
||||||
|
of the public at large and to the detriment of our heirs and
|
||||||
|
successors. We intend this dedication to be an overt act of
|
||||||
|
relinquishment in perpetuity of all present and future rights to this
|
||||||
|
software under copyright law.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
For more information, please refer to <http://unlicense.org/>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,879 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef __VOIPCONTROLLER_H
|
||||||
|
#define __VOIPCONTROLLER_H
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#endif
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <TargetConditionals.h>
|
||||||
|
#include "os/darwin/AudioUnitIO.h"
|
||||||
|
#endif
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include "video/VideoSource.h"
|
||||||
|
#include "video/VideoRenderer.h"
|
||||||
|
#include <atomic>
|
||||||
|
#include "video/ScreamCongestionController.h"
|
||||||
|
#include "audio/AudioInput.h"
|
||||||
|
#include "BlockingQueue.h"
|
||||||
|
#include "audio/AudioOutput.h"
|
||||||
|
#include "audio/AudioIO.h"
|
||||||
|
#include "JitterBuffer.h"
|
||||||
|
#include "OpusDecoder.h"
|
||||||
|
#include "OpusEncoder.h"
|
||||||
|
#include "EchoCanceller.h"
|
||||||
|
#include "CongestionControl.h"
|
||||||
|
#include "NetworkSocket.h"
|
||||||
|
#include "Buffers.h"
|
||||||
|
#include "PacketReassembler.h"
|
||||||
|
#include "MessageThread.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
#define LIBTGVOIP_VERSION "2.4.4"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#undef GetCurrentTime
|
||||||
|
#undef ERROR_TIMEOUT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define TGVOIP_PEER_CAP_GROUP_CALLS 1
|
||||||
|
#define TGVOIP_PEER_CAP_VIDEO_CAPTURE 2
|
||||||
|
#define TGVOIP_PEER_CAP_VIDEO_DISPLAY 4
|
||||||
|
|
||||||
|
namespace tgvoip{
|
||||||
|
|
||||||
|
enum{
|
||||||
|
PROXY_NONE=0,
|
||||||
|
PROXY_SOCKS5,
|
||||||
|
//PROXY_HTTP
|
||||||
|
};
|
||||||
|
|
||||||
|
enum{
|
||||||
|
STATE_WAIT_INIT=1,
|
||||||
|
STATE_WAIT_INIT_ACK,
|
||||||
|
STATE_ESTABLISHED,
|
||||||
|
STATE_FAILED,
|
||||||
|
STATE_RECONNECTING
|
||||||
|
};
|
||||||
|
|
||||||
|
enum{
|
||||||
|
ERROR_UNKNOWN=0,
|
||||||
|
ERROR_INCOMPATIBLE,
|
||||||
|
ERROR_TIMEOUT,
|
||||||
|
ERROR_AUDIO_IO,
|
||||||
|
ERROR_PROXY
|
||||||
|
};
|
||||||
|
|
||||||
|
enum{
|
||||||
|
NET_TYPE_UNKNOWN=0,
|
||||||
|
NET_TYPE_GPRS,
|
||||||
|
NET_TYPE_EDGE,
|
||||||
|
NET_TYPE_3G,
|
||||||
|
NET_TYPE_HSPA,
|
||||||
|
NET_TYPE_LTE,
|
||||||
|
NET_TYPE_WIFI,
|
||||||
|
NET_TYPE_ETHERNET,
|
||||||
|
NET_TYPE_OTHER_HIGH_SPEED,
|
||||||
|
NET_TYPE_OTHER_LOW_SPEED,
|
||||||
|
NET_TYPE_DIALUP,
|
||||||
|
NET_TYPE_OTHER_MOBILE
|
||||||
|
};
|
||||||
|
|
||||||
|
enum{
|
||||||
|
DATA_SAVING_NEVER=0,
|
||||||
|
DATA_SAVING_MOBILE,
|
||||||
|
DATA_SAVING_ALWAYS
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CryptoFunctions{
|
||||||
|
void (*rand_bytes)(uint8_t* buffer, size_t length);
|
||||||
|
void (*sha1)(uint8_t* msg, size_t length, uint8_t* output);
|
||||||
|
void (*sha256)(uint8_t* msg, size_t length, uint8_t* output);
|
||||||
|
void (*aes_ige_encrypt)(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv);
|
||||||
|
void (*aes_ige_decrypt)(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv);
|
||||||
|
void (*aes_ctr_encrypt)(uint8_t* inout, size_t length, uint8_t* key, uint8_t* iv, uint8_t* ecount, uint32_t* num);
|
||||||
|
void (*aes_cbc_encrypt)(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv);
|
||||||
|
void (*aes_cbc_decrypt)(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CellularCarrierInfo{
|
||||||
|
std::string name;
|
||||||
|
std::string mcc;
|
||||||
|
std::string mnc;
|
||||||
|
std::string countryCode;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Endpoint{
|
||||||
|
friend class VoIPController;
|
||||||
|
friend class VoIPGroupController;
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum Type{
|
||||||
|
UDP_P2P_INET=1,
|
||||||
|
UDP_P2P_LAN,
|
||||||
|
UDP_RELAY,
|
||||||
|
TCP_RELAY
|
||||||
|
};
|
||||||
|
|
||||||
|
Endpoint(int64_t id, uint16_t port, const IPv4Address& address, const IPv6Address& v6address, Type type, unsigned char* peerTag);
|
||||||
|
Endpoint();
|
||||||
|
~Endpoint();
|
||||||
|
const NetworkAddress& GetAddress() const;
|
||||||
|
NetworkAddress& GetAddress();
|
||||||
|
bool IsIPv6Only() const;
|
||||||
|
int64_t id;
|
||||||
|
uint16_t port;
|
||||||
|
IPv4Address address;
|
||||||
|
IPv6Address v6address;
|
||||||
|
Type type;
|
||||||
|
unsigned char peerTag[16];
|
||||||
|
|
||||||
|
private:
|
||||||
|
double lastPingTime;
|
||||||
|
uint32_t lastPingSeq;
|
||||||
|
HistoricBuffer<double, 6> rtts;
|
||||||
|
double averageRTT;
|
||||||
|
NetworkSocket* socket;
|
||||||
|
int udpPongCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AudioDevice{
|
||||||
|
public:
|
||||||
|
std::string id;
|
||||||
|
std::string displayName;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AudioOutputDevice : public AudioDevice{
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class AudioInputDevice : public AudioDevice{
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class AudioInputTester{
|
||||||
|
public:
|
||||||
|
AudioInputTester(const std::string deviceID);
|
||||||
|
~AudioInputTester();
|
||||||
|
TGVOIP_DISALLOW_COPY_AND_ASSIGN(AudioInputTester);
|
||||||
|
float GetAndResetLevel();
|
||||||
|
bool Failed(){
|
||||||
|
return io && io->Failed();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
void Update(int16_t* samples, size_t count);
|
||||||
|
audio::AudioIO* io=NULL;
|
||||||
|
audio::AudioInput* input=NULL;
|
||||||
|
int16_t maxSample=0;
|
||||||
|
std::string deviceID;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VoIPController{
|
||||||
|
friend class VoIPGroupController;
|
||||||
|
public:
|
||||||
|
TGVOIP_DISALLOW_COPY_AND_ASSIGN(VoIPController);
|
||||||
|
struct Config{
|
||||||
|
Config(double initTimeout=30.0, double recvTimeout=20.0, int dataSaving=DATA_SAVING_NEVER, bool enableAEC=false, bool enableNS=false, bool enableAGC=false, bool enableCallUpgrade=false){
|
||||||
|
this->initTimeout=initTimeout;
|
||||||
|
this->recvTimeout=recvTimeout;
|
||||||
|
this->dataSaving=dataSaving;
|
||||||
|
this->enableAEC=enableAEC;
|
||||||
|
this->enableNS=enableNS;
|
||||||
|
this->enableAGC=enableAGC;
|
||||||
|
this->enableCallUpgrade=enableCallUpgrade;
|
||||||
|
}
|
||||||
|
|
||||||
|
double initTimeout;
|
||||||
|
double recvTimeout;
|
||||||
|
int dataSaving;
|
||||||
|
#ifndef _WIN32
|
||||||
|
std::string logFilePath="";
|
||||||
|
std::string statsDumpFilePath="";
|
||||||
|
#else
|
||||||
|
std::wstring logFilePath=L"";
|
||||||
|
std::wstring statsDumpFilePath=L"";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool enableAEC;
|
||||||
|
bool enableNS;
|
||||||
|
bool enableAGC;
|
||||||
|
|
||||||
|
bool enableCallUpgrade;
|
||||||
|
|
||||||
|
bool logPacketStats=false;
|
||||||
|
bool enableVolumeControl=false;
|
||||||
|
|
||||||
|
bool enableVideoSend=false;
|
||||||
|
bool enableVideoReceive=false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TrafficStats{
|
||||||
|
uint64_t bytesSentWifi;
|
||||||
|
uint64_t bytesRecvdWifi;
|
||||||
|
uint64_t bytesSentMobile;
|
||||||
|
uint64_t bytesRecvdMobile;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
VoIPController();
|
||||||
|
virtual ~VoIPController();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the initial endpoints (relays)
|
||||||
|
* @param endpoints Endpoints converted from phone.PhoneConnection TL objects
|
||||||
|
* @param allowP2p Whether p2p connectivity is allowed
|
||||||
|
* @param connectionMaxLayer The max_layer field from the phoneCallProtocol object returned by Telegram server.
|
||||||
|
* DO NOT HARDCODE THIS VALUE, it's extremely important for backwards compatibility.
|
||||||
|
*/
|
||||||
|
void SetRemoteEndpoints(std::vector<Endpoint> endpoints, bool allowP2p, int32_t connectionMaxLayer);
|
||||||
|
/**
|
||||||
|
* Initialize and start all the internal threads
|
||||||
|
*/
|
||||||
|
void Start();
|
||||||
|
/**
|
||||||
|
* Stop any internal threads. Don't call any other methods after this.
|
||||||
|
*/
|
||||||
|
void Stop();
|
||||||
|
/**
|
||||||
|
* Initiate connection
|
||||||
|
*/
|
||||||
|
void Connect();
|
||||||
|
Endpoint& GetRemoteEndpoint();
|
||||||
|
/**
|
||||||
|
* Get the debug info string to be displayed in client UI
|
||||||
|
*/
|
||||||
|
virtual std::string GetDebugString();
|
||||||
|
/**
|
||||||
|
* Notify the library of network type change
|
||||||
|
* @param type The new network type
|
||||||
|
*/
|
||||||
|
virtual void SetNetworkType(int type);
|
||||||
|
/**
|
||||||
|
* Get the average round-trip time for network packets
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
double GetAverageRTT();
|
||||||
|
static double GetCurrentTime();
|
||||||
|
/**
|
||||||
|
* Use this field to store any of your context data associated with this call
|
||||||
|
*/
|
||||||
|
void* implData;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param mute
|
||||||
|
*/
|
||||||
|
virtual void SetMicMute(bool mute);
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* @param isOutgoing
|
||||||
|
*/
|
||||||
|
void SetEncryptionKey(char* key, bool isOutgoing);
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param cfg
|
||||||
|
*/
|
||||||
|
void SetConfig(const Config& cfg);
|
||||||
|
void DebugCtl(int request, int param);
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param stats
|
||||||
|
*/
|
||||||
|
void GetStats(TrafficStats* stats);
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
int64_t GetPreferredRelayID();
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
int GetLastError();
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static CryptoFunctions crypto;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
static const char* GetVersion();
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
std::string GetDebugLog();
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
static std::vector<AudioInputDevice> EnumerateAudioInputs();
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
static std::vector<AudioOutputDevice> EnumerateAudioOutputs();
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
void SetCurrentAudioInput(std::string id);
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
void SetCurrentAudioOutput(std::string id);
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
std::string GetCurrentAudioInputID();
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
std::string GetCurrentAudioOutputID();
|
||||||
|
/**
|
||||||
|
* Set the proxy server to route the data through. Call this before connecting.
|
||||||
|
* @param protocol PROXY_NONE or PROXY_SOCKS5
|
||||||
|
* @param address IP address or domain name of the server
|
||||||
|
* @param port Port of the server
|
||||||
|
* @param username Username; empty string for anonymous
|
||||||
|
* @param password Password; empty string if none
|
||||||
|
*/
|
||||||
|
void SetProxy(int protocol, std::string address, uint16_t port, std::string username, std::string password);
|
||||||
|
/**
|
||||||
|
* Get the number of signal bars to display in the client UI.
|
||||||
|
* @return the number of signal bars, from 1 to 4
|
||||||
|
*/
|
||||||
|
int GetSignalBarsCount();
|
||||||
|
/**
|
||||||
|
* Enable or disable AGC (automatic gain control) on audio output. Should only be enabled on phones when the earpiece speaker is being used.
|
||||||
|
* The audio output will be louder with this on.
|
||||||
|
* AGC with speakerphone or other kinds of loud speakers has detrimental effects on some echo cancellation implementations.
|
||||||
|
* @param enabled I usually pick argument names to be self-explanatory
|
||||||
|
*/
|
||||||
|
void SetAudioOutputGainControlEnabled(bool enabled);
|
||||||
|
/**
|
||||||
|
* Get the additional capabilities of the peer client app
|
||||||
|
* @return corresponding TGVOIP_PEER_CAP_* flags OR'ed together
|
||||||
|
*/
|
||||||
|
uint32_t GetPeerCapabilities();
|
||||||
|
/**
|
||||||
|
* Send the peer the key for the group call to prepare this private call to an upgrade to a E2E group call.
|
||||||
|
* The peer must have the TGVOIP_PEER_CAP_GROUP_CALLS capability. After the peer acknowledges the key, Callbacks::groupCallKeySent will be called.
|
||||||
|
* @param key newly-generated group call key, must be exactly 265 bytes long
|
||||||
|
*/
|
||||||
|
void SendGroupCallKey(unsigned char* key);
|
||||||
|
/**
|
||||||
|
* In an incoming call, request the peer to generate a new encryption key, send it to you and upgrade this call to a E2E group call.
|
||||||
|
*/
|
||||||
|
void RequestCallUpgrade();
|
||||||
|
void SetEchoCancellationStrength(int strength);
|
||||||
|
int GetConnectionState();
|
||||||
|
bool NeedRate();
|
||||||
|
/**
|
||||||
|
* Get the maximum connection layer supported by this libtgvoip version.
|
||||||
|
* Pass this as <code>max_layer</code> in the phone.phoneConnection TL object when requesting and accepting calls.
|
||||||
|
*/
|
||||||
|
static int32_t GetConnectionMaxLayer(){
|
||||||
|
return 92;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Get the persistable state of the library, like proxy capabilities, to save somewhere on the disk. Call this at the end of the call.
|
||||||
|
* Using this will speed up the connection establishment in some cases.
|
||||||
|
*/
|
||||||
|
std::vector<uint8_t> GetPersistentState();
|
||||||
|
/**
|
||||||
|
* Load the persistable state. Call this before starting the call.
|
||||||
|
*/
|
||||||
|
void SetPersistentState(std::vector<uint8_t> state);
|
||||||
|
|
||||||
|
#if defined(TGVOIP_USE_CALLBACK_AUDIO_IO)
|
||||||
|
void SetAudioDataCallbacks(std::function<void(int16_t*, size_t)> input, std::function<void(int16_t*, size_t)> output);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void SetVideoCodecSpecificData(const std::vector<Buffer>& data);
|
||||||
|
|
||||||
|
struct Callbacks{
|
||||||
|
void (*connectionStateChanged)(VoIPController*, int);
|
||||||
|
void (*signalBarCountChanged)(VoIPController*, int);
|
||||||
|
void (*groupCallKeySent)(VoIPController*);
|
||||||
|
void (*groupCallKeyReceived)(VoIPController*, const unsigned char*);
|
||||||
|
void (*upgradeToGroupCallRequested)(VoIPController*);
|
||||||
|
};
|
||||||
|
void SetCallbacks(Callbacks callbacks);
|
||||||
|
|
||||||
|
float GetOutputLevel(){
|
||||||
|
return 0.0f;
|
||||||
|
};
|
||||||
|
int GetVideoResolutionForCurrentBitrate();
|
||||||
|
void SetVideoSource(video::VideoSource* source);
|
||||||
|
void SetVideoRenderer(video::VideoRenderer* renderer);
|
||||||
|
|
||||||
|
void SetInputVolume(float level);
|
||||||
|
void SetOutputVolume(float level);
|
||||||
|
#if defined(__APPLE__) && defined(TARGET_OS_OSX)
|
||||||
|
void SetAudioOutputDuckingEnabled(bool enabled);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Stream;
|
||||||
|
struct UnacknowledgedExtraData;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
struct RecentOutgoingPacket{
|
||||||
|
uint32_t seq;
|
||||||
|
uint16_t id; // for group calls only
|
||||||
|
double sendTime;
|
||||||
|
double ackTime;
|
||||||
|
uint8_t type;
|
||||||
|
uint32_t size;
|
||||||
|
};
|
||||||
|
struct PendingOutgoingPacket{
|
||||||
|
PendingOutgoingPacket(uint32_t seq, unsigned char type, size_t len, Buffer&& data, int64_t endpoint){
|
||||||
|
this->seq=seq;
|
||||||
|
this->type=type;
|
||||||
|
this->len=len;
|
||||||
|
this->data=std::move(data);
|
||||||
|
this->endpoint=endpoint;
|
||||||
|
}
|
||||||
|
PendingOutgoingPacket(PendingOutgoingPacket&& other){
|
||||||
|
seq=other.seq;
|
||||||
|
type=other.type;
|
||||||
|
len=other.len;
|
||||||
|
data=std::move(other.data);
|
||||||
|
endpoint=other.endpoint;
|
||||||
|
}
|
||||||
|
PendingOutgoingPacket& operator=(PendingOutgoingPacket&& other){
|
||||||
|
if(this!=&other){
|
||||||
|
seq=other.seq;
|
||||||
|
type=other.type;
|
||||||
|
len=other.len;
|
||||||
|
data=std::move(other.data);
|
||||||
|
endpoint=other.endpoint;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
TGVOIP_DISALLOW_COPY_AND_ASSIGN(PendingOutgoingPacket);
|
||||||
|
uint32_t seq;
|
||||||
|
unsigned char type;
|
||||||
|
size_t len;
|
||||||
|
Buffer data;
|
||||||
|
int64_t endpoint;
|
||||||
|
};
|
||||||
|
struct QueuedPacket{
|
||||||
|
#if defined(_MSC_VER) && _MSC_VER <= 1800 // VS2013 doesn't support auto-generating move constructors
|
||||||
|
//TGVOIP_DISALLOW_COPY_AND_ASSIGN(QueuedPacket);
|
||||||
|
QueuedPacket(QueuedPacket&& other){
|
||||||
|
data=std::move(other.data);
|
||||||
|
type=other.type;
|
||||||
|
seqs=other.seqs;
|
||||||
|
firstSentTime=other.firstSentTime;
|
||||||
|
lastSentTime=other.lastSentTime;
|
||||||
|
retryInterval=other.retryInterval;
|
||||||
|
timeout=other.timeout;
|
||||||
|
}
|
||||||
|
QueuedPacket(){
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
Buffer data;
|
||||||
|
unsigned char type;
|
||||||
|
HistoricBuffer<uint32_t, 16> seqs;
|
||||||
|
double firstSentTime;
|
||||||
|
double lastSentTime;
|
||||||
|
double retryInterval;
|
||||||
|
double timeout;
|
||||||
|
};
|
||||||
|
virtual void ProcessIncomingPacket(NetworkPacket& packet, Endpoint& srcEndpoint);
|
||||||
|
virtual void ProcessExtraData(Buffer& data);
|
||||||
|
virtual void WritePacketHeader(uint32_t seq, BufferOutputStream* s, unsigned char type, uint32_t length);
|
||||||
|
virtual void SendPacket(unsigned char* data, size_t len, Endpoint& ep, PendingOutgoingPacket& srcPacket);
|
||||||
|
virtual void SendInit();
|
||||||
|
virtual void SendUdpPing(Endpoint& endpoint);
|
||||||
|
virtual void SendRelayPings();
|
||||||
|
virtual void OnAudioOutputReady();
|
||||||
|
virtual void SendExtra(Buffer& data, unsigned char type);
|
||||||
|
void SendStreamFlags(Stream& stream);
|
||||||
|
void SendStreamCSD(Stream& stream);
|
||||||
|
void InitializeTimers();
|
||||||
|
void ResetEndpointPingStats();
|
||||||
|
void SendVideoFrame(const Buffer& frame, uint32_t flags, uint32_t rotation);
|
||||||
|
void ProcessIncomingVideoFrame(Buffer frame, uint32_t pts, bool keyframe, uint16_t rotation);
|
||||||
|
std::shared_ptr<Stream> GetStreamByType(int type, bool outgoing);
|
||||||
|
Endpoint* GetEndpointForPacket(const PendingOutgoingPacket& pkt);
|
||||||
|
bool SendOrEnqueuePacket(PendingOutgoingPacket pkt, bool enqueue=true);
|
||||||
|
static std::string NetworkTypeToString(int type);
|
||||||
|
CellularCarrierInfo GetCarrierInfo();
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Stream{
|
||||||
|
int32_t userID;
|
||||||
|
unsigned char id;
|
||||||
|
unsigned char type;
|
||||||
|
uint32_t codec;
|
||||||
|
bool enabled;
|
||||||
|
bool extraECEnabled;
|
||||||
|
uint16_t frameDuration;
|
||||||
|
std::shared_ptr<JitterBuffer> jitterBuffer;
|
||||||
|
std::shared_ptr<OpusDecoder> decoder;
|
||||||
|
std::shared_ptr<PacketReassembler> packetReassembler;
|
||||||
|
std::shared_ptr<CallbackWrapper> callbackWrapper;
|
||||||
|
std::vector<Buffer> codecSpecificData;
|
||||||
|
bool csdIsValid=false;
|
||||||
|
int resolution;
|
||||||
|
unsigned int width=0;
|
||||||
|
unsigned int height=0;
|
||||||
|
uint16_t rotation=0;
|
||||||
|
};
|
||||||
|
struct UnacknowledgedExtraData{
|
||||||
|
#if defined(_MSC_VER) && _MSC_VER <= 1800 // VS2013 doesn't support auto-generating move constructors
|
||||||
|
UnacknowledgedExtraData(UnacknowledgedExtraData&& other){
|
||||||
|
type=other.type;
|
||||||
|
data=std::move(other.data);
|
||||||
|
firstContainingSeq=other.firstContainingSeq;
|
||||||
|
}
|
||||||
|
UnacknowledgedExtraData(unsigned char _type, Buffer&& _data, uint32_t _firstContainingSeq){
|
||||||
|
type=_type;
|
||||||
|
data=_data;
|
||||||
|
firstContainingSeq=_firstContainingSeq;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
unsigned char type;
|
||||||
|
Buffer data;
|
||||||
|
uint32_t firstContainingSeq;
|
||||||
|
};
|
||||||
|
enum{
|
||||||
|
UDP_UNKNOWN=0,
|
||||||
|
UDP_PING_PENDING,
|
||||||
|
UDP_PING_SENT,
|
||||||
|
UDP_AVAILABLE,
|
||||||
|
UDP_NOT_AVAILABLE,
|
||||||
|
UDP_BAD
|
||||||
|
};
|
||||||
|
struct DebugLoggedPacket{
|
||||||
|
int32_t seq;
|
||||||
|
double timestamp;
|
||||||
|
int32_t length;
|
||||||
|
};
|
||||||
|
struct SentVideoFrame{
|
||||||
|
uint32_t num;
|
||||||
|
uint32_t fragmentCount;
|
||||||
|
std::vector<uint32_t> unacknowledgedPackets;
|
||||||
|
uint32_t fragmentsInQueue;
|
||||||
|
};
|
||||||
|
struct PendingVideoFrameFragment{
|
||||||
|
uint32_t pts;
|
||||||
|
Buffer data;
|
||||||
|
};
|
||||||
|
|
||||||
|
void RunRecvThread();
|
||||||
|
void RunSendThread();
|
||||||
|
void HandleAudioInput(unsigned char* data, size_t len, unsigned char* secondaryData, size_t secondaryLen);
|
||||||
|
void UpdateAudioBitrateLimit();
|
||||||
|
void SetState(int state);
|
||||||
|
void UpdateAudioOutputState();
|
||||||
|
void InitUDPProxy();
|
||||||
|
void UpdateDataSavingState();
|
||||||
|
void KDF(unsigned char* msgKey, size_t x, unsigned char* aesKey, unsigned char* aesIv);
|
||||||
|
void KDF2(unsigned char* msgKey, size_t x, unsigned char* aesKey, unsigned char* aesIv);
|
||||||
|
static void AudioInputCallback(unsigned char* data, size_t length, unsigned char* secondaryData, size_t secondaryLength, void* param);
|
||||||
|
void SendPublicEndpointsRequest();
|
||||||
|
void SendPublicEndpointsRequest(const Endpoint& relay);
|
||||||
|
Endpoint& GetEndpointByType(int type);
|
||||||
|
void SendPacketReliably(unsigned char type, unsigned char* data, size_t len, double retryInterval, double timeout);
|
||||||
|
uint32_t GenerateOutSeq();
|
||||||
|
void ActuallySendPacket(NetworkPacket& pkt, Endpoint& ep);
|
||||||
|
void InitializeAudio();
|
||||||
|
void StartAudio();
|
||||||
|
void ProcessAcknowledgedOutgoingExtra(UnacknowledgedExtraData& extra);
|
||||||
|
void AddIPv6Relays();
|
||||||
|
void AddTCPRelays();
|
||||||
|
void SendUdpPings();
|
||||||
|
void EvaluateUdpPingResults();
|
||||||
|
void UpdateRTT();
|
||||||
|
void UpdateCongestion();
|
||||||
|
void UpdateAudioBitrate();
|
||||||
|
void UpdateSignalBars();
|
||||||
|
void UpdateQueuedPackets();
|
||||||
|
void SendNopPacket();
|
||||||
|
void TickJitterBufferAngCongestionControl();
|
||||||
|
void ResetUdpAvailability();
|
||||||
|
std::string GetPacketTypeString(unsigned char type);
|
||||||
|
void SetupOutgoingVideoStream();
|
||||||
|
bool WasOutgoingPacketAcknowledged(uint32_t seq);
|
||||||
|
RecentOutgoingPacket* GetRecentOutgoingPacket(uint32_t seq);
|
||||||
|
|
||||||
|
int state;
|
||||||
|
std::map<int64_t, Endpoint> endpoints;
|
||||||
|
int64_t currentEndpoint=0;
|
||||||
|
int64_t preferredRelay=0;
|
||||||
|
int64_t peerPreferredRelay=0;
|
||||||
|
bool runReceiver;
|
||||||
|
std::atomic<uint32_t> seq;
|
||||||
|
uint32_t lastRemoteSeq;
|
||||||
|
uint32_t lastRemoteAckSeq;
|
||||||
|
uint32_t lastSentSeq;
|
||||||
|
std::vector<RecentOutgoingPacket> recentOutgoingPackets;
|
||||||
|
double recvPacketTimes[32];
|
||||||
|
HistoricBuffer<uint32_t, 10, double> sendLossCountHistory;
|
||||||
|
uint32_t audioTimestampIn;
|
||||||
|
uint32_t audioTimestampOut;
|
||||||
|
tgvoip::audio::AudioIO* audioIO=NULL;
|
||||||
|
tgvoip::audio::AudioInput* audioInput=NULL;
|
||||||
|
tgvoip::audio::AudioOutput* audioOutput=NULL;
|
||||||
|
OpusEncoder* encoder;
|
||||||
|
std::vector<PendingOutgoingPacket> sendQueue;
|
||||||
|
EchoCanceller* echoCanceller;
|
||||||
|
Mutex sendBufferMutex;
|
||||||
|
Mutex endpointsMutex;
|
||||||
|
Mutex socketSelectMutex;
|
||||||
|
bool stopping;
|
||||||
|
bool audioOutStarted;
|
||||||
|
Thread* recvThread;
|
||||||
|
Thread* sendThread;
|
||||||
|
uint32_t packetsReceived;
|
||||||
|
uint32_t recvLossCount;
|
||||||
|
uint32_t prevSendLossCount;
|
||||||
|
uint32_t firstSentPing;
|
||||||
|
HistoricBuffer<double, 32> rttHistory;
|
||||||
|
bool waitingForAcks;
|
||||||
|
int networkType;
|
||||||
|
int dontSendPackets;
|
||||||
|
int lastError;
|
||||||
|
bool micMuted;
|
||||||
|
uint32_t maxBitrate;
|
||||||
|
std::vector<std::shared_ptr<Stream>> outgoingStreams;
|
||||||
|
std::vector<std::shared_ptr<Stream>> incomingStreams;
|
||||||
|
unsigned char encryptionKey[256];
|
||||||
|
unsigned char keyFingerprint[8];
|
||||||
|
unsigned char callID[16];
|
||||||
|
double stateChangeTime;
|
||||||
|
bool waitingForRelayPeerInfo;
|
||||||
|
bool allowP2p;
|
||||||
|
bool dataSavingMode;
|
||||||
|
bool dataSavingRequestedByPeer;
|
||||||
|
std::string activeNetItfName;
|
||||||
|
double publicEndpointsReqTime;
|
||||||
|
std::vector<QueuedPacket> queuedPackets;
|
||||||
|
Mutex audioIOMutex;
|
||||||
|
Mutex queuedPacketsMutex;
|
||||||
|
double connectionInitTime;
|
||||||
|
double lastRecvPacketTime;
|
||||||
|
Config config;
|
||||||
|
int32_t peerVersion;
|
||||||
|
CongestionControl* conctl;
|
||||||
|
TrafficStats stats;
|
||||||
|
bool receivedInit;
|
||||||
|
bool receivedInitAck;
|
||||||
|
bool isOutgoing;
|
||||||
|
NetworkSocket* udpSocket;
|
||||||
|
NetworkSocket* realUdpSocket;
|
||||||
|
FILE* statsDump;
|
||||||
|
std::string currentAudioInput;
|
||||||
|
std::string currentAudioOutput;
|
||||||
|
bool useTCP;
|
||||||
|
bool useUDP;
|
||||||
|
bool didAddTcpRelays;
|
||||||
|
SocketSelectCanceller* selectCanceller;
|
||||||
|
HistoricBuffer<unsigned char, 4, int> signalBarsHistory;
|
||||||
|
bool audioStarted=false;
|
||||||
|
|
||||||
|
int udpConnectivityState;
|
||||||
|
double lastUdpPingTime;
|
||||||
|
int udpPingCount;
|
||||||
|
int echoCancellationStrength;
|
||||||
|
|
||||||
|
int proxyProtocol;
|
||||||
|
std::string proxyAddress;
|
||||||
|
uint16_t proxyPort;
|
||||||
|
std::string proxyUsername;
|
||||||
|
std::string proxyPassword;
|
||||||
|
IPv4Address* resolvedProxyAddress;
|
||||||
|
|
||||||
|
uint32_t peerCapabilities;
|
||||||
|
Callbacks callbacks;
|
||||||
|
bool didReceiveGroupCallKey;
|
||||||
|
bool didReceiveGroupCallKeyAck;
|
||||||
|
bool didSendGroupCallKey;
|
||||||
|
bool didSendUpgradeRequest;
|
||||||
|
bool didInvokeUpgradeCallback;
|
||||||
|
|
||||||
|
int32_t connectionMaxLayer;
|
||||||
|
bool useMTProto2;
|
||||||
|
bool setCurrentEndpointToTCP;
|
||||||
|
|
||||||
|
std::vector<UnacknowledgedExtraData> currentExtras;
|
||||||
|
std::unordered_map<uint8_t, uint64_t> lastReceivedExtrasByType;
|
||||||
|
bool useIPv6;
|
||||||
|
bool peerIPv6Available;
|
||||||
|
IPv6Address myIPv6;
|
||||||
|
bool shittyInternetMode;
|
||||||
|
int extraEcLevel=0;
|
||||||
|
std::vector<Buffer> ecAudioPackets;
|
||||||
|
bool didAddIPv6Relays;
|
||||||
|
bool didSendIPv6Endpoint;
|
||||||
|
int publicEndpointsReqCount=0;
|
||||||
|
MessageThread messageThread;
|
||||||
|
bool wasEstablished=false;
|
||||||
|
bool receivedFirstStreamPacket=false;
|
||||||
|
std::atomic<unsigned int> unsentStreamPackets;
|
||||||
|
HistoricBuffer<unsigned int, 5> unsentStreamPacketsHistory;
|
||||||
|
bool needReInitUdpProxy=true;
|
||||||
|
bool needRate=false;
|
||||||
|
std::vector<DebugLoggedPacket> debugLoggedPackets;
|
||||||
|
|
||||||
|
uint32_t initTimeoutID=MessageThread::INVALID_ID;
|
||||||
|
uint32_t noStreamsNopID=MessageThread::INVALID_ID;
|
||||||
|
uint32_t udpPingTimeoutID=MessageThread::INVALID_ID;
|
||||||
|
|
||||||
|
effects::Volume outputVolume;
|
||||||
|
effects::Volume inputVolume;
|
||||||
|
|
||||||
|
std::vector<uint32_t> peerVideoDecoders;
|
||||||
|
int peerMaxVideoResolution=0;
|
||||||
|
|
||||||
|
#if defined(TGVOIP_USE_CALLBACK_AUDIO_IO)
|
||||||
|
std::function<void(int16_t*, size_t)> audioInputDataCallback;
|
||||||
|
std::function<void(int16_t*, size_t)> audioOutputDataCallback;
|
||||||
|
#endif
|
||||||
|
#if defined(__APPLE__) && defined(TARGET_OS_OSX)
|
||||||
|
bool macAudioDuckingEnabled=true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
video::VideoSource* videoSource=NULL;
|
||||||
|
video::VideoRenderer* videoRenderer=NULL;
|
||||||
|
double firstVideoFrameTime=0.0;
|
||||||
|
uint32_t videoFrameCount=0;
|
||||||
|
uint32_t lastReceivedVideoFrameNumber=UINT32_MAX;
|
||||||
|
std::vector<SentVideoFrame> sentVideoFrames;
|
||||||
|
Mutex sentVideoFramesMutex;
|
||||||
|
bool videoKeyframeRequested=false;
|
||||||
|
video::ScreamCongestionController videoCongestionControl;
|
||||||
|
std::vector<PendingVideoFrameFragment> videoPacingQueue;
|
||||||
|
uint32_t sendVideoPacketID=MessageThread::INVALID_ID;
|
||||||
|
uint32_t videoPacketLossCount=0;
|
||||||
|
uint32_t currentVideoBitrate=0;
|
||||||
|
double lastVideoResolutionChangeTime=0.0;
|
||||||
|
|
||||||
|
/*** debug report problems ***/
|
||||||
|
bool wasReconnecting=false;
|
||||||
|
bool wasExtraEC=false;
|
||||||
|
bool wasEncoderLaggy=false;
|
||||||
|
bool wasNetworkHandover=false;
|
||||||
|
|
||||||
|
/*** persistable state values ***/
|
||||||
|
bool proxySupportsUDP=true;
|
||||||
|
bool proxySupportsTCP=true;
|
||||||
|
std::string lastTestedProxyServer="";
|
||||||
|
|
||||||
|
/*** server config values ***/
|
||||||
|
uint32_t maxAudioBitrate;
|
||||||
|
uint32_t maxAudioBitrateEDGE;
|
||||||
|
uint32_t maxAudioBitrateGPRS;
|
||||||
|
uint32_t maxAudioBitrateSaving;
|
||||||
|
uint32_t initAudioBitrate;
|
||||||
|
uint32_t initAudioBitrateEDGE;
|
||||||
|
uint32_t initAudioBitrateGPRS;
|
||||||
|
uint32_t initAudioBitrateSaving;
|
||||||
|
uint32_t minAudioBitrate;
|
||||||
|
uint32_t audioBitrateStepIncr;
|
||||||
|
uint32_t audioBitrateStepDecr;
|
||||||
|
double relaySwitchThreshold;
|
||||||
|
double p2pToRelaySwitchThreshold;
|
||||||
|
double relayToP2pSwitchThreshold;
|
||||||
|
double reconnectingTimeout;
|
||||||
|
uint32_t needRateFlags;
|
||||||
|
double rateMaxAcceptableRTT;
|
||||||
|
double rateMaxAcceptableSendLoss;
|
||||||
|
double packetLossToEnableExtraEC;
|
||||||
|
uint32_t maxUnsentStreamPackets;
|
||||||
|
|
||||||
|
public:
|
||||||
|
#ifdef __APPLE__
|
||||||
|
static double machTimebase;
|
||||||
|
static uint64_t machTimestart;
|
||||||
|
#endif
|
||||||
|
#ifdef _WIN32
|
||||||
|
static int64_t win32TimeScale;
|
||||||
|
static bool didInitWin32TimeScale;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
class VoIPGroupController : public VoIPController{
|
||||||
|
public:
|
||||||
|
VoIPGroupController(int32_t timeDifference);
|
||||||
|
virtual ~VoIPGroupController();
|
||||||
|
void SetGroupCallInfo(unsigned char* encryptionKey, unsigned char* reflectorGroupTag, unsigned char* reflectorSelfTag, unsigned char* reflectorSelfSecret, unsigned char* reflectorSelfTagHash, int32_t selfUserID, IPv4Address reflectorAddress, IPv6Address reflectorAddressV6, uint16_t reflectorPort);
|
||||||
|
void AddGroupCallParticipant(int32_t userID, unsigned char* memberTagHash, unsigned char* serializedStreams, size_t streamsLength);
|
||||||
|
void RemoveGroupCallParticipant(int32_t userID);
|
||||||
|
float GetParticipantAudioLevel(int32_t userID);
|
||||||
|
virtual void SetMicMute(bool mute);
|
||||||
|
void SetParticipantVolume(int32_t userID, float volume);
|
||||||
|
void SetParticipantStreams(int32_t userID, unsigned char* serializedStreams, size_t length);
|
||||||
|
static size_t GetInitialStreams(unsigned char* buf, size_t size);
|
||||||
|
|
||||||
|
struct Callbacks : public VoIPController::Callbacks{
|
||||||
|
void (*updateStreams)(VoIPGroupController*, unsigned char*, size_t);
|
||||||
|
void (*participantAudioStateChanged)(VoIPGroupController*, int32_t, bool);
|
||||||
|
|
||||||
|
};
|
||||||
|
void SetCallbacks(Callbacks callbacks);
|
||||||
|
virtual std::string GetDebugString();
|
||||||
|
virtual void SetNetworkType(int type);
|
||||||
|
protected:
|
||||||
|
virtual void ProcessIncomingPacket(NetworkPacket& packet, Endpoint& srcEndpoint);
|
||||||
|
virtual void SendInit();
|
||||||
|
virtual void SendUdpPing(Endpoint& endpoint);
|
||||||
|
virtual void SendRelayPings();
|
||||||
|
virtual void SendPacket(unsigned char* data, size_t len, Endpoint& ep, PendingOutgoingPacket& srcPacket);
|
||||||
|
virtual void WritePacketHeader(uint32_t seq, BufferOutputStream* s, unsigned char type, uint32_t length);
|
||||||
|
virtual void OnAudioOutputReady();
|
||||||
|
private:
|
||||||
|
int32_t GetCurrentUnixtime();
|
||||||
|
std::vector<std::shared_ptr<Stream>> DeserializeStreams(BufferInputStream& in);
|
||||||
|
void SendRecentPacketsRequest();
|
||||||
|
void SendSpecialReflectorRequest(unsigned char* data, size_t len);
|
||||||
|
void SerializeAndUpdateOutgoingStreams();
|
||||||
|
struct GroupCallParticipant{
|
||||||
|
int32_t userID;
|
||||||
|
unsigned char memberTagHash[32];
|
||||||
|
std::vector<std::shared_ptr<Stream>> streams;
|
||||||
|
AudioLevelMeter* levelMeter;
|
||||||
|
};
|
||||||
|
std::vector<GroupCallParticipant> participants;
|
||||||
|
unsigned char reflectorSelfTag[16];
|
||||||
|
unsigned char reflectorSelfSecret[16];
|
||||||
|
unsigned char reflectorSelfTagHash[32];
|
||||||
|
int32_t userSelfID;
|
||||||
|
Endpoint groupReflector;
|
||||||
|
AudioMixer* audioMixer;
|
||||||
|
AudioLevelMeter selfLevelMeter;
|
||||||
|
Callbacks groupCallbacks;
|
||||||
|
struct PacketIdMapping{
|
||||||
|
uint32_t seq;
|
||||||
|
uint16_t id;
|
||||||
|
double ackTime;
|
||||||
|
};
|
||||||
|
std::vector<PacketIdMapping> recentSentPackets;
|
||||||
|
Mutex sentPacketsMutex;
|
||||||
|
Mutex participantsMutex;
|
||||||
|
int32_t timeDifference;
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,816 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "VoIPController.h"
|
||||||
|
#include "logging.h"
|
||||||
|
#include "VoIPServerConfig.h"
|
||||||
|
#include "PrivateDefines.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
using namespace tgvoip;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
VoIPGroupController::VoIPGroupController(int32_t timeDifference){
|
||||||
|
audioMixer=new AudioMixer();
|
||||||
|
memset(&callbacks, 0, sizeof(callbacks));
|
||||||
|
userSelfID=0;
|
||||||
|
this->timeDifference=timeDifference;
|
||||||
|
LOGV("Created VoIPGroupController; timeDifference=%d", timeDifference);
|
||||||
|
}
|
||||||
|
|
||||||
|
VoIPGroupController::~VoIPGroupController(){
|
||||||
|
if(audioOutput){
|
||||||
|
audioOutput->Stop();
|
||||||
|
}
|
||||||
|
LOGD("before stop audio mixer");
|
||||||
|
audioMixer->Stop();
|
||||||
|
delete audioMixer;
|
||||||
|
|
||||||
|
for(vector<GroupCallParticipant>::iterator p=participants.begin();p!=participants.end();p++){
|
||||||
|
if(p->levelMeter)
|
||||||
|
delete p->levelMeter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPGroupController::SetGroupCallInfo(unsigned char *encryptionKey, unsigned char *reflectorGroupTag, unsigned char *reflectorSelfTag, unsigned char *reflectorSelfSecret, unsigned char* reflectorSelfTagHash, int32_t selfUserID, IPv4Address reflectorAddress, IPv6Address reflectorAddressV6, uint16_t reflectorPort){
|
||||||
|
Endpoint e;
|
||||||
|
e.address=reflectorAddress;
|
||||||
|
e.v6address=reflectorAddressV6;
|
||||||
|
e.port=reflectorPort;
|
||||||
|
memcpy(e.peerTag, reflectorGroupTag, 16);
|
||||||
|
e.type=Endpoint::Type::UDP_RELAY;
|
||||||
|
e.id=FOURCC('G','R','P','R');
|
||||||
|
endpoints[e.id]=e;
|
||||||
|
groupReflector=e;
|
||||||
|
currentEndpoint=e.id;
|
||||||
|
|
||||||
|
memcpy(this->encryptionKey, encryptionKey, 256);
|
||||||
|
memcpy(this->reflectorSelfTag, reflectorSelfTag, 16);
|
||||||
|
memcpy(this->reflectorSelfSecret, reflectorSelfSecret, 16);
|
||||||
|
memcpy(this->reflectorSelfTagHash, reflectorSelfTagHash, 16);
|
||||||
|
uint8_t sha256[SHA256_LENGTH];
|
||||||
|
crypto.sha256((uint8_t*) encryptionKey, 256, sha256);
|
||||||
|
memcpy(callID, sha256+(SHA256_LENGTH-16), 16);
|
||||||
|
memcpy(keyFingerprint, sha256+(SHA256_LENGTH-16), 8);
|
||||||
|
this->userSelfID=selfUserID;
|
||||||
|
|
||||||
|
//LOGD("reflectorSelfTag = %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", reflectorSelfTag[0], reflectorSelfTag[1], reflectorSelfTag[2], reflectorSelfTag[3], reflectorSelfTag[4], reflectorSelfTag[5], reflectorSelfTag[6], reflectorSelfTag[7], reflectorSelfTag[8], reflectorSelfTag[9], reflectorSelfTag[10], reflectorSelfTag[11], reflectorSelfTag[12], reflectorSelfTag[13], reflectorSelfTag[14], reflectorSelfTag[15]);
|
||||||
|
//LOGD("reflectorSelfSecret = %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", reflectorSelfSecret[0], reflectorSelfSecret[1], reflectorSelfSecret[2], reflectorSelfSecret[3], reflectorSelfSecret[4], reflectorSelfSecret[5], reflectorSelfSecret[6], reflectorSelfSecret[7], reflectorSelfSecret[8], reflectorSelfSecret[9], reflectorSelfSecret[10], reflectorSelfSecret[11], reflectorSelfSecret[12], reflectorSelfSecret[13], reflectorSelfSecret[14], reflectorSelfSecret[15]);
|
||||||
|
//LOGD("reflectorSelfTagHash = %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", reflectorSelfTagHash[0], reflectorSelfTagHash[1], reflectorSelfTagHash[2], reflectorSelfTagHash[3], reflectorSelfTagHash[4], reflectorSelfTagHash[5], reflectorSelfTagHash[6], reflectorSelfTagHash[7], reflectorSelfTagHash[8], reflectorSelfTagHash[9], reflectorSelfTagHash[10], reflectorSelfTagHash[11], reflectorSelfTagHash[12], reflectorSelfTagHash[13], reflectorSelfTagHash[14], reflectorSelfTagHash[15]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPGroupController::AddGroupCallParticipant(int32_t userID, unsigned char *memberTagHash, unsigned char* serializedStreams, size_t streamsLength){
|
||||||
|
if(userID==userSelfID)
|
||||||
|
return;
|
||||||
|
if(userSelfID==0)
|
||||||
|
return;
|
||||||
|
//if(streamsLength==0)
|
||||||
|
// return;
|
||||||
|
MutexGuard m(participantsMutex);
|
||||||
|
LOGV("Adding group call user %d, streams length %u", userID, (unsigned int)streamsLength);
|
||||||
|
|
||||||
|
for(vector<GroupCallParticipant>::iterator p=participants.begin();p!=participants.end();++p){
|
||||||
|
if(p->userID==userID){
|
||||||
|
LOGE("user %d already added", userID);
|
||||||
|
abort();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupCallParticipant p;
|
||||||
|
p.userID=userID;
|
||||||
|
memcpy(p.memberTagHash, memberTagHash, sizeof(p.memberTagHash));
|
||||||
|
p.levelMeter=new AudioLevelMeter();
|
||||||
|
|
||||||
|
BufferInputStream ss(serializedStreams, streamsLength);
|
||||||
|
vector<shared_ptr<Stream>> streams=DeserializeStreams(ss);
|
||||||
|
|
||||||
|
unsigned char audioStreamID=0;
|
||||||
|
|
||||||
|
for(vector<shared_ptr<Stream>>::iterator _s=streams.begin();_s!=streams.end();++_s){
|
||||||
|
shared_ptr<Stream>& s=*_s;
|
||||||
|
s->userID=userID;
|
||||||
|
if(s->type==STREAM_TYPE_AUDIO && s->codec==CODEC_OPUS && !audioStreamID){
|
||||||
|
audioStreamID=s->id;
|
||||||
|
s->jitterBuffer=make_shared<JitterBuffer>(nullptr, s->frameDuration);
|
||||||
|
if(s->frameDuration>50)
|
||||||
|
s->jitterBuffer->SetMinPacketCount((uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_initial_delay_60", 2));
|
||||||
|
else if(s->frameDuration>30)
|
||||||
|
s->jitterBuffer->SetMinPacketCount((uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_initial_delay_40", 4));
|
||||||
|
else
|
||||||
|
s->jitterBuffer->SetMinPacketCount((uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_initial_delay_20", 6));
|
||||||
|
s->callbackWrapper=make_shared<CallbackWrapper>();
|
||||||
|
s->decoder=make_shared<OpusDecoder>(s->callbackWrapper, false, false);
|
||||||
|
s->decoder->SetJitterBuffer(s->jitterBuffer);
|
||||||
|
s->decoder->SetFrameDuration(s->frameDuration);
|
||||||
|
s->decoder->SetDTX(true);
|
||||||
|
s->decoder->SetLevelMeter(p.levelMeter);
|
||||||
|
audioMixer->AddInput(s->callbackWrapper);
|
||||||
|
}
|
||||||
|
incomingStreams.push_back(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!audioStreamID){
|
||||||
|
LOGW("User %d has no usable audio stream", userID);
|
||||||
|
}
|
||||||
|
|
||||||
|
p.streams.insert(p.streams.end(), streams.begin(), streams.end());
|
||||||
|
participants.push_back(p);
|
||||||
|
LOGI("Added group call participant %d", userID);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPGroupController::RemoveGroupCallParticipant(int32_t userID){
|
||||||
|
MutexGuard m(participantsMutex);
|
||||||
|
vector<shared_ptr<Stream>>::iterator stm=incomingStreams.begin();
|
||||||
|
while(stm!=incomingStreams.end()){
|
||||||
|
if((*stm)->userID==userID){
|
||||||
|
LOGI("Removed stream %d belonging to user %d", (*stm)->id, userID);
|
||||||
|
audioMixer->RemoveInput((*stm)->callbackWrapper);
|
||||||
|
(*stm)->decoder->Stop();
|
||||||
|
//delete (*stm)->decoder;
|
||||||
|
//delete (*stm)->jitterBuffer;
|
||||||
|
//delete (*stm)->callbackWrapper;
|
||||||
|
stm=incomingStreams.erase(stm);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
++stm;
|
||||||
|
}
|
||||||
|
for(vector<GroupCallParticipant>::iterator p=participants.begin();p!=participants.end();++p){
|
||||||
|
if(p->userID==userID){
|
||||||
|
if(p->levelMeter)
|
||||||
|
delete p->levelMeter;
|
||||||
|
participants.erase(p);
|
||||||
|
LOGI("Removed group call participant %d", userID);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<shared_ptr<VoIPController::Stream>> VoIPGroupController::DeserializeStreams(BufferInputStream& in){
|
||||||
|
vector<shared_ptr<Stream>> res;
|
||||||
|
try{
|
||||||
|
unsigned char count=in.ReadByte();
|
||||||
|
for(unsigned char i=0;i<count;i++){
|
||||||
|
uint16_t len=(uint16_t) in.ReadInt16();
|
||||||
|
BufferInputStream inner=in.GetPartBuffer(len, true);
|
||||||
|
shared_ptr<Stream> s=make_shared<Stream>();
|
||||||
|
s->id=inner.ReadByte();
|
||||||
|
s->type=inner.ReadByte();
|
||||||
|
s->codec=(uint32_t) inner.ReadInt32();
|
||||||
|
uint32_t flags=(uint32_t) inner.ReadInt32();
|
||||||
|
s->enabled=(flags & STREAM_FLAG_ENABLED)==STREAM_FLAG_ENABLED;
|
||||||
|
s->frameDuration=(uint16_t) inner.ReadInt16();
|
||||||
|
res.push_back(s);
|
||||||
|
}
|
||||||
|
}catch(out_of_range& x){
|
||||||
|
LOGW("Error deserializing streams: %s", x.what());
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPGroupController::SetParticipantStreams(int32_t userID, unsigned char *serializedStreams, size_t length){
|
||||||
|
LOGD("Set participant streams for %d", userID);
|
||||||
|
MutexGuard m(participantsMutex);
|
||||||
|
for(vector<GroupCallParticipant>::iterator p=participants.begin();p!=participants.end();++p){
|
||||||
|
if(p->userID==userID){
|
||||||
|
BufferInputStream in(serializedStreams, length);
|
||||||
|
vector<shared_ptr<Stream>> streams=DeserializeStreams(in);
|
||||||
|
for(vector<shared_ptr<Stream>>::iterator ns=streams.begin();ns!=streams.end();++ns){
|
||||||
|
bool found=false;
|
||||||
|
for(vector<shared_ptr<Stream>>::iterator s=p->streams.begin();s!=p->streams.end();++s){
|
||||||
|
if((*s)->id==(*ns)->id){
|
||||||
|
(*s)->enabled=(*ns)->enabled;
|
||||||
|
if(groupCallbacks.participantAudioStateChanged)
|
||||||
|
groupCallbacks.participantAudioStateChanged(this, userID, (*s)->enabled);
|
||||||
|
found=true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!found){
|
||||||
|
LOGW("Tried to add stream %d for user %d but adding/removing streams is not supported", (*ns)->id, userID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t VoIPGroupController::GetInitialStreams(unsigned char *buf, size_t size){
|
||||||
|
BufferOutputStream s(buf, size);
|
||||||
|
s.WriteByte(1); // streams count
|
||||||
|
|
||||||
|
s.WriteInt16(12); // this object length
|
||||||
|
s.WriteByte(1); // stream id
|
||||||
|
s.WriteByte(STREAM_TYPE_AUDIO);
|
||||||
|
s.WriteInt32(CODEC_OPUS);
|
||||||
|
s.WriteInt32(STREAM_FLAG_ENABLED | STREAM_FLAG_DTX); // flags
|
||||||
|
s.WriteInt16(60); // frame duration
|
||||||
|
|
||||||
|
return s.GetLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPGroupController::SendInit(){
|
||||||
|
SendRecentPacketsRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPGroupController::ProcessIncomingPacket(NetworkPacket &packet, Endpoint& srcEndpoint){
|
||||||
|
//LOGD("Received incoming packet from %s:%u, %u bytes", packet.address->ToString().c_str(), packet.port, packet.length);
|
||||||
|
if(packet.length<17 || packet.length>2000){
|
||||||
|
LOGW("Received packet has wrong length %d", (int)packet.length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BufferOutputStream sigData(packet.length);
|
||||||
|
sigData.WriteBytes(packet.data, packet.length-16);
|
||||||
|
sigData.WriteBytes(reflectorSelfSecret, 16);
|
||||||
|
unsigned char sig[32];
|
||||||
|
crypto.sha256(sigData.GetBuffer(), sigData.GetLength(), sig);
|
||||||
|
if(memcmp(sig, packet.data+(packet.length-16), 16)!=0){
|
||||||
|
LOGW("Received packet has incorrect signature");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reflector special response
|
||||||
|
if(memcmp(packet.data, reflectorSelfTagHash, 16)==0 && packet.length>60){
|
||||||
|
//LOGI("possible reflector special response");
|
||||||
|
unsigned char firstBlock[16];
|
||||||
|
unsigned char iv[16];
|
||||||
|
memcpy(iv, packet.data+16, 16);
|
||||||
|
unsigned char key[32];
|
||||||
|
crypto.sha256(reflectorSelfSecret, 16, key);
|
||||||
|
crypto.aes_cbc_decrypt(packet.data+32, firstBlock, 16, key, iv);
|
||||||
|
BufferInputStream in(firstBlock, 16);
|
||||||
|
in.Seek(8);
|
||||||
|
size_t len=(size_t) in.ReadInt32();
|
||||||
|
int32_t tlid=in.ReadInt32();
|
||||||
|
//LOGD("special response: len=%d, tlid=0x%08X", len, tlid);
|
||||||
|
if(len%4==0 && len+60<=packet.length && packet.length<=1500){
|
||||||
|
lastRecvPacketTime=GetCurrentTime();
|
||||||
|
memcpy(iv, packet.data+16, 16);
|
||||||
|
unsigned char buf[1500];
|
||||||
|
crypto.aes_cbc_decrypt(packet.data+32, buf, len+16, key, iv);
|
||||||
|
try{
|
||||||
|
if(tlid==TLID_UDP_REFLECTOR_LAST_PACKETS_INFO){
|
||||||
|
MutexGuard m(sentPacketsMutex);
|
||||||
|
//LOGV("received udpReflector.lastPacketsInfo");
|
||||||
|
in=BufferInputStream(buf, len+16);
|
||||||
|
in.Seek(16);
|
||||||
|
/*int32_t date=*/in.ReadInt32();
|
||||||
|
/*int64_t queryID=*/in.ReadInt64();
|
||||||
|
int32_t vectorMagic=in.ReadInt32();
|
||||||
|
if(vectorMagic!=TLID_VECTOR){
|
||||||
|
LOGW("last packets info: expected vector, got %08X", vectorMagic);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int32_t recvCount=in.ReadInt32();
|
||||||
|
//LOGV("%d received packets", recvCount);
|
||||||
|
for(int i=0;i<recvCount;i++){
|
||||||
|
uint32_t p=(uint32_t) in.ReadInt32();
|
||||||
|
//LOGV("Relay received packet: %08X", p);
|
||||||
|
uint16_t id=(uint16_t) (p & 0xFFFF);
|
||||||
|
//LOGV("ack id %04X", id);
|
||||||
|
for(vector<PacketIdMapping>::iterator pkt=recentSentPackets.begin();pkt!=recentSentPackets.end();++pkt){
|
||||||
|
//LOGV("== sent id %04X", pkt->id);
|
||||||
|
if(pkt->id==id){
|
||||||
|
if(!pkt->ackTime){
|
||||||
|
pkt->ackTime=GetCurrentTime();
|
||||||
|
conctl->PacketAcknowledged(pkt->seq);
|
||||||
|
//LOGV("relay acknowledged packet %u", pkt->seq);
|
||||||
|
if(seqgt(pkt->seq, lastRemoteAckSeq))
|
||||||
|
lastRemoteAckSeq=pkt->seq;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vectorMagic=in.ReadInt32();
|
||||||
|
if(vectorMagic!=TLID_VECTOR){
|
||||||
|
LOGW("last packets info: expected vector, got %08X", vectorMagic);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int32_t sentCount=in.ReadInt32();
|
||||||
|
//LOGV("%d sent packets", sentCount);
|
||||||
|
for(int i=0;i<sentCount;i++){
|
||||||
|
/*int32_t p=*/in.ReadInt32();
|
||||||
|
//LOGV("Sent packet: %08X", p);
|
||||||
|
}
|
||||||
|
if(udpConnectivityState!=UDP_AVAILABLE)
|
||||||
|
udpConnectivityState=UDP_AVAILABLE;
|
||||||
|
if(state!=STATE_ESTABLISHED)
|
||||||
|
SetState(STATE_ESTABLISHED);
|
||||||
|
if(!audioInput){
|
||||||
|
InitializeAudio();
|
||||||
|
if(state!=STATE_FAILED){
|
||||||
|
// audioOutput->Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch(out_of_range& x){
|
||||||
|
LOGE("Error parsing special response: %s", x.what());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(packet.length<32)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// it's a packet relayed from another participant - find the sender
|
||||||
|
MutexGuard m(participantsMutex);
|
||||||
|
GroupCallParticipant* sender=NULL;
|
||||||
|
for(vector<GroupCallParticipant>::iterator p=participants.begin();p!=participants.end();++p){
|
||||||
|
if(memcmp(packet.data, p->memberTagHash, 16)==0){
|
||||||
|
//LOGV("received data packet from user %d", p->userID);
|
||||||
|
sender=&*p;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!sender){
|
||||||
|
LOGV("Received data packet is from unknown user");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(memcmp(packet.data+16, keyFingerprint, 8)!=0){
|
||||||
|
LOGW("received packet has wrong key fingerprint");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferInputStream in(packet.data, packet.length-16);
|
||||||
|
in.Seek(16+8); // peer tag + key fingerprint
|
||||||
|
|
||||||
|
unsigned char msgKey[16];
|
||||||
|
in.ReadBytes(msgKey, 16);
|
||||||
|
|
||||||
|
unsigned char decrypted[1500];
|
||||||
|
unsigned char aesKey[32], aesIv[32];
|
||||||
|
KDF2(msgKey, 0, aesKey, aesIv);
|
||||||
|
size_t decryptedLen=in.Remaining()-16;
|
||||||
|
if(decryptedLen>sizeof(decrypted))
|
||||||
|
return;
|
||||||
|
//LOGV("-> MSG KEY: %08x %08x %08x %08x, hashed %u", *reinterpret_cast<int32_t*>(msgKey), *reinterpret_cast<int32_t*>(msgKey+4), *reinterpret_cast<int32_t*>(msgKey+8), *reinterpret_cast<int32_t*>(msgKey+12), decryptedLen-4);
|
||||||
|
uint8_t *decryptOffset = packet.data + in.GetOffset();
|
||||||
|
if ((((intptr_t)decryptOffset) % sizeof(long)) != 0) {
|
||||||
|
LOGE("alignment2 packet.data+in.GetOffset()");
|
||||||
|
}
|
||||||
|
if (decryptedLen % sizeof(long) != 0) {
|
||||||
|
LOGE("alignment2 decryptedLen");
|
||||||
|
}
|
||||||
|
crypto.aes_ige_decrypt(packet.data+in.GetOffset(), decrypted, decryptedLen, aesKey, aesIv);
|
||||||
|
|
||||||
|
in=BufferInputStream(decrypted, decryptedLen);
|
||||||
|
//LOGD("received packet length: %d", in.ReadInt32());
|
||||||
|
|
||||||
|
BufferOutputStream buf(decryptedLen+32);
|
||||||
|
size_t x=0;
|
||||||
|
buf.WriteBytes(encryptionKey+88+x, 32);
|
||||||
|
buf.WriteBytes(decrypted+4, decryptedLen-4);
|
||||||
|
unsigned char msgKeyLarge[32];
|
||||||
|
crypto.sha256(buf.GetBuffer(), buf.GetLength(), msgKeyLarge);
|
||||||
|
|
||||||
|
if(memcmp(msgKey, msgKeyLarge+8, 16)!=0){
|
||||||
|
LOGW("Received packet from user %d has wrong hash", sender->userID);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t innerLen=(uint32_t) in.ReadInt32();
|
||||||
|
if(innerLen>decryptedLen-4){
|
||||||
|
LOGW("Received packet has wrong inner length (%d with total of %u)", (int)innerLen, (unsigned int)decryptedLen);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(decryptedLen-innerLen<12){
|
||||||
|
LOGW("Received packet has too little padding (%u)", (unsigned int)(decryptedLen-innerLen));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
in=BufferInputStream(decrypted+4, (size_t) innerLen);
|
||||||
|
|
||||||
|
uint32_t tlid=(uint32_t) in.ReadInt32();
|
||||||
|
if(tlid!=TLID_DECRYPTED_AUDIO_BLOCK){
|
||||||
|
LOGW("Received packet has unknown TL ID 0x%08x", tlid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
in.Seek(in.GetOffset()+16); // random bytes
|
||||||
|
int32_t flags=in.ReadInt32();
|
||||||
|
if(!(flags & PFLAG_HAS_SEQ) || !(flags & PFLAG_HAS_SENDER_TAG_HASH)){
|
||||||
|
LOGW("Received packet has wrong flags");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/*uint32_t seq=(uint32_t) */in.ReadInt32();
|
||||||
|
unsigned char senderTagHash[16];
|
||||||
|
in.ReadBytes(senderTagHash, 16);
|
||||||
|
if(memcmp(senderTagHash, sender->memberTagHash, 16)!=0){
|
||||||
|
LOGW("Received packet has wrong inner sender tag hash");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//int32_t oneMoreInnerLengthWhyDoWeEvenNeedThis;
|
||||||
|
if(flags & PFLAG_HAS_DATA){
|
||||||
|
/*oneMoreInnerLengthWhyDoWeEvenNeedThis=*/in.ReadTlLength();
|
||||||
|
}
|
||||||
|
unsigned char type=(unsigned char) ((flags >> 24) & 0xFF);
|
||||||
|
lastRecvPacketTime=GetCurrentTime();
|
||||||
|
|
||||||
|
if(type==PKT_STREAM_DATA || type==PKT_STREAM_DATA_X2 || type==PKT_STREAM_DATA_X3){
|
||||||
|
if(state!=STATE_ESTABLISHED && receivedInitAck)
|
||||||
|
SetState(STATE_ESTABLISHED);
|
||||||
|
int count;
|
||||||
|
switch(type){
|
||||||
|
case PKT_STREAM_DATA_X2:
|
||||||
|
count=2;
|
||||||
|
break;
|
||||||
|
case PKT_STREAM_DATA_X3:
|
||||||
|
count=3;
|
||||||
|
break;
|
||||||
|
case PKT_STREAM_DATA:
|
||||||
|
default:
|
||||||
|
count=1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int i;
|
||||||
|
//if(srcEndpoint->type==Endpoint::Type::UDP_RELAY && srcEndpoint!=peerPreferredRelay){
|
||||||
|
// peerPreferredRelay=srcEndpoint;
|
||||||
|
//}
|
||||||
|
for(i=0;i<count;i++){
|
||||||
|
unsigned char streamID=in.ReadByte();
|
||||||
|
unsigned char sflags=(unsigned char) (streamID & 0xC0);
|
||||||
|
uint16_t sdlen=(uint16_t) (sflags & STREAM_DATA_FLAG_LEN16 ? in.ReadInt16() : in.ReadByte());
|
||||||
|
uint32_t pts=(uint32_t) in.ReadInt32();
|
||||||
|
//LOGD("stream data, pts=%d, len=%d, rem=%d", pts, sdlen, in.Remaining());
|
||||||
|
audioTimestampIn=pts;
|
||||||
|
/*if(!audioOutStarted && audioOutput){
|
||||||
|
audioOutput->Start();
|
||||||
|
audioOutStarted=true;
|
||||||
|
}*/
|
||||||
|
if(in.GetOffset()+sdlen>in.GetLength()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for(vector<shared_ptr<Stream>>::iterator stm=sender->streams.begin();stm!=sender->streams.end();++stm){
|
||||||
|
if((*stm)->id==streamID){
|
||||||
|
if((*stm)->jitterBuffer){
|
||||||
|
(*stm)->jitterBuffer->HandleInput(decrypted+4+in.GetOffset(), sdlen, pts, false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(i<count-1)
|
||||||
|
in.Seek(in.GetOffset()+sdlen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPGroupController::SendUdpPing(Endpoint& endpoint){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPGroupController::SetNetworkType(int type){
|
||||||
|
networkType=type;
|
||||||
|
UpdateDataSavingState();
|
||||||
|
UpdateAudioBitrateLimit();
|
||||||
|
string itfName=udpSocket->GetLocalInterfaceInfo(NULL, NULL);
|
||||||
|
if(itfName!=activeNetItfName){
|
||||||
|
udpSocket->OnActiveInterfaceChanged();
|
||||||
|
LOGI("Active network interface changed: %s -> %s", activeNetItfName.c_str(), itfName.c_str());
|
||||||
|
bool isFirstChange=activeNetItfName.length()==0;
|
||||||
|
activeNetItfName=itfName;
|
||||||
|
if(isFirstChange)
|
||||||
|
return;
|
||||||
|
udpConnectivityState=UDP_UNKNOWN;
|
||||||
|
udpPingCount=0;
|
||||||
|
lastUdpPingTime=0;
|
||||||
|
if(proxyProtocol==PROXY_SOCKS5)
|
||||||
|
InitUDPProxy();
|
||||||
|
selectCanceller->CancelSelect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPGroupController::SendRecentPacketsRequest(){
|
||||||
|
BufferOutputStream out(1024);
|
||||||
|
out.WriteInt32(TLID_UDP_REFLECTOR_REQUEST_PACKETS_INFO); // TL function
|
||||||
|
out.WriteInt32(GetCurrentUnixtime()); // date:int
|
||||||
|
out.WriteInt64(0); // query_id:long
|
||||||
|
out.WriteInt32(64); // recv_num:int
|
||||||
|
out.WriteInt32(0); // sent_num:int
|
||||||
|
SendSpecialReflectorRequest(out.GetBuffer(), out.GetLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPGroupController::SendSpecialReflectorRequest(unsigned char *data, size_t len){
|
||||||
|
BufferOutputStream out(1024);
|
||||||
|
unsigned char buf[1500];
|
||||||
|
crypto.rand_bytes(buf, 8);
|
||||||
|
out.WriteBytes(buf, 8);
|
||||||
|
out.WriteInt32((int32_t)len);
|
||||||
|
out.WriteBytes(data, len);
|
||||||
|
if(out.GetLength()%16!=0){
|
||||||
|
size_t paddingLen=16-(out.GetLength()%16);
|
||||||
|
crypto.rand_bytes(buf, paddingLen);
|
||||||
|
out.WriteBytes(buf, paddingLen);
|
||||||
|
}
|
||||||
|
unsigned char iv[16];
|
||||||
|
crypto.rand_bytes(iv, 16);
|
||||||
|
unsigned char key[32];
|
||||||
|
crypto.sha256(reflectorSelfSecret, 16, key);
|
||||||
|
unsigned char _iv[16];
|
||||||
|
memcpy(_iv, iv, 16);
|
||||||
|
size_t encryptedLen=out.GetLength();
|
||||||
|
crypto.aes_cbc_encrypt(out.GetBuffer(), buf, encryptedLen, key, _iv);
|
||||||
|
out.Reset();
|
||||||
|
out.WriteBytes(reflectorSelfTag, 16);
|
||||||
|
out.WriteBytes(iv, 16);
|
||||||
|
out.WriteBytes(buf, encryptedLen);
|
||||||
|
out.WriteBytes(reflectorSelfSecret, 16);
|
||||||
|
crypto.sha256(out.GetBuffer(), out.GetLength(), buf);
|
||||||
|
out.Rewind(16);
|
||||||
|
out.WriteBytes(buf, 16);
|
||||||
|
|
||||||
|
NetworkPacket pkt={0};
|
||||||
|
pkt.address=&groupReflector.address;
|
||||||
|
pkt.port=groupReflector.port;
|
||||||
|
pkt.protocol=PROTO_UDP;
|
||||||
|
pkt.data=out.GetBuffer();
|
||||||
|
pkt.length=out.GetLength();
|
||||||
|
ActuallySendPacket(pkt, groupReflector);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPGroupController::SendRelayPings(){
|
||||||
|
//LOGV("Send relay pings 2");
|
||||||
|
double currentTime=GetCurrentTime();
|
||||||
|
if(currentTime-groupReflector.lastPingTime>=0.25){
|
||||||
|
SendRecentPacketsRequest();
|
||||||
|
groupReflector.lastPingTime=currentTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPGroupController::OnAudioOutputReady(){
|
||||||
|
encoder->SetDTX(true);
|
||||||
|
audioMixer->SetOutput(audioOutput);
|
||||||
|
audioMixer->SetEchoCanceller(echoCanceller);
|
||||||
|
audioMixer->Start();
|
||||||
|
audioOutput->Start();
|
||||||
|
audioOutStarted=true;
|
||||||
|
encoder->SetLevelMeter(&selfLevelMeter);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPGroupController::WritePacketHeader(uint32_t seq, BufferOutputStream *s, unsigned char type, uint32_t length){
|
||||||
|
s->WriteInt32(TLID_DECRYPTED_AUDIO_BLOCK);
|
||||||
|
int64_t randomID;
|
||||||
|
crypto.rand_bytes((uint8_t *) &randomID, 8);
|
||||||
|
s->WriteInt64(randomID);
|
||||||
|
unsigned char randBytes[7];
|
||||||
|
crypto.rand_bytes(randBytes, 7);
|
||||||
|
s->WriteByte(7);
|
||||||
|
s->WriteBytes(randBytes, 7);
|
||||||
|
uint32_t pflags=PFLAG_HAS_SEQ | PFLAG_HAS_SENDER_TAG_HASH;
|
||||||
|
if(length>0)
|
||||||
|
pflags|=PFLAG_HAS_DATA;
|
||||||
|
pflags|=((uint32_t) type) << 24;
|
||||||
|
s->WriteInt32(pflags);
|
||||||
|
|
||||||
|
if(type==PKT_STREAM_DATA || type==PKT_STREAM_DATA_X2 || type==PKT_STREAM_DATA_X3){
|
||||||
|
conctl->PacketSent(seq, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*if(pflags & PFLAG_HAS_CALL_ID){
|
||||||
|
s->WriteBytes(callID, 16);
|
||||||
|
}*/
|
||||||
|
//s->WriteInt32(lastRemoteSeq);
|
||||||
|
s->WriteInt32(seq);
|
||||||
|
s->WriteBytes(reflectorSelfTagHash, 16);
|
||||||
|
if(length>0){
|
||||||
|
if(length<=253){
|
||||||
|
s->WriteByte((unsigned char) length);
|
||||||
|
}else{
|
||||||
|
s->WriteByte(254);
|
||||||
|
s->WriteByte((unsigned char) (length & 0xFF));
|
||||||
|
s->WriteByte((unsigned char) ((length >> 8) & 0xFF));
|
||||||
|
s->WriteByte((unsigned char) ((length >> 16) & 0xFF));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPGroupController::SendPacket(unsigned char *data, size_t len, Endpoint& ep, PendingOutgoingPacket& srcPacket){
|
||||||
|
if(stopping)
|
||||||
|
return;
|
||||||
|
if(ep.type==Endpoint::Type::TCP_RELAY && !useTCP)
|
||||||
|
return;
|
||||||
|
BufferOutputStream out(len+128);
|
||||||
|
//LOGV("send group packet %u", len);
|
||||||
|
|
||||||
|
out.WriteBytes(reflectorSelfTag, 16);
|
||||||
|
|
||||||
|
if(len>0){
|
||||||
|
BufferOutputStream inner(len+128);
|
||||||
|
inner.WriteInt32((uint32_t)len);
|
||||||
|
inner.WriteBytes(data, len);
|
||||||
|
size_t padLen=16-inner.GetLength()%16;
|
||||||
|
if(padLen<12)
|
||||||
|
padLen+=16;
|
||||||
|
unsigned char padding[28];
|
||||||
|
crypto.rand_bytes((uint8_t *) padding, padLen);
|
||||||
|
inner.WriteBytes(padding, padLen);
|
||||||
|
assert(inner.GetLength()%16==0);
|
||||||
|
|
||||||
|
unsigned char key[32], iv[32], msgKey[16];
|
||||||
|
out.WriteBytes(keyFingerprint, 8);
|
||||||
|
BufferOutputStream buf(len+32);
|
||||||
|
size_t x=0;
|
||||||
|
buf.WriteBytes(encryptionKey+88+x, 32);
|
||||||
|
buf.WriteBytes(inner.GetBuffer()+4, inner.GetLength()-4);
|
||||||
|
unsigned char msgKeyLarge[32];
|
||||||
|
crypto.sha256(buf.GetBuffer(), buf.GetLength(), msgKeyLarge);
|
||||||
|
memcpy(msgKey, msgKeyLarge+8, 16);
|
||||||
|
KDF2(msgKey, 0, key, iv);
|
||||||
|
out.WriteBytes(msgKey, 16);
|
||||||
|
//LOGV("<- MSG KEY: %08x %08x %08x %08x, hashed %u", *reinterpret_cast<int32_t*>(msgKey), *reinterpret_cast<int32_t*>(msgKey+4), *reinterpret_cast<int32_t*>(msgKey+8), *reinterpret_cast<int32_t*>(msgKey+12), inner.GetLength()-4);
|
||||||
|
|
||||||
|
unsigned char aesOut[MSC_STACK_FALLBACK(inner.GetLength(), 1500)];
|
||||||
|
crypto.aes_ige_encrypt(inner.GetBuffer(), aesOut, inner.GetLength(), key, iv);
|
||||||
|
out.WriteBytes(aesOut, inner.GetLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
// relay signature
|
||||||
|
out.WriteBytes(reflectorSelfSecret, 16);
|
||||||
|
unsigned char sig[32];
|
||||||
|
crypto.sha256(out.GetBuffer(), out.GetLength(), sig);
|
||||||
|
out.Rewind(16);
|
||||||
|
out.WriteBytes(sig, 16);
|
||||||
|
|
||||||
|
if(srcPacket.type==PKT_STREAM_DATA || srcPacket.type==PKT_STREAM_DATA_X2 || srcPacket.type==PKT_STREAM_DATA_X3){
|
||||||
|
PacketIdMapping mapping={srcPacket.seq, *reinterpret_cast<uint16_t*>(sig+14), 0};
|
||||||
|
MutexGuard m(sentPacketsMutex);
|
||||||
|
recentSentPackets.push_back(mapping);
|
||||||
|
//LOGD("sent packet with id: %04X", mapping.id);
|
||||||
|
while(recentSentPackets.size()>64)
|
||||||
|
recentSentPackets.erase(recentSentPackets.begin());
|
||||||
|
}
|
||||||
|
lastSentSeq=srcPacket.seq;
|
||||||
|
|
||||||
|
if(IS_MOBILE_NETWORK(networkType))
|
||||||
|
stats.bytesSentMobile+=(uint64_t)out.GetLength();
|
||||||
|
else
|
||||||
|
stats.bytesSentWifi+=(uint64_t)out.GetLength();
|
||||||
|
|
||||||
|
NetworkPacket pkt={0};
|
||||||
|
pkt.address=(NetworkAddress*)&ep.address;
|
||||||
|
pkt.port=ep.port;
|
||||||
|
pkt.length=out.GetLength();
|
||||||
|
pkt.data=out.GetBuffer();
|
||||||
|
pkt.protocol=ep.type==Endpoint::Type::TCP_RELAY ? PROTO_TCP : PROTO_UDP;
|
||||||
|
ActuallySendPacket(pkt, ep);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPGroupController::SetCallbacks(VoIPGroupController::Callbacks callbacks){
|
||||||
|
VoIPController::SetCallbacks(callbacks);
|
||||||
|
this->groupCallbacks=callbacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t VoIPGroupController::GetCurrentUnixtime(){
|
||||||
|
return time(NULL)+timeDifference;
|
||||||
|
}
|
||||||
|
|
||||||
|
float VoIPGroupController::GetParticipantAudioLevel(int32_t userID){
|
||||||
|
if(userID==userSelfID)
|
||||||
|
return selfLevelMeter.GetLevel();
|
||||||
|
MutexGuard m(participantsMutex);
|
||||||
|
for(vector<GroupCallParticipant>::iterator p=participants.begin(); p!=participants.end(); ++p){
|
||||||
|
if(p->userID==userID){
|
||||||
|
return p->levelMeter->GetLevel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPGroupController::SetMicMute(bool mute){
|
||||||
|
micMuted=mute;
|
||||||
|
if(audioInput){
|
||||||
|
if(mute)
|
||||||
|
audioInput->Stop();
|
||||||
|
else
|
||||||
|
audioInput->Start();
|
||||||
|
if(!audioInput->IsInitialized()){
|
||||||
|
lastError=ERROR_AUDIO_IO;
|
||||||
|
SetState(STATE_FAILED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outgoingStreams[0]->enabled=!mute;
|
||||||
|
SerializeAndUpdateOutgoingStreams();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPGroupController::SetParticipantVolume(int32_t userID, float volume){
|
||||||
|
MutexGuard m(participantsMutex);
|
||||||
|
for(vector<GroupCallParticipant>::iterator p=participants.begin();p!=participants.end();++p){
|
||||||
|
if(p->userID==userID){
|
||||||
|
for(vector<shared_ptr<Stream>>::iterator s=p->streams.begin();s!=p->streams.end();++s){
|
||||||
|
if((*s)->type==STREAM_TYPE_AUDIO){
|
||||||
|
if((*s)->decoder){
|
||||||
|
float db;
|
||||||
|
if(volume==0.0f)
|
||||||
|
db=-INFINITY;
|
||||||
|
else if(volume<1.0f)
|
||||||
|
db=-50.0f*(1.0f-volume);
|
||||||
|
else if(volume>1.0f && volume<=2.0f)
|
||||||
|
db=10.0f*(volume-1.0f);
|
||||||
|
else
|
||||||
|
db=0.0f;
|
||||||
|
//LOGV("Setting user %u audio volume to %.2f dB", userID, db);
|
||||||
|
audioMixer->SetInputVolume((*s)->callbackWrapper, db);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPGroupController::SerializeAndUpdateOutgoingStreams(){
|
||||||
|
BufferOutputStream out(1024);
|
||||||
|
out.WriteByte((unsigned char) outgoingStreams.size());
|
||||||
|
|
||||||
|
for(vector<shared_ptr<Stream>>::iterator s=outgoingStreams.begin(); s!=outgoingStreams.end(); ++s){
|
||||||
|
BufferOutputStream o(128);
|
||||||
|
o.WriteByte((*s)->id);
|
||||||
|
o.WriteByte((*s)->type);
|
||||||
|
o.WriteInt32((*s)->codec);
|
||||||
|
o.WriteInt32((unsigned char) (((*s)->enabled ? STREAM_FLAG_ENABLED : 0) | STREAM_FLAG_DTX));
|
||||||
|
o.WriteInt16((*s)->frameDuration);
|
||||||
|
out.WriteInt16((int16_t) o.GetLength());
|
||||||
|
out.WriteBytes(o.GetBuffer(), o.GetLength());
|
||||||
|
}
|
||||||
|
if(groupCallbacks.updateStreams)
|
||||||
|
groupCallbacks.updateStreams(this, out.GetBuffer(), out.GetLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string VoIPGroupController::GetDebugString(){
|
||||||
|
std::string r="Remote endpoints: \n";
|
||||||
|
char buffer[2048];
|
||||||
|
for(pair<const int64_t, Endpoint>& _endpoint:endpoints){
|
||||||
|
Endpoint& endpoint=_endpoint.second;
|
||||||
|
const char* type;
|
||||||
|
switch(endpoint.type){
|
||||||
|
case Endpoint::Type::UDP_P2P_INET:
|
||||||
|
type="UDP_P2P_INET";
|
||||||
|
break;
|
||||||
|
case Endpoint::Type::UDP_P2P_LAN:
|
||||||
|
type="UDP_P2P_LAN";
|
||||||
|
break;
|
||||||
|
case Endpoint::Type::UDP_RELAY:
|
||||||
|
type="UDP_RELAY";
|
||||||
|
break;
|
||||||
|
case Endpoint::Type::TCP_RELAY:
|
||||||
|
type="TCP_RELAY";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
type="UNKNOWN";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
snprintf(buffer, sizeof(buffer), "%s:%u %dms [%s%s]\n", endpoint.address.ToString().c_str(), endpoint.port, (int)(endpoint.averageRTT*1000), type, currentEndpoint==endpoint.id ? ", IN_USE" : "");
|
||||||
|
r+=buffer;
|
||||||
|
}
|
||||||
|
double avgLate[3];
|
||||||
|
shared_ptr<JitterBuffer> jitterBuffer=incomingStreams.size()==1 ? incomingStreams[0]->jitterBuffer : NULL;
|
||||||
|
if(jitterBuffer)
|
||||||
|
jitterBuffer->GetAverageLateCount(avgLate);
|
||||||
|
else
|
||||||
|
memset(avgLate, 0, 3*sizeof(double));
|
||||||
|
snprintf(buffer, sizeof(buffer),
|
||||||
|
"RTT avg/min: %d/%d\n"
|
||||||
|
"Congestion window: %d/%d bytes\n"
|
||||||
|
"Key fingerprint: %02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX\n"
|
||||||
|
"Last sent/ack'd seq: %u/%u\n"
|
||||||
|
"Send/recv losses: %u/%u (%d%%)\n"
|
||||||
|
"Audio bitrate: %d kbit\n"
|
||||||
|
"Bytes sent/recvd: %llu/%llu\n\n",
|
||||||
|
(int)(conctl->GetAverageRTT()*1000), (int)(conctl->GetMinimumRTT()*1000),
|
||||||
|
int(conctl->GetInflightDataSize()), int(conctl->GetCongestionWindow()),
|
||||||
|
keyFingerprint[0],keyFingerprint[1],keyFingerprint[2],keyFingerprint[3],keyFingerprint[4],keyFingerprint[5],keyFingerprint[6],keyFingerprint[7],
|
||||||
|
lastSentSeq, lastRemoteAckSeq,
|
||||||
|
conctl->GetSendLossCount(), recvLossCount, encoder ? encoder->GetPacketLoss() : 0,
|
||||||
|
encoder ? (encoder->GetBitrate()/1000) : 0,
|
||||||
|
(long long unsigned int)(stats.bytesSentMobile+stats.bytesSentWifi),
|
||||||
|
(long long unsigned int)(stats.bytesRecvdMobile+stats.bytesRecvdWifi));
|
||||||
|
|
||||||
|
MutexGuard m(participantsMutex);
|
||||||
|
for(vector<GroupCallParticipant>::iterator p=participants.begin();p!=participants.end();++p){
|
||||||
|
snprintf(buffer, sizeof(buffer), "Participant id: %d\n", p->userID);
|
||||||
|
r+=buffer;
|
||||||
|
for(vector<shared_ptr<Stream>>::iterator stm=p->streams.begin();stm!=p->streams.end();++stm){
|
||||||
|
char* codec=reinterpret_cast<char*>(&(*stm)->codec);
|
||||||
|
snprintf(buffer, sizeof(buffer), "Stream %d (type %d, codec '%c%c%c%c', %sabled)\n",
|
||||||
|
(*stm)->id, (*stm)->type, codec[3], codec[2], codec[1], codec[0], (*stm)->enabled ? "en" : "dis");
|
||||||
|
r+=buffer;
|
||||||
|
if((*stm)->enabled){
|
||||||
|
if((*stm)->jitterBuffer){
|
||||||
|
snprintf(buffer, sizeof(buffer), "Jitter buffer: %d/%.2f\n",
|
||||||
|
(*stm)->jitterBuffer->GetMinPacketCount(), (*stm)->jitterBuffer->GetAverageDelay());
|
||||||
|
r+=buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r+="\n";
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "VoIPServerConfig.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "logging.h"
|
||||||
|
#include <sstream>
|
||||||
|
#include <locale>
|
||||||
|
|
||||||
|
using namespace tgvoip;
|
||||||
|
|
||||||
|
ServerConfig* ServerConfig::sharedInstance=NULL;
|
||||||
|
|
||||||
|
ServerConfig::ServerConfig(){
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerConfig::~ServerConfig(){
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerConfig *ServerConfig::GetSharedInstance(){
|
||||||
|
if(!sharedInstance)
|
||||||
|
sharedInstance=new ServerConfig();
|
||||||
|
return sharedInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ServerConfig::GetBoolean(std::string name, bool fallback){
|
||||||
|
MutexGuard sync(mutex);
|
||||||
|
if(ContainsKey(name) && config[name].is_bool())
|
||||||
|
return config[name].bool_value();
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
double ServerConfig::GetDouble(std::string name, double fallback){
|
||||||
|
MutexGuard sync(mutex);
|
||||||
|
if(ContainsKey(name) && config[name].is_number())
|
||||||
|
return config[name].number_value();
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t ServerConfig::GetInt(std::string name, int32_t fallback){
|
||||||
|
MutexGuard sync(mutex);
|
||||||
|
if(ContainsKey(name) && config[name].is_number())
|
||||||
|
return config[name].int_value();
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ServerConfig::GetString(std::string name, std::string fallback){
|
||||||
|
MutexGuard sync(mutex);
|
||||||
|
if(ContainsKey(name) && config[name].is_string())
|
||||||
|
return config[name].string_value();
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerConfig::Update(std::string jsonString){
|
||||||
|
MutexGuard sync(mutex);
|
||||||
|
LOGD("=== Updating voip config ===");
|
||||||
|
LOGD("%s", jsonString.c_str());
|
||||||
|
std::string jsonError;
|
||||||
|
config=json11::Json::parse(jsonString, jsonError);
|
||||||
|
if(!jsonError.empty())
|
||||||
|
LOGE("Error parsing server config: %s", jsonError.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool ServerConfig::ContainsKey(std::string key){
|
||||||
|
return config.object_items().find(key)!=config.object_items().end();
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef TGVOIP_VOIPSERVERCONFIG_H
|
||||||
|
#define TGVOIP_VOIPSERVERCONFIG_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "threading.h"
|
||||||
|
#include "json11.hpp"
|
||||||
|
|
||||||
|
namespace tgvoip{
|
||||||
|
|
||||||
|
class ServerConfig{
|
||||||
|
public:
|
||||||
|
ServerConfig();
|
||||||
|
~ServerConfig();
|
||||||
|
static ServerConfig* GetSharedInstance();
|
||||||
|
int32_t GetInt(std::string name, int32_t fallback);
|
||||||
|
double GetDouble(std::string name, double fallback);
|
||||||
|
std::string GetString(std::string name, std::string fallback);
|
||||||
|
bool GetBoolean(std::string name, bool fallback);
|
||||||
|
void Update(std::string jsonString);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static ServerConfig* sharedInstance;
|
||||||
|
bool ContainsKey(std::string key);
|
||||||
|
json11::Json config;
|
||||||
|
Mutex mutex;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //TGVOIP_VOIPSERVERCONFIG_H
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,92 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#include "AudioIO.h"
|
||||||
|
#include "../logging.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(TGVOIP_USE_CALLBACK_AUDIO_IO)
|
||||||
|
#include "AudioIOCallback.h"
|
||||||
|
#elif defined(__ANDROID__)
|
||||||
|
#include "../os/android/AudioInputAndroid.h"
|
||||||
|
#include "../os/android/AudioOutputAndroid.h"
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
#include <TargetConditionals.h>
|
||||||
|
#include "../os/darwin/AudioUnitIO.h"
|
||||||
|
#if TARGET_OS_OSX
|
||||||
|
#include "../os/darwin/AudioInputAudioUnitOSX.h"
|
||||||
|
#include "../os/darwin/AudioOutputAudioUnitOSX.h"
|
||||||
|
#endif
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
#ifdef TGVOIP_WINXP_COMPAT
|
||||||
|
#include "../os/windows/AudioInputWave.h"
|
||||||
|
#include "../os/windows/AudioOutputWave.h"
|
||||||
|
#endif
|
||||||
|
#include "../os/windows/AudioInputWASAPI.h"
|
||||||
|
#include "../os/windows/AudioOutputWASAPI.h"
|
||||||
|
#elif defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__gnu_hurd__)
|
||||||
|
#ifndef WITHOUT_ALSA
|
||||||
|
#include "../os/linux/AudioInputALSA.h"
|
||||||
|
#include "../os/linux/AudioOutputALSA.h"
|
||||||
|
#endif
|
||||||
|
#ifndef WITHOUT_PULSE
|
||||||
|
#include "../os/linux/AudioPulse.h"
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#error "Unsupported operating system"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace tgvoip;
|
||||||
|
using namespace tgvoip::audio;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
AudioIO* AudioIO::Create(std::string inputDevice, std::string outputDevice){
|
||||||
|
#if defined(TGVOIP_USE_CALLBACK_AUDIO_IO)
|
||||||
|
return new AudioIOCallback();
|
||||||
|
#elif defined(__ANDROID__)
|
||||||
|
return new ContextlessAudioIO<AudioInputAndroid, AudioOutputAndroid>();
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
#if TARGET_OS_OSX
|
||||||
|
if(kCFCoreFoundationVersionNumber<kCFCoreFoundationVersionNumber10_7)
|
||||||
|
return new ContextlessAudioIO<AudioInputAudioUnitLegacy, AudioOutputAudioUnitLegacy>(inputDevice, outputDevice);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
return new AudioUnitIO(inputDevice, outputDevice);
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
#ifdef TGVOIP_WINXP_COMPAT
|
||||||
|
if(LOBYTE(LOWORD(GetVersion()))<6)
|
||||||
|
return new ContextlessAudioIO<AudioInputWave, AudioOutputWave>(inputDevice, outputDevice);
|
||||||
|
#endif
|
||||||
|
return new ContextlessAudioIO<AudioInputWASAPI, AudioOutputWASAPI>(inputDevice, outputDevice);
|
||||||
|
#elif defined(__linux__)
|
||||||
|
#ifndef WITHOUT_ALSA
|
||||||
|
#ifndef WITHOUT_PULSE
|
||||||
|
if(AudioPulse::Load()){
|
||||||
|
AudioIO* io=new AudioPulse(inputDevice, outputDevice);
|
||||||
|
if(!io->Failed() && io->GetInput()->IsInitialized() && io->GetOutput()->IsInitialized())
|
||||||
|
return io;
|
||||||
|
LOGW("PulseAudio available but not working; trying ALSA");
|
||||||
|
delete io;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return new ContextlessAudioIO<AudioInputALSA, AudioOutputALSA>(inputDevice, outputDevice);
|
||||||
|
#else
|
||||||
|
return new AudioPulse(inputDevice, outputDevice);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AudioIO::Failed(){
|
||||||
|
return failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AudioIO::GetErrorDescription(){
|
||||||
|
return error;
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef LIBTGVOIP_AUDIOIO_H
|
||||||
|
#define LIBTGVOIP_AUDIOIO_H
|
||||||
|
|
||||||
|
#include "AudioInput.h"
|
||||||
|
#include "AudioOutput.h"
|
||||||
|
#include "../utils.h"
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace tgvoip{
|
||||||
|
namespace audio {
|
||||||
|
class AudioIO{
|
||||||
|
public:
|
||||||
|
AudioIO(){};
|
||||||
|
virtual ~AudioIO(){};
|
||||||
|
TGVOIP_DISALLOW_COPY_AND_ASSIGN(AudioIO);
|
||||||
|
static AudioIO* Create(std::string inputDevice, std::string outputDevice);
|
||||||
|
virtual AudioInput* GetInput()=0;
|
||||||
|
virtual AudioOutput* GetOutput()=0;
|
||||||
|
bool Failed();
|
||||||
|
std::string GetErrorDescription();
|
||||||
|
protected:
|
||||||
|
bool failed=false;
|
||||||
|
std::string error;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class I, class O> class ContextlessAudioIO : public AudioIO{
|
||||||
|
public:
|
||||||
|
ContextlessAudioIO(){
|
||||||
|
input=new I();
|
||||||
|
output=new O();
|
||||||
|
}
|
||||||
|
|
||||||
|
ContextlessAudioIO(std::string inputDeviceID, std::string outputDeviceID){
|
||||||
|
input=new I(inputDeviceID);
|
||||||
|
output=new O(outputDeviceID);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~ContextlessAudioIO(){
|
||||||
|
delete input;
|
||||||
|
delete output;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual AudioInput* GetInput(){
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual AudioOutput* GetOutput(){
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
I* input;
|
||||||
|
O* output;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //LIBTGVOIP_AUDIOIO_H
|
|
@ -0,0 +1,121 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "AudioIOCallback.h"
|
||||||
|
#include "../VoIPController.h"
|
||||||
|
#include "../logging.h"
|
||||||
|
|
||||||
|
using namespace tgvoip;
|
||||||
|
using namespace tgvoip::audio;
|
||||||
|
|
||||||
|
#pragma mark - IO
|
||||||
|
|
||||||
|
AudioIOCallback::AudioIOCallback(){
|
||||||
|
input=new AudioInputCallback();
|
||||||
|
output=new AudioOutputCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioIOCallback::~AudioIOCallback(){
|
||||||
|
delete input;
|
||||||
|
delete output;
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioInput* AudioIOCallback::GetInput(){
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioOutput* AudioIOCallback::GetOutput(){
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Input
|
||||||
|
|
||||||
|
AudioInputCallback::AudioInputCallback(){
|
||||||
|
thread=new Thread(std::bind(&AudioInputCallback::RunThread, this));
|
||||||
|
thread->SetName("AudioInputCallback");
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioInputCallback::~AudioInputCallback(){
|
||||||
|
running=false;
|
||||||
|
thread->Join();
|
||||||
|
delete thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioInputCallback::Start(){
|
||||||
|
if(!running){
|
||||||
|
running=true;
|
||||||
|
thread->Start();
|
||||||
|
}
|
||||||
|
recording=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioInputCallback::Stop(){
|
||||||
|
recording=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioInputCallback::SetDataCallback(std::function<void(int16_t*, size_t)> c){
|
||||||
|
dataCallback=c;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioInputCallback::RunThread(){
|
||||||
|
int16_t buf[960];
|
||||||
|
while(running){
|
||||||
|
double t=VoIPController::GetCurrentTime();
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
dataCallback(buf, 960);
|
||||||
|
InvokeCallback(reinterpret_cast<unsigned char*>(buf), 960*2);
|
||||||
|
double sl=0.02-(VoIPController::GetCurrentTime()-t);
|
||||||
|
if(sl>0)
|
||||||
|
Thread::Sleep(sl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Output
|
||||||
|
|
||||||
|
AudioOutputCallback::AudioOutputCallback(){
|
||||||
|
thread=new Thread(std::bind(&AudioOutputCallback::RunThread, this));
|
||||||
|
thread->SetName("AudioOutputCallback");
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioOutputCallback::~AudioOutputCallback(){
|
||||||
|
running=false;
|
||||||
|
thread->Join();
|
||||||
|
delete thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputCallback::Start(){
|
||||||
|
if(!running){
|
||||||
|
running=true;
|
||||||
|
thread->Start();
|
||||||
|
}
|
||||||
|
playing=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputCallback::Stop(){
|
||||||
|
playing=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AudioOutputCallback::IsPlaying(){
|
||||||
|
return playing;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputCallback::SetDataCallback(std::function<void(int16_t*, size_t)> c){
|
||||||
|
dataCallback=c;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputCallback::RunThread(){
|
||||||
|
int16_t buf[960];
|
||||||
|
while(running){
|
||||||
|
double t=VoIPController::GetCurrentTime();
|
||||||
|
InvokeCallback(reinterpret_cast<unsigned char*>(buf), 960*2);
|
||||||
|
dataCallback(buf, 960);
|
||||||
|
double sl=0.02-(VoIPController::GetCurrentTime()-t);
|
||||||
|
if(sl>0)
|
||||||
|
Thread::Sleep(sl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LIBTGVOIP_AUDIO_IO_CALLBACK
|
||||||
|
#define LIBTGVOIP_AUDIO_IO_CALLBACK
|
||||||
|
|
||||||
|
#include "AudioIO.h"
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include "../threading.h"
|
||||||
|
|
||||||
|
namespace tgvoip{
|
||||||
|
namespace audio{
|
||||||
|
class AudioInputCallback : public AudioInput{
|
||||||
|
public:
|
||||||
|
AudioInputCallback();
|
||||||
|
virtual ~AudioInputCallback();
|
||||||
|
virtual void Start() override;
|
||||||
|
virtual void Stop() override;
|
||||||
|
void SetDataCallback(std::function<void(int16_t*, size_t)> c);
|
||||||
|
private:
|
||||||
|
void RunThread();
|
||||||
|
bool running=false;
|
||||||
|
bool recording=false;
|
||||||
|
Thread* thread;
|
||||||
|
std::function<void(int16_t*, size_t)> dataCallback;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AudioOutputCallback : public AudioOutput{
|
||||||
|
public:
|
||||||
|
AudioOutputCallback();
|
||||||
|
virtual ~AudioOutputCallback();
|
||||||
|
virtual void Start() override;
|
||||||
|
virtual void Stop() override;
|
||||||
|
virtual bool IsPlaying() override;
|
||||||
|
void SetDataCallback(std::function<void(int16_t*, size_t)> c);
|
||||||
|
private:
|
||||||
|
void RunThread();
|
||||||
|
bool running=false;
|
||||||
|
bool playing=false;
|
||||||
|
Thread* thread;
|
||||||
|
std::function<void(int16_t*, size_t)> dataCallback;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AudioIOCallback : public AudioIO{
|
||||||
|
public:
|
||||||
|
AudioIOCallback();
|
||||||
|
virtual ~AudioIOCallback();
|
||||||
|
virtual AudioInput* GetInput() override;
|
||||||
|
virtual AudioOutput* GetOutput() override;
|
||||||
|
private:
|
||||||
|
AudioInputCallback* input;
|
||||||
|
AudioOutputCallback* output;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* LIBTGVOIP_AUDIO_IO_CALLBACK */
|
|
@ -0,0 +1,97 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "AudioInput.h"
|
||||||
|
#include "../logging.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(TGVOIP_USE_CALLBACK_AUDIO_IO)
|
||||||
|
// nothing
|
||||||
|
#elif defined(__ANDROID__)
|
||||||
|
#include "../os/android/AudioInputAndroid.h"
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
#include <TargetConditionals.h>
|
||||||
|
#include "../os/darwin/AudioInputAudioUnit.h"
|
||||||
|
#if TARGET_OS_OSX
|
||||||
|
#include "../os/darwin/AudioInputAudioUnitOSX.h"
|
||||||
|
#endif
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
#ifdef TGVOIP_WINXP_COMPAT
|
||||||
|
#include "../os/windows/AudioInputWave.h"
|
||||||
|
#endif
|
||||||
|
#include "../os/windows/AudioInputWASAPI.h"
|
||||||
|
#elif defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__gnu_hurd__)
|
||||||
|
#ifndef WITHOUT_ALSA
|
||||||
|
#include "../os/linux/AudioInputALSA.h"
|
||||||
|
#endif
|
||||||
|
#ifndef WITHOUT_PULSE
|
||||||
|
#include "../os/linux/AudioPulse.h"
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#error "Unsupported operating system"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace tgvoip;
|
||||||
|
using namespace tgvoip::audio;
|
||||||
|
|
||||||
|
int32_t AudioInput::estimatedDelay=60;
|
||||||
|
|
||||||
|
AudioInput::AudioInput() : currentDevice("default"){
|
||||||
|
failed=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioInput::AudioInput(std::string deviceID) : currentDevice(deviceID){
|
||||||
|
failed=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AudioInput::~AudioInput(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AudioInput::IsInitialized(){
|
||||||
|
return !failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioInput::EnumerateDevices(std::vector<AudioInputDevice>& devs){
|
||||||
|
#if defined(TGVOIP_USE_CALLBACK_AUDIO_IO)
|
||||||
|
// not supported
|
||||||
|
#elif defined(__APPLE__) && TARGET_OS_OSX
|
||||||
|
AudioInputAudioUnitLegacy::EnumerateDevices(devs);
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
#ifdef TGVOIP_WINXP_COMPAT
|
||||||
|
if(LOBYTE(LOWORD(GetVersion()))<6){
|
||||||
|
AudioInputWave::EnumerateDevices(devs);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
AudioInputWASAPI::EnumerateDevices(devs);
|
||||||
|
#elif defined(__linux__) && !defined(__ANDROID__)
|
||||||
|
#if !defined(WITHOUT_PULSE) && !defined(WITHOUT_ALSA)
|
||||||
|
if(!AudioInputPulse::EnumerateDevices(devs))
|
||||||
|
AudioInputALSA::EnumerateDevices(devs);
|
||||||
|
#elif defined(WITHOUT_PULSE)
|
||||||
|
AudioInputALSA::EnumerateDevices(devs);
|
||||||
|
#else
|
||||||
|
AudioInputPulse::EnumerateDevices(devs)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AudioInput::GetCurrentDevice(){
|
||||||
|
return currentDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioInput::SetCurrentDevice(std::string deviceID){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t AudioInput::GetEstimatedDelay(){
|
||||||
|
return estimatedDelay;
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LIBTGVOIP_AUDIOINPUT_H
|
||||||
|
#define LIBTGVOIP_AUDIOINPUT_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include "../MediaStreamItf.h"
|
||||||
|
|
||||||
|
namespace tgvoip{
|
||||||
|
|
||||||
|
class AudioInputDevice;
|
||||||
|
class AudioOutputDevice;
|
||||||
|
|
||||||
|
namespace audio{
|
||||||
|
class AudioInput : public MediaStreamItf{
|
||||||
|
public:
|
||||||
|
AudioInput();
|
||||||
|
AudioInput(std::string deviceID);
|
||||||
|
virtual ~AudioInput();
|
||||||
|
|
||||||
|
bool IsInitialized();
|
||||||
|
virtual std::string GetCurrentDevice();
|
||||||
|
virtual void SetCurrentDevice(std::string deviceID);
|
||||||
|
//static AudioInput* Create(std::string deviceID, void* platformSpecific);
|
||||||
|
static void EnumerateDevices(std::vector<AudioInputDevice>& devs);
|
||||||
|
static int32_t GetEstimatedDelay();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string currentDevice;
|
||||||
|
bool failed;
|
||||||
|
static int32_t estimatedDelay;
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
|
||||||
|
#endif //LIBTGVOIP_AUDIOINPUT_H
|
|
@ -0,0 +1,109 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "AudioOutput.h"
|
||||||
|
#include "../logging.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(TGVOIP_USE_CALLBACK_AUDIO_IO)
|
||||||
|
// nothing
|
||||||
|
#elif defined(__ANDROID__)
|
||||||
|
#include "../os/android/AudioOutputOpenSLES.h"
|
||||||
|
#include "../os/android/AudioOutputAndroid.h"
|
||||||
|
#include <sys/system_properties.h>
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
#include <TargetConditionals.h>
|
||||||
|
#include "../os/darwin/AudioOutputAudioUnit.h"
|
||||||
|
#if TARGET_OS_OSX
|
||||||
|
#include "../os/darwin/AudioOutputAudioUnitOSX.h"
|
||||||
|
#endif
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
#ifdef TGVOIP_WINXP_COMPAT
|
||||||
|
#include "../os/windows/AudioOutputWave.h"
|
||||||
|
#endif
|
||||||
|
#include "../os/windows/AudioOutputWASAPI.h"
|
||||||
|
#elif defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__gnu_hurd__)
|
||||||
|
#ifndef WITHOUT_ALSA
|
||||||
|
#include "../os/linux/AudioOutputALSA.h"
|
||||||
|
#endif
|
||||||
|
#ifndef WITHOUT_PULSE
|
||||||
|
#include "../os/linux/AudioOutputPulse.h"
|
||||||
|
#include "../os/linux/AudioPulse.h"
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#error "Unsupported operating system"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace tgvoip;
|
||||||
|
using namespace tgvoip::audio;
|
||||||
|
|
||||||
|
int32_t AudioOutput::estimatedDelay=60;
|
||||||
|
|
||||||
|
AudioOutput::AudioOutput() : currentDevice("default"){
|
||||||
|
failed=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioOutput::AudioOutput(std::string deviceID) : currentDevice(deviceID){
|
||||||
|
failed=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioOutput::~AudioOutput(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int32_t AudioOutput::GetEstimatedDelay(){
|
||||||
|
#if defined(__ANDROID__)
|
||||||
|
char sdkNum[PROP_VALUE_MAX];
|
||||||
|
__system_property_get("ro.build.version.sdk", sdkNum);
|
||||||
|
int systemVersion=atoi(sdkNum);
|
||||||
|
return systemVersion<21 ? 150 : 50;
|
||||||
|
#endif
|
||||||
|
return estimatedDelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AudioOutput::EnumerateDevices(std::vector<AudioOutputDevice>& devs){
|
||||||
|
#if defined(TGVOIP_USE_CALLBACK_AUDIO_IO)
|
||||||
|
// not supported
|
||||||
|
#elif defined(__APPLE__) && TARGET_OS_OSX
|
||||||
|
AudioOutputAudioUnitLegacy::EnumerateDevices(devs);
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
#ifdef TGVOIP_WINXP_COMPAT
|
||||||
|
if(LOBYTE(LOWORD(GetVersion()))<6){
|
||||||
|
AudioOutputWave::EnumerateDevices(devs);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
AudioOutputWASAPI::EnumerateDevices(devs);
|
||||||
|
#elif defined(__linux__) && !defined(__ANDROID__)
|
||||||
|
#if !defined(WITHOUT_PULSE) && !defined(WITHOUT_ALSA)
|
||||||
|
if(!AudioOutputPulse::EnumerateDevices(devs))
|
||||||
|
AudioOutputALSA::EnumerateDevices(devs);
|
||||||
|
#elif defined(WITHOUT_PULSE)
|
||||||
|
AudioOutputALSA::EnumerateDevices(devs);
|
||||||
|
#else
|
||||||
|
AudioOutputPulse::EnumerateDevices(devs)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string AudioOutput::GetCurrentDevice(){
|
||||||
|
return currentDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutput::SetCurrentDevice(std::string deviceID){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AudioOutput::IsInitialized(){
|
||||||
|
return !failed;
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LIBTGVOIP_AUDIOOUTPUT_H
|
||||||
|
#define LIBTGVOIP_AUDIOOUTPUT_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include "../MediaStreamItf.h"
|
||||||
|
|
||||||
|
namespace tgvoip{
|
||||||
|
|
||||||
|
class AudioInputDevice;
|
||||||
|
class AudioOutputDevice;
|
||||||
|
|
||||||
|
namespace audio{
|
||||||
|
class AudioOutput : public MediaStreamItf{
|
||||||
|
public:
|
||||||
|
AudioOutput();
|
||||||
|
AudioOutput(std::string deviceID);
|
||||||
|
virtual ~AudioOutput();
|
||||||
|
virtual bool IsPlaying()=0;
|
||||||
|
static int32_t GetEstimatedDelay();
|
||||||
|
virtual std::string GetCurrentDevice();
|
||||||
|
virtual void SetCurrentDevice(std::string deviceID);
|
||||||
|
//static std::unique_ptr<AudioOutput> Create(std::string deviceID, void* platformSpecific);
|
||||||
|
static void EnumerateDevices(std::vector<AudioOutputDevice>& devs);
|
||||||
|
bool IsInitialized();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string currentDevice;
|
||||||
|
bool failed;
|
||||||
|
static int32_t estimatedDelay;
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
|
||||||
|
#endif //LIBTGVOIP_AUDIOOUTPUT_H
|
|
@ -0,0 +1,117 @@
|
||||||
|
//
|
||||||
|
// Created by Grishka on 01.04.17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "Resampler.h"
|
||||||
|
|
||||||
|
using namespace tgvoip::audio;
|
||||||
|
static const int16_t hann[960]={
|
||||||
|
0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0002, 0x0003, 0x0004, 0x0006, 0x0007, 0x0009, 0x000B, 0x000D, 0x000F, 0x0011, 0x0014, 0x0016, 0x0019, 0x001C, 0x0020,
|
||||||
|
0x0023, 0x0027, 0x002A, 0x002E, 0x0033, 0x0037, 0x003B, 0x0040, 0x0045, 0x004A, 0x004F, 0x0054, 0x005A, 0x0060, 0x0065, 0x006B, 0x0072, 0x0078, 0x007F, 0x0085,
|
||||||
|
0x008C, 0x0093, 0x009B, 0x00A2, 0x00AA, 0x00B2, 0x00B9, 0x00C2, 0x00CA, 0x00D2, 0x00DB, 0x00E4, 0x00ED, 0x00F6, 0x00FF, 0x0109, 0x0113, 0x011C, 0x0127, 0x0131,
|
||||||
|
0x013B, 0x0146, 0x0150, 0x015B, 0x0166, 0x0172, 0x017D, 0x0189, 0x0194, 0x01A0, 0x01AC, 0x01B9, 0x01C5, 0x01D2, 0x01DF, 0x01EC, 0x01F9, 0x0206, 0x0213, 0x0221,
|
||||||
|
0x022F, 0x023D, 0x024B, 0x0259, 0x0268, 0x0276, 0x0285, 0x0294, 0x02A3, 0x02B3, 0x02C2, 0x02D2, 0x02E2, 0x02F2, 0x0302, 0x0312, 0x0323, 0x0333, 0x0344, 0x0355,
|
||||||
|
0x0366, 0x0378, 0x0389, 0x039B, 0x03AD, 0x03BF, 0x03D1, 0x03E3, 0x03F6, 0x0408, 0x041B, 0x042E, 0x0441, 0x0455, 0x0468, 0x047C, 0x0490, 0x04A4, 0x04B8, 0x04CC,
|
||||||
|
0x04E0, 0x04F5, 0x050A, 0x051F, 0x0534, 0x0549, 0x055F, 0x0574, 0x058A, 0x05A0, 0x05B6, 0x05CC, 0x05E2, 0x05F9, 0x0610, 0x0627, 0x063E, 0x0655, 0x066C, 0x0684,
|
||||||
|
0x069B, 0x06B3, 0x06CB, 0x06E3, 0x06FC, 0x0714, 0x072D, 0x0745, 0x075E, 0x0777, 0x0791, 0x07AA, 0x07C3, 0x07DD, 0x07F7, 0x0811, 0x082B, 0x0845, 0x0860, 0x087A,
|
||||||
|
0x0895, 0x08B0, 0x08CB, 0x08E6, 0x0902, 0x091D, 0x0939, 0x0955, 0x0971, 0x098D, 0x09A9, 0x09C6, 0x09E2, 0x09FF, 0x0A1C, 0x0A39, 0x0A56, 0x0A73, 0x0A91, 0x0AAE,
|
||||||
|
0x0ACC, 0x0AEA, 0x0B08, 0x0B26, 0x0B44, 0x0B63, 0x0B81, 0x0BA0, 0x0BBF, 0x0BDE, 0x0BFD, 0x0C1D, 0x0C3C, 0x0C5C, 0x0C7B, 0x0C9B, 0x0CBB, 0x0CDC, 0x0CFC, 0x0D1C,
|
||||||
|
0x0D3D, 0x0D5E, 0x0D7F, 0x0DA0, 0x0DC1, 0x0DE2, 0x0E04, 0x0E25, 0x0E47, 0x0E69, 0x0E8B, 0x0EAD, 0x0ECF, 0x0EF1, 0x0F14, 0x0F37, 0x0F59, 0x0F7C, 0x0F9F, 0x0FC2,
|
||||||
|
0x0FE6, 0x1009, 0x102D, 0x1051, 0x1074, 0x1098, 0x10BC, 0x10E1, 0x1105, 0x112A, 0x114E, 0x1173, 0x1198, 0x11BD, 0x11E2, 0x1207, 0x122D, 0x1252, 0x1278, 0x129D,
|
||||||
|
0x12C3, 0x12E9, 0x130F, 0x1336, 0x135C, 0x1383, 0x13A9, 0x13D0, 0x13F7, 0x141E, 0x1445, 0x146C, 0x1494, 0x14BB, 0x14E3, 0x150A, 0x1532, 0x155A, 0x1582, 0x15AA,
|
||||||
|
0x15D3, 0x15FB, 0x1623, 0x164C, 0x1675, 0x169E, 0x16C7, 0x16F0, 0x1719, 0x1742, 0x176C, 0x1795, 0x17BF, 0x17E9, 0x1813, 0x183D, 0x1867, 0x1891, 0x18BB, 0x18E6,
|
||||||
|
0x1910, 0x193B, 0x1965, 0x1990, 0x19BB, 0x19E6, 0x1A11, 0x1A3D, 0x1A68, 0x1A93, 0x1ABF, 0x1AEB, 0x1B17, 0x1B42, 0x1B6E, 0x1B9A, 0x1BC7, 0x1BF3, 0x1C1F, 0x1C4C,
|
||||||
|
0x1C78, 0x1CA5, 0x1CD2, 0x1CFF, 0x1D2C, 0x1D59, 0x1D86, 0x1DB3, 0x1DE0, 0x1E0E, 0x1E3B, 0x1E69, 0x1E97, 0x1EC4, 0x1EF2, 0x1F20, 0x1F4E, 0x1F7C, 0x1FAB, 0x1FD9,
|
||||||
|
0x2007, 0x2036, 0x2065, 0x2093, 0x20C2, 0x20F1, 0x2120, 0x214F, 0x217E, 0x21AD, 0x21DD, 0x220C, 0x223B, 0x226B, 0x229A, 0x22CA, 0x22FA, 0x232A, 0x235A, 0x238A,
|
||||||
|
0x23BA, 0x23EA, 0x241A, 0x244B, 0x247B, 0x24AB, 0x24DC, 0x250D, 0x253D, 0x256E, 0x259F, 0x25D0, 0x2601, 0x2632, 0x2663, 0x2694, 0x26C5, 0x26F7, 0x2728, 0x275A,
|
||||||
|
0x278B, 0x27BD, 0x27EE, 0x2820, 0x2852, 0x2884, 0x28B6, 0x28E8, 0x291A, 0x294C, 0x297E, 0x29B0, 0x29E3, 0x2A15, 0x2A47, 0x2A7A, 0x2AAC, 0x2ADF, 0x2B12, 0x2B44,
|
||||||
|
0x2B77, 0x2BAA, 0x2BDD, 0x2C10, 0x2C43, 0x2C76, 0x2CA9, 0x2CDC, 0x2D0F, 0x2D43, 0x2D76, 0x2DA9, 0x2DDD, 0x2E10, 0x2E44, 0x2E77, 0x2EAB, 0x2EDF, 0x2F12, 0x2F46,
|
||||||
|
0x2F7A, 0x2FAE, 0x2FE2, 0x3016, 0x304A, 0x307E, 0x30B2, 0x30E6, 0x311A, 0x314E, 0x3182, 0x31B7, 0x31EB, 0x321F, 0x3254, 0x3288, 0x32BD, 0x32F1, 0x3326, 0x335A,
|
||||||
|
0x338F, 0x33C3, 0x33F8, 0x342D, 0x3461, 0x3496, 0x34CB, 0x3500, 0x3535, 0x356A, 0x359F, 0x35D4, 0x3608, 0x363D, 0x3673, 0x36A8, 0x36DD, 0x3712, 0x3747, 0x377C,
|
||||||
|
0x37B1, 0x37E6, 0x381C, 0x3851, 0x3886, 0x38BB, 0x38F1, 0x3926, 0x395B, 0x3991, 0x39C6, 0x39FC, 0x3A31, 0x3A66, 0x3A9C, 0x3AD1, 0x3B07, 0x3B3C, 0x3B72, 0x3BA7,
|
||||||
|
0x3BDD, 0x3C12, 0x3C48, 0x3C7D, 0x3CB3, 0x3CE9, 0x3D1E, 0x3D54, 0x3D89, 0x3DBF, 0x3DF5, 0x3E2A, 0x3E60, 0x3E95, 0x3ECB, 0x3F01, 0x3F36, 0x3F6C, 0x3FA2, 0x3FD7,
|
||||||
|
0x400D, 0x4043, 0x4078, 0x40AE, 0x40E3, 0x4119, 0x414F, 0x4184, 0x41BA, 0x41F0, 0x4225, 0x425B, 0x4290, 0x42C6, 0x42FC, 0x4331, 0x4367, 0x439C, 0x43D2, 0x4407,
|
||||||
|
0x443D, 0x4472, 0x44A8, 0x44DD, 0x4513, 0x4548, 0x457E, 0x45B3, 0x45E9, 0x461E, 0x4654, 0x4689, 0x46BE, 0x46F4, 0x4729, 0x475E, 0x4793, 0x47C9, 0x47FE, 0x4833,
|
||||||
|
0x4868, 0x489E, 0x48D3, 0x4908, 0x493D, 0x4972, 0x49A7, 0x49DC, 0x4A11, 0x4A46, 0x4A7B, 0x4AB0, 0x4AE5, 0x4B1A, 0x4B4E, 0x4B83, 0x4BB8, 0x4BED, 0x4C21, 0x4C56,
|
||||||
|
0x4C8B, 0x4CBF, 0x4CF4, 0x4D28, 0x4D5D, 0x4D91, 0x4DC6, 0x4DFA, 0x4E2E, 0x4E63, 0x4E97, 0x4ECB, 0x4EFF, 0x4F33, 0x4F67, 0x4F9B, 0x4FCF, 0x5003, 0x5037, 0x506B,
|
||||||
|
0x509F, 0x50D3, 0x5106, 0x513A, 0x516E, 0x51A1, 0x51D5, 0x5208, 0x523C, 0x526F, 0x52A3, 0x52D6, 0x5309, 0x533C, 0x536F, 0x53A3, 0x53D6, 0x5409, 0x543B, 0x546E,
|
||||||
|
0x54A1, 0x54D4, 0x5507, 0x5539, 0x556C, 0x559E, 0x55D1, 0x5603, 0x5636, 0x5668, 0x569A, 0x56CC, 0x56FE, 0x5730, 0x5762, 0x5794, 0x57C6, 0x57F8, 0x5829, 0x585B,
|
||||||
|
0x588D, 0x58BE, 0x58F0, 0x5921, 0x5952, 0x5984, 0x59B5, 0x59E6, 0x5A17, 0x5A48, 0x5A79, 0x5AA9, 0x5ADA, 0x5B0B, 0x5B3B, 0x5B6C, 0x5B9C, 0x5BCD, 0x5BFD, 0x5C2D,
|
||||||
|
0x5C5D, 0x5C8D, 0x5CBD, 0x5CED, 0x5D1D, 0x5D4D, 0x5D7C, 0x5DAC, 0x5DDB, 0x5E0B, 0x5E3A, 0x5E69, 0x5E99, 0x5EC8, 0x5EF7, 0x5F26, 0x5F54, 0x5F83, 0x5FB2, 0x5FE0,
|
||||||
|
0x600F, 0x603D, 0x606B, 0x609A, 0x60C8, 0x60F6, 0x6124, 0x6152, 0x617F, 0x61AD, 0x61DB, 0x6208, 0x6235, 0x6263, 0x6290, 0x62BD, 0x62EA, 0x6317, 0x6344, 0x6370,
|
||||||
|
0x639D, 0x63CA, 0x63F6, 0x6422, 0x644E, 0x647B, 0x64A7, 0x64D3, 0x64FE, 0x652A, 0x6556, 0x6581, 0x65AD, 0x65D8, 0x6603, 0x662E, 0x6659, 0x6684, 0x66AF, 0x66DA,
|
||||||
|
0x6704, 0x672F, 0x6759, 0x6783, 0x67AD, 0x67D7, 0x6801, 0x682B, 0x6855, 0x687E, 0x68A8, 0x68D1, 0x68FB, 0x6924, 0x694D, 0x6976, 0x699F, 0x69C7, 0x69F0, 0x6A18,
|
||||||
|
0x6A41, 0x6A69, 0x6A91, 0x6AB9, 0x6AE1, 0x6B09, 0x6B30, 0x6B58, 0x6B7F, 0x6BA6, 0x6BCE, 0x6BF5, 0x6C1C, 0x6C42, 0x6C69, 0x6C90, 0x6CB6, 0x6CDC, 0x6D03, 0x6D29,
|
||||||
|
0x6D4F, 0x6D74, 0x6D9A, 0x6DC0, 0x6DE5, 0x6E0A, 0x6E30, 0x6E55, 0x6E7A, 0x6E9E, 0x6EC3, 0x6EE8, 0x6F0C, 0x6F30, 0x6F55, 0x6F79, 0x6F9D, 0x6FC0, 0x6FE4, 0x7008,
|
||||||
|
0x702B, 0x704E, 0x7071, 0x7094, 0x70B7, 0x70DA, 0x70FC, 0x711F, 0x7141, 0x7163, 0x7185, 0x71A7, 0x71C9, 0x71EB, 0x720C, 0x722E, 0x724F, 0x7270, 0x7291, 0x72B2,
|
||||||
|
0x72D2, 0x72F3, 0x7313, 0x7333, 0x7354, 0x7374, 0x7393, 0x73B3, 0x73D3, 0x73F2, 0x7411, 0x7430, 0x744F, 0x746E, 0x748D, 0x74AB, 0x74CA, 0x74E8, 0x7506, 0x7524,
|
||||||
|
0x7542, 0x7560, 0x757D, 0x759B, 0x75B8, 0x75D5, 0x75F2, 0x760F, 0x762B, 0x7648, 0x7664, 0x7680, 0x769C, 0x76B8, 0x76D4, 0x76F0, 0x770B, 0x7726, 0x7741, 0x775C,
|
||||||
|
0x7777, 0x7792, 0x77AC, 0x77C7, 0x77E1, 0x77FB, 0x7815, 0x782F, 0x7848, 0x7862, 0x787B, 0x7894, 0x78AD, 0x78C6, 0x78DF, 0x78F7, 0x7910, 0x7928, 0x7940, 0x7958,
|
||||||
|
0x7970, 0x7987, 0x799F, 0x79B6, 0x79CD, 0x79E4, 0x79FB, 0x7A11, 0x7A28, 0x7A3E, 0x7A54, 0x7A6A, 0x7A80, 0x7A96, 0x7AAB, 0x7AC1, 0x7AD6, 0x7AEB, 0x7B00, 0x7B14,
|
||||||
|
0x7B29, 0x7B3D, 0x7B51, 0x7B65, 0x7B79, 0x7B8D, 0x7BA1, 0x7BB4, 0x7BC7, 0x7BDA, 0x7BED, 0x7C00, 0x7C13, 0x7C25, 0x7C37, 0x7C49, 0x7C5B, 0x7C6D, 0x7C7F, 0x7C90,
|
||||||
|
0x7CA1, 0x7CB2, 0x7CC3, 0x7CD4, 0x7CE5, 0x7CF5, 0x7D05, 0x7D15, 0x7D25, 0x7D35, 0x7D45, 0x7D54, 0x7D63, 0x7D72, 0x7D81, 0x7D90, 0x7D9F, 0x7DAD, 0x7DBB, 0x7DC9,
|
||||||
|
0x7DD7, 0x7DE5, 0x7DF2, 0x7E00, 0x7E0D, 0x7E1A, 0x7E27, 0x7E34, 0x7E40, 0x7E4C, 0x7E59, 0x7E65, 0x7E71, 0x7E7C, 0x7E88, 0x7E93, 0x7E9E, 0x7EA9, 0x7EB4, 0x7EBF,
|
||||||
|
0x7EC9, 0x7ED3, 0x7EDE, 0x7EE7, 0x7EF1, 0x7EFB, 0x7F04, 0x7F0E, 0x7F17, 0x7F20, 0x7F28, 0x7F31, 0x7F39, 0x7F41, 0x7F4A, 0x7F51, 0x7F59, 0x7F61, 0x7F68, 0x7F6F,
|
||||||
|
0x7F76, 0x7F7D, 0x7F84, 0x7F8A, 0x7F90, 0x7F97, 0x7F9D, 0x7FA2, 0x7FA8, 0x7FAD, 0x7FB3, 0x7FB8, 0x7FBD, 0x7FC1, 0x7FC6, 0x7FCA, 0x7FCF, 0x7FD3, 0x7FD6, 0x7FDA,
|
||||||
|
0x7FDE, 0x7FE1, 0x7FE4, 0x7FE7, 0x7FEA, 0x7FED, 0x7FEF, 0x7FF1, 0x7FF3, 0x7FF5, 0x7FF7, 0x7FF9, 0x7FFA, 0x7FFB, 0x7FFC, 0x7FFD, 0x7FFE, 0x7FFE, 0x7FFF, 0x7FFF
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MIN(a, b) (a<b ? a : b)
|
||||||
|
|
||||||
|
size_t Resampler::Convert48To44(int16_t *from, int16_t *to, size_t fromLen, size_t toLen){
|
||||||
|
size_t outLen=fromLen*147/160;
|
||||||
|
if(toLen<outLen)
|
||||||
|
outLen=toLen;
|
||||||
|
unsigned int offset;
|
||||||
|
for(offset=0;offset<outLen;offset++){
|
||||||
|
float offsetf=offset*160.0f/147.0f;
|
||||||
|
float factor=offsetf-floorf(offsetf);
|
||||||
|
to[offset]=(int16_t)((from[(int)floorf(offsetf)]*(1-factor))+(from[(int)ceilf(offsetf)]*(factor)));
|
||||||
|
}
|
||||||
|
return outLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Resampler::Convert44To48(int16_t *from, int16_t *to, size_t fromLen, size_t toLen){
|
||||||
|
size_t outLen=fromLen*160/147;
|
||||||
|
if(toLen<outLen)
|
||||||
|
outLen=toLen;
|
||||||
|
unsigned int offset;
|
||||||
|
for(offset=0;offset<outLen;offset++){
|
||||||
|
float offsetf=offset*147.0f/160.0f;
|
||||||
|
float factor=offsetf-floorf(offsetf);
|
||||||
|
to[offset]=(int16_t)((from[(int)floorf(offsetf)]*(1-factor))+(from[(int)ceilf(offsetf)]*(factor)));
|
||||||
|
}
|
||||||
|
return outLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t Resampler::Convert(int16_t *from, int16_t *to, size_t fromLen, size_t toLen, int num, int denom){
|
||||||
|
size_t outLen=fromLen*num/denom;
|
||||||
|
if(toLen<outLen)
|
||||||
|
outLen=toLen;
|
||||||
|
unsigned int offset;
|
||||||
|
for(offset=0;offset<outLen;offset++){
|
||||||
|
float offsetf=offset*(float)denom/(float)num;
|
||||||
|
float factor=offsetf-floorf(offsetf);
|
||||||
|
to[offset]=(int16_t)((from[(int)floorf(offsetf)]*(1-factor))+(from[(int)ceilf(offsetf)]*(factor)));
|
||||||
|
}
|
||||||
|
return outLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Resampler::Rescale60To80(int16_t *in, int16_t *out){
|
||||||
|
memcpy(out, in, 960*2);
|
||||||
|
memcpy(out+960*3, in+960*2, 960*2);
|
||||||
|
for(int i=0;i<960;i++){
|
||||||
|
out[960+i]=(int16_t)(((int32_t)in[960+i]*hann[959-i]) >> 15) + (int16_t)(((int32_t)in[480+i]*hann[i]) >> 15);
|
||||||
|
out[1920+i]=(int16_t)(((int32_t)in[960+480+i]*hann[959-i]) >> 15) + (int16_t)(((int32_t)in[960+i]*hann[i]) >> 15);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Resampler::Rescale60To40(int16_t *in, int16_t *out){
|
||||||
|
for(int i=0;i<960;i++){
|
||||||
|
out[i]=(int16_t)(((int32_t)in[i]*hann[959-i]) >> 15) + (int16_t)(((int32_t)in[480+i]*hann[i]) >> 15);
|
||||||
|
out[960+i]=(int16_t)(((int32_t)in[1920+i]*hann[i]) >> 15) + (int16_t)(((int32_t)in[1440+i]*hann[959-i]) >> 15);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
//
|
||||||
|
// Created by Grishka on 01.04.17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LIBTGVOIP_RESAMPLER_H
|
||||||
|
#define LIBTGVOIP_RESAMPLER_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace tgvoip{ namespace audio{
|
||||||
|
class Resampler{
|
||||||
|
public:
|
||||||
|
static size_t Convert48To44(int16_t* from, int16_t* to, size_t fromLen, size_t toLen);
|
||||||
|
static size_t Convert44To48(int16_t* from, int16_t* to, size_t fromLen, size_t toLen);
|
||||||
|
static size_t Convert(int16_t* from, int16_t* to, size_t fromLen, size_t toLen, int num, int denom);
|
||||||
|
static void Rescale60To80(int16_t* in, int16_t* out);
|
||||||
|
static void Rescale60To40(int16_t* in, int16_t* out);
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
|
||||||
|
#endif //LIBTGVOIP_RESAMPLER_H
|
|
@ -0,0 +1,736 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include "../../VoIPServerConfig.h"
|
||||||
|
#include "../../VoIPController.h"
|
||||||
|
#include "../../os/android/AudioOutputOpenSLES.h"
|
||||||
|
#include "../../os/android/AudioInputOpenSLES.h"
|
||||||
|
#include "../../os/android/AudioInputAndroid.h"
|
||||||
|
#include "../../os/android/AudioOutputAndroid.h"
|
||||||
|
#include "../../os/android/VideoSourceAndroid.h"
|
||||||
|
#include "../../os/android/VideoRendererAndroid.h"
|
||||||
|
#include "../../audio/Resampler.h"
|
||||||
|
#include "../../os/android/JNIUtilities.h"
|
||||||
|
#include "../../PrivateDefines.h"
|
||||||
|
#include "../../logging.h"
|
||||||
|
|
||||||
|
#ifdef TGVOIP_HAS_CONFIG
|
||||||
|
#include <tgvoip_config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
JavaVM* sharedJVM;
|
||||||
|
jfieldID audioRecordInstanceFld=NULL;
|
||||||
|
jfieldID audioTrackInstanceFld=NULL;
|
||||||
|
jmethodID setStateMethod=NULL;
|
||||||
|
jmethodID setSignalBarsMethod=NULL;
|
||||||
|
jmethodID setSelfStreamsMethod=NULL;
|
||||||
|
jmethodID setParticipantAudioEnabledMethod=NULL;
|
||||||
|
jmethodID groupCallKeyReceivedMethod=NULL;
|
||||||
|
jmethodID groupCallKeySentMethod=NULL;
|
||||||
|
jmethodID callUpgradeRequestReceivedMethod=NULL;
|
||||||
|
jclass jniUtilitiesClass=NULL;
|
||||||
|
|
||||||
|
struct ImplDataAndroid{
|
||||||
|
jobject javaObject;
|
||||||
|
std::string persistentStateFile="";
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef TGVOIP_PACKAGE_PATH
|
||||||
|
#define TGVOIP_PACKAGE_PATH "org/telegram/messenger/voip"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TGVOIP_PEER_TAG_VARIABLE_NAME
|
||||||
|
#define TGVOIP_PEER_TAG_VARIABLE_NAME "peer_tag"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TGVOIP_ENDPOINT_CLASS
|
||||||
|
#define TGVOIP_ENDPOINT_CLASS "org/telegram/tgnet/TLRPC$TL_phoneConnection"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace tgvoip;
|
||||||
|
using namespace tgvoip::audio;
|
||||||
|
|
||||||
|
namespace tgvoip {
|
||||||
|
#pragma mark - Callbacks
|
||||||
|
|
||||||
|
void updateConnectionState(VoIPController *cntrlr, int state){
|
||||||
|
ImplDataAndroid *impl=(ImplDataAndroid *) cntrlr->implData;
|
||||||
|
jni::AttachAndCallVoidMethod(setStateMethod, impl->javaObject, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateSignalBarCount(VoIPController *cntrlr, int count){
|
||||||
|
ImplDataAndroid *impl=(ImplDataAndroid *) cntrlr->implData;
|
||||||
|
jni::AttachAndCallVoidMethod(setSignalBarsMethod, impl->javaObject, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateGroupCallStreams(VoIPGroupController *cntrlr, unsigned char *streams, size_t len){
|
||||||
|
ImplDataAndroid *impl=(ImplDataAndroid *) cntrlr->implData;
|
||||||
|
if(!impl->javaObject)
|
||||||
|
return;
|
||||||
|
jni::DoWithJNI([streams, len, &impl](JNIEnv* env){
|
||||||
|
if(setSelfStreamsMethod){
|
||||||
|
jbyteArray jstreams=env->NewByteArray(static_cast<jsize>(len));
|
||||||
|
jbyte *el=env->GetByteArrayElements(jstreams, NULL);
|
||||||
|
memcpy(el, streams, len);
|
||||||
|
env->ReleaseByteArrayElements(jstreams, el, 0);
|
||||||
|
env->CallVoidMethod(impl->javaObject, setSelfStreamsMethod, jstreams);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void groupCallKeyReceived(VoIPController *cntrlr, const unsigned char *key){
|
||||||
|
ImplDataAndroid *impl=(ImplDataAndroid *) cntrlr->implData;
|
||||||
|
if(!impl->javaObject)
|
||||||
|
return;
|
||||||
|
jni::DoWithJNI([key, &impl](JNIEnv* env){
|
||||||
|
if(groupCallKeyReceivedMethod){
|
||||||
|
jbyteArray jkey=env->NewByteArray(256);
|
||||||
|
jbyte *el=env->GetByteArrayElements(jkey, NULL);
|
||||||
|
memcpy(el, key, 256);
|
||||||
|
env->ReleaseByteArrayElements(jkey, el, 0);
|
||||||
|
env->CallVoidMethod(impl->javaObject, groupCallKeyReceivedMethod, jkey);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void groupCallKeySent(VoIPController *cntrlr){
|
||||||
|
ImplDataAndroid *impl=(ImplDataAndroid *) cntrlr->implData;
|
||||||
|
jni::AttachAndCallVoidMethod(groupCallKeySentMethod, impl->javaObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
void callUpgradeRequestReceived(VoIPController* cntrlr){
|
||||||
|
ImplDataAndroid *impl=(ImplDataAndroid *) cntrlr->implData;
|
||||||
|
jni::AttachAndCallVoidMethod(callUpgradeRequestReceivedMethod, impl->javaObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateParticipantAudioState(VoIPGroupController *cntrlr, int32_t userID, bool enabled){
|
||||||
|
ImplDataAndroid *impl=(ImplDataAndroid *) cntrlr->implData;
|
||||||
|
jni::AttachAndCallVoidMethod(setParticipantAudioEnabledMethod, impl->javaObject, userID, enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - VoIPController
|
||||||
|
|
||||||
|
uint32_t AndroidCodecToFOURCC(std::string mime){
|
||||||
|
if(mime=="video/avc")
|
||||||
|
return CODEC_AVC;
|
||||||
|
else if(mime=="video/hevc")
|
||||||
|
return CODEC_HEVC;
|
||||||
|
else if(mime=="video/x-vnd.on2.vp8")
|
||||||
|
return CODEC_VP8;
|
||||||
|
else if(mime=="video/x-vnd.on2.vp9")
|
||||||
|
return CODEC_VP9;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
jlong VoIPController_nativeInit(JNIEnv* env, jobject thiz, jstring persistentStateFile) {
|
||||||
|
ImplDataAndroid* impl=new ImplDataAndroid();
|
||||||
|
impl->javaObject=env->NewGlobalRef(thiz);
|
||||||
|
if(persistentStateFile){
|
||||||
|
impl->persistentStateFile=jni::JavaStringToStdString(env, persistentStateFile);
|
||||||
|
}
|
||||||
|
VoIPController* cntrlr=new VoIPController();
|
||||||
|
cntrlr->implData=impl;
|
||||||
|
VoIPController::Callbacks callbacks;
|
||||||
|
callbacks.connectionStateChanged=updateConnectionState;
|
||||||
|
callbacks.signalBarCountChanged=updateSignalBarCount;
|
||||||
|
callbacks.groupCallKeyReceived=groupCallKeyReceived;
|
||||||
|
callbacks.groupCallKeySent=groupCallKeySent;
|
||||||
|
callbacks.upgradeToGroupCallRequested=callUpgradeRequestReceived;
|
||||||
|
cntrlr->SetCallbacks(callbacks);
|
||||||
|
if(!impl->persistentStateFile.empty()){
|
||||||
|
FILE* f=fopen(impl->persistentStateFile.c_str(), "r");
|
||||||
|
if(f){
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
size_t len=static_cast<size_t>(ftell(f));
|
||||||
|
fseek(f, 0, SEEK_SET);
|
||||||
|
if(len<1024*512 && len>0){
|
||||||
|
char *fbuf=static_cast<char *>(malloc(len));
|
||||||
|
fread(fbuf, 1, len, f);
|
||||||
|
std::vector<uint8_t> state(fbuf, fbuf+len);
|
||||||
|
free(fbuf);
|
||||||
|
cntrlr->SetPersistentState(state);
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*if(video::VideoRendererAndroid::availableDecoders.empty() || video::VideoSourceAndroid::availableEncoders.empty()){
|
||||||
|
video::VideoRendererAndroid::availableDecoders.clear();
|
||||||
|
video::VideoSourceAndroid::availableEncoders.clear();
|
||||||
|
jmethodID getCodecsMethod=env->GetStaticMethodID(jniUtilitiesClass, "getSupportedVideoCodecs", "()[[Ljava/lang/String;");
|
||||||
|
jobjectArray codecs=static_cast<jobjectArray>(env->CallStaticObjectMethod(jniUtilitiesClass, getCodecsMethod));
|
||||||
|
jobjectArray encoders=static_cast<jobjectArray>(env->GetObjectArrayElement(codecs, 0));
|
||||||
|
jobjectArray decoders=static_cast<jobjectArray>(env->GetObjectArrayElement(codecs, 1));
|
||||||
|
for(jsize i=0;i<env->GetArrayLength(encoders);i++){
|
||||||
|
std::string codec=jni::JavaStringToStdString(env, static_cast<jstring>(env->GetObjectArrayElement(encoders, i)));
|
||||||
|
uint32_t id=AndroidCodecToFOURCC(codec);
|
||||||
|
if(id)
|
||||||
|
video::VideoSourceAndroid::availableEncoders.push_back(id);
|
||||||
|
}
|
||||||
|
for(jsize i=0;i<env->GetArrayLength(decoders);i++){
|
||||||
|
std::string codec=jni::JavaStringToStdString(env, static_cast<jstring>(env->GetObjectArrayElement(decoders, i)));
|
||||||
|
uint32_t id=AndroidCodecToFOURCC(codec);
|
||||||
|
if(id)
|
||||||
|
video::VideoRendererAndroid::availableDecoders.push_back(id);
|
||||||
|
}
|
||||||
|
jmethodID getMaxResolutionMethod=env->GetStaticMethodID(jniUtilitiesClass, "getMaxVideoResolution", "()I");
|
||||||
|
video::VideoRendererAndroid::maxResolution=env->CallStaticIntMethod(jniUtilitiesClass, getMaxResolutionMethod);
|
||||||
|
}*/
|
||||||
|
return (jlong)(intptr_t)cntrlr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPController_nativeStart(JNIEnv* env, jobject thiz, jlong inst){
|
||||||
|
((VoIPController*)(intptr_t)inst)->Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPController_nativeConnect(JNIEnv* env, jobject thiz, jlong inst){
|
||||||
|
((VoIPController*)(intptr_t)inst)->Connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPController_nativeSetProxy(JNIEnv* env, jobject thiz, jlong inst, jstring _address, jint port, jstring _username, jstring _password){
|
||||||
|
((VoIPController*)(intptr_t)inst)->SetProxy(PROXY_SOCKS5, jni::JavaStringToStdString(env, _address), (uint16_t)port, jni::JavaStringToStdString(env, _username), jni::JavaStringToStdString(env, _password));
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPController_nativeSetEncryptionKey(JNIEnv* env, jobject thiz, jlong inst, jbyteArray key, jboolean isOutgoing){
|
||||||
|
jbyte* akey=env->GetByteArrayElements(key, NULL);
|
||||||
|
((VoIPController*)(intptr_t)inst)->SetEncryptionKey((char *) akey, isOutgoing);
|
||||||
|
env->ReleaseByteArrayElements(key, akey, JNI_ABORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPController_nativeSetRemoteEndpoints(JNIEnv* env, jobject thiz, jlong inst, jobjectArray endpoints, jboolean allowP2p, jboolean tcp, jint connectionMaxLayer){
|
||||||
|
size_t len=(size_t) env->GetArrayLength(endpoints);
|
||||||
|
std::vector<Endpoint> eps;
|
||||||
|
/*public String ip;
|
||||||
|
public String ipv6;
|
||||||
|
public int port;
|
||||||
|
public byte[] peer_tag;*/
|
||||||
|
jclass epClass=env->GetObjectClass(env->GetObjectArrayElement(endpoints, 0));
|
||||||
|
jfieldID ipFld=env->GetFieldID(epClass, "ip", "Ljava/lang/String;");
|
||||||
|
jfieldID ipv6Fld=env->GetFieldID(epClass, "ipv6", "Ljava/lang/String;");
|
||||||
|
jfieldID portFld=env->GetFieldID(epClass, "port", "I");
|
||||||
|
jfieldID peerTagFld=env->GetFieldID(epClass, TGVOIP_PEER_TAG_VARIABLE_NAME, "[B");
|
||||||
|
jfieldID idFld=env->GetFieldID(epClass, "id", "J");
|
||||||
|
int i;
|
||||||
|
for(i=0;i<len;i++){
|
||||||
|
jobject endpoint=env->GetObjectArrayElement(endpoints, i);
|
||||||
|
jstring ip=(jstring) env->GetObjectField(endpoint, ipFld);
|
||||||
|
jstring ipv6=(jstring) env->GetObjectField(endpoint, ipv6Fld);
|
||||||
|
jint port=env->GetIntField(endpoint, portFld);
|
||||||
|
jlong id=env->GetLongField(endpoint, idFld);
|
||||||
|
jbyteArray peerTag=(jbyteArray) env->GetObjectField(endpoint, peerTagFld);
|
||||||
|
IPv4Address v4addr(jni::JavaStringToStdString(env, ip));
|
||||||
|
IPv6Address v6addr("::0");
|
||||||
|
if(ipv6 && env->GetStringLength(ipv6)){
|
||||||
|
v6addr=IPv6Address(jni::JavaStringToStdString(env, ipv6));
|
||||||
|
}
|
||||||
|
unsigned char pTag[16];
|
||||||
|
if(peerTag && env->GetArrayLength(peerTag)){
|
||||||
|
jbyte* peerTagBytes=env->GetByteArrayElements(peerTag, NULL);
|
||||||
|
memcpy(pTag, peerTagBytes, 16);
|
||||||
|
env->ReleaseByteArrayElements(peerTag, peerTagBytes, JNI_ABORT);
|
||||||
|
}
|
||||||
|
eps.push_back(Endpoint((int64_t)id, (uint16_t)port, v4addr, v6addr, tcp ? Endpoint::Type::TCP_RELAY : Endpoint::Type::UDP_RELAY, pTag));
|
||||||
|
}
|
||||||
|
((VoIPController*)(intptr_t)inst)->SetRemoteEndpoints(eps, allowP2p, connectionMaxLayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPController_nativeSetNativeBufferSize(JNIEnv* env, jclass thiz, jint size){
|
||||||
|
AudioOutputOpenSLES::nativeBufferSize=(unsigned int) size;
|
||||||
|
AudioInputOpenSLES::nativeBufferSize=(unsigned int) size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPController_nativeRelease(JNIEnv* env, jobject thiz, jlong inst){
|
||||||
|
//env->DeleteGlobalRef(AudioInputAndroid::jniClass);
|
||||||
|
|
||||||
|
VoIPController* ctlr=((VoIPController*)(intptr_t)inst);
|
||||||
|
ImplDataAndroid* impl=(ImplDataAndroid*)ctlr->implData;
|
||||||
|
ctlr->Stop();
|
||||||
|
std::vector<uint8_t> state=ctlr->GetPersistentState();
|
||||||
|
delete ctlr;
|
||||||
|
env->DeleteGlobalRef(impl->javaObject);
|
||||||
|
if(!impl->persistentStateFile.empty()){
|
||||||
|
FILE* f=fopen(impl->persistentStateFile.c_str(), "w");
|
||||||
|
if(f){
|
||||||
|
fwrite(state.data(), 1, state.size(), f);
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete impl;
|
||||||
|
}
|
||||||
|
|
||||||
|
jstring VoIPController_nativeGetDebugString(JNIEnv* env, jobject thiz, jlong inst){
|
||||||
|
std::string str=((VoIPController*)(intptr_t)inst)->GetDebugString();
|
||||||
|
return env->NewStringUTF(str.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPController_nativeSetNetworkType(JNIEnv* env, jobject thiz, jlong inst, jint type){
|
||||||
|
((VoIPController*)(intptr_t)inst)->SetNetworkType(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPController_nativeSetMicMute(JNIEnv* env, jobject thiz, jlong inst, jboolean mute){
|
||||||
|
((VoIPController*)(intptr_t)inst)->SetMicMute(mute);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPController_nativeSetConfig(JNIEnv* env, jobject thiz, jlong inst, jdouble recvTimeout, jdouble initTimeout, jint dataSavingMode, jboolean enableAEC, jboolean enableNS, jboolean enableAGC, jstring logFilePath, jstring statsDumpPath, jboolean logPacketStats){
|
||||||
|
VoIPController::Config cfg;
|
||||||
|
cfg.initTimeout=initTimeout;
|
||||||
|
cfg.recvTimeout=recvTimeout;
|
||||||
|
cfg.dataSaving=dataSavingMode;
|
||||||
|
cfg.enableAEC=enableAEC;
|
||||||
|
cfg.enableNS=enableNS;
|
||||||
|
cfg.enableAGC=enableAGC;
|
||||||
|
cfg.enableCallUpgrade=false;
|
||||||
|
cfg.logPacketStats=logPacketStats;
|
||||||
|
if(logFilePath){
|
||||||
|
cfg.logFilePath=jni::JavaStringToStdString(env, logFilePath);
|
||||||
|
}
|
||||||
|
if(statsDumpPath){
|
||||||
|
cfg.statsDumpFilePath=jni::JavaStringToStdString(env, statsDumpPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove before push
|
||||||
|
cfg.enableVideoReceive=cfg.enableVideoSend=true;
|
||||||
|
|
||||||
|
((VoIPController*)(intptr_t)inst)->SetConfig(cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPController_nativeDebugCtl(JNIEnv* env, jobject thiz, jlong inst, jint request, jint param){
|
||||||
|
((VoIPController*)(intptr_t)inst)->DebugCtl(request, param);
|
||||||
|
}
|
||||||
|
|
||||||
|
jstring VoIPController_nativeGetVersion(JNIEnv* env, jclass clasz){
|
||||||
|
return env->NewStringUTF(VoIPController::GetVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
jlong VoIPController_nativeGetPreferredRelayID(JNIEnv* env, jclass clasz, jlong inst){
|
||||||
|
return ((VoIPController*)(intptr_t)inst)->GetPreferredRelayID();
|
||||||
|
}
|
||||||
|
|
||||||
|
jint VoIPController_nativeGetLastError(JNIEnv* env, jclass clasz, jlong inst){
|
||||||
|
return ((VoIPController*)(intptr_t)inst)->GetLastError();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPController_nativeGetStats(JNIEnv* env, jclass clasz, jlong inst, jobject stats){
|
||||||
|
VoIPController::TrafficStats _stats;
|
||||||
|
((VoIPController*)(intptr_t)inst)->GetStats(&_stats);
|
||||||
|
jclass cls=env->GetObjectClass(stats);
|
||||||
|
env->SetLongField(stats, env->GetFieldID(cls, "bytesSentWifi", "J"), _stats.bytesSentWifi);
|
||||||
|
env->SetLongField(stats, env->GetFieldID(cls, "bytesSentMobile", "J"), _stats.bytesSentMobile);
|
||||||
|
env->SetLongField(stats, env->GetFieldID(cls, "bytesRecvdWifi", "J"), _stats.bytesRecvdWifi);
|
||||||
|
env->SetLongField(stats, env->GetFieldID(cls, "bytesRecvdMobile", "J"), _stats.bytesRecvdMobile);
|
||||||
|
}
|
||||||
|
|
||||||
|
jstring VoIPController_nativeGetDebugLog(JNIEnv* env, jobject thiz, jlong inst){
|
||||||
|
VoIPController* ctlr=((VoIPController*)(intptr_t)inst);
|
||||||
|
std::string log=ctlr->GetDebugLog();
|
||||||
|
return env->NewStringUTF(log.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPController_nativeSetAudioOutputGainControlEnabled(JNIEnv* env, jclass clasz, jlong inst, jboolean enabled){
|
||||||
|
((VoIPController*)(intptr_t)inst)->SetAudioOutputGainControlEnabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPController_nativeSetEchoCancellationStrength(JNIEnv* env, jclass cls, jlong inst, jint strength){
|
||||||
|
((VoIPController*)(intptr_t)inst)->SetEchoCancellationStrength(strength);
|
||||||
|
}
|
||||||
|
|
||||||
|
jint VoIPController_nativeGetPeerCapabilities(JNIEnv* env, jclass cls, jlong inst){
|
||||||
|
return ((VoIPController*)(intptr_t)inst)->GetPeerCapabilities();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPController_nativeSendGroupCallKey(JNIEnv* env, jclass cls, jlong inst, jbyteArray _key){
|
||||||
|
jbyte* key=env->GetByteArrayElements(_key, NULL);
|
||||||
|
((VoIPController*)(intptr_t)inst)->SendGroupCallKey((unsigned char *) key);
|
||||||
|
env->ReleaseByteArrayElements(_key, key, JNI_ABORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPController_nativeRequestCallUpgrade(JNIEnv* env, jclass cls, jlong inst){
|
||||||
|
((VoIPController*)(intptr_t)inst)->RequestCallUpgrade();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPController_nativeSetVideoSource(JNIEnv* env, jobject thiz, jlong inst, jlong source){
|
||||||
|
((VoIPController*)(intptr_t)inst)->SetVideoSource((video::VideoSource*)(intptr_t)source);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPController_nativeSetVideoRenderer(JNIEnv* env, jobject thiz, jlong inst, jlong renderer){
|
||||||
|
((VoIPController*)(intptr_t)inst)->SetVideoRenderer((video::VideoRenderer*)(intptr_t)renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
jboolean VoIPController_nativeNeedRate(JNIEnv* env, jclass cls, jlong inst){
|
||||||
|
return static_cast<jboolean>(((VoIPController*)(intptr_t)inst)->NeedRate());
|
||||||
|
}
|
||||||
|
|
||||||
|
jint VoIPController_getConnectionMaxLayer(JNIEnv* env, jclass cls){
|
||||||
|
return VoIPController::GetConnectionMaxLayer();
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - AudioRecordJNI
|
||||||
|
|
||||||
|
void AudioRecordJNI_nativeCallback(JNIEnv* env, jobject thiz, jobject buffer){
|
||||||
|
jlong inst=env->GetLongField(thiz, audioRecordInstanceFld);
|
||||||
|
AudioInputAndroid* in=(AudioInputAndroid*)(intptr_t)inst;
|
||||||
|
in->HandleCallback(env, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - AudioTrackJNI
|
||||||
|
|
||||||
|
void AudioTrackJNI_nativeCallback(JNIEnv* env, jobject thiz, jbyteArray buffer){
|
||||||
|
jlong inst=env->GetLongField(thiz, audioTrackInstanceFld);
|
||||||
|
AudioOutputAndroid* in=(AudioOutputAndroid*)(intptr_t)inst;
|
||||||
|
in->HandleCallback(env, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - VoIPServerConfig
|
||||||
|
|
||||||
|
void VoIPServerConfig_nativeSetConfig(JNIEnv* env, jclass clasz, jstring jsonString){
|
||||||
|
ServerConfig::GetSharedInstance()->Update(jni::JavaStringToStdString(env, jsonString));
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Resampler
|
||||||
|
|
||||||
|
jint Resampler_convert44to48(JNIEnv* env, jclass cls, jobject from, jobject to){
|
||||||
|
return (jint)tgvoip::audio::Resampler::Convert44To48((int16_t *) env->GetDirectBufferAddress(from), (int16_t *) env->GetDirectBufferAddress(to), (size_t) (env->GetDirectBufferCapacity(from)/2), (size_t) (env->GetDirectBufferCapacity(to)/2));
|
||||||
|
}
|
||||||
|
|
||||||
|
jint Resampler_convert48to44(JNIEnv* env, jclass cls, jobject from, jobject to){
|
||||||
|
return (jint)tgvoip::audio::Resampler::Convert48To44((int16_t *) env->GetDirectBufferAddress(from), (int16_t *) env->GetDirectBufferAddress(to), (size_t) (env->GetDirectBufferCapacity(from)/2), (size_t) (env->GetDirectBufferCapacity(to)/2));
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - VoIPGroupController
|
||||||
|
|
||||||
|
#ifndef TGVOIP_NO_GROUP_CALLS
|
||||||
|
jlong VoIPGroupController_nativeInit(JNIEnv* env, jobject thiz, jint timeDifference){
|
||||||
|
ImplDataAndroid* impl=(ImplDataAndroid*) malloc(sizeof(ImplDataAndroid));
|
||||||
|
impl->javaObject=env->NewGlobalRef(thiz);
|
||||||
|
VoIPGroupController* cntrlr=new VoIPGroupController(timeDifference);
|
||||||
|
cntrlr->implData=impl;
|
||||||
|
|
||||||
|
VoIPGroupController::Callbacks callbacks;
|
||||||
|
callbacks.connectionStateChanged=updateConnectionState;
|
||||||
|
callbacks.updateStreams=updateGroupCallStreams;
|
||||||
|
callbacks.participantAudioStateChanged=updateParticipantAudioState;
|
||||||
|
callbacks.signalBarCountChanged=NULL;
|
||||||
|
cntrlr->SetCallbacks(callbacks);
|
||||||
|
|
||||||
|
return (jlong)(intptr_t)cntrlr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPGroupController_nativeSetGroupCallInfo(JNIEnv* env, jclass cls, jlong inst, jbyteArray _encryptionKey, jbyteArray _reflectorGroupTag, jbyteArray _reflectorSelfTag, jbyteArray _reflectorSelfSecret, jbyteArray _reflectorSelfTagHash, jint selfUserID, jstring reflectorAddress, jstring reflectorAddressV6, jint reflectorPort){
|
||||||
|
VoIPGroupController* ctlr=((VoIPGroupController*)(intptr_t)inst);
|
||||||
|
jbyte* encryptionKey=env->GetByteArrayElements(_encryptionKey, NULL);
|
||||||
|
jbyte* reflectorGroupTag=env->GetByteArrayElements(_reflectorGroupTag, NULL);
|
||||||
|
jbyte* reflectorSelfTag=env->GetByteArrayElements(_reflectorSelfTag, NULL);
|
||||||
|
jbyte* reflectorSelfSecret=env->GetByteArrayElements(_reflectorSelfSecret, NULL);
|
||||||
|
jbyte* reflectorSelfTagHash=env->GetByteArrayElements(_reflectorSelfTagHash, NULL);
|
||||||
|
|
||||||
|
|
||||||
|
const char* ipChars=env->GetStringUTFChars(reflectorAddress, NULL);
|
||||||
|
std::string ipLiteral(ipChars);
|
||||||
|
IPv4Address v4addr(ipLiteral);
|
||||||
|
IPv6Address v6addr("::0");
|
||||||
|
env->ReleaseStringUTFChars(reflectorAddress, ipChars);
|
||||||
|
if(reflectorAddressV6 && env->GetStringLength(reflectorAddressV6)){
|
||||||
|
const char* ipv6Chars=env->GetStringUTFChars(reflectorAddressV6, NULL);
|
||||||
|
v6addr=IPv6Address(ipv6Chars);
|
||||||
|
env->ReleaseStringUTFChars(reflectorAddressV6, ipv6Chars);
|
||||||
|
}
|
||||||
|
ctlr->SetGroupCallInfo((unsigned char *) encryptionKey, (unsigned char *) reflectorGroupTag, (unsigned char *) reflectorSelfTag, (unsigned char *) reflectorSelfSecret, (unsigned char*) reflectorSelfTagHash, selfUserID, v4addr, v6addr, (uint16_t)reflectorPort);
|
||||||
|
|
||||||
|
env->ReleaseByteArrayElements(_encryptionKey, encryptionKey, JNI_ABORT);
|
||||||
|
env->ReleaseByteArrayElements(_reflectorGroupTag, reflectorGroupTag, JNI_ABORT);
|
||||||
|
env->ReleaseByteArrayElements(_reflectorSelfTag, reflectorSelfTag, JNI_ABORT);
|
||||||
|
env->ReleaseByteArrayElements(_reflectorSelfSecret, reflectorSelfSecret, JNI_ABORT);
|
||||||
|
env->ReleaseByteArrayElements(_reflectorSelfTagHash, reflectorSelfTagHash, JNI_ABORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPGroupController_nativeAddGroupCallParticipant(JNIEnv* env, jclass cls, jlong inst, jint userID, jbyteArray _memberTagHash, jbyteArray _streams){
|
||||||
|
VoIPGroupController* ctlr=((VoIPGroupController*)(intptr_t)inst);
|
||||||
|
jbyte* memberTagHash=env->GetByteArrayElements(_memberTagHash, NULL);
|
||||||
|
jbyte* streams=_streams ? env->GetByteArrayElements(_streams, NULL) : NULL;
|
||||||
|
|
||||||
|
ctlr->AddGroupCallParticipant(userID, (unsigned char *) memberTagHash, (unsigned char *) streams, (size_t) env->GetArrayLength(_streams));
|
||||||
|
|
||||||
|
env->ReleaseByteArrayElements(_memberTagHash, memberTagHash, JNI_ABORT);
|
||||||
|
if(_streams)
|
||||||
|
env->ReleaseByteArrayElements(_streams, streams, JNI_ABORT);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPGroupController_nativeRemoveGroupCallParticipant(JNIEnv* env, jclass cls, jlong inst, jint userID){
|
||||||
|
VoIPGroupController* ctlr=((VoIPGroupController*)(intptr_t)inst);
|
||||||
|
ctlr->RemoveGroupCallParticipant(userID);
|
||||||
|
}
|
||||||
|
|
||||||
|
jfloat VoIPGroupController_nativeGetParticipantAudioLevel(JNIEnv* env, jclass cls, jlong inst, jint userID){
|
||||||
|
return ((VoIPGroupController*)(intptr_t)inst)->GetParticipantAudioLevel(userID);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPGroupController_nativeSetParticipantVolume(JNIEnv* env, jclass cls, jlong inst, jint userID, jfloat volume){
|
||||||
|
((VoIPGroupController*)(intptr_t)inst)->SetParticipantVolume(userID, volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
jbyteArray VoIPGroupController_getInitialStreams(JNIEnv* env, jclass cls){
|
||||||
|
unsigned char buf[1024];
|
||||||
|
size_t len=VoIPGroupController::GetInitialStreams(buf, sizeof(buf));
|
||||||
|
jbyteArray arr=env->NewByteArray(len);
|
||||||
|
jbyte* arrElems=env->GetByteArrayElements(arr, NULL);
|
||||||
|
memcpy(arrElems, buf, len);
|
||||||
|
env->ReleaseByteArrayElements(arr, arrElems, 0);
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoIPGroupController_nativeSetParticipantStreams(JNIEnv* env, jclass cls, jlong inst, jint userID, jbyteArray _streams){
|
||||||
|
jbyte* streams=env->GetByteArrayElements(_streams, NULL);
|
||||||
|
|
||||||
|
((VoIPGroupController*)(intptr_t)inst)->SetParticipantStreams(userID, (unsigned char *) streams, (size_t) env->GetArrayLength(_streams));
|
||||||
|
|
||||||
|
env->ReleaseByteArrayElements(_streams, streams, JNI_ABORT);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#pragma mark - VideoSource
|
||||||
|
|
||||||
|
jlong VideoSource_nativeInit(JNIEnv* env, jobject thiz){
|
||||||
|
return (jlong)(intptr_t)new video::VideoSourceAndroid(env->NewGlobalRef(thiz));
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSource_nativeRelease(JNIEnv* env, jobject thiz, jlong inst){
|
||||||
|
delete (video::VideoSource*)(intptr_t)inst;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSource_nativeSetVideoStreamParameters(JNIEnv* env, jobject thiz, jlong inst, jobjectArray _csd, jint width, jint height){
|
||||||
|
std::vector<Buffer> csd;
|
||||||
|
if(_csd){
|
||||||
|
for(int i=0; i<env->GetArrayLength(_csd); i++){
|
||||||
|
jobject _buf=env->GetObjectArrayElement(_csd, i);
|
||||||
|
size_t len=static_cast<size_t>(env->GetDirectBufferCapacity(_buf));
|
||||||
|
Buffer buf(len);
|
||||||
|
buf.CopyFrom(env->GetDirectBufferAddress(_buf), 0, len);
|
||||||
|
csd.push_back(std::move(buf));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
((video::VideoSourceAndroid*)(intptr_t)inst)->SetStreamParameters(std::move(csd), width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSource_nativeSendFrame(JNIEnv* env, jobject thiz, jlong inst, jobject buffer, jint offset, jint length, jint flags){
|
||||||
|
size_t bufsize=(size_t)env->GetDirectBufferCapacity(buffer);
|
||||||
|
Buffer buf(static_cast<size_t>(length));
|
||||||
|
buf.CopyFrom(((char*)env->GetDirectBufferAddress(buffer))+offset, 0, static_cast<size_t>(length));
|
||||||
|
((video::VideoSourceAndroid*)(intptr_t)inst)->SendFrame(std::move(buf), static_cast<uint32_t>(flags));
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSource_nativeSetRotation(JNIEnv* env, jobject thiz, jlong inst, jint rotation){
|
||||||
|
((video::VideoSourceAndroid*)(intptr_t)inst)->SetRotation((unsigned int)rotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - VideoRenderer
|
||||||
|
|
||||||
|
jlong VideoRenderer_nativeInit(JNIEnv* env, jobject thiz){
|
||||||
|
return (jlong)(intptr_t)new video::VideoRendererAndroid(env->NewGlobalRef(thiz));
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - VLog
|
||||||
|
|
||||||
|
template<int level> void VLog_log(JNIEnv* env, jclass cls, jstring jmsg){
|
||||||
|
const char* format="[java] %s";
|
||||||
|
std::string msg=jni::JavaStringToStdString(env, jmsg);
|
||||||
|
switch(level){
|
||||||
|
case 0:
|
||||||
|
LOGV(format, msg.c_str());
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
LOGD(format, msg.c_str());
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
LOGI(format, msg.c_str());
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
LOGW(format, msg.c_str());
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
LOGE(format, msg.c_str());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void tgvoipRegisterNatives(JNIEnv* env){
|
||||||
|
jclass controller=env->FindClass(TGVOIP_PACKAGE_PATH "/VoIPController");
|
||||||
|
jclass groupController=env->FindClass(TGVOIP_PACKAGE_PATH "/VoIPGroupController");
|
||||||
|
if(env->ExceptionCheck()){
|
||||||
|
env->ExceptionClear(); // is returning NULL from FindClass not enough?
|
||||||
|
}
|
||||||
|
jclass audioRecordJNI=env->FindClass(TGVOIP_PACKAGE_PATH "/AudioRecordJNI");
|
||||||
|
jclass audioTrackJNI=env->FindClass(TGVOIP_PACKAGE_PATH "/AudioTrackJNI");
|
||||||
|
jclass serverConfig=env->FindClass(TGVOIP_PACKAGE_PATH "/VoIPServerConfig");
|
||||||
|
jclass resampler=env->FindClass(TGVOIP_PACKAGE_PATH "/Resampler");
|
||||||
|
jclass videoSource=env->FindClass(TGVOIP_PACKAGE_PATH "/VideoSource");
|
||||||
|
if(env->ExceptionCheck()){
|
||||||
|
env->ExceptionClear(); // is returning NULL from FindClass not enough?
|
||||||
|
}
|
||||||
|
jclass videoRenderer=env->FindClass(TGVOIP_PACKAGE_PATH "/VideoRenderer");
|
||||||
|
if(env->ExceptionCheck()){
|
||||||
|
env->ExceptionClear(); // is returning NULL from FindClass not enough?
|
||||||
|
}
|
||||||
|
jclass vlog=env->FindClass(TGVOIP_PACKAGE_PATH "/VLog");
|
||||||
|
if(env->ExceptionCheck()){
|
||||||
|
env->ExceptionClear();
|
||||||
|
}
|
||||||
|
assert(controller && audioRecordJNI && audioTrackJNI && serverConfig && resampler);
|
||||||
|
|
||||||
|
audioRecordInstanceFld=env->GetFieldID(audioRecordJNI, "nativeInst", "J");
|
||||||
|
audioTrackInstanceFld=env->GetFieldID(audioTrackJNI, "nativeInst", "J");
|
||||||
|
|
||||||
|
env->GetJavaVM(&sharedJVM);
|
||||||
|
if(!AudioInputAndroid::jniClass){
|
||||||
|
jclass cls=env->FindClass(TGVOIP_PACKAGE_PATH "/AudioRecordJNI");
|
||||||
|
AudioInputAndroid::jniClass=(jclass) env->NewGlobalRef(cls);
|
||||||
|
AudioInputAndroid::initMethod=env->GetMethodID(cls, "init", "(IIII)V");
|
||||||
|
AudioInputAndroid::releaseMethod=env->GetMethodID(cls, "release", "()V");
|
||||||
|
AudioInputAndroid::startMethod=env->GetMethodID(cls, "start", "()Z");
|
||||||
|
AudioInputAndroid::stopMethod=env->GetMethodID(cls, "stop", "()V");
|
||||||
|
AudioInputAndroid::getEnabledEffectsMaskMethod=env->GetMethodID(cls, "getEnabledEffectsMask", "()I");
|
||||||
|
|
||||||
|
cls=env->FindClass(TGVOIP_PACKAGE_PATH "/AudioTrackJNI");
|
||||||
|
AudioOutputAndroid::jniClass=(jclass) env->NewGlobalRef(cls);
|
||||||
|
AudioOutputAndroid::initMethod=env->GetMethodID(cls, "init", "(IIII)V");
|
||||||
|
AudioOutputAndroid::releaseMethod=env->GetMethodID(cls, "release", "()V");
|
||||||
|
AudioOutputAndroid::startMethod=env->GetMethodID(cls, "start", "()V");
|
||||||
|
AudioOutputAndroid::stopMethod=env->GetMethodID(cls, "stop", "()V");
|
||||||
|
|
||||||
|
if(videoRenderer){
|
||||||
|
video::VideoRendererAndroid::decodeAndDisplayMethod=env->GetMethodID(videoRenderer, "decodeAndDisplay", "(Ljava/nio/ByteBuffer;IJ)V");
|
||||||
|
video::VideoRendererAndroid::resetMethod=env->GetMethodID(videoRenderer, "reset", "(Ljava/lang/String;II[[B)V");
|
||||||
|
video::VideoRendererAndroid::setStreamEnabledMethod=env->GetMethodID(videoRenderer, "setStreamEnabled", "(Z)V");
|
||||||
|
video::VideoRendererAndroid::setRotationMethod=env->GetMethodID(videoRenderer, "setRotation", "(I)V");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setStateMethod=env->GetMethodID(controller, "handleStateChange", "(I)V");
|
||||||
|
setSignalBarsMethod=env->GetMethodID(controller, "handleSignalBarsChange", "(I)V");
|
||||||
|
groupCallKeyReceivedMethod=env->GetMethodID(controller, "groupCallKeyReceived", "([B)V");
|
||||||
|
groupCallKeySentMethod=env->GetMethodID(controller, "groupCallKeySent", "()V");
|
||||||
|
callUpgradeRequestReceivedMethod=env->GetMethodID(controller, "callUpgradeRequestReceived", "()V");
|
||||||
|
|
||||||
|
if(!jniUtilitiesClass)
|
||||||
|
jniUtilitiesClass=(jclass) env->NewGlobalRef(env->FindClass(TGVOIP_PACKAGE_PATH "/JNIUtilities"));
|
||||||
|
|
||||||
|
// VoIPController
|
||||||
|
JNINativeMethod controllerMethods[]={
|
||||||
|
{"nativeInit", "(Ljava/lang/String;)J", (void*)&tgvoip::VoIPController_nativeInit},
|
||||||
|
{"nativeStart", "(J)V", (void*)&tgvoip::VoIPController_nativeStart},
|
||||||
|
{"nativeConnect", "(J)V", (void*)&tgvoip::VoIPController_nativeConnect},
|
||||||
|
{"nativeSetProxy", "(JLjava/lang/String;ILjava/lang/String;Ljava/lang/String;)V", (void*)&tgvoip::VoIPController_nativeSetProxy},
|
||||||
|
{"nativeSetEncryptionKey", "(J[BZ)V", (void*)&tgvoip::VoIPController_nativeSetEncryptionKey},
|
||||||
|
{"nativeSetRemoteEndpoints", "(J[L" TGVOIP_ENDPOINT_CLASS ";ZZI)V", (void*)&tgvoip::VoIPController_nativeSetRemoteEndpoints},
|
||||||
|
{"nativeSetNativeBufferSize", "(I)V", (void*)&tgvoip::VoIPController_nativeSetNativeBufferSize},
|
||||||
|
{"nativeRelease", "(J)V", (void*)&tgvoip::VoIPController_nativeRelease},
|
||||||
|
{"nativeGetDebugString", "(J)Ljava/lang/String;", (void*)&tgvoip::VoIPController_nativeGetDebugString},
|
||||||
|
{"nativeSetNetworkType", "(JI)V", (void*)&tgvoip::VoIPController_nativeSetNetworkType},
|
||||||
|
{"nativeSetMicMute", "(JZ)V", (void*)&tgvoip::VoIPController_nativeSetMicMute},
|
||||||
|
{"nativeSetConfig", "(JDDIZZZLjava/lang/String;Ljava/lang/String;Z)V", (void*)&tgvoip::VoIPController_nativeSetConfig},
|
||||||
|
{"nativeDebugCtl", "(JII)V", (void*)&tgvoip::VoIPController_nativeDebugCtl},
|
||||||
|
{"nativeGetVersion", "()Ljava/lang/String;", (void*)&tgvoip::VoIPController_nativeGetVersion},
|
||||||
|
{"nativeGetPreferredRelayID", "(J)J", (void*)&tgvoip::VoIPController_nativeGetPreferredRelayID},
|
||||||
|
{"nativeGetLastError", "(J)I", (void*)&tgvoip::VoIPController_nativeGetLastError},
|
||||||
|
{"nativeGetStats", "(JL" TGVOIP_PACKAGE_PATH "/VoIPController$Stats;)V", (void*)&tgvoip::VoIPController_nativeGetStats},
|
||||||
|
{"nativeGetDebugLog", "(J)Ljava/lang/String;", (void*)&tgvoip::VoIPController_nativeGetDebugLog},
|
||||||
|
{"nativeSetAudioOutputGainControlEnabled", "(JZ)V", (void*)&tgvoip::VoIPController_nativeSetAudioOutputGainControlEnabled},
|
||||||
|
{"nativeSetEchoCancellationStrength", "(JI)V", (void*)&tgvoip::VoIPController_nativeSetEchoCancellationStrength},
|
||||||
|
{"nativeGetPeerCapabilities", "(J)I", (void*)&tgvoip::VoIPController_nativeGetPeerCapabilities},
|
||||||
|
{"nativeSendGroupCallKey", "(J[B)V", (void*)&tgvoip::VoIPController_nativeSendGroupCallKey},
|
||||||
|
{"nativeRequestCallUpgrade", "(J)V", (void*)&tgvoip::VoIPController_nativeRequestCallUpgrade},
|
||||||
|
{"nativeNeedRate", "(J)Z", (void*)&tgvoip::VoIPController_nativeNeedRate},
|
||||||
|
{"getConnectionMaxLayer", "()I", (void*)&tgvoip::VoIPController_getConnectionMaxLayer},
|
||||||
|
{"nativeSetVideoSource", "(JJ)V", (void*)&tgvoip::VoIPController_nativeSetVideoSource},
|
||||||
|
{"nativeSetVideoRenderer", "(JJ)V", (void*)&tgvoip::VoIPController_nativeSetVideoRenderer}
|
||||||
|
};
|
||||||
|
env->RegisterNatives(controller, controllerMethods, sizeof(controllerMethods)/sizeof(JNINativeMethod));
|
||||||
|
|
||||||
|
// VoIPGroupController
|
||||||
|
#ifndef TGVOIP_NO_GROUP_CALLS
|
||||||
|
if(groupController){
|
||||||
|
setStateMethod=env->GetMethodID(groupController, "handleStateChange", "(I)V");
|
||||||
|
setParticipantAudioEnabledMethod=env->GetMethodID(groupController, "setParticipantAudioEnabled", "(IZ)V");
|
||||||
|
setSelfStreamsMethod=env->GetMethodID(groupController, "setSelfStreams", "([B)V");
|
||||||
|
|
||||||
|
JNINativeMethod groupControllerMethods[]={
|
||||||
|
{"nativeInit", "(I)J", (void*)&tgvoip::VoIPGroupController_nativeInit},
|
||||||
|
{"nativeSetGroupCallInfo", "(J[B[B[B[B[BILjava/lang/String;Ljava/lang/String;I)V", (void*)&tgvoip::VoIPGroupController_nativeSetGroupCallInfo},
|
||||||
|
{"nativeAddGroupCallParticipant", "(JI[B[B)V", (void*)&tgvoip::VoIPGroupController_nativeAddGroupCallParticipant},
|
||||||
|
{"nativeRemoveGroupCallParticipant", "(JI)V", (void*)&tgvoip::VoIPGroupController_nativeRemoveGroupCallParticipant},
|
||||||
|
{"nativeGetParticipantAudioLevel", "(JI)F", (void*)&tgvoip::VoIPGroupController_nativeGetParticipantAudioLevel},
|
||||||
|
{"nativeSetParticipantVolume", "(JIF)V", (void*)&tgvoip::VoIPGroupController_nativeSetParticipantVolume},
|
||||||
|
{"getInitialStreams", "()[B", (void*)&tgvoip::VoIPGroupController_getInitialStreams},
|
||||||
|
{"nativeSetParticipantStreams", "(JI[B)V", (void*)&tgvoip::VoIPGroupController_nativeSetParticipantStreams}
|
||||||
|
};
|
||||||
|
env->RegisterNatives(groupController, groupControllerMethods, sizeof(groupControllerMethods)/sizeof(JNINativeMethod));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// AudioRecordJNI
|
||||||
|
JNINativeMethod audioRecordMethods[]={
|
||||||
|
{"nativeCallback", "(Ljava/nio/ByteBuffer;)V", (void*)&tgvoip::AudioRecordJNI_nativeCallback}
|
||||||
|
};
|
||||||
|
env->RegisterNatives(audioRecordJNI, audioRecordMethods, sizeof(audioRecordMethods)/sizeof(JNINativeMethod));
|
||||||
|
|
||||||
|
// AudioTrackJNI
|
||||||
|
JNINativeMethod audioTrackMethods[]={
|
||||||
|
{"nativeCallback", "([B)V", (void*)&tgvoip::AudioTrackJNI_nativeCallback}
|
||||||
|
};
|
||||||
|
env->RegisterNatives(audioTrackJNI, audioTrackMethods, sizeof(audioTrackMethods)/sizeof(JNINativeMethod));
|
||||||
|
|
||||||
|
// VoIPServerConfig
|
||||||
|
JNINativeMethod serverConfigMethods[]={
|
||||||
|
{"nativeSetConfig", "(Ljava/lang/String;)V", (void*)&tgvoip::VoIPServerConfig_nativeSetConfig}
|
||||||
|
};
|
||||||
|
env->RegisterNatives(serverConfig, serverConfigMethods, sizeof(serverConfigMethods)/sizeof(JNINativeMethod));
|
||||||
|
|
||||||
|
// Resampler
|
||||||
|
JNINativeMethod resamplerMethods[]={
|
||||||
|
{"convert44to48", "(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)I", (void*)&tgvoip::Resampler_convert44to48},
|
||||||
|
{"convert48to44", "(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)I", (void*)&tgvoip::Resampler_convert48to44}
|
||||||
|
};
|
||||||
|
env->RegisterNatives(resampler, resamplerMethods, sizeof(resamplerMethods)/sizeof(JNINativeMethod));
|
||||||
|
|
||||||
|
if(videoSource){
|
||||||
|
// VideoSource
|
||||||
|
JNINativeMethod videoSourceMethods[]={
|
||||||
|
{"nativeInit", "()J", (void *) &tgvoip::VideoSource_nativeInit},
|
||||||
|
{"nativeRelease", "(J)V", (void *) &tgvoip::VideoSource_nativeRelease},
|
||||||
|
{"nativeSetVideoStreamParameters", "(J[Ljava/nio/ByteBuffer;II)V", (void *) &tgvoip::VideoSource_nativeSetVideoStreamParameters},
|
||||||
|
{"nativeSendFrame", "(JLjava/nio/ByteBuffer;III)V", (void *) &tgvoip::VideoSource_nativeSendFrame},
|
||||||
|
{"nativeSetRotation", "(JI)V", (void*)&tgvoip::VideoSource_nativeSetRotation}
|
||||||
|
};
|
||||||
|
env->RegisterNatives(videoSource, videoSourceMethods, sizeof(videoSourceMethods)/sizeof(JNINativeMethod));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(videoRenderer){
|
||||||
|
// VideoRenderer
|
||||||
|
JNINativeMethod videoRendererMethods[]={
|
||||||
|
{"nativeInit", "()J", (void *) &tgvoip::VideoRenderer_nativeInit}
|
||||||
|
};
|
||||||
|
env->RegisterNatives(videoRenderer, videoRendererMethods, sizeof(videoRendererMethods)/sizeof(JNINativeMethod));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(vlog){
|
||||||
|
// VLog
|
||||||
|
JNINativeMethod vlogMethods[]={
|
||||||
|
{"v", "(Ljava/lang/String;)V", (void *) &tgvoip::VLog_log<0>},
|
||||||
|
{"d", "(Ljava/lang/String;)V", (void *) &tgvoip::VLog_log<1>},
|
||||||
|
{"i", "(Ljava/lang/String;)V", (void *) &tgvoip::VLog_log<2>},
|
||||||
|
{"w", "(Ljava/lang/String;)V", (void *) &tgvoip::VLog_log<3>},
|
||||||
|
{"e", "(Ljava/lang/String;)V", (void *) &tgvoip::VLog_log<4>}
|
||||||
|
};
|
||||||
|
env->RegisterNatives(vlog, vlogMethods, sizeof(vlogMethods)/sizeof(JNINativeMethod));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
//
|
||||||
|
// Created by Grishka on 14.08.2018.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef TELEGRAM_TG_VOIP_JNI_H
|
||||||
|
#define TELEGRAM_TG_VOIP_JNI_H
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"{
|
||||||
|
#endif
|
||||||
|
void tgvoipRegisterNatives(JNIEnv* env);
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif //TELEGRAM_TG_VOIP_JNI_H
|
|
@ -0,0 +1,348 @@
|
||||||
|
#! /bin/sh
|
||||||
|
# Wrapper for compilers which do not understand '-c -o'.
|
||||||
|
|
||||||
|
scriptversion=2018-03-07.03; # UTC
|
||||||
|
|
||||||
|
# Copyright (C) 1999-2018 Free Software Foundation, Inc.
|
||||||
|
# Written by Tom Tromey <tromey@cygnus.com>.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
# any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# As a special exception to the GNU General Public License, if you
|
||||||
|
# distribute this file as part of a program that contains a
|
||||||
|
# configuration script generated by Autoconf, you may include it under
|
||||||
|
# the same distribution terms that you use for the rest of that program.
|
||||||
|
|
||||||
|
# This file is maintained in Automake, please report
|
||||||
|
# bugs to <bug-automake@gnu.org> or send patches to
|
||||||
|
# <automake-patches@gnu.org>.
|
||||||
|
|
||||||
|
nl='
|
||||||
|
'
|
||||||
|
|
||||||
|
# We need space, tab and new line, in precisely that order. Quoting is
|
||||||
|
# there to prevent tools from complaining about whitespace usage.
|
||||||
|
IFS=" "" $nl"
|
||||||
|
|
||||||
|
file_conv=
|
||||||
|
|
||||||
|
# func_file_conv build_file lazy
|
||||||
|
# Convert a $build file to $host form and store it in $file
|
||||||
|
# Currently only supports Windows hosts. If the determined conversion
|
||||||
|
# type is listed in (the comma separated) LAZY, no conversion will
|
||||||
|
# take place.
|
||||||
|
func_file_conv ()
|
||||||
|
{
|
||||||
|
file=$1
|
||||||
|
case $file in
|
||||||
|
/ | /[!/]*) # absolute file, and not a UNC file
|
||||||
|
if test -z "$file_conv"; then
|
||||||
|
# lazily determine how to convert abs files
|
||||||
|
case `uname -s` in
|
||||||
|
MINGW*)
|
||||||
|
file_conv=mingw
|
||||||
|
;;
|
||||||
|
CYGWIN*)
|
||||||
|
file_conv=cygwin
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
file_conv=wine
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
case $file_conv/,$2, in
|
||||||
|
*,$file_conv,*)
|
||||||
|
;;
|
||||||
|
mingw/*)
|
||||||
|
file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
|
||||||
|
;;
|
||||||
|
cygwin/*)
|
||||||
|
file=`cygpath -m "$file" || echo "$file"`
|
||||||
|
;;
|
||||||
|
wine/*)
|
||||||
|
file=`winepath -w "$file" || echo "$file"`
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# func_cl_dashL linkdir
|
||||||
|
# Make cl look for libraries in LINKDIR
|
||||||
|
func_cl_dashL ()
|
||||||
|
{
|
||||||
|
func_file_conv "$1"
|
||||||
|
if test -z "$lib_path"; then
|
||||||
|
lib_path=$file
|
||||||
|
else
|
||||||
|
lib_path="$lib_path;$file"
|
||||||
|
fi
|
||||||
|
linker_opts="$linker_opts -LIBPATH:$file"
|
||||||
|
}
|
||||||
|
|
||||||
|
# func_cl_dashl library
|
||||||
|
# Do a library search-path lookup for cl
|
||||||
|
func_cl_dashl ()
|
||||||
|
{
|
||||||
|
lib=$1
|
||||||
|
found=no
|
||||||
|
save_IFS=$IFS
|
||||||
|
IFS=';'
|
||||||
|
for dir in $lib_path $LIB
|
||||||
|
do
|
||||||
|
IFS=$save_IFS
|
||||||
|
if $shared && test -f "$dir/$lib.dll.lib"; then
|
||||||
|
found=yes
|
||||||
|
lib=$dir/$lib.dll.lib
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
if test -f "$dir/$lib.lib"; then
|
||||||
|
found=yes
|
||||||
|
lib=$dir/$lib.lib
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
if test -f "$dir/lib$lib.a"; then
|
||||||
|
found=yes
|
||||||
|
lib=$dir/lib$lib.a
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
IFS=$save_IFS
|
||||||
|
|
||||||
|
if test "$found" != yes; then
|
||||||
|
lib=$lib.lib
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# func_cl_wrapper cl arg...
|
||||||
|
# Adjust compile command to suit cl
|
||||||
|
func_cl_wrapper ()
|
||||||
|
{
|
||||||
|
# Assume a capable shell
|
||||||
|
lib_path=
|
||||||
|
shared=:
|
||||||
|
linker_opts=
|
||||||
|
for arg
|
||||||
|
do
|
||||||
|
if test -n "$eat"; then
|
||||||
|
eat=
|
||||||
|
else
|
||||||
|
case $1 in
|
||||||
|
-o)
|
||||||
|
# configure might choose to run compile as 'compile cc -o foo foo.c'.
|
||||||
|
eat=1
|
||||||
|
case $2 in
|
||||||
|
*.o | *.[oO][bB][jJ])
|
||||||
|
func_file_conv "$2"
|
||||||
|
set x "$@" -Fo"$file"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
func_file_conv "$2"
|
||||||
|
set x "$@" -Fe"$file"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
-I)
|
||||||
|
eat=1
|
||||||
|
func_file_conv "$2" mingw
|
||||||
|
set x "$@" -I"$file"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-I*)
|
||||||
|
func_file_conv "${1#-I}" mingw
|
||||||
|
set x "$@" -I"$file"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-l)
|
||||||
|
eat=1
|
||||||
|
func_cl_dashl "$2"
|
||||||
|
set x "$@" "$lib"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-l*)
|
||||||
|
func_cl_dashl "${1#-l}"
|
||||||
|
set x "$@" "$lib"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-L)
|
||||||
|
eat=1
|
||||||
|
func_cl_dashL "$2"
|
||||||
|
;;
|
||||||
|
-L*)
|
||||||
|
func_cl_dashL "${1#-L}"
|
||||||
|
;;
|
||||||
|
-static)
|
||||||
|
shared=false
|
||||||
|
;;
|
||||||
|
-Wl,*)
|
||||||
|
arg=${1#-Wl,}
|
||||||
|
save_ifs="$IFS"; IFS=','
|
||||||
|
for flag in $arg; do
|
||||||
|
IFS="$save_ifs"
|
||||||
|
linker_opts="$linker_opts $flag"
|
||||||
|
done
|
||||||
|
IFS="$save_ifs"
|
||||||
|
;;
|
||||||
|
-Xlinker)
|
||||||
|
eat=1
|
||||||
|
linker_opts="$linker_opts $2"
|
||||||
|
;;
|
||||||
|
-*)
|
||||||
|
set x "$@" "$1"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*.cc | *.CC | *.cxx | *.CXX | *.[cC]++)
|
||||||
|
func_file_conv "$1"
|
||||||
|
set x "$@" -Tp"$file"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO])
|
||||||
|
func_file_conv "$1" mingw
|
||||||
|
set x "$@" "$file"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
set x "$@" "$1"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
if test -n "$linker_opts"; then
|
||||||
|
linker_opts="-link$linker_opts"
|
||||||
|
fi
|
||||||
|
exec "$@" $linker_opts
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
eat=
|
||||||
|
|
||||||
|
case $1 in
|
||||||
|
'')
|
||||||
|
echo "$0: No command. Try '$0 --help' for more information." 1>&2
|
||||||
|
exit 1;
|
||||||
|
;;
|
||||||
|
-h | --h*)
|
||||||
|
cat <<\EOF
|
||||||
|
Usage: compile [--help] [--version] PROGRAM [ARGS]
|
||||||
|
|
||||||
|
Wrapper for compilers which do not understand '-c -o'.
|
||||||
|
Remove '-o dest.o' from ARGS, run PROGRAM with the remaining
|
||||||
|
arguments, and rename the output as expected.
|
||||||
|
|
||||||
|
If you are trying to build a whole package this is not the
|
||||||
|
right script to run: please start by reading the file 'INSTALL'.
|
||||||
|
|
||||||
|
Report bugs to <bug-automake@gnu.org>.
|
||||||
|
EOF
|
||||||
|
exit $?
|
||||||
|
;;
|
||||||
|
-v | --v*)
|
||||||
|
echo "compile $scriptversion"
|
||||||
|
exit $?
|
||||||
|
;;
|
||||||
|
cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \
|
||||||
|
icl | *[/\\]icl | icl.exe | *[/\\]icl.exe )
|
||||||
|
func_cl_wrapper "$@" # Doesn't return...
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
ofile=
|
||||||
|
cfile=
|
||||||
|
|
||||||
|
for arg
|
||||||
|
do
|
||||||
|
if test -n "$eat"; then
|
||||||
|
eat=
|
||||||
|
else
|
||||||
|
case $1 in
|
||||||
|
-o)
|
||||||
|
# configure might choose to run compile as 'compile cc -o foo foo.c'.
|
||||||
|
# So we strip '-o arg' only if arg is an object.
|
||||||
|
eat=1
|
||||||
|
case $2 in
|
||||||
|
*.o | *.obj)
|
||||||
|
ofile=$2
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
set x "$@" -o "$2"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
*.c)
|
||||||
|
cfile=$1
|
||||||
|
set x "$@" "$1"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
set x "$@" "$1"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
if test -z "$ofile" || test -z "$cfile"; then
|
||||||
|
# If no '-o' option was seen then we might have been invoked from a
|
||||||
|
# pattern rule where we don't need one. That is ok -- this is a
|
||||||
|
# normal compilation that the losing compiler can handle. If no
|
||||||
|
# '.c' file was seen then we are probably linking. That is also
|
||||||
|
# ok.
|
||||||
|
exec "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Name of file we expect compiler to create.
|
||||||
|
cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
|
||||||
|
|
||||||
|
# Create the lock directory.
|
||||||
|
# Note: use '[/\\:.-]' here to ensure that we don't use the same name
|
||||||
|
# that we are using for the .o file. Also, base the name on the expected
|
||||||
|
# object file name, since that is what matters with a parallel build.
|
||||||
|
lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
|
||||||
|
while true; do
|
||||||
|
if mkdir "$lockdir" >/dev/null 2>&1; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
# FIXME: race condition here if user kills between mkdir and trap.
|
||||||
|
trap "rmdir '$lockdir'; exit 1" 1 2 15
|
||||||
|
|
||||||
|
# Run the compile.
|
||||||
|
"$@"
|
||||||
|
ret=$?
|
||||||
|
|
||||||
|
if test -f "$cofile"; then
|
||||||
|
test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
|
||||||
|
elif test -f "${cofile}bj"; then
|
||||||
|
test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
|
||||||
|
fi
|
||||||
|
|
||||||
|
rmdir "$lockdir"
|
||||||
|
exit $ret
|
||||||
|
|
||||||
|
# Local Variables:
|
||||||
|
# mode: shell-script
|
||||||
|
# sh-indentation: 2
|
||||||
|
# eval: (add-hook 'before-save-hook 'time-stamp)
|
||||||
|
# time-stamp-start: "scriptversion="
|
||||||
|
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||||
|
# time-stamp-time-zone: "UTC0"
|
||||||
|
# time-stamp-end: "; # UTC"
|
||||||
|
# End:
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,265 @@
|
||||||
|
/* config.h.in. Generated from configure.ac by autoheader. */
|
||||||
|
|
||||||
|
/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP
|
||||||
|
systems. This function is required for `alloca.c' support on those systems.
|
||||||
|
*/
|
||||||
|
#undef CRAY_STACKSEG_END
|
||||||
|
|
||||||
|
/* Define to 1 if using `alloca.c'. */
|
||||||
|
#undef C_ALLOCA
|
||||||
|
|
||||||
|
/* Define to 1 if you have `alloca', as a function or macro. */
|
||||||
|
#undef HAVE_ALLOCA
|
||||||
|
|
||||||
|
/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix).
|
||||||
|
*/
|
||||||
|
#undef HAVE_ALLOCA_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <arpa/inet.h> header file. */
|
||||||
|
#undef HAVE_ARPA_INET_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `clock_gettime' function. */
|
||||||
|
#undef HAVE_CLOCK_GETTIME
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||||
|
#undef HAVE_DLFCN_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <float.h> header file. */
|
||||||
|
#undef HAVE_FLOAT_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `floor' function. */
|
||||||
|
#undef HAVE_FLOOR
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `gettimeofday' function. */
|
||||||
|
#undef HAVE_GETTIMEOFDAY
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `inet_ntoa' function. */
|
||||||
|
#undef HAVE_INET_NTOA
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||||
|
#undef HAVE_INTTYPES_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `crypto' library (-lcrypto). */
|
||||||
|
#undef HAVE_LIBCRYPTO
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `dl' library (-ldl). */
|
||||||
|
#undef HAVE_LIBDL
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `m' library (-lm). */
|
||||||
|
#undef HAVE_LIBM
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `opus' library (-lopus). */
|
||||||
|
#undef HAVE_LIBOPUS
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `pthread' library (-lpthread). */
|
||||||
|
#undef HAVE_LIBPTHREAD
|
||||||
|
|
||||||
|
/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
|
||||||
|
to 0 otherwise. */
|
||||||
|
#undef HAVE_MALLOC
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <malloc.h> header file. */
|
||||||
|
#undef HAVE_MALLOC_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `memmove' function. */
|
||||||
|
#undef HAVE_MEMMOVE
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <memory.h> header file. */
|
||||||
|
#undef HAVE_MEMORY_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `memset' function. */
|
||||||
|
#undef HAVE_MEMSET
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <netdb.h> header file. */
|
||||||
|
#undef HAVE_NETDB_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <netinet/in.h> header file. */
|
||||||
|
#undef HAVE_NETINET_IN_H
|
||||||
|
|
||||||
|
/* Define to 1 if the system has the type `ptrdiff_t'. */
|
||||||
|
#undef HAVE_PTRDIFF_T
|
||||||
|
|
||||||
|
/* Define to 1 if your system has a GNU libc compatible `realloc' function,
|
||||||
|
and to 0 otherwise. */
|
||||||
|
#undef HAVE_REALLOC
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `select' function. */
|
||||||
|
#undef HAVE_SELECT
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `socket' function. */
|
||||||
|
#undef HAVE_SOCKET
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `sqrt' function. */
|
||||||
|
#undef HAVE_SQRT
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <stddef.h> header file. */
|
||||||
|
#undef HAVE_STDDEF_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <stdint.h> header file. */
|
||||||
|
#undef HAVE_STDINT_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||||
|
#undef HAVE_STDLIB_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `strcasecmp' function. */
|
||||||
|
#undef HAVE_STRCASECMP
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `strchr' function. */
|
||||||
|
#undef HAVE_STRCHR
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `strerror' function. */
|
||||||
|
#undef HAVE_STRERROR
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <strings.h> header file. */
|
||||||
|
#undef HAVE_STRINGS_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <string.h> header file. */
|
||||||
|
#undef HAVE_STRING_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `strncasecmp' function. */
|
||||||
|
#undef HAVE_STRNCASECMP
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `strstr' function. */
|
||||||
|
#undef HAVE_STRSTR
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `strtol' function. */
|
||||||
|
#undef HAVE_STRTOL
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `strtoul' function. */
|
||||||
|
#undef HAVE_STRTOUL
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/ioctl.h> header file. */
|
||||||
|
#undef HAVE_SYS_IOCTL_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/socket.h> header file. */
|
||||||
|
#undef HAVE_SYS_SOCKET_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||||
|
#undef HAVE_SYS_STAT_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/time.h> header file. */
|
||||||
|
#undef HAVE_SYS_TIME_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||||
|
#undef HAVE_SYS_TYPES_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `uname' function. */
|
||||||
|
#undef HAVE_UNAME
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <unistd.h> header file. */
|
||||||
|
#undef HAVE_UNISTD_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <wchar.h> header file. */
|
||||||
|
#undef HAVE_WCHAR_H
|
||||||
|
|
||||||
|
/* Define to 1 if the system has the type `_Bool'. */
|
||||||
|
#undef HAVE__BOOL
|
||||||
|
|
||||||
|
/* Define to the sub-directory where libtool stores uninstalled libraries. */
|
||||||
|
#undef LT_OBJDIR
|
||||||
|
|
||||||
|
/* Name of package */
|
||||||
|
#undef PACKAGE
|
||||||
|
|
||||||
|
/* Define to the address where bug reports for this package should be sent. */
|
||||||
|
#undef PACKAGE_BUGREPORT
|
||||||
|
|
||||||
|
/* Define to the full name of this package. */
|
||||||
|
#undef PACKAGE_NAME
|
||||||
|
|
||||||
|
/* Define to the full name and version of this package. */
|
||||||
|
#undef PACKAGE_STRING
|
||||||
|
|
||||||
|
/* Define to the one symbol short name of this package. */
|
||||||
|
#undef PACKAGE_TARNAME
|
||||||
|
|
||||||
|
/* Define to the home page for this package. */
|
||||||
|
#undef PACKAGE_URL
|
||||||
|
|
||||||
|
/* Define to the version of this package. */
|
||||||
|
#undef PACKAGE_VERSION
|
||||||
|
|
||||||
|
/* If using the C implementation of alloca, define if you know the
|
||||||
|
direction of stack growth for your system; otherwise it will be
|
||||||
|
automatically deduced at runtime.
|
||||||
|
STACK_DIRECTION > 0 => grows toward higher addresses
|
||||||
|
STACK_DIRECTION < 0 => grows toward lower addresses
|
||||||
|
STACK_DIRECTION = 0 => direction of growth unknown */
|
||||||
|
#undef STACK_DIRECTION
|
||||||
|
|
||||||
|
/* Define to 1 if you have the ANSI C header files. */
|
||||||
|
#undef STDC_HEADERS
|
||||||
|
|
||||||
|
/* Version number of package */
|
||||||
|
#undef VERSION
|
||||||
|
|
||||||
|
/* Define to disable ALSA support */
|
||||||
|
#undef WITHOUT_ALSA
|
||||||
|
|
||||||
|
/* Define to disable PulseAudio support */
|
||||||
|
#undef WITHOUT_PULSE
|
||||||
|
|
||||||
|
/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
|
||||||
|
<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
|
||||||
|
#define below would cause a syntax error. */
|
||||||
|
#undef _UINT32_T
|
||||||
|
|
||||||
|
/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,
|
||||||
|
<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
|
||||||
|
#define below would cause a syntax error. */
|
||||||
|
#undef _UINT64_T
|
||||||
|
|
||||||
|
/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,
|
||||||
|
<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
|
||||||
|
#define below would cause a syntax error. */
|
||||||
|
#undef _UINT8_T
|
||||||
|
|
||||||
|
/* Define to `__inline__' or `__inline' if that's what the C compiler
|
||||||
|
calls it, or to nothing if 'inline' is not supported under any name. */
|
||||||
|
#ifndef __cplusplus
|
||||||
|
#undef inline
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Define to the type of a signed integer type of width exactly 16 bits if
|
||||||
|
such a type exists and the standard includes do not define it. */
|
||||||
|
#undef int16_t
|
||||||
|
|
||||||
|
/* Define to the type of a signed integer type of width exactly 32 bits if
|
||||||
|
such a type exists and the standard includes do not define it. */
|
||||||
|
#undef int32_t
|
||||||
|
|
||||||
|
/* Define to the type of a signed integer type of width exactly 64 bits if
|
||||||
|
such a type exists and the standard includes do not define it. */
|
||||||
|
#undef int64_t
|
||||||
|
|
||||||
|
/* Define to the type of a signed integer type of width exactly 8 bits if such
|
||||||
|
a type exists and the standard includes do not define it. */
|
||||||
|
#undef int8_t
|
||||||
|
|
||||||
|
/* Define to rpl_malloc if the replacement function should be used. */
|
||||||
|
#undef malloc
|
||||||
|
|
||||||
|
/* Define to rpl_realloc if the replacement function should be used. */
|
||||||
|
#undef realloc
|
||||||
|
|
||||||
|
/* Define to `unsigned int' if <sys/types.h> does not define. */
|
||||||
|
#undef size_t
|
||||||
|
|
||||||
|
/* Define to `int' if <sys/types.h> does not define. */
|
||||||
|
#undef ssize_t
|
||||||
|
|
||||||
|
/* Define to the type of an unsigned integer type of width exactly 16 bits if
|
||||||
|
such a type exists and the standard includes do not define it. */
|
||||||
|
#undef uint16_t
|
||||||
|
|
||||||
|
/* Define to the type of an unsigned integer type of width exactly 32 bits if
|
||||||
|
such a type exists and the standard includes do not define it. */
|
||||||
|
#undef uint32_t
|
||||||
|
|
||||||
|
/* Define to the type of an unsigned integer type of width exactly 64 bits if
|
||||||
|
such a type exists and the standard includes do not define it. */
|
||||||
|
#undef uint64_t
|
||||||
|
|
||||||
|
/* Define to the type of an unsigned integer type of width exactly 8 bits if
|
||||||
|
such a type exists and the standard includes do not define it. */
|
||||||
|
#undef uint8_t
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,113 @@
|
||||||
|
# -*- Autoconf -*-
|
||||||
|
# Process this file with autoconf to produce a configure script.
|
||||||
|
|
||||||
|
AC_PREREQ([2.69])
|
||||||
|
AC_INIT([libtgvoip], [2.4.4], [https://github.com/grishka/libtgvoip/issues])
|
||||||
|
AC_CONFIG_SRCDIR([config.h.in])
|
||||||
|
AC_CONFIG_HEADERS([config.h])
|
||||||
|
AM_INIT_AUTOMAKE([subdir-objects])
|
||||||
|
AM_SILENT_RULES([yes])
|
||||||
|
LT_INIT
|
||||||
|
|
||||||
|
# Checks for programs.
|
||||||
|
AC_PROG_CXX
|
||||||
|
AC_PROG_CC
|
||||||
|
AC_PROG_OBJCXX
|
||||||
|
AM_PROG_AS
|
||||||
|
AC_PROG_RANLIB
|
||||||
|
|
||||||
|
# Checks for libraries.
|
||||||
|
AC_CHECK_LIB([crypto], [SHA1], [], [AC_MSG_FAILURE([libssl-dev is required but not found])])
|
||||||
|
AC_CHECK_LIB([m], [floorf])
|
||||||
|
AC_CHECK_LIB([opus], [opus_decoder_create], [], [AC_MSG_FAILURE([libopus-dev is required but not found])])
|
||||||
|
AC_CHECK_LIB([pthread], [pthread_create])
|
||||||
|
|
||||||
|
AC_CANONICAL_HOST
|
||||||
|
|
||||||
|
AS_CASE([$host_cpu],
|
||||||
|
[i?86], [cpu_x86=yes],
|
||||||
|
[x86_64], [cpu_x86=yes],
|
||||||
|
[arm*], [cpu_arm=yes],
|
||||||
|
[AS_ECHO("!! WARNING: libtgvoip wasn't tested with your CPU architecture ($host_cpu)")]
|
||||||
|
)
|
||||||
|
AS_CASE([$host_cpu],
|
||||||
|
[armv7*], [cpu_armv7=yes]
|
||||||
|
)
|
||||||
|
AS_ECHO("Detected CPU: $host_cpu")
|
||||||
|
AM_CONDITIONAL(TARGET_CPU_X86, test "x$cpu_x86" == xyes)
|
||||||
|
AM_CONDITIONAL(TARGET_CPU_ARM, test "x$cpu_arm" == xyes)
|
||||||
|
AM_CONDITIONAL(TARGET_CPU_ARMV7, test "x$cpu_armv7" == xyes)
|
||||||
|
AS_ECHO("Detected OS: $host_os")
|
||||||
|
|
||||||
|
AS_CASE([$host_os],
|
||||||
|
[darwin*], [os_osx=yes]
|
||||||
|
)
|
||||||
|
AM_CONDITIONAL(TARGET_OS_OSX, test "x$os_osx" == xyes)
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([audio-callback], [AS_HELP_STRING([--enable-audio-callback], [enable callback-based audio I/O])], [], [enable_audio_callback=no])
|
||||||
|
AM_CONDITIONAL(ENABLE_AUDIO_CALLBACK, test "x$enable_audio_callback" == xyes)
|
||||||
|
|
||||||
|
AS_IF([test "x$os_osx" != xyes && test "x$enable_audio_callback" != xyes], [ # Linux
|
||||||
|
AC_CHECK_LIB([dl], [dlopen])
|
||||||
|
|
||||||
|
AC_ARG_WITH([pulse], [AS_HELP_STRING([--without-pulse], [disable PulseAudio support])], [], [with_pulse=yes])
|
||||||
|
AC_ARG_WITH([alsa], [AS_HELP_STRING([--without-alsa], [disable ALSA support])], [], [with_alsa=yes])
|
||||||
|
|
||||||
|
AS_IF([test "x$with_pulse" == xno && test "x$with_alsa" == xno], [
|
||||||
|
AC_MSG_FAILURE([You can only disable either ALSA or PulseAudio, not both]);
|
||||||
|
])
|
||||||
|
|
||||||
|
AS_IF([test "x$with_pulse" != xno], [
|
||||||
|
AC_CHECK_LIB([pulse], [pa_context_new], [
|
||||||
|
AS_ECHO_N( ) # what is the proper way to check for a library without linking it?
|
||||||
|
], [
|
||||||
|
AC_MSG_FAILURE([libpulse-dev is required during build, but you don't have it installed. Use --without-pulse to disable PulseAudio support.])
|
||||||
|
])
|
||||||
|
], [
|
||||||
|
AC_DEFINE([WITHOUT_PULSE], [1], [Define to disable PulseAudio support])
|
||||||
|
])
|
||||||
|
|
||||||
|
AS_IF([test "x$with_alsa" != xno], [
|
||||||
|
AC_CHECK_LIB([asound], [snd_pcm_open], [
|
||||||
|
AS_ECHO_N( ) # what is the proper way to check for a library without linking it?
|
||||||
|
], [
|
||||||
|
AC_MSG_FAILURE([libasound-dev is required during build, but you don't have it installed. Use --without-alsa to disable ALSA support.])
|
||||||
|
])
|
||||||
|
], [
|
||||||
|
AC_DEFINE([WITHOUT_ALSA], [1], [Define to disable ALSA support])
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
|
||||||
|
AM_CONDITIONAL(WITH_PULSE, test "x$with_pulse" == xyes)
|
||||||
|
AM_CONDITIONAL(WITH_ALSA, test "x$with_alsa" == xyes)
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([dsp], [AS_HELP_STRING([--disable-dsp], [disable signal processing (echo cancellation, noise suppression, and automatic gain control)])], [], [enable_dsp=yes])
|
||||||
|
AM_CONDITIONAL(ENABLE_DSP, test "x$enable_dsp" == xyes)
|
||||||
|
|
||||||
|
# Checks for header files.
|
||||||
|
AC_FUNC_ALLOCA
|
||||||
|
AC_CHECK_HEADERS([arpa/inet.h float.h malloc.h netdb.h netinet/in.h stddef.h stdint.h stdlib.h string.h sys/ioctl.h sys/socket.h sys/time.h unistd.h wchar.h])
|
||||||
|
|
||||||
|
# Checks for typedefs, structures, and compiler characteristics.
|
||||||
|
AC_CHECK_HEADER_STDBOOL
|
||||||
|
AC_C_INLINE
|
||||||
|
AC_TYPE_INT16_T
|
||||||
|
AC_TYPE_INT32_T
|
||||||
|
AC_TYPE_INT64_T
|
||||||
|
AC_TYPE_INT8_T
|
||||||
|
AC_TYPE_SIZE_T
|
||||||
|
AC_TYPE_SSIZE_T
|
||||||
|
AC_TYPE_UINT16_T
|
||||||
|
AC_TYPE_UINT32_T
|
||||||
|
AC_TYPE_UINT64_T
|
||||||
|
AC_TYPE_UINT8_T
|
||||||
|
AC_CHECK_TYPES([ptrdiff_t])
|
||||||
|
|
||||||
|
# Checks for library functions.
|
||||||
|
AC_FUNC_ERROR_AT_LINE
|
||||||
|
AC_FUNC_MALLOC
|
||||||
|
AC_FUNC_REALLOC
|
||||||
|
AC_CHECK_FUNCS([clock_gettime floor gettimeofday inet_ntoa memmove memset select socket sqrt strcasecmp strchr strerror strncasecmp strstr strtol strtoul uname])
|
||||||
|
|
||||||
|
AC_CONFIG_FILES([Makefile])
|
||||||
|
AC_OUTPUT
|
|
@ -0,0 +1,791 @@
|
||||||
|
#! /bin/sh
|
||||||
|
# depcomp - compile a program generating dependencies as side-effects
|
||||||
|
|
||||||
|
scriptversion=2018-03-07.03; # UTC
|
||||||
|
|
||||||
|
# Copyright (C) 1999-2018 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
# any later version.
|
||||||
|
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# As a special exception to the GNU General Public License, if you
|
||||||
|
# distribute this file as part of a program that contains a
|
||||||
|
# configuration script generated by Autoconf, you may include it under
|
||||||
|
# the same distribution terms that you use for the rest of that program.
|
||||||
|
|
||||||
|
# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
|
||||||
|
|
||||||
|
case $1 in
|
||||||
|
'')
|
||||||
|
echo "$0: No command. Try '$0 --help' for more information." 1>&2
|
||||||
|
exit 1;
|
||||||
|
;;
|
||||||
|
-h | --h*)
|
||||||
|
cat <<\EOF
|
||||||
|
Usage: depcomp [--help] [--version] PROGRAM [ARGS]
|
||||||
|
|
||||||
|
Run PROGRAMS ARGS to compile a file, generating dependencies
|
||||||
|
as side-effects.
|
||||||
|
|
||||||
|
Environment variables:
|
||||||
|
depmode Dependency tracking mode.
|
||||||
|
source Source file read by 'PROGRAMS ARGS'.
|
||||||
|
object Object file output by 'PROGRAMS ARGS'.
|
||||||
|
DEPDIR directory where to store dependencies.
|
||||||
|
depfile Dependency file to output.
|
||||||
|
tmpdepfile Temporary file to use when outputting dependencies.
|
||||||
|
libtool Whether libtool is used (yes/no).
|
||||||
|
|
||||||
|
Report bugs to <bug-automake@gnu.org>.
|
||||||
|
EOF
|
||||||
|
exit $?
|
||||||
|
;;
|
||||||
|
-v | --v*)
|
||||||
|
echo "depcomp $scriptversion"
|
||||||
|
exit $?
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Get the directory component of the given path, and save it in the
|
||||||
|
# global variables '$dir'. Note that this directory component will
|
||||||
|
# be either empty or ending with a '/' character. This is deliberate.
|
||||||
|
set_dir_from ()
|
||||||
|
{
|
||||||
|
case $1 in
|
||||||
|
*/*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;;
|
||||||
|
*) dir=;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get the suffix-stripped basename of the given path, and save it the
|
||||||
|
# global variable '$base'.
|
||||||
|
set_base_from ()
|
||||||
|
{
|
||||||
|
base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'`
|
||||||
|
}
|
||||||
|
|
||||||
|
# If no dependency file was actually created by the compiler invocation,
|
||||||
|
# we still have to create a dummy depfile, to avoid errors with the
|
||||||
|
# Makefile "include basename.Plo" scheme.
|
||||||
|
make_dummy_depfile ()
|
||||||
|
{
|
||||||
|
echo "#dummy" > "$depfile"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Factor out some common post-processing of the generated depfile.
|
||||||
|
# Requires the auxiliary global variable '$tmpdepfile' to be set.
|
||||||
|
aix_post_process_depfile ()
|
||||||
|
{
|
||||||
|
# If the compiler actually managed to produce a dependency file,
|
||||||
|
# post-process it.
|
||||||
|
if test -f "$tmpdepfile"; then
|
||||||
|
# Each line is of the form 'foo.o: dependency.h'.
|
||||||
|
# Do two passes, one to just change these to
|
||||||
|
# $object: dependency.h
|
||||||
|
# and one to simply output
|
||||||
|
# dependency.h:
|
||||||
|
# which is needed to avoid the deleted-header problem.
|
||||||
|
{ sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile"
|
||||||
|
sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile"
|
||||||
|
} > "$depfile"
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
else
|
||||||
|
make_dummy_depfile
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# A tabulation character.
|
||||||
|
tab=' '
|
||||||
|
# A newline character.
|
||||||
|
nl='
|
||||||
|
'
|
||||||
|
# Character ranges might be problematic outside the C locale.
|
||||||
|
# These definitions help.
|
||||||
|
upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
||||||
|
lower=abcdefghijklmnopqrstuvwxyz
|
||||||
|
digits=0123456789
|
||||||
|
alpha=${upper}${lower}
|
||||||
|
|
||||||
|
if test -z "$depmode" || test -z "$source" || test -z "$object"; then
|
||||||
|
echo "depcomp: Variables source, object and depmode must be set" 1>&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
|
||||||
|
depfile=${depfile-`echo "$object" |
|
||||||
|
sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
|
||||||
|
tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
|
||||||
|
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
|
||||||
|
# Avoid interferences from the environment.
|
||||||
|
gccflag= dashmflag=
|
||||||
|
|
||||||
|
# Some modes work just like other modes, but use different flags. We
|
||||||
|
# parameterize here, but still list the modes in the big case below,
|
||||||
|
# to make depend.m4 easier to write. Note that we *cannot* use a case
|
||||||
|
# here, because this file can only contain one case statement.
|
||||||
|
if test "$depmode" = hp; then
|
||||||
|
# HP compiler uses -M and no extra arg.
|
||||||
|
gccflag=-M
|
||||||
|
depmode=gcc
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "$depmode" = dashXmstdout; then
|
||||||
|
# This is just like dashmstdout with a different argument.
|
||||||
|
dashmflag=-xM
|
||||||
|
depmode=dashmstdout
|
||||||
|
fi
|
||||||
|
|
||||||
|
cygpath_u="cygpath -u -f -"
|
||||||
|
if test "$depmode" = msvcmsys; then
|
||||||
|
# This is just like msvisualcpp but w/o cygpath translation.
|
||||||
|
# Just convert the backslash-escaped backslashes to single forward
|
||||||
|
# slashes to satisfy depend.m4
|
||||||
|
cygpath_u='sed s,\\\\,/,g'
|
||||||
|
depmode=msvisualcpp
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "$depmode" = msvc7msys; then
|
||||||
|
# This is just like msvc7 but w/o cygpath translation.
|
||||||
|
# Just convert the backslash-escaped backslashes to single forward
|
||||||
|
# slashes to satisfy depend.m4
|
||||||
|
cygpath_u='sed s,\\\\,/,g'
|
||||||
|
depmode=msvc7
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "$depmode" = xlc; then
|
||||||
|
# IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information.
|
||||||
|
gccflag=-qmakedep=gcc,-MF
|
||||||
|
depmode=gcc
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$depmode" in
|
||||||
|
gcc3)
|
||||||
|
## gcc 3 implements dependency tracking that does exactly what
|
||||||
|
## we want. Yay! Note: for some reason libtool 1.4 doesn't like
|
||||||
|
## it if -MD -MP comes after the -MF stuff. Hmm.
|
||||||
|
## Unfortunately, FreeBSD c89 acceptance of flags depends upon
|
||||||
|
## the command line argument order; so add the flags where they
|
||||||
|
## appear in depend2.am. Note that the slowdown incurred here
|
||||||
|
## affects only configure: in makefiles, %FASTDEP% shortcuts this.
|
||||||
|
for arg
|
||||||
|
do
|
||||||
|
case $arg in
|
||||||
|
-c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
|
||||||
|
*) set fnord "$@" "$arg" ;;
|
||||||
|
esac
|
||||||
|
shift # fnord
|
||||||
|
shift # $arg
|
||||||
|
done
|
||||||
|
"$@"
|
||||||
|
stat=$?
|
||||||
|
if test $stat -ne 0; then
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
exit $stat
|
||||||
|
fi
|
||||||
|
mv "$tmpdepfile" "$depfile"
|
||||||
|
;;
|
||||||
|
|
||||||
|
gcc)
|
||||||
|
## Note that this doesn't just cater to obsosete pre-3.x GCC compilers.
|
||||||
|
## but also to in-use compilers like IMB xlc/xlC and the HP C compiler.
|
||||||
|
## (see the conditional assignment to $gccflag above).
|
||||||
|
## There are various ways to get dependency output from gcc. Here's
|
||||||
|
## why we pick this rather obscure method:
|
||||||
|
## - Don't want to use -MD because we'd like the dependencies to end
|
||||||
|
## up in a subdir. Having to rename by hand is ugly.
|
||||||
|
## (We might end up doing this anyway to support other compilers.)
|
||||||
|
## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
|
||||||
|
## -MM, not -M (despite what the docs say). Also, it might not be
|
||||||
|
## supported by the other compilers which use the 'gcc' depmode.
|
||||||
|
## - Using -M directly means running the compiler twice (even worse
|
||||||
|
## than renaming).
|
||||||
|
if test -z "$gccflag"; then
|
||||||
|
gccflag=-MD,
|
||||||
|
fi
|
||||||
|
"$@" -Wp,"$gccflag$tmpdepfile"
|
||||||
|
stat=$?
|
||||||
|
if test $stat -ne 0; then
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
exit $stat
|
||||||
|
fi
|
||||||
|
rm -f "$depfile"
|
||||||
|
echo "$object : \\" > "$depfile"
|
||||||
|
# The second -e expression handles DOS-style file names with drive
|
||||||
|
# letters.
|
||||||
|
sed -e 's/^[^:]*: / /' \
|
||||||
|
-e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
|
||||||
|
## This next piece of magic avoids the "deleted header file" problem.
|
||||||
|
## The problem is that when a header file which appears in a .P file
|
||||||
|
## is deleted, the dependency causes make to die (because there is
|
||||||
|
## typically no way to rebuild the header). We avoid this by adding
|
||||||
|
## dummy dependencies for each header file. Too bad gcc doesn't do
|
||||||
|
## this for us directly.
|
||||||
|
## Some versions of gcc put a space before the ':'. On the theory
|
||||||
|
## that the space means something, we add a space to the output as
|
||||||
|
## well. hp depmode also adds that space, but also prefixes the VPATH
|
||||||
|
## to the object. Take care to not repeat it in the output.
|
||||||
|
## Some versions of the HPUX 10.20 sed can't process this invocation
|
||||||
|
## correctly. Breaking it into two sed invocations is a workaround.
|
||||||
|
tr ' ' "$nl" < "$tmpdepfile" \
|
||||||
|
| sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \
|
||||||
|
| sed -e 's/$/ :/' >> "$depfile"
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
;;
|
||||||
|
|
||||||
|
hp)
|
||||||
|
# This case exists only to let depend.m4 do its work. It works by
|
||||||
|
# looking at the text of this script. This case will never be run,
|
||||||
|
# since it is checked for above.
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
|
||||||
|
sgi)
|
||||||
|
if test "$libtool" = yes; then
|
||||||
|
"$@" "-Wp,-MDupdate,$tmpdepfile"
|
||||||
|
else
|
||||||
|
"$@" -MDupdate "$tmpdepfile"
|
||||||
|
fi
|
||||||
|
stat=$?
|
||||||
|
if test $stat -ne 0; then
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
exit $stat
|
||||||
|
fi
|
||||||
|
rm -f "$depfile"
|
||||||
|
|
||||||
|
if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
|
||||||
|
echo "$object : \\" > "$depfile"
|
||||||
|
# Clip off the initial element (the dependent). Don't try to be
|
||||||
|
# clever and replace this with sed code, as IRIX sed won't handle
|
||||||
|
# lines with more than a fixed number of characters (4096 in
|
||||||
|
# IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
|
||||||
|
# the IRIX cc adds comments like '#:fec' to the end of the
|
||||||
|
# dependency line.
|
||||||
|
tr ' ' "$nl" < "$tmpdepfile" \
|
||||||
|
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \
|
||||||
|
| tr "$nl" ' ' >> "$depfile"
|
||||||
|
echo >> "$depfile"
|
||||||
|
# The second pass generates a dummy entry for each header file.
|
||||||
|
tr ' ' "$nl" < "$tmpdepfile" \
|
||||||
|
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
|
||||||
|
>> "$depfile"
|
||||||
|
else
|
||||||
|
make_dummy_depfile
|
||||||
|
fi
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
;;
|
||||||
|
|
||||||
|
xlc)
|
||||||
|
# This case exists only to let depend.m4 do its work. It works by
|
||||||
|
# looking at the text of this script. This case will never be run,
|
||||||
|
# since it is checked for above.
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
|
||||||
|
aix)
|
||||||
|
# The C for AIX Compiler uses -M and outputs the dependencies
|
||||||
|
# in a .u file. In older versions, this file always lives in the
|
||||||
|
# current directory. Also, the AIX compiler puts '$object:' at the
|
||||||
|
# start of each line; $object doesn't have directory information.
|
||||||
|
# Version 6 uses the directory in both cases.
|
||||||
|
set_dir_from "$object"
|
||||||
|
set_base_from "$object"
|
||||||
|
if test "$libtool" = yes; then
|
||||||
|
tmpdepfile1=$dir$base.u
|
||||||
|
tmpdepfile2=$base.u
|
||||||
|
tmpdepfile3=$dir.libs/$base.u
|
||||||
|
"$@" -Wc,-M
|
||||||
|
else
|
||||||
|
tmpdepfile1=$dir$base.u
|
||||||
|
tmpdepfile2=$dir$base.u
|
||||||
|
tmpdepfile3=$dir$base.u
|
||||||
|
"$@" -M
|
||||||
|
fi
|
||||||
|
stat=$?
|
||||||
|
if test $stat -ne 0; then
|
||||||
|
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||||
|
exit $stat
|
||||||
|
fi
|
||||||
|
|
||||||
|
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||||
|
do
|
||||||
|
test -f "$tmpdepfile" && break
|
||||||
|
done
|
||||||
|
aix_post_process_depfile
|
||||||
|
;;
|
||||||
|
|
||||||
|
tcc)
|
||||||
|
# tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26
|
||||||
|
# FIXME: That version still under development at the moment of writing.
|
||||||
|
# Make that this statement remains true also for stable, released
|
||||||
|
# versions.
|
||||||
|
# It will wrap lines (doesn't matter whether long or short) with a
|
||||||
|
# trailing '\', as in:
|
||||||
|
#
|
||||||
|
# foo.o : \
|
||||||
|
# foo.c \
|
||||||
|
# foo.h \
|
||||||
|
#
|
||||||
|
# It will put a trailing '\' even on the last line, and will use leading
|
||||||
|
# spaces rather than leading tabs (at least since its commit 0394caf7
|
||||||
|
# "Emit spaces for -MD").
|
||||||
|
"$@" -MD -MF "$tmpdepfile"
|
||||||
|
stat=$?
|
||||||
|
if test $stat -ne 0; then
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
exit $stat
|
||||||
|
fi
|
||||||
|
rm -f "$depfile"
|
||||||
|
# Each non-empty line is of the form 'foo.o : \' or ' dep.h \'.
|
||||||
|
# We have to change lines of the first kind to '$object: \'.
|
||||||
|
sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile"
|
||||||
|
# And for each line of the second kind, we have to emit a 'dep.h:'
|
||||||
|
# dummy dependency, to avoid the deleted-header problem.
|
||||||
|
sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile"
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
;;
|
||||||
|
|
||||||
|
## The order of this option in the case statement is important, since the
|
||||||
|
## shell code in configure will try each of these formats in the order
|
||||||
|
## listed in this file. A plain '-MD' option would be understood by many
|
||||||
|
## compilers, so we must ensure this comes after the gcc and icc options.
|
||||||
|
pgcc)
|
||||||
|
# Portland's C compiler understands '-MD'.
|
||||||
|
# Will always output deps to 'file.d' where file is the root name of the
|
||||||
|
# source file under compilation, even if file resides in a subdirectory.
|
||||||
|
# The object file name does not affect the name of the '.d' file.
|
||||||
|
# pgcc 10.2 will output
|
||||||
|
# foo.o: sub/foo.c sub/foo.h
|
||||||
|
# and will wrap long lines using '\' :
|
||||||
|
# foo.o: sub/foo.c ... \
|
||||||
|
# sub/foo.h ... \
|
||||||
|
# ...
|
||||||
|
set_dir_from "$object"
|
||||||
|
# Use the source, not the object, to determine the base name, since
|
||||||
|
# that's sadly what pgcc will do too.
|
||||||
|
set_base_from "$source"
|
||||||
|
tmpdepfile=$base.d
|
||||||
|
|
||||||
|
# For projects that build the same source file twice into different object
|
||||||
|
# files, the pgcc approach of using the *source* file root name can cause
|
||||||
|
# problems in parallel builds. Use a locking strategy to avoid stomping on
|
||||||
|
# the same $tmpdepfile.
|
||||||
|
lockdir=$base.d-lock
|
||||||
|
trap "
|
||||||
|
echo '$0: caught signal, cleaning up...' >&2
|
||||||
|
rmdir '$lockdir'
|
||||||
|
exit 1
|
||||||
|
" 1 2 13 15
|
||||||
|
numtries=100
|
||||||
|
i=$numtries
|
||||||
|
while test $i -gt 0; do
|
||||||
|
# mkdir is a portable test-and-set.
|
||||||
|
if mkdir "$lockdir" 2>/dev/null; then
|
||||||
|
# This process acquired the lock.
|
||||||
|
"$@" -MD
|
||||||
|
stat=$?
|
||||||
|
# Release the lock.
|
||||||
|
rmdir "$lockdir"
|
||||||
|
break
|
||||||
|
else
|
||||||
|
# If the lock is being held by a different process, wait
|
||||||
|
# until the winning process is done or we timeout.
|
||||||
|
while test -d "$lockdir" && test $i -gt 0; do
|
||||||
|
sleep 1
|
||||||
|
i=`expr $i - 1`
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
i=`expr $i - 1`
|
||||||
|
done
|
||||||
|
trap - 1 2 13 15
|
||||||
|
if test $i -le 0; then
|
||||||
|
echo "$0: failed to acquire lock after $numtries attempts" >&2
|
||||||
|
echo "$0: check lockdir '$lockdir'" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test $stat -ne 0; then
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
exit $stat
|
||||||
|
fi
|
||||||
|
rm -f "$depfile"
|
||||||
|
# Each line is of the form `foo.o: dependent.h',
|
||||||
|
# or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
|
||||||
|
# Do two passes, one to just change these to
|
||||||
|
# `$object: dependent.h' and one to simply `dependent.h:'.
|
||||||
|
sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
|
||||||
|
# Some versions of the HPUX 10.20 sed can't process this invocation
|
||||||
|
# correctly. Breaking it into two sed invocations is a workaround.
|
||||||
|
sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \
|
||||||
|
| sed -e 's/$/ :/' >> "$depfile"
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
;;
|
||||||
|
|
||||||
|
hp2)
|
||||||
|
# The "hp" stanza above does not work with aCC (C++) and HP's ia64
|
||||||
|
# compilers, which have integrated preprocessors. The correct option
|
||||||
|
# to use with these is +Maked; it writes dependencies to a file named
|
||||||
|
# 'foo.d', which lands next to the object file, wherever that
|
||||||
|
# happens to be.
|
||||||
|
# Much of this is similar to the tru64 case; see comments there.
|
||||||
|
set_dir_from "$object"
|
||||||
|
set_base_from "$object"
|
||||||
|
if test "$libtool" = yes; then
|
||||||
|
tmpdepfile1=$dir$base.d
|
||||||
|
tmpdepfile2=$dir.libs/$base.d
|
||||||
|
"$@" -Wc,+Maked
|
||||||
|
else
|
||||||
|
tmpdepfile1=$dir$base.d
|
||||||
|
tmpdepfile2=$dir$base.d
|
||||||
|
"$@" +Maked
|
||||||
|
fi
|
||||||
|
stat=$?
|
||||||
|
if test $stat -ne 0; then
|
||||||
|
rm -f "$tmpdepfile1" "$tmpdepfile2"
|
||||||
|
exit $stat
|
||||||
|
fi
|
||||||
|
|
||||||
|
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
|
||||||
|
do
|
||||||
|
test -f "$tmpdepfile" && break
|
||||||
|
done
|
||||||
|
if test -f "$tmpdepfile"; then
|
||||||
|
sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile"
|
||||||
|
# Add 'dependent.h:' lines.
|
||||||
|
sed -ne '2,${
|
||||||
|
s/^ *//
|
||||||
|
s/ \\*$//
|
||||||
|
s/$/:/
|
||||||
|
p
|
||||||
|
}' "$tmpdepfile" >> "$depfile"
|
||||||
|
else
|
||||||
|
make_dummy_depfile
|
||||||
|
fi
|
||||||
|
rm -f "$tmpdepfile" "$tmpdepfile2"
|
||||||
|
;;
|
||||||
|
|
||||||
|
tru64)
|
||||||
|
# The Tru64 compiler uses -MD to generate dependencies as a side
|
||||||
|
# effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'.
|
||||||
|
# At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
|
||||||
|
# dependencies in 'foo.d' instead, so we check for that too.
|
||||||
|
# Subdirectories are respected.
|
||||||
|
set_dir_from "$object"
|
||||||
|
set_base_from "$object"
|
||||||
|
|
||||||
|
if test "$libtool" = yes; then
|
||||||
|
# Libtool generates 2 separate objects for the 2 libraries. These
|
||||||
|
# two compilations output dependencies in $dir.libs/$base.o.d and
|
||||||
|
# in $dir$base.o.d. We have to check for both files, because
|
||||||
|
# one of the two compilations can be disabled. We should prefer
|
||||||
|
# $dir$base.o.d over $dir.libs/$base.o.d because the latter is
|
||||||
|
# automatically cleaned when .libs/ is deleted, while ignoring
|
||||||
|
# the former would cause a distcleancheck panic.
|
||||||
|
tmpdepfile1=$dir$base.o.d # libtool 1.5
|
||||||
|
tmpdepfile2=$dir.libs/$base.o.d # Likewise.
|
||||||
|
tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504
|
||||||
|
"$@" -Wc,-MD
|
||||||
|
else
|
||||||
|
tmpdepfile1=$dir$base.d
|
||||||
|
tmpdepfile2=$dir$base.d
|
||||||
|
tmpdepfile3=$dir$base.d
|
||||||
|
"$@" -MD
|
||||||
|
fi
|
||||||
|
|
||||||
|
stat=$?
|
||||||
|
if test $stat -ne 0; then
|
||||||
|
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||||
|
exit $stat
|
||||||
|
fi
|
||||||
|
|
||||||
|
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||||
|
do
|
||||||
|
test -f "$tmpdepfile" && break
|
||||||
|
done
|
||||||
|
# Same post-processing that is required for AIX mode.
|
||||||
|
aix_post_process_depfile
|
||||||
|
;;
|
||||||
|
|
||||||
|
msvc7)
|
||||||
|
if test "$libtool" = yes; then
|
||||||
|
showIncludes=-Wc,-showIncludes
|
||||||
|
else
|
||||||
|
showIncludes=-showIncludes
|
||||||
|
fi
|
||||||
|
"$@" $showIncludes > "$tmpdepfile"
|
||||||
|
stat=$?
|
||||||
|
grep -v '^Note: including file: ' "$tmpdepfile"
|
||||||
|
if test $stat -ne 0; then
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
exit $stat
|
||||||
|
fi
|
||||||
|
rm -f "$depfile"
|
||||||
|
echo "$object : \\" > "$depfile"
|
||||||
|
# The first sed program below extracts the file names and escapes
|
||||||
|
# backslashes for cygpath. The second sed program outputs the file
|
||||||
|
# name when reading, but also accumulates all include files in the
|
||||||
|
# hold buffer in order to output them again at the end. This only
|
||||||
|
# works with sed implementations that can handle large buffers.
|
||||||
|
sed < "$tmpdepfile" -n '
|
||||||
|
/^Note: including file: *\(.*\)/ {
|
||||||
|
s//\1/
|
||||||
|
s/\\/\\\\/g
|
||||||
|
p
|
||||||
|
}' | $cygpath_u | sort -u | sed -n '
|
||||||
|
s/ /\\ /g
|
||||||
|
s/\(.*\)/'"$tab"'\1 \\/p
|
||||||
|
s/.\(.*\) \\/\1:/
|
||||||
|
H
|
||||||
|
$ {
|
||||||
|
s/.*/'"$tab"'/
|
||||||
|
G
|
||||||
|
p
|
||||||
|
}' >> "$depfile"
|
||||||
|
echo >> "$depfile" # make sure the fragment doesn't end with a backslash
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
;;
|
||||||
|
|
||||||
|
msvc7msys)
|
||||||
|
# This case exists only to let depend.m4 do its work. It works by
|
||||||
|
# looking at the text of this script. This case will never be run,
|
||||||
|
# since it is checked for above.
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
|
||||||
|
#nosideeffect)
|
||||||
|
# This comment above is used by automake to tell side-effect
|
||||||
|
# dependency tracking mechanisms from slower ones.
|
||||||
|
|
||||||
|
dashmstdout)
|
||||||
|
# Important note: in order to support this mode, a compiler *must*
|
||||||
|
# always write the preprocessed file to stdout, regardless of -o.
|
||||||
|
"$@" || exit $?
|
||||||
|
|
||||||
|
# Remove the call to Libtool.
|
||||||
|
if test "$libtool" = yes; then
|
||||||
|
while test "X$1" != 'X--mode=compile'; do
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove '-o $object'.
|
||||||
|
IFS=" "
|
||||||
|
for arg
|
||||||
|
do
|
||||||
|
case $arg in
|
||||||
|
-o)
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
$object)
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
set fnord "$@" "$arg"
|
||||||
|
shift # fnord
|
||||||
|
shift # $arg
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
test -z "$dashmflag" && dashmflag=-M
|
||||||
|
# Require at least two characters before searching for ':'
|
||||||
|
# in the target name. This is to cope with DOS-style filenames:
|
||||||
|
# a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise.
|
||||||
|
"$@" $dashmflag |
|
||||||
|
sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile"
|
||||||
|
rm -f "$depfile"
|
||||||
|
cat < "$tmpdepfile" > "$depfile"
|
||||||
|
# Some versions of the HPUX 10.20 sed can't process this sed invocation
|
||||||
|
# correctly. Breaking it into two sed invocations is a workaround.
|
||||||
|
tr ' ' "$nl" < "$tmpdepfile" \
|
||||||
|
| sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
|
||||||
|
| sed -e 's/$/ :/' >> "$depfile"
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
;;
|
||||||
|
|
||||||
|
dashXmstdout)
|
||||||
|
# This case only exists to satisfy depend.m4. It is never actually
|
||||||
|
# run, as this mode is specially recognized in the preamble.
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
|
||||||
|
makedepend)
|
||||||
|
"$@" || exit $?
|
||||||
|
# Remove any Libtool call
|
||||||
|
if test "$libtool" = yes; then
|
||||||
|
while test "X$1" != 'X--mode=compile'; do
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
# X makedepend
|
||||||
|
shift
|
||||||
|
cleared=no eat=no
|
||||||
|
for arg
|
||||||
|
do
|
||||||
|
case $cleared in
|
||||||
|
no)
|
||||||
|
set ""; shift
|
||||||
|
cleared=yes ;;
|
||||||
|
esac
|
||||||
|
if test $eat = yes; then
|
||||||
|
eat=no
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
case "$arg" in
|
||||||
|
-D*|-I*)
|
||||||
|
set fnord "$@" "$arg"; shift ;;
|
||||||
|
# Strip any option that makedepend may not understand. Remove
|
||||||
|
# the object too, otherwise makedepend will parse it as a source file.
|
||||||
|
-arch)
|
||||||
|
eat=yes ;;
|
||||||
|
-*|$object)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
set fnord "$@" "$arg"; shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
obj_suffix=`echo "$object" | sed 's/^.*\././'`
|
||||||
|
touch "$tmpdepfile"
|
||||||
|
${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
|
||||||
|
rm -f "$depfile"
|
||||||
|
# makedepend may prepend the VPATH from the source file name to the object.
|
||||||
|
# No need to regex-escape $object, excess matching of '.' is harmless.
|
||||||
|
sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile"
|
||||||
|
# Some versions of the HPUX 10.20 sed can't process the last invocation
|
||||||
|
# correctly. Breaking it into two sed invocations is a workaround.
|
||||||
|
sed '1,2d' "$tmpdepfile" \
|
||||||
|
| tr ' ' "$nl" \
|
||||||
|
| sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
|
||||||
|
| sed -e 's/$/ :/' >> "$depfile"
|
||||||
|
rm -f "$tmpdepfile" "$tmpdepfile".bak
|
||||||
|
;;
|
||||||
|
|
||||||
|
cpp)
|
||||||
|
# Important note: in order to support this mode, a compiler *must*
|
||||||
|
# always write the preprocessed file to stdout.
|
||||||
|
"$@" || exit $?
|
||||||
|
|
||||||
|
# Remove the call to Libtool.
|
||||||
|
if test "$libtool" = yes; then
|
||||||
|
while test "X$1" != 'X--mode=compile'; do
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove '-o $object'.
|
||||||
|
IFS=" "
|
||||||
|
for arg
|
||||||
|
do
|
||||||
|
case $arg in
|
||||||
|
-o)
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
$object)
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
set fnord "$@" "$arg"
|
||||||
|
shift # fnord
|
||||||
|
shift # $arg
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
"$@" -E \
|
||||||
|
| sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
|
||||||
|
-e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
|
||||||
|
| sed '$ s: \\$::' > "$tmpdepfile"
|
||||||
|
rm -f "$depfile"
|
||||||
|
echo "$object : \\" > "$depfile"
|
||||||
|
cat < "$tmpdepfile" >> "$depfile"
|
||||||
|
sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
;;
|
||||||
|
|
||||||
|
msvisualcpp)
|
||||||
|
# Important note: in order to support this mode, a compiler *must*
|
||||||
|
# always write the preprocessed file to stdout.
|
||||||
|
"$@" || exit $?
|
||||||
|
|
||||||
|
# Remove the call to Libtool.
|
||||||
|
if test "$libtool" = yes; then
|
||||||
|
while test "X$1" != 'X--mode=compile'; do
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
|
||||||
|
IFS=" "
|
||||||
|
for arg
|
||||||
|
do
|
||||||
|
case "$arg" in
|
||||||
|
-o)
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
$object)
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
"-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
|
||||||
|
set fnord "$@"
|
||||||
|
shift
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
set fnord "$@" "$arg"
|
||||||
|
shift
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
"$@" -E 2>/dev/null |
|
||||||
|
sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
|
||||||
|
rm -f "$depfile"
|
||||||
|
echo "$object : \\" > "$depfile"
|
||||||
|
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile"
|
||||||
|
echo "$tab" >> "$depfile"
|
||||||
|
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
;;
|
||||||
|
|
||||||
|
msvcmsys)
|
||||||
|
# This case exists only to let depend.m4 do its work. It works by
|
||||||
|
# looking at the text of this script. This case will never be run,
|
||||||
|
# since it is checked for above.
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
|
||||||
|
none)
|
||||||
|
exec "$@"
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo "Unknown depmode $depmode" 1>&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
|
||||||
|
# Local Variables:
|
||||||
|
# mode: shell-script
|
||||||
|
# sh-indentation: 2
|
||||||
|
# eval: (add-hook 'before-save-hook 'time-stamp)
|
||||||
|
# time-stamp-start: "scriptversion="
|
||||||
|
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||||
|
# time-stamp-time-zone: "UTC0"
|
||||||
|
# time-stamp-end: "; # UTC"
|
||||||
|
# End:
|
|
@ -0,0 +1,518 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# install - install a program, script, or datafile
|
||||||
|
|
||||||
|
scriptversion=2018-03-11.20; # UTC
|
||||||
|
|
||||||
|
# This originates from X11R5 (mit/util/scripts/install.sh), which was
|
||||||
|
# later released in X11R6 (xc/config/util/install.sh) with the
|
||||||
|
# following copyright and license.
|
||||||
|
#
|
||||||
|
# Copyright (C) 1994 X Consortium
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to
|
||||||
|
# deal in the Software without restriction, including without limitation the
|
||||||
|
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
# sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
|
||||||
|
# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
#
|
||||||
|
# Except as contained in this notice, the name of the X Consortium shall not
|
||||||
|
# be used in advertising or otherwise to promote the sale, use or other deal-
|
||||||
|
# ings in this Software without prior written authorization from the X Consor-
|
||||||
|
# tium.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# FSF changes to this file are in the public domain.
|
||||||
|
#
|
||||||
|
# Calling this script install-sh is preferred over install.sh, to prevent
|
||||||
|
# 'make' implicit rules from creating a file called install from it
|
||||||
|
# when there is no Makefile.
|
||||||
|
#
|
||||||
|
# This script is compatible with the BSD install script, but was written
|
||||||
|
# from scratch.
|
||||||
|
|
||||||
|
tab=' '
|
||||||
|
nl='
|
||||||
|
'
|
||||||
|
IFS=" $tab$nl"
|
||||||
|
|
||||||
|
# Set DOITPROG to "echo" to test this script.
|
||||||
|
|
||||||
|
doit=${DOITPROG-}
|
||||||
|
doit_exec=${doit:-exec}
|
||||||
|
|
||||||
|
# Put in absolute file names if you don't have them in your path;
|
||||||
|
# or use environment vars.
|
||||||
|
|
||||||
|
chgrpprog=${CHGRPPROG-chgrp}
|
||||||
|
chmodprog=${CHMODPROG-chmod}
|
||||||
|
chownprog=${CHOWNPROG-chown}
|
||||||
|
cmpprog=${CMPPROG-cmp}
|
||||||
|
cpprog=${CPPROG-cp}
|
||||||
|
mkdirprog=${MKDIRPROG-mkdir}
|
||||||
|
mvprog=${MVPROG-mv}
|
||||||
|
rmprog=${RMPROG-rm}
|
||||||
|
stripprog=${STRIPPROG-strip}
|
||||||
|
|
||||||
|
posix_mkdir=
|
||||||
|
|
||||||
|
# Desired mode of installed file.
|
||||||
|
mode=0755
|
||||||
|
|
||||||
|
chgrpcmd=
|
||||||
|
chmodcmd=$chmodprog
|
||||||
|
chowncmd=
|
||||||
|
mvcmd=$mvprog
|
||||||
|
rmcmd="$rmprog -f"
|
||||||
|
stripcmd=
|
||||||
|
|
||||||
|
src=
|
||||||
|
dst=
|
||||||
|
dir_arg=
|
||||||
|
dst_arg=
|
||||||
|
|
||||||
|
copy_on_change=false
|
||||||
|
is_target_a_directory=possibly
|
||||||
|
|
||||||
|
usage="\
|
||||||
|
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
|
||||||
|
or: $0 [OPTION]... SRCFILES... DIRECTORY
|
||||||
|
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
|
||||||
|
or: $0 [OPTION]... -d DIRECTORIES...
|
||||||
|
|
||||||
|
In the 1st form, copy SRCFILE to DSTFILE.
|
||||||
|
In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
|
||||||
|
In the 4th, create DIRECTORIES.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--help display this help and exit.
|
||||||
|
--version display version info and exit.
|
||||||
|
|
||||||
|
-c (ignored)
|
||||||
|
-C install only if different (preserve the last data modification time)
|
||||||
|
-d create directories instead of installing files.
|
||||||
|
-g GROUP $chgrpprog installed files to GROUP.
|
||||||
|
-m MODE $chmodprog installed files to MODE.
|
||||||
|
-o USER $chownprog installed files to USER.
|
||||||
|
-s $stripprog installed files.
|
||||||
|
-t DIRECTORY install into DIRECTORY.
|
||||||
|
-T report an error if DSTFILE is a directory.
|
||||||
|
|
||||||
|
Environment variables override the default commands:
|
||||||
|
CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
|
||||||
|
RMPROG STRIPPROG
|
||||||
|
"
|
||||||
|
|
||||||
|
while test $# -ne 0; do
|
||||||
|
case $1 in
|
||||||
|
-c) ;;
|
||||||
|
|
||||||
|
-C) copy_on_change=true;;
|
||||||
|
|
||||||
|
-d) dir_arg=true;;
|
||||||
|
|
||||||
|
-g) chgrpcmd="$chgrpprog $2"
|
||||||
|
shift;;
|
||||||
|
|
||||||
|
--help) echo "$usage"; exit $?;;
|
||||||
|
|
||||||
|
-m) mode=$2
|
||||||
|
case $mode in
|
||||||
|
*' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
|
||||||
|
echo "$0: invalid mode: $mode" >&2
|
||||||
|
exit 1;;
|
||||||
|
esac
|
||||||
|
shift;;
|
||||||
|
|
||||||
|
-o) chowncmd="$chownprog $2"
|
||||||
|
shift;;
|
||||||
|
|
||||||
|
-s) stripcmd=$stripprog;;
|
||||||
|
|
||||||
|
-t)
|
||||||
|
is_target_a_directory=always
|
||||||
|
dst_arg=$2
|
||||||
|
# Protect names problematic for 'test' and other utilities.
|
||||||
|
case $dst_arg in
|
||||||
|
-* | [=\(\)!]) dst_arg=./$dst_arg;;
|
||||||
|
esac
|
||||||
|
shift;;
|
||||||
|
|
||||||
|
-T) is_target_a_directory=never;;
|
||||||
|
|
||||||
|
--version) echo "$0 $scriptversion"; exit $?;;
|
||||||
|
|
||||||
|
--) shift
|
||||||
|
break;;
|
||||||
|
|
||||||
|
-*) echo "$0: invalid option: $1" >&2
|
||||||
|
exit 1;;
|
||||||
|
|
||||||
|
*) break;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
# We allow the use of options -d and -T together, by making -d
|
||||||
|
# take the precedence; this is for compatibility with GNU install.
|
||||||
|
|
||||||
|
if test -n "$dir_arg"; then
|
||||||
|
if test -n "$dst_arg"; then
|
||||||
|
echo "$0: target directory not allowed when installing a directory." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
|
||||||
|
# When -d is used, all remaining arguments are directories to create.
|
||||||
|
# When -t is used, the destination is already specified.
|
||||||
|
# Otherwise, the last argument is the destination. Remove it from $@.
|
||||||
|
for arg
|
||||||
|
do
|
||||||
|
if test -n "$dst_arg"; then
|
||||||
|
# $@ is not empty: it contains at least $arg.
|
||||||
|
set fnord "$@" "$dst_arg"
|
||||||
|
shift # fnord
|
||||||
|
fi
|
||||||
|
shift # arg
|
||||||
|
dst_arg=$arg
|
||||||
|
# Protect names problematic for 'test' and other utilities.
|
||||||
|
case $dst_arg in
|
||||||
|
-* | [=\(\)!]) dst_arg=./$dst_arg;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test $# -eq 0; then
|
||||||
|
if test -z "$dir_arg"; then
|
||||||
|
echo "$0: no input file specified." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# It's OK to call 'install-sh -d' without argument.
|
||||||
|
# This can happen when creating conditional directories.
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test -z "$dir_arg"; then
|
||||||
|
if test $# -gt 1 || test "$is_target_a_directory" = always; then
|
||||||
|
if test ! -d "$dst_arg"; then
|
||||||
|
echo "$0: $dst_arg: Is not a directory." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test -z "$dir_arg"; then
|
||||||
|
do_exit='(exit $ret); exit $ret'
|
||||||
|
trap "ret=129; $do_exit" 1
|
||||||
|
trap "ret=130; $do_exit" 2
|
||||||
|
trap "ret=141; $do_exit" 13
|
||||||
|
trap "ret=143; $do_exit" 15
|
||||||
|
|
||||||
|
# Set umask so as not to create temps with too-generous modes.
|
||||||
|
# However, 'strip' requires both read and write access to temps.
|
||||||
|
case $mode in
|
||||||
|
# Optimize common cases.
|
||||||
|
*644) cp_umask=133;;
|
||||||
|
*755) cp_umask=22;;
|
||||||
|
|
||||||
|
*[0-7])
|
||||||
|
if test -z "$stripcmd"; then
|
||||||
|
u_plus_rw=
|
||||||
|
else
|
||||||
|
u_plus_rw='% 200'
|
||||||
|
fi
|
||||||
|
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
|
||||||
|
*)
|
||||||
|
if test -z "$stripcmd"; then
|
||||||
|
u_plus_rw=
|
||||||
|
else
|
||||||
|
u_plus_rw=,u+rw
|
||||||
|
fi
|
||||||
|
cp_umask=$mode$u_plus_rw;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
for src
|
||||||
|
do
|
||||||
|
# Protect names problematic for 'test' and other utilities.
|
||||||
|
case $src in
|
||||||
|
-* | [=\(\)!]) src=./$src;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if test -n "$dir_arg"; then
|
||||||
|
dst=$src
|
||||||
|
dstdir=$dst
|
||||||
|
test -d "$dstdir"
|
||||||
|
dstdir_status=$?
|
||||||
|
else
|
||||||
|
|
||||||
|
# Waiting for this to be detected by the "$cpprog $src $dsttmp" command
|
||||||
|
# might cause directories to be created, which would be especially bad
|
||||||
|
# if $src (and thus $dsttmp) contains '*'.
|
||||||
|
if test ! -f "$src" && test ! -d "$src"; then
|
||||||
|
echo "$0: $src does not exist." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test -z "$dst_arg"; then
|
||||||
|
echo "$0: no destination specified." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
dst=$dst_arg
|
||||||
|
|
||||||
|
# If destination is a directory, append the input filename.
|
||||||
|
if test -d "$dst"; then
|
||||||
|
if test "$is_target_a_directory" = never; then
|
||||||
|
echo "$0: $dst_arg: Is a directory" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
dstdir=$dst
|
||||||
|
dstbase=`basename "$src"`
|
||||||
|
case $dst in
|
||||||
|
*/) dst=$dst$dstbase;;
|
||||||
|
*) dst=$dst/$dstbase;;
|
||||||
|
esac
|
||||||
|
dstdir_status=0
|
||||||
|
else
|
||||||
|
dstdir=`dirname "$dst"`
|
||||||
|
test -d "$dstdir"
|
||||||
|
dstdir_status=$?
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
case $dstdir in
|
||||||
|
*/) dstdirslash=$dstdir;;
|
||||||
|
*) dstdirslash=$dstdir/;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
obsolete_mkdir_used=false
|
||||||
|
|
||||||
|
if test $dstdir_status != 0; then
|
||||||
|
case $posix_mkdir in
|
||||||
|
'')
|
||||||
|
# Create intermediate dirs using mode 755 as modified by the umask.
|
||||||
|
# This is like FreeBSD 'install' as of 1997-10-28.
|
||||||
|
umask=`umask`
|
||||||
|
case $stripcmd.$umask in
|
||||||
|
# Optimize common cases.
|
||||||
|
*[2367][2367]) mkdir_umask=$umask;;
|
||||||
|
.*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
|
||||||
|
|
||||||
|
*[0-7])
|
||||||
|
mkdir_umask=`expr $umask + 22 \
|
||||||
|
- $umask % 100 % 40 + $umask % 20 \
|
||||||
|
- $umask % 10 % 4 + $umask % 2
|
||||||
|
`;;
|
||||||
|
*) mkdir_umask=$umask,go-w;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# With -d, create the new directory with the user-specified mode.
|
||||||
|
# Otherwise, rely on $mkdir_umask.
|
||||||
|
if test -n "$dir_arg"; then
|
||||||
|
mkdir_mode=-m$mode
|
||||||
|
else
|
||||||
|
mkdir_mode=
|
||||||
|
fi
|
||||||
|
|
||||||
|
posix_mkdir=false
|
||||||
|
case $umask in
|
||||||
|
*[123567][0-7][0-7])
|
||||||
|
# POSIX mkdir -p sets u+wx bits regardless of umask, which
|
||||||
|
# is incompatible with FreeBSD 'install' when (umask & 300) != 0.
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# Note that $RANDOM variable is not portable (e.g. dash); Use it
|
||||||
|
# here however when possible just to lower collision chance.
|
||||||
|
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
|
||||||
|
|
||||||
|
trap 'ret=$?; rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null; exit $ret' 0
|
||||||
|
|
||||||
|
# Because "mkdir -p" follows existing symlinks and we likely work
|
||||||
|
# directly in world-writeable /tmp, make sure that the '$tmpdir'
|
||||||
|
# directory is successfully created first before we actually test
|
||||||
|
# 'mkdir -p' feature.
|
||||||
|
if (umask $mkdir_umask &&
|
||||||
|
$mkdirprog $mkdir_mode "$tmpdir" &&
|
||||||
|
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
if test -z "$dir_arg" || {
|
||||||
|
# Check for POSIX incompatibilities with -m.
|
||||||
|
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
|
||||||
|
# other-writable bit of parent directory when it shouldn't.
|
||||||
|
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
|
||||||
|
test_tmpdir="$tmpdir/a"
|
||||||
|
ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
|
||||||
|
case $ls_ld_tmpdir in
|
||||||
|
d????-?r-*) different_mode=700;;
|
||||||
|
d????-?--*) different_mode=755;;
|
||||||
|
*) false;;
|
||||||
|
esac &&
|
||||||
|
$mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
|
||||||
|
ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
|
||||||
|
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
then posix_mkdir=:
|
||||||
|
fi
|
||||||
|
rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
|
||||||
|
else
|
||||||
|
# Remove any dirs left behind by ancient mkdir implementations.
|
||||||
|
rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
|
||||||
|
fi
|
||||||
|
trap '' 0;;
|
||||||
|
esac;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if
|
||||||
|
$posix_mkdir && (
|
||||||
|
umask $mkdir_umask &&
|
||||||
|
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
|
||||||
|
)
|
||||||
|
then :
|
||||||
|
else
|
||||||
|
|
||||||
|
# The umask is ridiculous, or mkdir does not conform to POSIX,
|
||||||
|
# or it failed possibly due to a race condition. Create the
|
||||||
|
# directory the slow way, step by step, checking for races as we go.
|
||||||
|
|
||||||
|
case $dstdir in
|
||||||
|
/*) prefix='/';;
|
||||||
|
[-=\(\)!]*) prefix='./';;
|
||||||
|
*) prefix='';;
|
||||||
|
esac
|
||||||
|
|
||||||
|
oIFS=$IFS
|
||||||
|
IFS=/
|
||||||
|
set -f
|
||||||
|
set fnord $dstdir
|
||||||
|
shift
|
||||||
|
set +f
|
||||||
|
IFS=$oIFS
|
||||||
|
|
||||||
|
prefixes=
|
||||||
|
|
||||||
|
for d
|
||||||
|
do
|
||||||
|
test X"$d" = X && continue
|
||||||
|
|
||||||
|
prefix=$prefix$d
|
||||||
|
if test -d "$prefix"; then
|
||||||
|
prefixes=
|
||||||
|
else
|
||||||
|
if $posix_mkdir; then
|
||||||
|
(umask=$mkdir_umask &&
|
||||||
|
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
|
||||||
|
# Don't fail if two instances are running concurrently.
|
||||||
|
test -d "$prefix" || exit 1
|
||||||
|
else
|
||||||
|
case $prefix in
|
||||||
|
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
|
||||||
|
*) qprefix=$prefix;;
|
||||||
|
esac
|
||||||
|
prefixes="$prefixes '$qprefix'"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
prefix=$prefix/
|
||||||
|
done
|
||||||
|
|
||||||
|
if test -n "$prefixes"; then
|
||||||
|
# Don't fail if two instances are running concurrently.
|
||||||
|
(umask $mkdir_umask &&
|
||||||
|
eval "\$doit_exec \$mkdirprog $prefixes") ||
|
||||||
|
test -d "$dstdir" || exit 1
|
||||||
|
obsolete_mkdir_used=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test -n "$dir_arg"; then
|
||||||
|
{ test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
|
||||||
|
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
|
||||||
|
{ test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
|
||||||
|
test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
|
||||||
|
else
|
||||||
|
|
||||||
|
# Make a couple of temp file names in the proper directory.
|
||||||
|
dsttmp=${dstdirslash}_inst.$$_
|
||||||
|
rmtmp=${dstdirslash}_rm.$$_
|
||||||
|
|
||||||
|
# Trap to clean up those temp files at exit.
|
||||||
|
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
|
||||||
|
|
||||||
|
# Copy the file name to the temp name.
|
||||||
|
(umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
|
||||||
|
|
||||||
|
# and set any options; do chmod last to preserve setuid bits.
|
||||||
|
#
|
||||||
|
# If any of these fail, we abort the whole thing. If we want to
|
||||||
|
# ignore errors from any of these, just make sure not to ignore
|
||||||
|
# errors from the above "$doit $cpprog $src $dsttmp" command.
|
||||||
|
#
|
||||||
|
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
|
||||||
|
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
|
||||||
|
{ test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
|
||||||
|
{ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
|
||||||
|
|
||||||
|
# If -C, don't bother to copy if it wouldn't change the file.
|
||||||
|
if $copy_on_change &&
|
||||||
|
old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
|
||||||
|
new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
|
||||||
|
set -f &&
|
||||||
|
set X $old && old=:$2:$4:$5:$6 &&
|
||||||
|
set X $new && new=:$2:$4:$5:$6 &&
|
||||||
|
set +f &&
|
||||||
|
test "$old" = "$new" &&
|
||||||
|
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
rm -f "$dsttmp"
|
||||||
|
else
|
||||||
|
# Rename the file to the real destination.
|
||||||
|
$doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
|
||||||
|
|
||||||
|
# The rename failed, perhaps because mv can't rename something else
|
||||||
|
# to itself, or perhaps because mv is so ancient that it does not
|
||||||
|
# support -f.
|
||||||
|
{
|
||||||
|
# Now remove or move aside any old file at destination location.
|
||||||
|
# We try this two ways since rm can't unlink itself on some
|
||||||
|
# systems and the destination file might be busy for other
|
||||||
|
# reasons. In this case, the final cleanup might fail but the new
|
||||||
|
# file should still install successfully.
|
||||||
|
{
|
||||||
|
test ! -f "$dst" ||
|
||||||
|
$doit $rmcmd -f "$dst" 2>/dev/null ||
|
||||||
|
{ $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
|
||||||
|
{ $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
|
||||||
|
} ||
|
||||||
|
{ echo "$0: cannot unlink or rename $dst" >&2
|
||||||
|
(exit 1); exit 1
|
||||||
|
}
|
||||||
|
} &&
|
||||||
|
|
||||||
|
# Now rename the file to the real destination.
|
||||||
|
$doit $mvcmd "$dsttmp" "$dst"
|
||||||
|
}
|
||||||
|
fi || exit 1
|
||||||
|
|
||||||
|
trap '' 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Local variables:
|
||||||
|
# eval: (add-hook 'before-save-hook 'time-stamp)
|
||||||
|
# time-stamp-start: "scriptversion="
|
||||||
|
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||||
|
# time-stamp-time-zone: "UTC0"
|
||||||
|
# time-stamp-end: "; # UTC"
|
||||||
|
# End:
|
|
@ -0,0 +1,795 @@
|
||||||
|
/* Copyright (c) 2013 Dropbox, Inc.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "json11.hpp"
|
||||||
|
#include <cassert>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <limits>
|
||||||
|
#include <sstream>
|
||||||
|
#include <locale>
|
||||||
|
|
||||||
|
namespace json11 {
|
||||||
|
|
||||||
|
static const int max_depth = 200;
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
using std::vector;
|
||||||
|
using std::map;
|
||||||
|
using std::make_shared;
|
||||||
|
using std::initializer_list;
|
||||||
|
using std::move;
|
||||||
|
|
||||||
|
/* Helper for representing null - just a do-nothing struct, plus comparison
|
||||||
|
* operators so the helpers in JsonValue work. We can't use nullptr_t because
|
||||||
|
* it may not be orderable.
|
||||||
|
*/
|
||||||
|
struct NullStruct {
|
||||||
|
bool operator==(NullStruct) const { return true; }
|
||||||
|
bool operator<(NullStruct) const { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/* * * * * * * * * * * * * * * * * * * *
|
||||||
|
* Serialization
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void dump(NullStruct, string &out) {
|
||||||
|
out += "null";
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump(double value, string &out) {
|
||||||
|
if (std::isfinite(value)) {
|
||||||
|
std::ostringstream stm;
|
||||||
|
stm.imbue(std::locale("C"));
|
||||||
|
stm << value;
|
||||||
|
out += stm.str();
|
||||||
|
} else {
|
||||||
|
out += "null";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump(int value, string &out) {
|
||||||
|
char buf[32];
|
||||||
|
snprintf(buf, sizeof buf, "%d", value);
|
||||||
|
out += buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump(bool value, string &out) {
|
||||||
|
out += value ? "true" : "false";
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump(const string &value, string &out) {
|
||||||
|
out += '"';
|
||||||
|
for (size_t i = 0; i < value.length(); i++) {
|
||||||
|
const char ch = value[i];
|
||||||
|
if (ch == '\\') {
|
||||||
|
out += "\\\\";
|
||||||
|
} else if (ch == '"') {
|
||||||
|
out += "\\\"";
|
||||||
|
} else if (ch == '\b') {
|
||||||
|
out += "\\b";
|
||||||
|
} else if (ch == '\f') {
|
||||||
|
out += "\\f";
|
||||||
|
} else if (ch == '\n') {
|
||||||
|
out += "\\n";
|
||||||
|
} else if (ch == '\r') {
|
||||||
|
out += "\\r";
|
||||||
|
} else if (ch == '\t') {
|
||||||
|
out += "\\t";
|
||||||
|
} else if (static_cast<uint8_t>(ch) <= 0x1f) {
|
||||||
|
char buf[8];
|
||||||
|
snprintf(buf, sizeof buf, "\\u%04x", ch);
|
||||||
|
out += buf;
|
||||||
|
} else if (static_cast<uint8_t>(ch) == 0xe2 && static_cast<uint8_t>(value[i+1]) == 0x80
|
||||||
|
&& static_cast<uint8_t>(value[i+2]) == 0xa8) {
|
||||||
|
out += "\\u2028";
|
||||||
|
i += 2;
|
||||||
|
} else if (static_cast<uint8_t>(ch) == 0xe2 && static_cast<uint8_t>(value[i+1]) == 0x80
|
||||||
|
&& static_cast<uint8_t>(value[i+2]) == 0xa9) {
|
||||||
|
out += "\\u2029";
|
||||||
|
i += 2;
|
||||||
|
} else {
|
||||||
|
out += ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out += '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump(const Json::array &values, string &out) {
|
||||||
|
bool first = true;
|
||||||
|
out += "[";
|
||||||
|
for (const auto &value : values) {
|
||||||
|
if (!first)
|
||||||
|
out += ", ";
|
||||||
|
value.dump(out);
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
out += "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump(const Json::object &values, string &out) {
|
||||||
|
bool first = true;
|
||||||
|
out += "{";
|
||||||
|
for (const auto &kv : values) {
|
||||||
|
if (!first)
|
||||||
|
out += ", ";
|
||||||
|
dump(kv.first, out);
|
||||||
|
out += ": ";
|
||||||
|
kv.second.dump(out);
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
out += "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
void Json::dump(string &out) const {
|
||||||
|
m_ptr->dump(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* * * * * * * * * * * * * * * * * * * *
|
||||||
|
* Value wrappers
|
||||||
|
*/
|
||||||
|
|
||||||
|
template <Json::Type tag, typename T>
|
||||||
|
class Value : public JsonValue {
|
||||||
|
protected:
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
explicit Value(const T &value) : m_value(value) {}
|
||||||
|
explicit Value(T &&value) : m_value(move(value)) {}
|
||||||
|
|
||||||
|
// Get type tag
|
||||||
|
Json::Type type() const override {
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comparisons
|
||||||
|
bool equals(const JsonValue * other) const override {
|
||||||
|
return m_value == static_cast<const Value<tag, T> *>(other)->m_value;
|
||||||
|
}
|
||||||
|
bool less(const JsonValue * other) const override {
|
||||||
|
return m_value < static_cast<const Value<tag, T> *>(other)->m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const T m_value;
|
||||||
|
void dump(string &out) const override { json11::dump(m_value, out); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class JsonDouble final : public Value<Json::NUMBER, double> {
|
||||||
|
double number_value() const override { return m_value; }
|
||||||
|
int int_value() const override { return static_cast<int>(m_value); }
|
||||||
|
bool equals(const JsonValue * other) const override { return m_value == other->number_value(); }
|
||||||
|
bool less(const JsonValue * other) const override { return m_value < other->number_value(); }
|
||||||
|
public:
|
||||||
|
explicit JsonDouble(double value) : Value(value) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class JsonInt final : public Value<Json::NUMBER, int> {
|
||||||
|
double number_value() const override { return m_value; }
|
||||||
|
int int_value() const override { return m_value; }
|
||||||
|
bool equals(const JsonValue * other) const override { return m_value == other->number_value(); }
|
||||||
|
bool less(const JsonValue * other) const override { return m_value < other->number_value(); }
|
||||||
|
public:
|
||||||
|
explicit JsonInt(int value) : Value(value) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class JsonBoolean final : public Value<Json::BOOL, bool> {
|
||||||
|
bool bool_value() const override { return m_value; }
|
||||||
|
public:
|
||||||
|
explicit JsonBoolean(bool value) : Value(value) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class JsonString final : public Value<Json::STRING, string> {
|
||||||
|
const string &string_value() const override { return m_value; }
|
||||||
|
public:
|
||||||
|
explicit JsonString(const string &value) : Value(value) {}
|
||||||
|
explicit JsonString(string &&value) : Value(move(value)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class JsonArray final : public Value<Json::ARRAY, Json::array> {
|
||||||
|
const Json::array &array_items() const override { return m_value; }
|
||||||
|
const Json & operator[](size_t i) const override;
|
||||||
|
public:
|
||||||
|
explicit JsonArray(const Json::array &value) : Value(value) {}
|
||||||
|
explicit JsonArray(Json::array &&value) : Value(move(value)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class JsonObject final : public Value<Json::OBJECT, Json::object> {
|
||||||
|
const Json::object &object_items() const override { return m_value; }
|
||||||
|
const Json & operator[](const string &key) const override;
|
||||||
|
public:
|
||||||
|
explicit JsonObject(const Json::object &value) : Value(value) {}
|
||||||
|
explicit JsonObject(Json::object &&value) : Value(move(value)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class JsonNull final : public Value<Json::NUL, NullStruct> {
|
||||||
|
public:
|
||||||
|
JsonNull() : Value({}) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* * * * * * * * * * * * * * * * * * * *
|
||||||
|
* Static globals - static-init-safe
|
||||||
|
*/
|
||||||
|
struct Statics {
|
||||||
|
const std::shared_ptr<JsonValue> null = make_shared<JsonNull>();
|
||||||
|
const std::shared_ptr<JsonValue> t = make_shared<JsonBoolean>(true);
|
||||||
|
const std::shared_ptr<JsonValue> f = make_shared<JsonBoolean>(false);
|
||||||
|
const string empty_string;
|
||||||
|
const vector<Json> empty_vector;
|
||||||
|
const map<string, Json> empty_map;
|
||||||
|
Statics() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const Statics & statics() {
|
||||||
|
static const Statics s {};
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Json & static_null() {
|
||||||
|
// This has to be separate, not in Statics, because Json() accesses statics().null.
|
||||||
|
static const Json json_null;
|
||||||
|
return json_null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* * * * * * * * * * * * * * * * * * * *
|
||||||
|
* Constructors
|
||||||
|
*/
|
||||||
|
|
||||||
|
Json::Json() noexcept : m_ptr(statics().null) {}
|
||||||
|
Json::Json(std::nullptr_t) noexcept : m_ptr(statics().null) {}
|
||||||
|
Json::Json(double value) : m_ptr(make_shared<JsonDouble>(value)) {}
|
||||||
|
Json::Json(int value) : m_ptr(make_shared<JsonInt>(value)) {}
|
||||||
|
Json::Json(bool value) : m_ptr(value ? statics().t : statics().f) {}
|
||||||
|
Json::Json(const string &value) : m_ptr(make_shared<JsonString>(value)) {}
|
||||||
|
Json::Json(string &&value) : m_ptr(make_shared<JsonString>(move(value))) {}
|
||||||
|
Json::Json(const char * value) : m_ptr(make_shared<JsonString>(value)) {}
|
||||||
|
Json::Json(const Json::array &values) : m_ptr(make_shared<JsonArray>(values)) {}
|
||||||
|
Json::Json(Json::array &&values) : m_ptr(make_shared<JsonArray>(move(values))) {}
|
||||||
|
Json::Json(const Json::object &values) : m_ptr(make_shared<JsonObject>(values)) {}
|
||||||
|
Json::Json(Json::object &&values) : m_ptr(make_shared<JsonObject>(move(values))) {}
|
||||||
|
|
||||||
|
/* * * * * * * * * * * * * * * * * * * *
|
||||||
|
* Accessors
|
||||||
|
*/
|
||||||
|
|
||||||
|
Json::Type Json::type() const { return m_ptr->type(); }
|
||||||
|
double Json::number_value() const { return m_ptr->number_value(); }
|
||||||
|
int Json::int_value() const { return m_ptr->int_value(); }
|
||||||
|
bool Json::bool_value() const { return m_ptr->bool_value(); }
|
||||||
|
const string & Json::string_value() const { return m_ptr->string_value(); }
|
||||||
|
const vector<Json> & Json::array_items() const { return m_ptr->array_items(); }
|
||||||
|
const map<string, Json> & Json::object_items() const { return m_ptr->object_items(); }
|
||||||
|
const Json & Json::operator[] (size_t i) const { return (*m_ptr)[i]; }
|
||||||
|
const Json & Json::operator[] (const string &key) const { return (*m_ptr)[key]; }
|
||||||
|
|
||||||
|
double JsonValue::number_value() const { return 0; }
|
||||||
|
int JsonValue::int_value() const { return 0; }
|
||||||
|
bool JsonValue::bool_value() const { return false; }
|
||||||
|
const string & JsonValue::string_value() const { return statics().empty_string; }
|
||||||
|
const vector<Json> & JsonValue::array_items() const { return statics().empty_vector; }
|
||||||
|
const map<string, Json> & JsonValue::object_items() const { return statics().empty_map; }
|
||||||
|
const Json & JsonValue::operator[] (size_t) const { return static_null(); }
|
||||||
|
const Json & JsonValue::operator[] (const string &) const { return static_null(); }
|
||||||
|
|
||||||
|
const Json & JsonObject::operator[] (const string &key) const {
|
||||||
|
auto iter = m_value.find(key);
|
||||||
|
return (iter == m_value.end()) ? static_null() : iter->second;
|
||||||
|
}
|
||||||
|
const Json & JsonArray::operator[] (size_t i) const {
|
||||||
|
if (i >= m_value.size()) return static_null();
|
||||||
|
else return m_value[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* * * * * * * * * * * * * * * * * * * *
|
||||||
|
* Comparison
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool Json::operator== (const Json &other) const {
|
||||||
|
if (m_ptr == other.m_ptr)
|
||||||
|
return true;
|
||||||
|
if (m_ptr->type() != other.m_ptr->type())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return m_ptr->equals(other.m_ptr.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Json::operator< (const Json &other) const {
|
||||||
|
if (m_ptr == other.m_ptr)
|
||||||
|
return false;
|
||||||
|
if (m_ptr->type() != other.m_ptr->type())
|
||||||
|
return m_ptr->type() < other.m_ptr->type();
|
||||||
|
|
||||||
|
return m_ptr->less(other.m_ptr.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* * * * * * * * * * * * * * * * * * * *
|
||||||
|
* Parsing
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* esc(c)
|
||||||
|
*
|
||||||
|
* Format char c suitable for printing in an error message.
|
||||||
|
*/
|
||||||
|
static inline string esc(char c) {
|
||||||
|
char buf[12];
|
||||||
|
if (static_cast<uint8_t>(c) >= 0x20 && static_cast<uint8_t>(c) <= 0x7f) {
|
||||||
|
snprintf(buf, sizeof buf, "'%c' (%d)", c, c);
|
||||||
|
} else {
|
||||||
|
snprintf(buf, sizeof buf, "(%d)", c);
|
||||||
|
}
|
||||||
|
return string(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool in_range(long x, long lower, long upper) {
|
||||||
|
return (x >= lower && x <= upper);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
/* JsonParser
|
||||||
|
*
|
||||||
|
* Object that tracks all state of an in-progress parse.
|
||||||
|
*/
|
||||||
|
struct JsonParser final {
|
||||||
|
|
||||||
|
/* State
|
||||||
|
*/
|
||||||
|
const string &str;
|
||||||
|
size_t i;
|
||||||
|
string &err;
|
||||||
|
bool failed;
|
||||||
|
const JsonParse strategy;
|
||||||
|
|
||||||
|
/* fail(msg, err_ret = Json())
|
||||||
|
*
|
||||||
|
* Mark this parse as failed.
|
||||||
|
*/
|
||||||
|
Json fail(string &&msg) {
|
||||||
|
return fail(move(msg), Json());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T fail(string &&msg, const T err_ret) {
|
||||||
|
if (!failed)
|
||||||
|
err = std::move(msg);
|
||||||
|
failed = true;
|
||||||
|
return err_ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* consume_whitespace()
|
||||||
|
*
|
||||||
|
* Advance until the current character is non-whitespace.
|
||||||
|
*/
|
||||||
|
void consume_whitespace() {
|
||||||
|
while (str[i] == ' ' || str[i] == '\r' || str[i] == '\n' || str[i] == '\t')
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* consume_comment()
|
||||||
|
*
|
||||||
|
* Advance comments (c-style inline and multiline).
|
||||||
|
*/
|
||||||
|
bool consume_comment() {
|
||||||
|
bool comment_found = false;
|
||||||
|
if (str[i] == '/') {
|
||||||
|
i++;
|
||||||
|
if (i == str.size())
|
||||||
|
return fail("unexpected end of input after start of comment", false);
|
||||||
|
if (str[i] == '/') { // inline comment
|
||||||
|
i++;
|
||||||
|
// advance until next line, or end of input
|
||||||
|
while (i < str.size() && str[i] != '\n') {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
comment_found = true;
|
||||||
|
}
|
||||||
|
else if (str[i] == '*') { // multiline comment
|
||||||
|
i++;
|
||||||
|
if (i > str.size()-2)
|
||||||
|
return fail("unexpected end of input inside multi-line comment", false);
|
||||||
|
// advance until closing tokens
|
||||||
|
while (!(str[i] == '*' && str[i+1] == '/')) {
|
||||||
|
i++;
|
||||||
|
if (i > str.size()-2)
|
||||||
|
return fail(
|
||||||
|
"unexpected end of input inside multi-line comment", false);
|
||||||
|
}
|
||||||
|
i += 2;
|
||||||
|
comment_found = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return fail("malformed comment", false);
|
||||||
|
}
|
||||||
|
return comment_found;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* consume_garbage()
|
||||||
|
*
|
||||||
|
* Advance until the current character is non-whitespace and non-comment.
|
||||||
|
*/
|
||||||
|
void consume_garbage() {
|
||||||
|
consume_whitespace();
|
||||||
|
if(strategy == JsonParse::COMMENTS) {
|
||||||
|
bool comment_found = false;
|
||||||
|
do {
|
||||||
|
comment_found = consume_comment();
|
||||||
|
if (failed) return;
|
||||||
|
consume_whitespace();
|
||||||
|
}
|
||||||
|
while(comment_found);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get_next_token()
|
||||||
|
*
|
||||||
|
* Return the next non-whitespace character. If the end of the input is reached,
|
||||||
|
* flag an error and return 0.
|
||||||
|
*/
|
||||||
|
char get_next_token() {
|
||||||
|
consume_garbage();
|
||||||
|
if (failed) return (char)0;
|
||||||
|
if (i == str.size())
|
||||||
|
return fail("unexpected end of input", (char)0);
|
||||||
|
|
||||||
|
return str[i++];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* encode_utf8(pt, out)
|
||||||
|
*
|
||||||
|
* Encode pt as UTF-8 and add it to out.
|
||||||
|
*/
|
||||||
|
void encode_utf8(long pt, string & out) {
|
||||||
|
if (pt < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (pt < 0x80) {
|
||||||
|
out += static_cast<char>(pt);
|
||||||
|
} else if (pt < 0x800) {
|
||||||
|
out += static_cast<char>((pt >> 6) | 0xC0);
|
||||||
|
out += static_cast<char>((pt & 0x3F) | 0x80);
|
||||||
|
} else if (pt < 0x10000) {
|
||||||
|
out += static_cast<char>((pt >> 12) | 0xE0);
|
||||||
|
out += static_cast<char>(((pt >> 6) & 0x3F) | 0x80);
|
||||||
|
out += static_cast<char>((pt & 0x3F) | 0x80);
|
||||||
|
} else {
|
||||||
|
out += static_cast<char>((pt >> 18) | 0xF0);
|
||||||
|
out += static_cast<char>(((pt >> 12) & 0x3F) | 0x80);
|
||||||
|
out += static_cast<char>(((pt >> 6) & 0x3F) | 0x80);
|
||||||
|
out += static_cast<char>((pt & 0x3F) | 0x80);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parse_string()
|
||||||
|
*
|
||||||
|
* Parse a string, starting at the current position.
|
||||||
|
*/
|
||||||
|
string parse_string() {
|
||||||
|
string out;
|
||||||
|
long last_escaped_codepoint = -1;
|
||||||
|
while (true) {
|
||||||
|
if (i == str.size())
|
||||||
|
return fail("unexpected end of input in string", "");
|
||||||
|
|
||||||
|
char ch = str[i++];
|
||||||
|
|
||||||
|
if (ch == '"') {
|
||||||
|
encode_utf8(last_escaped_codepoint, out);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_range(ch, 0, 0x1f))
|
||||||
|
return fail("unescaped " + esc(ch) + " in string", "");
|
||||||
|
|
||||||
|
// The usual case: non-escaped characters
|
||||||
|
if (ch != '\\') {
|
||||||
|
encode_utf8(last_escaped_codepoint, out);
|
||||||
|
last_escaped_codepoint = -1;
|
||||||
|
out += ch;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle escapes
|
||||||
|
if (i == str.size())
|
||||||
|
return fail("unexpected end of input in string", "");
|
||||||
|
|
||||||
|
ch = str[i++];
|
||||||
|
|
||||||
|
if (ch == 'u') {
|
||||||
|
// Extract 4-byte escape sequence
|
||||||
|
string esc = str.substr(i, 4);
|
||||||
|
// Explicitly check length of the substring. The following loop
|
||||||
|
// relies on std::string returning the terminating NUL when
|
||||||
|
// accessing str[length]. Checking here reduces brittleness.
|
||||||
|
if (esc.length() < 4) {
|
||||||
|
return fail("bad \\u escape: " + esc, "");
|
||||||
|
}
|
||||||
|
for (size_t j = 0; j < 4; j++) {
|
||||||
|
if (!in_range(esc[j], 'a', 'f') && !in_range(esc[j], 'A', 'F')
|
||||||
|
&& !in_range(esc[j], '0', '9'))
|
||||||
|
return fail("bad \\u escape: " + esc, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
long codepoint = strtol(esc.data(), nullptr, 16);
|
||||||
|
|
||||||
|
// JSON specifies that characters outside the BMP shall be encoded as a pair
|
||||||
|
// of 4-hex-digit \u escapes encoding their surrogate pair components. Check
|
||||||
|
// whether we're in the middle of such a beast: the previous codepoint was an
|
||||||
|
// escaped lead (high) surrogate, and this is a trail (low) surrogate.
|
||||||
|
if (in_range(last_escaped_codepoint, 0xD800, 0xDBFF)
|
||||||
|
&& in_range(codepoint, 0xDC00, 0xDFFF)) {
|
||||||
|
// Reassemble the two surrogate pairs into one astral-plane character, per
|
||||||
|
// the UTF-16 algorithm.
|
||||||
|
encode_utf8((((last_escaped_codepoint - 0xD800) << 10)
|
||||||
|
| (codepoint - 0xDC00)) + 0x10000, out);
|
||||||
|
last_escaped_codepoint = -1;
|
||||||
|
} else {
|
||||||
|
encode_utf8(last_escaped_codepoint, out);
|
||||||
|
last_escaped_codepoint = codepoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 4;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
encode_utf8(last_escaped_codepoint, out);
|
||||||
|
last_escaped_codepoint = -1;
|
||||||
|
|
||||||
|
if (ch == 'b') {
|
||||||
|
out += '\b';
|
||||||
|
} else if (ch == 'f') {
|
||||||
|
out += '\f';
|
||||||
|
} else if (ch == 'n') {
|
||||||
|
out += '\n';
|
||||||
|
} else if (ch == 'r') {
|
||||||
|
out += '\r';
|
||||||
|
} else if (ch == 't') {
|
||||||
|
out += '\t';
|
||||||
|
} else if (ch == '"' || ch == '\\' || ch == '/') {
|
||||||
|
out += ch;
|
||||||
|
} else {
|
||||||
|
return fail("invalid escape character " + esc(ch), "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parse_number()
|
||||||
|
*
|
||||||
|
* Parse a double.
|
||||||
|
*/
|
||||||
|
Json parse_number() {
|
||||||
|
size_t start_pos = i;
|
||||||
|
|
||||||
|
if (str[i] == '-')
|
||||||
|
i++;
|
||||||
|
|
||||||
|
// Integer part
|
||||||
|
if (str[i] == '0') {
|
||||||
|
i++;
|
||||||
|
if (in_range(str[i], '0', '9'))
|
||||||
|
return fail("leading 0s not permitted in numbers");
|
||||||
|
} else if (in_range(str[i], '1', '9')) {
|
||||||
|
i++;
|
||||||
|
while (in_range(str[i], '0', '9'))
|
||||||
|
i++;
|
||||||
|
} else {
|
||||||
|
return fail("invalid " + esc(str[i]) + " in number");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str[i] != '.' && str[i] != 'e' && str[i] != 'E'
|
||||||
|
&& (i - start_pos) <= static_cast<size_t>(std::numeric_limits<int>::digits10)) {
|
||||||
|
return std::atoi(str.c_str() + start_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decimal part
|
||||||
|
if (str[i] == '.') {
|
||||||
|
i++;
|
||||||
|
if (!in_range(str[i], '0', '9'))
|
||||||
|
return fail("at least one digit required in fractional part");
|
||||||
|
|
||||||
|
while (in_range(str[i], '0', '9'))
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exponent part
|
||||||
|
if (str[i] == 'e' || str[i] == 'E') {
|
||||||
|
i++;
|
||||||
|
|
||||||
|
if (str[i] == '+' || str[i] == '-')
|
||||||
|
i++;
|
||||||
|
|
||||||
|
if (!in_range(str[i], '0', '9'))
|
||||||
|
return fail("at least one digit required in exponent");
|
||||||
|
|
||||||
|
while (in_range(str[i], '0', '9'))
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::istringstream stm(std::string(str.begin()+start_pos, str.end()));
|
||||||
|
stm.imbue(std::locale("C"));
|
||||||
|
double result;
|
||||||
|
stm >> result;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* expect(str, res)
|
||||||
|
*
|
||||||
|
* Expect that 'str' starts at the character that was just read. If it does, advance
|
||||||
|
* the input and return res. If not, flag an error.
|
||||||
|
*/
|
||||||
|
Json expect(const string &expected, Json res) {
|
||||||
|
assert(i != 0);
|
||||||
|
i--;
|
||||||
|
if (str.compare(i, expected.length(), expected) == 0) {
|
||||||
|
i += expected.length();
|
||||||
|
return res;
|
||||||
|
} else {
|
||||||
|
return fail("parse error: expected " + expected + ", got " + str.substr(i, expected.length()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parse_json()
|
||||||
|
*
|
||||||
|
* Parse a JSON object.
|
||||||
|
*/
|
||||||
|
Json parse_json(int depth) {
|
||||||
|
if (depth > max_depth) {
|
||||||
|
return fail("exceeded maximum nesting depth");
|
||||||
|
}
|
||||||
|
|
||||||
|
char ch = get_next_token();
|
||||||
|
if (failed)
|
||||||
|
return Json();
|
||||||
|
|
||||||
|
if (ch == '-' || (ch >= '0' && ch <= '9')) {
|
||||||
|
i--;
|
||||||
|
return parse_number();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch == 't')
|
||||||
|
return expect("true", true);
|
||||||
|
|
||||||
|
if (ch == 'f')
|
||||||
|
return expect("false", false);
|
||||||
|
|
||||||
|
if (ch == 'n')
|
||||||
|
return expect("null", Json());
|
||||||
|
|
||||||
|
if (ch == '"')
|
||||||
|
return parse_string();
|
||||||
|
|
||||||
|
if (ch == '{') {
|
||||||
|
map<string, Json> data;
|
||||||
|
ch = get_next_token();
|
||||||
|
if (ch == '}')
|
||||||
|
return data;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if (ch != '"')
|
||||||
|
return fail("expected '\"' in object, got " + esc(ch));
|
||||||
|
|
||||||
|
string key = parse_string();
|
||||||
|
if (failed)
|
||||||
|
return Json();
|
||||||
|
|
||||||
|
ch = get_next_token();
|
||||||
|
if (ch != ':')
|
||||||
|
return fail("expected ':' in object, got " + esc(ch));
|
||||||
|
|
||||||
|
data[std::move(key)] = parse_json(depth + 1);
|
||||||
|
if (failed)
|
||||||
|
return Json();
|
||||||
|
|
||||||
|
ch = get_next_token();
|
||||||
|
if (ch == '}')
|
||||||
|
break;
|
||||||
|
if (ch != ',')
|
||||||
|
return fail("expected ',' in object, got " + esc(ch));
|
||||||
|
|
||||||
|
ch = get_next_token();
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch == '[') {
|
||||||
|
vector<Json> data;
|
||||||
|
ch = get_next_token();
|
||||||
|
if (ch == ']')
|
||||||
|
return data;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
i--;
|
||||||
|
data.push_back(parse_json(depth + 1));
|
||||||
|
if (failed)
|
||||||
|
return Json();
|
||||||
|
|
||||||
|
ch = get_next_token();
|
||||||
|
if (ch == ']')
|
||||||
|
break;
|
||||||
|
if (ch != ',')
|
||||||
|
return fail("expected ',' in list, got " + esc(ch));
|
||||||
|
|
||||||
|
ch = get_next_token();
|
||||||
|
(void)ch;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fail("expected value, got " + esc(ch));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}//namespace {
|
||||||
|
|
||||||
|
Json Json::parse(const string &in, string &err, JsonParse strategy) {
|
||||||
|
JsonParser parser { in, 0, err, false, strategy };
|
||||||
|
Json result = parser.parse_json(0);
|
||||||
|
|
||||||
|
// Check for any trailing garbage
|
||||||
|
parser.consume_garbage();
|
||||||
|
if (parser.failed)
|
||||||
|
return Json();
|
||||||
|
if (parser.i != in.size())
|
||||||
|
return parser.fail("unexpected trailing " + esc(in[parser.i]));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Documented in json11.hpp
|
||||||
|
vector<Json> Json::parse_multi(const string &in,
|
||||||
|
std::string::size_type &parser_stop_pos,
|
||||||
|
string &err,
|
||||||
|
JsonParse strategy) {
|
||||||
|
JsonParser parser { in, 0, err, false, strategy };
|
||||||
|
parser_stop_pos = 0;
|
||||||
|
vector<Json> json_vec;
|
||||||
|
while (parser.i != in.size() && !parser.failed) {
|
||||||
|
json_vec.push_back(parser.parse_json(0));
|
||||||
|
if (parser.failed)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Check for another object
|
||||||
|
parser.consume_garbage();
|
||||||
|
if (parser.failed)
|
||||||
|
break;
|
||||||
|
parser_stop_pos = parser.i;
|
||||||
|
}
|
||||||
|
return json_vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* * * * * * * * * * * * * * * * * * * *
|
||||||
|
* Shape-checking
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool Json::has_shape(const shape & types, string & err) const {
|
||||||
|
if (!is_object()) {
|
||||||
|
err = "expected JSON object, got " + dump();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto & item : types) {
|
||||||
|
if ((*this)[item.first].type() != item.second) {
|
||||||
|
err = "bad type for " + item.first + " in " + dump();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace json11
|
|
@ -0,0 +1,232 @@
|
||||||
|
/* json11
|
||||||
|
*
|
||||||
|
* json11 is a tiny JSON library for C++11, providing JSON parsing and serialization.
|
||||||
|
*
|
||||||
|
* The core object provided by the library is json11::Json. A Json object represents any JSON
|
||||||
|
* value: null, bool, number (int or double), string (std::string), array (std::vector), or
|
||||||
|
* object (std::map).
|
||||||
|
*
|
||||||
|
* Json objects act like values: they can be assigned, copied, moved, compared for equality or
|
||||||
|
* order, etc. There are also helper methods Json::dump, to serialize a Json to a string, and
|
||||||
|
* Json::parse (static) to parse a std::string as a Json object.
|
||||||
|
*
|
||||||
|
* Internally, the various types of Json object are represented by the JsonValue class
|
||||||
|
* hierarchy.
|
||||||
|
*
|
||||||
|
* A note on numbers - JSON specifies the syntax of number formatting but not its semantics,
|
||||||
|
* so some JSON implementations distinguish between integers and floating-point numbers, while
|
||||||
|
* some don't. In json11, we choose the latter. Because some JSON implementations (namely
|
||||||
|
* Javascript itself) treat all numbers as the same type, distinguishing the two leads
|
||||||
|
* to JSON that will be *silently* changed by a round-trip through those implementations.
|
||||||
|
* Dangerous! To avoid that risk, json11 stores all numbers as double internally, but also
|
||||||
|
* provides integer helpers.
|
||||||
|
*
|
||||||
|
* Fortunately, double-precision IEEE754 ('double') can precisely store any integer in the
|
||||||
|
* range +/-2^53, which includes every 'int' on most systems. (Timestamps often use int64
|
||||||
|
* or long long to avoid the Y2038K problem; a double storing microseconds since some epoch
|
||||||
|
* will be exact for +/- 275 years.)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Copyright (c) 2013 Dropbox, Inc.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <initializer_list>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#if _MSC_VER <= 1800 // VS 2013
|
||||||
|
#ifndef noexcept
|
||||||
|
#define noexcept throw()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef snprintf
|
||||||
|
#define snprintf _snprintf_s
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace json11 {
|
||||||
|
|
||||||
|
enum JsonParse {
|
||||||
|
STANDARD, COMMENTS
|
||||||
|
};
|
||||||
|
|
||||||
|
class JsonValue;
|
||||||
|
|
||||||
|
class Json final {
|
||||||
|
public:
|
||||||
|
// Types
|
||||||
|
enum Type {
|
||||||
|
NUL, NUMBER, BOOL, STRING, ARRAY, OBJECT
|
||||||
|
};
|
||||||
|
|
||||||
|
// Array and object typedefs
|
||||||
|
typedef std::vector<Json> array;
|
||||||
|
typedef std::map<std::string, Json> object;
|
||||||
|
|
||||||
|
// Constructors for the various types of JSON value.
|
||||||
|
Json() noexcept; // NUL
|
||||||
|
Json(std::nullptr_t) noexcept; // NUL
|
||||||
|
Json(double value); // NUMBER
|
||||||
|
Json(int value); // NUMBER
|
||||||
|
Json(bool value); // BOOL
|
||||||
|
Json(const std::string &value); // STRING
|
||||||
|
Json(std::string &&value); // STRING
|
||||||
|
Json(const char * value); // STRING
|
||||||
|
Json(const array &values); // ARRAY
|
||||||
|
Json(array &&values); // ARRAY
|
||||||
|
Json(const object &values); // OBJECT
|
||||||
|
Json(object &&values); // OBJECT
|
||||||
|
|
||||||
|
// Implicit constructor: anything with a to_json() function.
|
||||||
|
template <class T, class = decltype(&T::to_json)>
|
||||||
|
Json(const T & t) : Json(t.to_json()) {}
|
||||||
|
|
||||||
|
// Implicit constructor: map-like objects (std::map, std::unordered_map, etc)
|
||||||
|
template <class M, typename std::enable_if<
|
||||||
|
std::is_constructible<std::string, decltype(std::declval<M>().begin()->first)>::value
|
||||||
|
&& std::is_constructible<Json, decltype(std::declval<M>().begin()->second)>::value,
|
||||||
|
int>::type = 0>
|
||||||
|
Json(const M & m) : Json(object(m.begin(), m.end())) {}
|
||||||
|
|
||||||
|
// Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc)
|
||||||
|
template <class V, typename std::enable_if<
|
||||||
|
std::is_constructible<Json, decltype(*std::declval<V>().begin())>::value,
|
||||||
|
int>::type = 0>
|
||||||
|
Json(const V & v) : Json(array(v.begin(), v.end())) {}
|
||||||
|
|
||||||
|
// This prevents Json(some_pointer) from accidentally producing a bool. Use
|
||||||
|
// Json(bool(some_pointer)) if that behavior is desired.
|
||||||
|
Json(void *) = delete;
|
||||||
|
|
||||||
|
// Accessors
|
||||||
|
Type type() const;
|
||||||
|
|
||||||
|
bool is_null() const { return type() == NUL; }
|
||||||
|
bool is_number() const { return type() == NUMBER; }
|
||||||
|
bool is_bool() const { return type() == BOOL; }
|
||||||
|
bool is_string() const { return type() == STRING; }
|
||||||
|
bool is_array() const { return type() == ARRAY; }
|
||||||
|
bool is_object() const { return type() == OBJECT; }
|
||||||
|
|
||||||
|
// Return the enclosed value if this is a number, 0 otherwise. Note that json11 does not
|
||||||
|
// distinguish between integer and non-integer numbers - number_value() and int_value()
|
||||||
|
// can both be applied to a NUMBER-typed object.
|
||||||
|
double number_value() const;
|
||||||
|
int int_value() const;
|
||||||
|
|
||||||
|
// Return the enclosed value if this is a boolean, false otherwise.
|
||||||
|
bool bool_value() const;
|
||||||
|
// Return the enclosed string if this is a string, "" otherwise.
|
||||||
|
const std::string &string_value() const;
|
||||||
|
// Return the enclosed std::vector if this is an array, or an empty vector otherwise.
|
||||||
|
const array &array_items() const;
|
||||||
|
// Return the enclosed std::map if this is an object, or an empty map otherwise.
|
||||||
|
const object &object_items() const;
|
||||||
|
|
||||||
|
// Return a reference to arr[i] if this is an array, Json() otherwise.
|
||||||
|
const Json & operator[](size_t i) const;
|
||||||
|
// Return a reference to obj[key] if this is an object, Json() otherwise.
|
||||||
|
const Json & operator[](const std::string &key) const;
|
||||||
|
|
||||||
|
// Serialize.
|
||||||
|
void dump(std::string &out) const;
|
||||||
|
std::string dump() const {
|
||||||
|
std::string out;
|
||||||
|
dump(out);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse. If parse fails, return Json() and assign an error message to err.
|
||||||
|
static Json parse(const std::string & in,
|
||||||
|
std::string & err,
|
||||||
|
JsonParse strategy = JsonParse::STANDARD);
|
||||||
|
static Json parse(const char * in,
|
||||||
|
std::string & err,
|
||||||
|
JsonParse strategy = JsonParse::STANDARD) {
|
||||||
|
if (in) {
|
||||||
|
return parse(std::string(in), err, strategy);
|
||||||
|
} else {
|
||||||
|
err = "null input";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Parse multiple objects, concatenated or separated by whitespace
|
||||||
|
static std::vector<Json> parse_multi(
|
||||||
|
const std::string & in,
|
||||||
|
std::string::size_type & parser_stop_pos,
|
||||||
|
std::string & err,
|
||||||
|
JsonParse strategy = JsonParse::STANDARD);
|
||||||
|
|
||||||
|
static inline std::vector<Json> parse_multi(
|
||||||
|
const std::string & in,
|
||||||
|
std::string & err,
|
||||||
|
JsonParse strategy = JsonParse::STANDARD) {
|
||||||
|
std::string::size_type parser_stop_pos;
|
||||||
|
return parse_multi(in, parser_stop_pos, err, strategy);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator== (const Json &rhs) const;
|
||||||
|
bool operator< (const Json &rhs) const;
|
||||||
|
bool operator!= (const Json &rhs) const { return !(*this == rhs); }
|
||||||
|
bool operator<= (const Json &rhs) const { return !(rhs < *this); }
|
||||||
|
bool operator> (const Json &rhs) const { return (rhs < *this); }
|
||||||
|
bool operator>= (const Json &rhs) const { return !(*this < rhs); }
|
||||||
|
|
||||||
|
/* has_shape(types, err)
|
||||||
|
*
|
||||||
|
* Return true if this is a JSON object and, for each item in types, has a field of
|
||||||
|
* the given type. If not, return false and set err to a descriptive message.
|
||||||
|
*/
|
||||||
|
typedef std::initializer_list<std::pair<std::string, Type>> shape;
|
||||||
|
bool has_shape(const shape & types, std::string & err) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<JsonValue> m_ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Internal class hierarchy - JsonValue objects are not exposed to users of this API.
|
||||||
|
class JsonValue {
|
||||||
|
protected:
|
||||||
|
friend class Json;
|
||||||
|
friend class JsonInt;
|
||||||
|
friend class JsonDouble;
|
||||||
|
virtual Json::Type type() const = 0;
|
||||||
|
virtual bool equals(const JsonValue * other) const = 0;
|
||||||
|
virtual bool less(const JsonValue * other) const = 0;
|
||||||
|
virtual void dump(std::string &out) const = 0;
|
||||||
|
virtual double number_value() const;
|
||||||
|
virtual int int_value() const;
|
||||||
|
virtual bool bool_value() const;
|
||||||
|
virtual const std::string &string_value() const;
|
||||||
|
virtual const Json::array &array_items() const;
|
||||||
|
virtual const Json &operator[](size_t i) const;
|
||||||
|
virtual const Json::object &object_items() const;
|
||||||
|
virtual const Json &operator[](const std::string &key) const;
|
||||||
|
virtual ~JsonValue() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace json11
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,423 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="Debug|Win32">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|Win32">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Debug|ARM">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>ARM</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|ARM">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>ARM</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>{21f10158-c078-4bd7-a82a-9c4aeb8e2f8e}</ProjectGuid>
|
||||||
|
<Keyword>Win32Proj</Keyword>
|
||||||
|
<ProjectName>libtgvoip</ProjectName>
|
||||||
|
<RootNamespace>libtgvoip</RootNamespace>
|
||||||
|
<DefaultLanguage>en-US</DefaultLanguage>
|
||||||
|
<MinimumVisualStudioVersion>12.0</MinimumVisualStudioVersion>
|
||||||
|
<AppContainerApplication>true</AppContainerApplication>
|
||||||
|
<ApplicationType>Windows Phone Silverlight</ApplicationType>
|
||||||
|
<ApplicationTypeRevision>8.1</ApplicationTypeRevision>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v120</PlatformToolset>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<PlatformToolset>v120</PlatformToolset>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
|
||||||
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v120</PlatformToolset>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">
|
||||||
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<PlatformToolset>v120</PlatformToolset>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<PropertyGroup />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||||
|
<PreprocessorDefinitions>_WINRT_DLL;TGVOIP_WP_SILVERLIGHT;_CRT_SECURE_NO_WARNINGS;NOMINMAX;WEBRTC_APM_DEBUG_DUMP=0;TGVOIP_USE_CUSTOM_CRYPTO;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||||
|
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||||
|
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<AdditionalIncludeDirectories>webrtc_dsp;../TelegramClient/TelegramClient.Opus/opus/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||||
|
<AdditionalDependencies>ws2_32.lib;phoneaudioses.lib;../TelegramClient/$(Platform)/$(Configuration)/TelegramClient.Opus/TelegramClient.Opus.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||||
|
<PreprocessorDefinitions>_WINRT_DLL;TGVOIP_WP_SILVERLIGHT;NDEBUG;_CRT_SECURE_NO_WARNINGS;NOMINMAX;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||||
|
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||||
|
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<AdditionalIncludeDirectories>webrtc_dsp;../TelegramClient/TelegramClient.Opus/opus/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||||
|
<AdditionalDependencies>ws2_32.lib;phoneaudioses.lib;../TelegramClient/$(Platform)/$(Configuration)/TelegramClient.Opus/TelegramClient.Opus.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||||
|
<ClCompile>
|
||||||
|
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||||
|
<PreprocessorDefinitions>_WINRT_DLL;TGVOIP_WP_SILVERLIGHT;_CRT_SECURE_NO_WARNINGS;NOMINMAX;WEBRTC_APM_DEBUG_DUMP=0;TGVOIP_USE_CUSTOM_CRYPTO;noexcept=;_ALLOW_KEYWORD_MACROS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||||
|
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||||
|
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<AdditionalIncludeDirectories>webrtc_dsp;../TelegramClient/TelegramClient.Opus/opus/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<DisableSpecificWarnings>4068;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||||
|
<AdditionalDependencies>ws2_32.lib;phoneaudioses.lib;../TelegramClient/$(Platform)/$(Configuration)/TelegramClient.Opus/TelegramClient.Opus.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||||
|
<ClCompile>
|
||||||
|
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||||
|
<PreprocessorDefinitions>_WINRT_DLL;TGVOIP_WP_SILVERLIGHT;NDEBUG;_CRT_SECURE_NO_WARNINGS;NOMINMAX;WEBRTC_APM_DEBUG_DUMP=0;TGVOIP_USE_CUSTOM_CRYPTO;noexcept=;_ALLOW_KEYWORD_MACROS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||||
|
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||||
|
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<AdditionalIncludeDirectories>webrtc_dsp;../TelegramClient/TelegramClient.Opus/opus/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<RuntimeTypeInfo>true</RuntimeTypeInfo>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||||
|
<AdditionalDependencies>ws2_32.lib;phoneaudioses.lib;../TelegramClient/$(Platform)/$(Configuration)/TelegramClient.Opus/TelegramClient.Opus.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="audio\AudioInput.h" />
|
||||||
|
<ClInclude Include="audio\AudioIO.h" />
|
||||||
|
<ClInclude Include="audio\AudioOutput.h" />
|
||||||
|
<ClInclude Include="audio\Resampler.h" />
|
||||||
|
<ClInclude Include="BlockingQueue.h" />
|
||||||
|
<ClInclude Include="Buffers.h" />
|
||||||
|
<ClInclude Include="CongestionControl.h" />
|
||||||
|
<ClInclude Include="EchoCanceller.h" />
|
||||||
|
<ClInclude Include="JitterBuffer.h" />
|
||||||
|
<ClInclude Include="logging.h" />
|
||||||
|
<ClInclude Include="MediaStreamItf.h" />
|
||||||
|
<ClInclude Include="MessageThread.h" />
|
||||||
|
<ClInclude Include="NetworkSocket.h" />
|
||||||
|
<ClInclude Include="OpusDecoder.h" />
|
||||||
|
<ClInclude Include="OpusEncoder.h" />
|
||||||
|
<ClInclude Include="os\windows\AudioInputWASAPI.h" />
|
||||||
|
<ClInclude Include="os\windows\AudioOutputWASAPI.h" />
|
||||||
|
<ClInclude Include="os\windows\NetworkSocketWinsock.h" />
|
||||||
|
<ClInclude Include="os\windows\WindowsSandboxUtils.h" />
|
||||||
|
<ClInclude Include="PacketReassembler.h" />
|
||||||
|
<ClInclude Include="threading.h" />
|
||||||
|
<ClInclude Include="VoIPController.h" />
|
||||||
|
<ClInclude Include="os\windows\CXWrapper.h" />
|
||||||
|
<ClInclude Include="VoIPServerConfig.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\base\array_view.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\base\atomicops.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\base\basictypes.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\base\checks.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\base\constructormagic.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\base\safe_compare.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\base\safe_conversions.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\base\safe_conversions_impl.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\base\sanitizer.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\base\stringutils.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\base\type_traits.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\common_audio\channel_buffer.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\common_audio\fft4g.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\common_audio\include\audio_util.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\common_audio\ring_buffer.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\complex_fft_tables.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\include\real_fft.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\include\signal_processing_library.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\include\spl_inl.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\include\spl_inl_armv7.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\include\spl_inl_mips.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_by_2_internal.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\common_audio\sparse_fir_filter.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\common_audio\wav_file.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\common_audio\wav_header.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_core.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_defines.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\echo_control_mobile.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_common.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core_optimized_methods.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_resampler.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aec\echo_cancellation.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\agc\legacy\analog_agc.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\agc\legacy\digital_agc.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\agc\legacy\gain_control.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\logging\apm_data_dumper.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\defines.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\noise_suppression.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\noise_suppression_x.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_core.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_defines.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\ns_core.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\windows_private.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\splitting_filter.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\three_band_filter_bank.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\block_mean_calculator.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\delay_estimator.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\delay_estimator_internal.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\delay_estimator_wrapper.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft_tables_common.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft_tables_neon_sse2.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\system_wrappers\include\asm_defines.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\system_wrappers\include\compile_assert_c.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\system_wrappers\include\cpu_features_wrapper.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\system_wrappers\include\metrics.h" />
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\typedefs.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="audio\AudioInput.cpp" />
|
||||||
|
<ClCompile Include="audio\AudioIO.cpp" />
|
||||||
|
<ClCompile Include="audio\AudioOutput.cpp" />
|
||||||
|
<ClCompile Include="audio\Resampler.cpp" />
|
||||||
|
<ClCompile Include="BlockingQueue.cpp" />
|
||||||
|
<ClCompile Include="Buffers.cpp" />
|
||||||
|
<ClCompile Include="CongestionControl.cpp" />
|
||||||
|
<ClCompile Include="EchoCanceller.cpp" />
|
||||||
|
<ClCompile Include="JitterBuffer.cpp" />
|
||||||
|
<ClCompile Include="logging.cpp" />
|
||||||
|
<ClCompile Include="MediaStreamItf.cpp" />
|
||||||
|
<ClCompile Include="MessageThread.cpp" />
|
||||||
|
<ClCompile Include="NetworkSocket.cpp" />
|
||||||
|
<ClCompile Include="OpusDecoder.cpp" />
|
||||||
|
<ClCompile Include="OpusEncoder.cpp" />
|
||||||
|
<ClCompile Include="os\windows\AudioInputWASAPI.cpp" />
|
||||||
|
<ClCompile Include="os\windows\AudioOutputWASAPI.cpp" />
|
||||||
|
<ClCompile Include="os\windows\NetworkSocketWinsock.cpp" />
|
||||||
|
<ClCompile Include="os\windows\WindowsSandboxUtils.cpp" />
|
||||||
|
<ClCompile Include="PacketReassembler.cpp" />
|
||||||
|
<ClCompile Include="VoIPController.cpp" />
|
||||||
|
<ClCompile Include="os\windows\CXWrapper.cpp" />
|
||||||
|
<ClCompile Include="VoIPServerConfig.cpp" />
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\base\checks.cc" />
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\base\stringutils.cc" />
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\audio_util.cc" />
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\channel_buffer.cc" />
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\fft4g.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\ring_buffer.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\auto_correlation.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\auto_corr_to_refl_coef.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\complex_bit_reverse.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\complex_fft.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\copy_set_operations.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\cross_correlation.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\cross_correlation_neon.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\division_operations.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\dot_product_with_scale.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\downsample_fast.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\downsample_fast_neon.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\energy.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\filter_ar.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\filter_ar_fast_q12.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\filter_ma_fast_q12.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\get_hanning_window.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\get_scaling_square.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\ilbc_specific_functions.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\levinson_durbin.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\lpc_to_refl_coef.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\min_max_operations.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\min_max_operations_neon.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\randomization_functions.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\real_fft.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\refl_coef_to_lpc.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_48khz.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_by_2.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_by_2_internal.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_fractional.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\splitting_filter_impl.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\spl_init.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\spl_inl.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\spl_sqrt.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\spl_sqrt_floor.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\sqrt_of_one_minus_x_squared.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\vector_scaling_operations.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\sparse_fir_filter.cc" />
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\wav_file.cc" />
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\wav_header.cc" />
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_core.cc" />
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_core_c.cc" />
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_core_neon.cc" />
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\echo_control_mobile.cc" />
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core.cc" />
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core_neon.cc" />
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core_sse2.cc" />
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_resampler.cc" />
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\echo_cancellation.cc" />
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\agc\legacy\analog_agc.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\agc\legacy\digital_agc.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\logging\apm_data_dumper.cc" />
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\noise_suppression.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\noise_suppression_x.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_core.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_core_c.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_core_neon.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\ns_core.c">
|
||||||
|
<CompileAsWinRT>false</CompileAsWinRT>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\splitting_filter.cc" />
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\three_band_filter_bank.cc" />
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\block_mean_calculator.cc" />
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\delay_estimator.cc" />
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\delay_estimator_wrapper.cc" />
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft.cc" />
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft_neon.cc" />
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft_sse2.cc" />
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\system_wrappers\source\cpu_features.cc" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
</ImportGroup>
|
||||||
|
</Project>
|
|
@ -0,0 +1,488 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup>
|
||||||
|
<Filter Include="Resources">
|
||||||
|
<UniqueIdentifier>11199e80-17a0-460f-a780-9bfde20eb11c</UniqueIdentifier>
|
||||||
|
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="audio">
|
||||||
|
<UniqueIdentifier>{c5b75146-c75a-4c56-aeb2-2781658d7b0a}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="windows">
|
||||||
|
<UniqueIdentifier>{de1527d9-7564-4e96-9653-6e023b90d2bc}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="webrtc_dsp">
|
||||||
|
<UniqueIdentifier>{3b15701a-65dd-4d52-92d4-a7b64a73b293}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="VoIPController.cpp" />
|
||||||
|
<ClCompile Include="VoIPServerConfig.cpp" />
|
||||||
|
<ClCompile Include="BlockingQueue.cpp" />
|
||||||
|
<ClCompile Include="CongestionControl.cpp" />
|
||||||
|
<ClCompile Include="EchoCanceller.cpp" />
|
||||||
|
<ClCompile Include="JitterBuffer.cpp" />
|
||||||
|
<ClCompile Include="logging.cpp" />
|
||||||
|
<ClCompile Include="MediaStreamItf.cpp" />
|
||||||
|
<ClCompile Include="NetworkSocket.cpp" />
|
||||||
|
<ClCompile Include="OpusDecoder.cpp" />
|
||||||
|
<ClCompile Include="OpusEncoder.cpp" />
|
||||||
|
<ClCompile Include="audio\AudioInput.cpp">
|
||||||
|
<Filter>audio</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="audio\AudioOutput.cpp">
|
||||||
|
<Filter>audio</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="audio\Resampler.cpp">
|
||||||
|
<Filter>audio</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="os\windows\AudioInputWASAPI.cpp">
|
||||||
|
<Filter>windows</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="os\windows\AudioOutputWASAPI.cpp">
|
||||||
|
<Filter>windows</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="os\windows\NetworkSocketWinsock.cpp">
|
||||||
|
<Filter>windows</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="os\windows\WindowsSandboxUtils.cpp">
|
||||||
|
<Filter>windows</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\base\checks.cc">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\base\stringutils.cc">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\audio_util.cc">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\channel_buffer.cc">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\fft4g.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\ring_buffer.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\auto_correlation.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\auto_corr_to_refl_coef.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\complex_bit_reverse.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\complex_fft.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\copy_set_operations.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\cross_correlation.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\cross_correlation_neon.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\division_operations.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\dot_product_with_scale.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\downsample_fast.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\downsample_fast_neon.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\energy.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\filter_ar.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\filter_ar_fast_q12.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\filter_ma_fast_q12.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\get_hanning_window.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\get_scaling_square.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\ilbc_specific_functions.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\levinson_durbin.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\lpc_to_refl_coef.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\min_max_operations.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\min_max_operations_neon.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\randomization_functions.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\real_fft.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\refl_coef_to_lpc.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_48khz.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_by_2.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_by_2_internal.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_fractional.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\splitting_filter_impl.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\spl_init.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\spl_inl.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\spl_sqrt.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\spl_sqrt_floor.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\sqrt_of_one_minus_x_squared.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\vector_scaling_operations.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\sparse_fir_filter.cc">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\wav_file.cc">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\common_audio\wav_header.cc">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core.cc">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core_neon.cc">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core_sse2.cc">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_resampler.cc">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\echo_cancellation.cc">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_core.cc">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_core_c.cc">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_core_neon.cc">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\echo_control_mobile.cc">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\agc\legacy\analog_agc.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\agc\legacy\digital_agc.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\logging\apm_data_dumper.cc">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\noise_suppression.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\noise_suppression_x.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_core.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_core_c.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_core_neon.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\ns_core.c">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\splitting_filter.cc">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\three_band_filter_bank.cc">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\block_mean_calculator.cc">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\delay_estimator.cc">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\delay_estimator_wrapper.cc">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft.cc">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft_neon.cc">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft_sse2.cc">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="webrtc_dsp\webrtc\system_wrappers\source\cpu_features.cc">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="os\windows\CXWrapper.cpp">
|
||||||
|
<Filter>windows</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="Buffers.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="VoIPController.h" />
|
||||||
|
<ClInclude Include="VoIPServerConfig.h" />
|
||||||
|
<ClInclude Include="BlockingQueue.h" />
|
||||||
|
<ClInclude Include="CongestionControl.h" />
|
||||||
|
<ClInclude Include="EchoCanceller.h" />
|
||||||
|
<ClInclude Include="JitterBuffer.h" />
|
||||||
|
<ClInclude Include="logging.h" />
|
||||||
|
<ClInclude Include="MediaStreamItf.h" />
|
||||||
|
<ClInclude Include="NetworkSocket.h" />
|
||||||
|
<ClInclude Include="OpusDecoder.h" />
|
||||||
|
<ClInclude Include="OpusEncoder.h" />
|
||||||
|
<ClInclude Include="threading.h" />
|
||||||
|
<ClInclude Include="audio\AudioInput.h">
|
||||||
|
<Filter>audio</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="audio\AudioOutput.h">
|
||||||
|
<Filter>audio</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="audio\Resampler.h">
|
||||||
|
<Filter>audio</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="os\windows\AudioInputWASAPI.h">
|
||||||
|
<Filter>windows</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="os\windows\AudioOutputWASAPI.h">
|
||||||
|
<Filter>windows</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="os\windows\NetworkSocketWinsock.h">
|
||||||
|
<Filter>windows</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="os\windows\WindowsSandboxUtils.h">
|
||||||
|
<Filter>windows</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\base\array_view.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\base\atomicops.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\base\basictypes.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\base\checks.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\base\constructormagic.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\base\safe_compare.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\base\safe_conversions.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\base\safe_conversions_impl.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\base\sanitizer.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\base\stringutils.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\base\type_traits.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\common_audio\channel_buffer.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\common_audio\fft4g.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\common_audio\include\audio_util.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\common_audio\ring_buffer.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\complex_fft_tables.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\include\real_fft.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\include\signal_processing_library.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\include\spl_inl.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\include\spl_inl_armv7.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\include\spl_inl_mips.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_by_2_internal.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\common_audio\sparse_fir_filter.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\common_audio\wav_file.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\common_audio\wav_header.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_common.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core_optimized_methods.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_resampler.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aec\echo_cancellation.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_core.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_defines.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\echo_control_mobile.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\agc\legacy\analog_agc.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\agc\legacy\digital_agc.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\agc\legacy\gain_control.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\logging\apm_data_dumper.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\defines.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\noise_suppression.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\noise_suppression_x.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_core.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_defines.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\ns_core.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\windows_private.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\splitting_filter.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\three_band_filter_bank.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\block_mean_calculator.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\delay_estimator.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\delay_estimator_internal.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\delay_estimator_wrapper.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft_tables_common.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft_tables_neon_sse2.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\system_wrappers\include\asm_defines.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\system_wrappers\include\compile_assert_c.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\system_wrappers\include\cpu_features_wrapper.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\system_wrappers\include\metrics.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="webrtc_dsp\webrtc\typedefs.h">
|
||||||
|
<Filter>webrtc_dsp</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="os\windows\CXWrapper.h">
|
||||||
|
<Filter>windows</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="Buffers.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
|
@ -0,0 +1,916 @@
|
||||||
|
# GYP project file for TDesktop
|
||||||
|
|
||||||
|
{
|
||||||
|
'targets': [
|
||||||
|
{
|
||||||
|
'target_name': 'libtgvoip',
|
||||||
|
'type': 'static_library',
|
||||||
|
'dependencies': [],
|
||||||
|
'defines': [
|
||||||
|
'WEBRTC_APM_DEBUG_DUMP=0',
|
||||||
|
'TGVOIP_USE_DESKTOP_DSP',
|
||||||
|
'WEBRTC_NS_FLOAT',
|
||||||
|
],
|
||||||
|
'variables': {
|
||||||
|
'tgvoip_src_loc': '.',
|
||||||
|
'official_build_target%': '',
|
||||||
|
'linux_path_opus_include%': '<(DEPTH)/../../../Libraries/opus/include',
|
||||||
|
},
|
||||||
|
'include_dirs': [
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp',
|
||||||
|
'<(linux_path_opus_include)',
|
||||||
|
],
|
||||||
|
'direct_dependent_settings': {
|
||||||
|
'include_dirs': [
|
||||||
|
'<(tgvoip_src_loc)',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'export_dependent_settings': [],
|
||||||
|
'sources': [
|
||||||
|
'<(tgvoip_src_loc)/BlockingQueue.cpp',
|
||||||
|
'<(tgvoip_src_loc)/BlockingQueue.h',
|
||||||
|
'<(tgvoip_src_loc)/Buffers.cpp',
|
||||||
|
'<(tgvoip_src_loc)/Buffers.h',
|
||||||
|
'<(tgvoip_src_loc)/CongestionControl.cpp',
|
||||||
|
'<(tgvoip_src_loc)/CongestionControl.h',
|
||||||
|
'<(tgvoip_src_loc)/EchoCanceller.cpp',
|
||||||
|
'<(tgvoip_src_loc)/EchoCanceller.h',
|
||||||
|
'<(tgvoip_src_loc)/JitterBuffer.cpp',
|
||||||
|
'<(tgvoip_src_loc)/JitterBuffer.h',
|
||||||
|
'<(tgvoip_src_loc)/logging.cpp',
|
||||||
|
'<(tgvoip_src_loc)/logging.h',
|
||||||
|
'<(tgvoip_src_loc)/MediaStreamItf.cpp',
|
||||||
|
'<(tgvoip_src_loc)/MediaStreamItf.h',
|
||||||
|
'<(tgvoip_src_loc)/OpusDecoder.cpp',
|
||||||
|
'<(tgvoip_src_loc)/OpusDecoder.h',
|
||||||
|
'<(tgvoip_src_loc)/OpusEncoder.cpp',
|
||||||
|
'<(tgvoip_src_loc)/OpusEncoder.h',
|
||||||
|
'<(tgvoip_src_loc)/threading.h',
|
||||||
|
'<(tgvoip_src_loc)/VoIPController.cpp',
|
||||||
|
'<(tgvoip_src_loc)/VoIPGroupController.cpp',
|
||||||
|
'<(tgvoip_src_loc)/VoIPController.h',
|
||||||
|
'<(tgvoip_src_loc)/PrivateDefines.h',
|
||||||
|
'<(tgvoip_src_loc)/VoIPServerConfig.cpp',
|
||||||
|
'<(tgvoip_src_loc)/VoIPServerConfig.h',
|
||||||
|
'<(tgvoip_src_loc)/audio/AudioInput.cpp',
|
||||||
|
'<(tgvoip_src_loc)/audio/AudioInput.h',
|
||||||
|
'<(tgvoip_src_loc)/audio/AudioOutput.cpp',
|
||||||
|
'<(tgvoip_src_loc)/audio/AudioOutput.h',
|
||||||
|
'<(tgvoip_src_loc)/audio/Resampler.cpp',
|
||||||
|
'<(tgvoip_src_loc)/audio/Resampler.h',
|
||||||
|
'<(tgvoip_src_loc)/NetworkSocket.cpp',
|
||||||
|
'<(tgvoip_src_loc)/NetworkSocket.h',
|
||||||
|
'<(tgvoip_src_loc)/PacketReassembler.cpp',
|
||||||
|
'<(tgvoip_src_loc)/PacketReassembler.h',
|
||||||
|
'<(tgvoip_src_loc)/MessageThread.cpp',
|
||||||
|
'<(tgvoip_src_loc)/MessageThread.h',
|
||||||
|
'<(tgvoip_src_loc)/audio/AudioIO.cpp',
|
||||||
|
'<(tgvoip_src_loc)/audio/AudioIO.h',
|
||||||
|
'<(tgvoip_src_loc)/video/ScreamCongestionController.cpp',
|
||||||
|
'<(tgvoip_src_loc)/video/ScreamCongestionController.h',
|
||||||
|
'<(tgvoip_src_loc)/video/VideoSource.cpp',
|
||||||
|
'<(tgvoip_src_loc)/video/VideoSource.h',
|
||||||
|
'<(tgvoip_src_loc)/video/VideoRenderer.cpp',
|
||||||
|
'<(tgvoip_src_loc)/video/VideoRenderer.h',
|
||||||
|
'<(tgvoip_src_loc)/json11.cpp',
|
||||||
|
'<(tgvoip_src_loc)/json11.hpp',
|
||||||
|
|
||||||
|
# Windows
|
||||||
|
'<(tgvoip_src_loc)/os/windows/NetworkSocketWinsock.cpp',
|
||||||
|
'<(tgvoip_src_loc)/os/windows/NetworkSocketWinsock.h',
|
||||||
|
'<(tgvoip_src_loc)/os/windows/AudioInputWave.cpp',
|
||||||
|
'<(tgvoip_src_loc)/os/windows/AudioInputWave.h',
|
||||||
|
'<(tgvoip_src_loc)/os/windows/AudioOutputWave.cpp',
|
||||||
|
'<(tgvoip_src_loc)/os/windows/AudioOutputWave.h',
|
||||||
|
'<(tgvoip_src_loc)/os/windows/AudioOutputWASAPI.cpp',
|
||||||
|
'<(tgvoip_src_loc)/os/windows/AudioOutputWASAPI.h',
|
||||||
|
'<(tgvoip_src_loc)/os/windows/AudioInputWASAPI.cpp',
|
||||||
|
'<(tgvoip_src_loc)/os/windows/AudioInputWASAPI.h',
|
||||||
|
'<(tgvoip_src_loc)/os/windows/WindowsSpecific.cpp',
|
||||||
|
'<(tgvoip_src_loc)/os/windows/WindowsSpecific.h',
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
'<(tgvoip_src_loc)/os/darwin/AudioInputAudioUnit.cpp',
|
||||||
|
'<(tgvoip_src_loc)/os/darwin/AudioInputAudioUnit.h',
|
||||||
|
'<(tgvoip_src_loc)/os/darwin/AudioOutputAudioUnit.cpp',
|
||||||
|
'<(tgvoip_src_loc)/os/darwin/AudioOutputAudioUnit.h',
|
||||||
|
'<(tgvoip_src_loc)/os/darwin/AudioInputAudioUnitOSX.cpp',
|
||||||
|
'<(tgvoip_src_loc)/os/darwin/AudioInputAudioUnitOSX.h',
|
||||||
|
'<(tgvoip_src_loc)/os/darwin/AudioOutputAudioUnitOSX.cpp',
|
||||||
|
'<(tgvoip_src_loc)/os/darwin/AudioOutputAudioUnitOSX.h',
|
||||||
|
'<(tgvoip_src_loc)/os/darwin/AudioUnitIO.cpp',
|
||||||
|
'<(tgvoip_src_loc)/os/darwin/AudioUnitIO.h',
|
||||||
|
'<(tgvoip_src_loc)/os/darwin/DarwinSpecific.mm',
|
||||||
|
'<(tgvoip_src_loc)/os/darwin/DarwinSpecific.h',
|
||||||
|
|
||||||
|
# Linux
|
||||||
|
'<(tgvoip_src_loc)/os/linux/AudioInputALSA.cpp',
|
||||||
|
'<(tgvoip_src_loc)/os/linux/AudioInputALSA.h',
|
||||||
|
'<(tgvoip_src_loc)/os/linux/AudioOutputALSA.cpp',
|
||||||
|
'<(tgvoip_src_loc)/os/linux/AudioOutputALSA.h',
|
||||||
|
'<(tgvoip_src_loc)/os/linux/AudioOutputPulse.cpp',
|
||||||
|
'<(tgvoip_src_loc)/os/linux/AudioOutputPulse.h',
|
||||||
|
'<(tgvoip_src_loc)/os/linux/AudioInputPulse.cpp',
|
||||||
|
'<(tgvoip_src_loc)/os/linux/AudioInputPulse.h',
|
||||||
|
'<(tgvoip_src_loc)/os/linux/AudioPulse.cpp',
|
||||||
|
'<(tgvoip_src_loc)/os/linux/AudioPulse.h',
|
||||||
|
|
||||||
|
# POSIX
|
||||||
|
'<(tgvoip_src_loc)/os/posix/NetworkSocketPosix.cpp',
|
||||||
|
'<(tgvoip_src_loc)/os/posix/NetworkSocketPosix.h',
|
||||||
|
|
||||||
|
# WebRTC APM
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/system_wrappers/include/field_trial.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/system_wrappers/include/cpu_features_wrapper.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/system_wrappers/include/asm_defines.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/system_wrappers/include/metrics.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/system_wrappers/include/compile_assert_c.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/system_wrappers/source/field_trial.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/system_wrappers/source/metrics.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/system_wrappers/source/cpu_features.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/typedefs.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/absl/strings/internal/memutil.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/absl/strings/internal/memutil.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/absl/strings/string_view.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/absl/strings/ascii.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/absl/strings/ascii.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/absl/strings/string_view.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/absl/types/optional.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/absl/types/bad_optional_access.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/absl/types/bad_optional_access.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/absl/types/optional.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/absl/memory/memory.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/absl/meta/type_traits.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/absl/algorithm/algorithm.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/absl/container/inlined_vector.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/absl/base/policy_checks.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/absl/base/port.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/absl/base/config.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/absl/base/internal/raw_logging.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/absl/base/internal/throw_delegate.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/absl/base/internal/invoke.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/absl/base/internal/inline_variable.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/absl/base/internal/atomic_hook.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/absl/base/internal/identity.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/absl/base/internal/raw_logging.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/absl/base/internal/throw_delegate.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/absl/base/attributes.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/absl/base/macros.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/absl/base/optimization.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/absl/base/log_severity.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/absl/utility/utility.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/string_to_number.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/constructormagic.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/race_checker.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/strings/string_builder.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/strings/string_builder.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/event_tracer.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/stringencode.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/memory/aligned_malloc.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/memory/aligned_malloc.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/timeutils.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/event.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/ignore_wundef.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/stringutils.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/arraysize.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/platform_file.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/swap_queue.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/string_to_number.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/trace_event.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/checks.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/deprecation.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/thread_checker_impl.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/sanitizer.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/scoped_ref_ptr.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/logging.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/timeutils.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/atomicops.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/stringencode.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/stringutils.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/checks.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/numerics/safe_minmax.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/numerics/safe_conversions.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/numerics/safe_conversions_impl.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/numerics/safe_compare.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/system/unused.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/system/inline.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/system/ignore_warnings.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/system/asm_defines.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/system/rtc_export.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/system/arch.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/platform_thread.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/platform_thread.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/platform_thread_types.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/protobuf_utils.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/thread_annotations.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/gtest_prod_util.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/function_view.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/criticalsection.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/criticalsection.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/platform_thread_types.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/refcount.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/event.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/thread_checker_impl.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/event_tracer.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/compile_assert_c.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/logging_webrtc.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/type_traits.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/platform_file.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/refcounter.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/logging_mac.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/thread_checker.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/race_checker.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/refcountedobject.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/third_party/rnnoise/src/rnn_vad_weights.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/third_party/rnnoise/src/rnn_activations.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/third_party/rnnoise/src/kiss_fft.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/third_party/rnnoise/src/kiss_fft.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/third_party/rnnoise/src/rnn_vad_weights.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/api/audio/audio_frame.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/api/audio/echo_canceller3_config.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/api/audio/echo_control.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/api/audio/audio_frame.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/api/audio/echo_canceller3_config.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/api/audio/echo_canceller3_factory.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/api/audio/echo_canceller3_factory.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/api/array_view.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/third_party/fft/fft.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/third_party/fft/fft.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/bandwidth_info.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/include/isac.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_estimator.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb16_tables.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_gain_tables.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/arith_routines_logist.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/os_specific_inline.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/filterbanks.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/entropy_coding.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/isac_vad.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/settings.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/transform.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb12_tables.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/arith_routines.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/crc.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_filter.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/encode_lpc_swb.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/filter_functions.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/decode.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lattice.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/intialize.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_tables.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_gain_swb_tables.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/bandwidth_estimator.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/isac_float_type.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_lag_tables.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/encode.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_analysis.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/spectrum_ar_model_tables.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/arith_routines_hist.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/codec.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_gain_tables.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb16_tables.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_estimator.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/entropy_coding.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/isac_vad.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/structs.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/filter_functions.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/encode_lpc_swb.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_filter.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/arith_routines.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/crc.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb12_tables.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_analysis.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/decode_bwe.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/spectrum_ar_model_tables.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/bandwidth_estimator.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_lag_tables.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/isac.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_gain_swb_tables.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_tables.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/rms_level.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/echo_detector/moving_max.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/echo_detector/circular_buffer.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/echo_detector/normalized_covariance_estimator.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/echo_detector/normalized_covariance_estimator.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/echo_detector/moving_max.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/echo_detector/circular_buffer.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/echo_detector/mean_variance_estimator.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/echo_detector/mean_variance_estimator.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/gain_control_for_experimental_agc.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/splitting_filter.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/gain_control_impl.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/rms_level.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/ns/ns_core.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/ns/nsx_core.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/ns/noise_suppression_x.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/ns/nsx_core_c.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/ns/defines.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/ns/noise_suppression.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/ns/ns_core.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/ns/nsx_core.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/ns/windows_private.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/ns/noise_suppression_x.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/ns/noise_suppression.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/ns/nsx_defines.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/residual_echo_detector.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/audio_processing_impl.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/audio_buffer.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/typing_detection.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/render_queue_item_verifier.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/include/audio_generator.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/include/config.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/include/audio_frame_view.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/include/mock_audio_processing.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/include/gain_control.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/include/audio_generator_factory.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/include/audio_processing_statistics.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/include/audio_generator_factory.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/include/aec_dump.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/include/aec_dump.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/include/audio_processing_statistics.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/include/audio_processing.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/include/audio_processing.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/include/config.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/interpolated_gain_curve.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/biquad_filter.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/interpolated_gain_curve.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/agc2_common.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/agc2_testing_common.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/adaptive_mode_level_estimator.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/gain_applier.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/signal_classifier.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/adaptive_agc.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/adaptive_digital_gain_applier.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/limiter.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/saturation_protector.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/vector_float_frame.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/rnn_vad/sequence_buffer.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/rnn_vad/rnn.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/rnn_vad/rnn.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/rnn_vad/test_utils.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/rnn_vad/pitch_info.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/rnn_vad/lp_residual.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/rnn_vad/ring_buffer.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/rnn_vad/symmetric_matrix_buffer.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/rnn_vad/spectral_features.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/rnn_vad/features_extraction.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/rnn_vad/common.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/rnn_vad/fft_util.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/rnn_vad/spectral_features.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/rnn_vad/pitch_search.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/rnn_vad/pitch_search.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/rnn_vad/features_extraction.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/rnn_vad/fft_util.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/rnn_vad/lp_residual.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/fixed_gain_controller.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/adaptive_mode_level_estimator_agc.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/vector_float_frame.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/down_sampler.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/noise_level_estimator.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/agc2_testing_common.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/fixed_digital_level_estimator.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/fixed_gain_controller.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/saturation_protector.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/vad_with_level.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/limiter_db_gain_curve.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/agc2_common.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/adaptive_mode_level_estimator_agc.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/adaptive_digital_gain_applier.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/vad_with_level.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/limiter_db_gain_curve.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/fixed_digital_level_estimator.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/adaptive_agc.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/gain_applier.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/down_sampler.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/noise_level_estimator.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/signal_classifier.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/noise_spectrum_estimator.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/compute_interpolated_gain_curve.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/compute_interpolated_gain_curve.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/biquad_filter.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/noise_spectrum_estimator.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/limiter.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc2/adaptive_mode_level_estimator.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/transient/moving_moments.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/transient/transient_detector.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/transient/wpd_tree.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/transient/transient_suppressor.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/transient/daubechies_8_wavelet_coeffs.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/transient/common.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/transient/wpd_node.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/transient/moving_moments.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/transient/wpd_tree.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/transient/wpd_node.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/transient/transient_suppressor.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/transient/transient_detector.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/transient/dyadic_decimator.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/low_cut_filter.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/noise_suppression_impl.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/level_estimator_impl.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/three_band_filter_bank.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec/echo_cancellation.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec/aec_resampler.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec/aec_resampler.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec/echo_cancellation.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec/aec_core.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec/aec_core.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec/aec_core_optimized_methods.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec/aec_core_sse2.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec/aec_common.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/voice_detection_impl.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/voice_detection_impl.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/echo_cancellation_impl.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/gain_control_for_experimental_agc.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc/agc.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc/loudness_histogram.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc/agc_manager_direct.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc/legacy/analog_agc.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc/legacy/gain_control.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc/legacy/digital_agc.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc/legacy/analog_agc.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc/legacy/digital_agc.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc/utility.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc/mock_agc.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc/loudness_histogram.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc/gain_map_internal.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc/utility.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc/agc_manager_direct.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/agc/agc.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/common.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/audio_processing_impl.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/audio_buffer.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/echo_control_mobile_impl.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/splitting_filter.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/low_cut_filter.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/audio_generator/file_audio_generator.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/audio_generator/file_audio_generator.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/gain_controller2.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/three_band_filter_bank.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/residual_echo_detector.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/echo_cancellation_impl.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/noise_suppression_impl.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/level_estimator_impl.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/gain_controller2.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aecm/aecm_core.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aecm/aecm_defines.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aecm/aecm_core.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aecm/aecm_core_c.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aecm/echo_control_mobile.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aecm/echo_control_mobile.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/render_reverb_model.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/downsampled_render_buffer.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/subtractor_output_analyzer.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/reverb_model_fallback.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/residual_echo_estimator.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/shadow_filter_update_gain.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/echo_remover_metrics.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/matched_filter_lag_aggregator.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/render_delay_buffer2.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/aec_state.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/suppression_filter.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/echo_path_variability.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/frame_blocker.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/subtractor.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/block_delay_buffer.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/adaptive_fir_filter.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/cascaded_biquad_filter.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/matched_filter.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/subtractor_output.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/render_signal_analyzer.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/aec3_fft.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/aec3_fft.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/echo_remover_metrics.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/fullband_erle_estimator.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/suppression_filter.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/block_processor.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/filter_analyzer.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/subtractor.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/echo_path_delay_estimator.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/subband_erle_estimator.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/render_delay_controller_metrics.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/render_delay_buffer.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/block_processor_metrics.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/vector_buffer.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/erl_estimator.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/aec_state.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/adaptive_fir_filter.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/fft_data.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/render_delay_controller.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/skew_estimator.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/render_delay_controller_metrics.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/comfort_noise_generator.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/echo_path_delay_estimator.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/erl_estimator.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/echo_remover.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/block_framer.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/erle_estimator.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/reverb_model.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/cascaded_biquad_filter.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/matrix_buffer.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/render_buffer.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/reverb_model_estimator.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/subtractor_output.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/stationarity_estimator.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/render_signal_analyzer.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/echo_path_variability.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/moving_average.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/render_reverb_model.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/subtractor_output_analyzer.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/suppression_gain.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/echo_audibility.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/block_processor_metrics.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/render_delay_controller.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/suppression_gain.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/moving_average.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/erle_estimator.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/subband_erle_estimator.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/reverb_model_estimator.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/aec3_common.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/residual_echo_estimator.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/block_processor.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/fullband_erle_estimator.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/matched_filter.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/stationarity_estimator.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/echo_canceller3.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/skew_estimator.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/reverb_decay_estimator.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/render_delay_controller2.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/render_buffer.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/suppression_gain_limiter.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/main_filter_update_gain.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/echo_remover.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/reverb_model_fallback.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/downsampled_render_buffer.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/vector_buffer.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/matrix_buffer.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/reverb_frequency_response.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/echo_audibility.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/fft_buffer.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/block_processor2.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/echo_canceller3.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/block_delay_buffer.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/aec3_common.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/fft_buffer.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/vector_math.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/decimator.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/frame_blocker.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/block_framer.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/suppression_gain_limiter.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/delay_estimate.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/comfort_noise_generator.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/reverb_model.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/main_filter_update_gain.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/matched_filter_lag_aggregator.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/shadow_filter_update_gain.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/filter_analyzer.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/reverb_decay_estimator.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/reverb_frequency_response.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/decimator.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec3/render_delay_buffer.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/echo_control_mobile_impl.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/gain_control_impl.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/typing_detection.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/logging/apm_data_dumper.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/logging/apm_data_dumper.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/vad/voice_activity_detector.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/vad/standalone_vad.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/vad/vad_audio_proc_internal.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/vad/pitch_internal.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/vad/vad_circular_buffer.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/vad/vad_circular_buffer.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/vad/pitch_based_vad.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/vad/vad_audio_proc.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/vad/pole_zero_filter.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/vad/pole_zero_filter.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/vad/pitch_based_vad.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/vad/gmm.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/vad/common.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/vad/vad_audio_proc.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/vad/voice_gmm_tables.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/vad/noise_gmm_tables.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/vad/pitch_internal.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/vad/gmm.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/vad/standalone_vad.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/vad/voice_activity_detector.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/utility/delay_estimator_internal.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/utility/ooura_fft.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/utility/ooura_fft.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/utility/delay_estimator_wrapper.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/utility/ooura_fft_sse2.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/utility/delay_estimator.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/utility/block_mean_calculator.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/utility/block_mean_calculator.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/utility/delay_estimator.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/utility/ooura_fft_tables_common.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/utility/delay_estimator_wrapper.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/mocks/mock_smoothing_filter.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/wav_file.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/window_generator.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/channel_buffer.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/fir_filter_factory.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/sparse_fir_filter.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/fir_filter_sse.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/window_generator.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/ring_buffer.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/fir_filter.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/include/audio_util.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/wav_header.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/real_fourier_ooura.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/audio_util.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/real_fourier_ooura.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/fir_filter_sse.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/smoothing_filter.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/resampler/push_sinc_resampler.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/resampler/sinc_resampler.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/resampler/resampler.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/resampler/sinc_resampler_sse.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/resampler/include/push_resampler.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/resampler/include/resampler.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/resampler/push_sinc_resampler.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/resampler/push_resampler.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/resampler/sinusoidal_linear_chirp_source.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/resampler/sinc_resampler.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/resampler/sinusoidal_linear_chirp_source.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/fir_filter_factory.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/audio_converter.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/wav_file.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/third_party/fft4g/fft4g.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/third_party/fft4g/fft4g.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/audio_converter.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/real_fourier.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/channel_buffer.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/real_fourier.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/sparse_fir_filter.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/smoothing_filter.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/fir_filter_c.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/ring_buffer.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/fir_filter_c.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/complex_fft_tables.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/complex_fft.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/filter_ma_fast_q12.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/levinson_durbin.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/dot_product_with_scale.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/auto_corr_to_refl_coef.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/resample_by_2_internal.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/energy.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/sqrt_of_one_minus_x_squared.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/downsample_fast.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/splitting_filter1.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/filter_ar_fast_q12.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/spl_init.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/lpc_to_refl_coef.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/cross_correlation.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/include/signal_processing_library.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/include/real_fft.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/include/spl_inl.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/division_operations.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/auto_correlation.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/get_scaling_square.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/dot_product_with_scale.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/resample_by_2_internal.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/resample.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/min_max_operations.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/refl_coef_to_lpc.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/filter_ar.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/vector_scaling_operations.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/resample_fractional.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/real_fft.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/ilbc_specific_functions.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/complex_bit_reverse.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/randomization_functions.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/copy_set_operations.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/resample_by_2.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/get_hanning_window.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/resample_48khz.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/spl_inl.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/spl_sqrt.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/wav_header.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/vad/vad_sp.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/vad/vad.cc',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/vad/webrtc_vad.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/vad/vad_core.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/vad/include/vad.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/vad/include/webrtc_vad.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/vad/vad_gmm.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/vad/vad_filterbank.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/vad/vad_core.c',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/vad/vad_sp.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/vad/vad_filterbank.h',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/common_audio/vad/vad_gmm.c',
|
||||||
|
|
||||||
|
# ARM/NEON sources
|
||||||
|
# TODO check if there's a good way to make these compile with ARM ports of TDesktop
|
||||||
|
#'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/ns/nsx_core_neon.c',
|
||||||
|
#'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aec/aec_core_neon.cc',
|
||||||
|
#'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/aecm/aecm_core_neon.cc',
|
||||||
|
#'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/utility/ooura_fft_tables_neon_sse2.h',
|
||||||
|
#'<(tgvoip_src_loc)/webrtc_dsp/modules/audio_processing/utility/ooura_fft_neon.cc',
|
||||||
|
#'<(tgvoip_src_loc)/webrtc_dsp/common_audio/fir_filter_neon.cc',
|
||||||
|
#'<(tgvoip_src_loc)/webrtc_dsp/common_audio/resampler/sinc_resampler_neon.cc',
|
||||||
|
#'<(tgvoip_src_loc)/webrtc_dsp/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor_arm.S',
|
||||||
|
#'<(tgvoip_src_loc)/webrtc_dsp/common_audio/fir_filter_neon.h',
|
||||||
|
#'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/downsample_fast_neon.c',
|
||||||
|
#'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/complex_bit_reverse_arm.S',
|
||||||
|
#'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/include/spl_inl_armv7.h',
|
||||||
|
#'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/min_max_operations_neon.c',
|
||||||
|
#'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/cross_correlation_neon.c',
|
||||||
|
#'<(tgvoip_src_loc)/webrtc_dsp/common_audio/signal_processing/filter_ar_fast_q12_armv7.S',
|
||||||
|
|
||||||
|
|
||||||
|
],
|
||||||
|
'libraries': [],
|
||||||
|
'configurations': {
|
||||||
|
'Debug': {},
|
||||||
|
'Release': {},
|
||||||
|
},
|
||||||
|
'conditions': [
|
||||||
|
[
|
||||||
|
'"<(OS)" != "win"', {
|
||||||
|
'sources/': [['exclude', '<(tgvoip_src_loc)/os/windows/']],
|
||||||
|
}, {
|
||||||
|
'sources/': [['exclude', '<(tgvoip_src_loc)/os/posix/']],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'"<(OS)" != "mac"', {
|
||||||
|
'sources/': [['exclude', '<(tgvoip_src_loc)/os/darwin/']],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'"<(OS)" != "linux"', {
|
||||||
|
'sources/': [['exclude', '<(tgvoip_src_loc)/os/linux/']],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'"<(OS)" == "mac"', {
|
||||||
|
'xcode_settings': {
|
||||||
|
'CLANG_CXX_LANGUAGE_STANDARD': 'c++11',
|
||||||
|
'ALWAYS_SEARCH_USER_PATHS': 'NO',
|
||||||
|
},
|
||||||
|
'defines': [
|
||||||
|
'WEBRTC_POSIX',
|
||||||
|
'WEBRTC_MAC',
|
||||||
|
'TARGET_OS_OSX',
|
||||||
|
],
|
||||||
|
'sources': [
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/logging_mac.mm',
|
||||||
|
'<(tgvoip_src_loc)/webrtc_dsp/rtc_base/logging_mac.h',
|
||||||
|
],
|
||||||
|
'conditions': [
|
||||||
|
[ '"<(official_build_target)" == "mac32"', {
|
||||||
|
'xcode_settings': {
|
||||||
|
'MACOSX_DEPLOYMENT_TARGET': '10.6',
|
||||||
|
'OTHER_CPLUSPLUSFLAGS': [ '-nostdinc++' ],
|
||||||
|
},
|
||||||
|
'include_dirs': [
|
||||||
|
'/usr/local/macold/include/c++/v1',
|
||||||
|
'<(DEPTH)/../../../Libraries/macold/openssl/include',
|
||||||
|
],
|
||||||
|
'defines': [
|
||||||
|
'TARGET_OSX32',
|
||||||
|
],
|
||||||
|
}, {
|
||||||
|
'xcode_settings': {
|
||||||
|
'MACOSX_DEPLOYMENT_TARGET': '10.8',
|
||||||
|
'CLANG_CXX_LIBRARY': 'libc++',
|
||||||
|
},
|
||||||
|
'include_dirs': [
|
||||||
|
'<(DEPTH)/../../../Libraries/openssl/include',
|
||||||
|
],
|
||||||
|
'direct_dependent_settings': {
|
||||||
|
'linkflags': [
|
||||||
|
'-framework VideoToolbox',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'sources': [
|
||||||
|
'<(tgvoip_src_loc)/os/darwin/TGVVideoRenderer.mm',
|
||||||
|
'<(tgvoip_src_loc)/os/darwin/TGVVideoRenderer.h',
|
||||||
|
'<(tgvoip_src_loc)/os/darwin/TGVVideoSource.mm',
|
||||||
|
'<(tgvoip_src_loc)/os/darwin/TGVVideoSource.h',
|
||||||
|
'<(tgvoip_src_loc)/os/darwin/VideoToolboxEncoderSource.mm',
|
||||||
|
'<(tgvoip_src_loc)/os/darwin/VideoToolboxEncoderSource.h',
|
||||||
|
'<(tgvoip_src_loc)/os/darwin/SampleBufferDisplayLayerRenderer.mm',
|
||||||
|
'<(tgvoip_src_loc)/os/darwin/SampleBufferDisplayLayerRenderer.h',
|
||||||
|
],
|
||||||
|
}],
|
||||||
|
['"<(official_build_target)" == "macstore"', {
|
||||||
|
'defines': [
|
||||||
|
'TGVOIP_NO_OSX_PRIVATE_API',
|
||||||
|
],
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'"<(OS)" == "win"', {
|
||||||
|
'msbuild_toolset': 'v141',
|
||||||
|
'defines': [
|
||||||
|
'NOMINMAX',
|
||||||
|
'_USING_V110_SDK71_',
|
||||||
|
'TGVOIP_WINXP_COMPAT',
|
||||||
|
'WEBRTC_WIN',
|
||||||
|
],
|
||||||
|
'libraries': [
|
||||||
|
'winmm',
|
||||||
|
'ws2_32',
|
||||||
|
'kernel32',
|
||||||
|
'user32',
|
||||||
|
],
|
||||||
|
'msvs_cygwin_shell': 0,
|
||||||
|
'msvs_settings': {
|
||||||
|
'VCCLCompilerTool': {
|
||||||
|
'ProgramDataBaseFileName': '$(OutDir)\\$(ProjectName).pdb',
|
||||||
|
'DebugInformationFormat': '3', # Program Database (/Zi)
|
||||||
|
'AdditionalOptions': [
|
||||||
|
'/MP', # Enable multi process build.
|
||||||
|
'/EHsc', # Catch C++ exceptions only, extern C functions never throw a C++ exception.
|
||||||
|
'/wd4068', # Disable "warning C4068: unknown pragma"
|
||||||
|
],
|
||||||
|
'TreatWChar_tAsBuiltInType': 'false',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'msvs_external_builder_build_cmd': [
|
||||||
|
'ninja.exe',
|
||||||
|
'-C',
|
||||||
|
'$(OutDir)',
|
||||||
|
'-k0',
|
||||||
|
'$(ProjectName)',
|
||||||
|
],
|
||||||
|
'configurations': {
|
||||||
|
'Debug': {
|
||||||
|
'defines': [
|
||||||
|
'_DEBUG',
|
||||||
|
],
|
||||||
|
'include_dirs': [
|
||||||
|
'<(DEPTH)/../../../Libraries/openssl/Debug/include',
|
||||||
|
],
|
||||||
|
'msvs_settings': {
|
||||||
|
'VCCLCompilerTool': {
|
||||||
|
'Optimization': '0', # Disabled (/Od)
|
||||||
|
'RuntimeLibrary': '1', # Multi-threaded Debug (/MTd)
|
||||||
|
'RuntimeTypeInfo': 'true',
|
||||||
|
},
|
||||||
|
'VCLibrarianTool': {
|
||||||
|
'AdditionalOptions': [
|
||||||
|
'/NODEFAULTLIB:LIBCMT'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'Release': {
|
||||||
|
'defines': [
|
||||||
|
'NDEBUG',
|
||||||
|
],
|
||||||
|
'include_dirs': [
|
||||||
|
'<(DEPTH)/../../../Libraries/openssl/Release/include',
|
||||||
|
],
|
||||||
|
'msvs_settings': {
|
||||||
|
'VCCLCompilerTool': {
|
||||||
|
'Optimization': '2', # Maximize Speed (/O2)
|
||||||
|
'InlineFunctionExpansion': '2', # Any suitable (/Ob2)
|
||||||
|
'EnableIntrinsicFunctions': 'true', # Yes (/Oi)
|
||||||
|
'FavorSizeOrSpeed': '1', # Favor fast code (/Ot)
|
||||||
|
'RuntimeLibrary': '0', # Multi-threaded (/MT)
|
||||||
|
'EnableEnhancedInstructionSet': '2', # Streaming SIMD Extensions 2 (/arch:SSE2)
|
||||||
|
'WholeProgramOptimization': 'true', # /GL
|
||||||
|
},
|
||||||
|
'VCLibrarianTool': {
|
||||||
|
'AdditionalOptions': [
|
||||||
|
'/LTCG',
|
||||||
|
]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'"<(OS)" == "linux"', {
|
||||||
|
'defines': [
|
||||||
|
'WEBRTC_POSIX',
|
||||||
|
'WEBRTC_LINUX',
|
||||||
|
],
|
||||||
|
'conditions': [
|
||||||
|
[ '"<!(uname -m)" == "i686"', {
|
||||||
|
'cflags_cc': [
|
||||||
|
'-msse2',
|
||||||
|
],
|
||||||
|
}]
|
||||||
|
],
|
||||||
|
'direct_dependent_settings': {
|
||||||
|
'libraries': [
|
||||||
|
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
7
TMessagesProj/jni/libtgvoip/libtgvoip.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
7
TMessagesProj/jni/libtgvoip/libtgvoip.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "self:libtgvoip.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>FILEHEADER</key>
|
||||||
|
<string>
|
||||||
|
libtgvoip is free and unencumbered public domain software.
|
||||||
|
For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
you should have received with this source code distribution.
|
||||||
|
</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
File diff suppressed because it is too large
Load Diff
7
TMessagesProj/jni/libtgvoip/libtgvoip_osx.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
7
TMessagesProj/jni/libtgvoip/libtgvoip_osx.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "self:libtgvoip.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>IDEDidComputeMac32BitWarning</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>FILEHEADER</key>
|
||||||
|
<string>
|
||||||
|
libtgvoip is free and unencumbered public domain software.
|
||||||
|
For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
you should have received with this source code distribution.
|
||||||
|
</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -0,0 +1,104 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "VoIPController.h"
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
#include <sys/system_properties.h>
|
||||||
|
#elif defined(__linux__)
|
||||||
|
#include <sys/utsname.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <TargetConditionals.h>
|
||||||
|
#include "os/darwin/DarwinSpecific.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FILE* tgvoipLogFile=NULL;
|
||||||
|
|
||||||
|
void tgvoip_log_file_printf(char level, const char* msg, ...){
|
||||||
|
if(tgvoipLogFile){
|
||||||
|
va_list argptr;
|
||||||
|
va_start(argptr, msg);
|
||||||
|
time_t t = time(0);
|
||||||
|
struct tm *now = localtime(&t);
|
||||||
|
fprintf(tgvoipLogFile, "%02d-%02d %02d:%02d:%02d %c: ", now->tm_mon + 1, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec, level);
|
||||||
|
vfprintf(tgvoipLogFile, msg, argptr);
|
||||||
|
fprintf(tgvoipLogFile, "\n");
|
||||||
|
fflush(tgvoipLogFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tgvoip_log_file_write_header(FILE* file){
|
||||||
|
if(file){
|
||||||
|
time_t t = time(0);
|
||||||
|
struct tm *now = localtime(&t);
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#if WINAPI_PARTITION_DESKTOP
|
||||||
|
char systemVersion[64];
|
||||||
|
OSVERSIONINFOA vInfo;
|
||||||
|
vInfo.dwOSVersionInfoSize=sizeof(vInfo);
|
||||||
|
GetVersionExA(&vInfo);
|
||||||
|
snprintf(systemVersion, sizeof(systemVersion), "Windows %d.%d.%d %s", vInfo.dwMajorVersion, vInfo.dwMinorVersion, vInfo.dwBuildNumber, vInfo.szCSDVersion);
|
||||||
|
#else
|
||||||
|
char* systemVersion="Windows RT";
|
||||||
|
#endif
|
||||||
|
#elif defined(__linux__)
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
char systemVersion[128];
|
||||||
|
char sysRel[PROP_VALUE_MAX];
|
||||||
|
char deviceVendor[PROP_VALUE_MAX];
|
||||||
|
char deviceModel[PROP_VALUE_MAX];
|
||||||
|
__system_property_get("ro.build.version.release", sysRel);
|
||||||
|
__system_property_get("ro.product.manufacturer", deviceVendor);
|
||||||
|
__system_property_get("ro.product.model", deviceModel);
|
||||||
|
snprintf(systemVersion, sizeof(systemVersion), "Android %s (%s %s)", sysRel, deviceVendor, deviceModel);
|
||||||
|
#else
|
||||||
|
struct utsname sysname;
|
||||||
|
uname(&sysname);
|
||||||
|
std::string sysver(sysname.sysname);
|
||||||
|
sysver+=" ";
|
||||||
|
sysver+=sysname.release;
|
||||||
|
sysver+=" (";
|
||||||
|
sysver+=sysname.version;
|
||||||
|
sysver+=")";
|
||||||
|
const char* systemVersion=sysver.c_str();
|
||||||
|
#endif
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
char osxVer[128];
|
||||||
|
tgvoip::DarwinSpecific::GetSystemName(osxVer, sizeof(osxVer));
|
||||||
|
char systemVersion[128];
|
||||||
|
#if TARGET_OS_OSX
|
||||||
|
snprintf(systemVersion, sizeof(systemVersion), "OS X %s", osxVer);
|
||||||
|
#elif TARGET_OS_IPHONE
|
||||||
|
snprintf(systemVersion, sizeof(systemVersion), "iOS %s", osxVer);
|
||||||
|
#else
|
||||||
|
snprintf(systemVersion, sizeof(systemVersion), "Unknown Darwin %s", osxVer);
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
const char* systemVersion="Unknown OS";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__aarch64__)
|
||||||
|
const char* cpuArch="ARM64";
|
||||||
|
#elif defined(__arm__) || defined(_M_ARM)
|
||||||
|
const char* cpuArch="ARM";
|
||||||
|
#elif defined(_M_X64) || defined(__x86_64__)
|
||||||
|
const char* cpuArch="x86_64";
|
||||||
|
#elif defined(_M_IX86) || defined(__i386__)
|
||||||
|
const char* cpuArch="x86";
|
||||||
|
#else
|
||||||
|
const char* cpuArch="Unknown CPU";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
fprintf(file, "---------------\nlibtgvoip v" LIBTGVOIP_VERSION " on %s %s\nLog started on %d/%02d/%d at %d:%02d:%02d\n---------------\n", systemVersion, cpuArch, now->tm_mday, now->tm_mon+1, now->tm_year+1900, now->tm_hour, now->tm_min, now->tm_sec);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,106 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef __LOGGING_H
|
||||||
|
#define __LOGGING_H
|
||||||
|
#define LSTR_INT(x) LSTR_DO_INT(x)
|
||||||
|
#define LSTR_DO_INT(x) #x
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <TargetConditionals.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
void tgvoip_log_file_printf(char level, const char* msg, ...);
|
||||||
|
void tgvoip_log_file_write_header(FILE* file);
|
||||||
|
|
||||||
|
#if defined(__ANDROID__)
|
||||||
|
|
||||||
|
#include <android/log.h>
|
||||||
|
|
||||||
|
//#define _LOG_WRAP(...) __BASE_FILE__":"LSTR_INT(__LINE__)": "__VA_ARGS__
|
||||||
|
#define _LOG_WRAP(...) __VA_ARGS__
|
||||||
|
#define TAG "tgvoip"
|
||||||
|
#define LOGV(...) {__android_log_print(ANDROID_LOG_VERBOSE, TAG, _LOG_WRAP(__VA_ARGS__)); tgvoip_log_file_printf('V', __VA_ARGS__);}
|
||||||
|
#define LOGD(...) {__android_log_print(ANDROID_LOG_DEBUG, TAG, _LOG_WRAP(__VA_ARGS__)); tgvoip_log_file_printf('D', __VA_ARGS__);}
|
||||||
|
#define LOGI(...) {__android_log_print(ANDROID_LOG_INFO, TAG, _LOG_WRAP(__VA_ARGS__)); tgvoip_log_file_printf('I', __VA_ARGS__);}
|
||||||
|
#define LOGW(...) {__android_log_print(ANDROID_LOG_WARN, TAG, _LOG_WRAP(__VA_ARGS__)); tgvoip_log_file_printf('W', __VA_ARGS__);}
|
||||||
|
#define LOGE(...) {__android_log_print(ANDROID_LOG_ERROR, TAG, _LOG_WRAP(__VA_ARGS__)); tgvoip_log_file_printf('E', __VA_ARGS__);}
|
||||||
|
|
||||||
|
#elif defined(__APPLE__) && TARGET_OS_IPHONE && defined(TGVOIP_HAVE_TGLOG)
|
||||||
|
|
||||||
|
#include "os/darwin/TGLogWrapper.h"
|
||||||
|
|
||||||
|
#define LOGV(msg, ...) {__tgvoip_call_tglog("V/tgvoip: " msg, ##__VA_ARGS__); tgvoip_log_file_printf('V', msg, ##__VA_ARGS__);}
|
||||||
|
#define LOGD(msg, ...) {__tgvoip_call_tglog("D/tgvoip: " msg, ##__VA_ARGS__); tgvoip_log_file_printf('D', msg, ##__VA_ARGS__);}
|
||||||
|
#define LOGI(msg, ...) {__tgvoip_call_tglog("I/tgvoip: " msg, ##__VA_ARGS__); tgvoip_log_file_printf('I', msg, ##__VA_ARGS__);}
|
||||||
|
#define LOGW(msg, ...) {__tgvoip_call_tglog("W/tgvoip: " msg, ##__VA_ARGS__); tgvoip_log_file_printf('W', msg, ##__VA_ARGS__);}
|
||||||
|
#define LOGE(msg, ...) {__tgvoip_call_tglog("E/tgvoip: " msg, ##__VA_ARGS__); tgvoip_log_file_printf('E', msg, ##__VA_ARGS__);}
|
||||||
|
|
||||||
|
#elif defined(_WIN32) && defined(_DEBUG)
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define _TGVOIP_W32_LOG_PRINT(verb, msg, ...){ char __log_buf[1024]; snprintf(__log_buf, 1024, "%c/tgvoip: " msg "\n", verb, ##__VA_ARGS__); OutputDebugStringA(__log_buf); tgvoip_log_file_printf((char)verb, msg, __VA_ARGS__);}
|
||||||
|
|
||||||
|
#define LOGV(msg, ...) _TGVOIP_W32_LOG_PRINT('V', msg, ##__VA_ARGS__)
|
||||||
|
#define LOGD(msg, ...) _TGVOIP_W32_LOG_PRINT('D', msg, ##__VA_ARGS__)
|
||||||
|
#define LOGI(msg, ...) _TGVOIP_W32_LOG_PRINT('I', msg, ##__VA_ARGS__)
|
||||||
|
#define LOGW(msg, ...) _TGVOIP_W32_LOG_PRINT('W', msg, ##__VA_ARGS__)
|
||||||
|
#define LOGE(msg, ...) _TGVOIP_W32_LOG_PRINT('E', msg, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#ifndef TGVOIP_NO_STDOUT_LOGS
|
||||||
|
#ifndef TGVOIP_NO_STDOUT_COLOR
|
||||||
|
#define _TGVOIP_LOG_PRINT(verb, color, msg, ...) {printf("\033[%dm%c/tgvoip: " msg "\033[0m\n", color, verb, ##__VA_ARGS__); tgvoip_log_file_printf(verb, msg, ##__VA_ARGS__);}
|
||||||
|
#else
|
||||||
|
#define _TGVOIP_LOG_PRINT(verb, color, msg, ...) {printf("%c/tgvoip: " msg "\n", verb, ##__VA_ARGS__); tgvoip_log_file_printf(verb, msg, ##__VA_ARGS__);}
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define _TGVOIP_LOG_PRINT(verb, color, msg, ...) {tgvoip_log_file_printf(verb, msg, ##__VA_ARGS__);}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define LOGV(msg, ...) _TGVOIP_LOG_PRINT('V', 90, msg, ##__VA_ARGS__)
|
||||||
|
#define LOGD(msg, ...) _TGVOIP_LOG_PRINT('D', 37, msg, ##__VA_ARGS__)
|
||||||
|
#define LOGI(msg, ...) _TGVOIP_LOG_PRINT('I', 94, msg, ##__VA_ARGS__)
|
||||||
|
#define LOGW(msg, ...) _TGVOIP_LOG_PRINT('W', 93, msg, ##__VA_ARGS__)
|
||||||
|
#define LOGE(msg, ...) _TGVOIP_LOG_PRINT('E', 91, msg, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(snprintf) && defined(_WIN32) && defined(__cplusplus_winrt)
|
||||||
|
#define snprintf _snprintf
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef TGVOIP_LOG_VERBOSITY
|
||||||
|
#if TGVOIP_LOG_VERBOSITY<5
|
||||||
|
#undef LOGV
|
||||||
|
#define LOGV(msg, ...)
|
||||||
|
#endif
|
||||||
|
#if TGVOIP_LOG_VERBOSITY<4
|
||||||
|
#undef LOGD
|
||||||
|
#define LOGD(msg, ...)
|
||||||
|
#endif
|
||||||
|
#if TGVOIP_LOG_VERBOSITY<3
|
||||||
|
#undef LOGI
|
||||||
|
#define LOGI(msg, ...)
|
||||||
|
#endif
|
||||||
|
#if TGVOIP_LOG_VERBOSITY<2
|
||||||
|
#undef LOGW
|
||||||
|
#define LOGW(msg, ...)
|
||||||
|
#endif
|
||||||
|
#if TGVOIP_LOG_VERBOSITY<1
|
||||||
|
#undef LOGE
|
||||||
|
#define LOGE(msg, ...)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif //__LOGGING_H
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,215 @@
|
||||||
|
#! /bin/sh
|
||||||
|
# Common wrapper for a few potentially missing GNU programs.
|
||||||
|
|
||||||
|
scriptversion=2018-03-07.03; # UTC
|
||||||
|
|
||||||
|
# Copyright (C) 1996-2018 Free Software Foundation, Inc.
|
||||||
|
# Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
|
||||||
|
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
# any later version.
|
||||||
|
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# As a special exception to the GNU General Public License, if you
|
||||||
|
# distribute this file as part of a program that contains a
|
||||||
|
# configuration script generated by Autoconf, you may include it under
|
||||||
|
# the same distribution terms that you use for the rest of that program.
|
||||||
|
|
||||||
|
if test $# -eq 0; then
|
||||||
|
echo 1>&2 "Try '$0 --help' for more information"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
case $1 in
|
||||||
|
|
||||||
|
--is-lightweight)
|
||||||
|
# Used by our autoconf macros to check whether the available missing
|
||||||
|
# script is modern enough.
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
|
||||||
|
--run)
|
||||||
|
# Back-compat with the calling convention used by older automake.
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
|
||||||
|
-h|--h|--he|--hel|--help)
|
||||||
|
echo "\
|
||||||
|
$0 [OPTION]... PROGRAM [ARGUMENT]...
|
||||||
|
|
||||||
|
Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due
|
||||||
|
to PROGRAM being missing or too old.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-h, --help display this help and exit
|
||||||
|
-v, --version output version information and exit
|
||||||
|
|
||||||
|
Supported PROGRAM values:
|
||||||
|
aclocal autoconf autoheader autom4te automake makeinfo
|
||||||
|
bison yacc flex lex help2man
|
||||||
|
|
||||||
|
Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and
|
||||||
|
'g' are ignored when checking the name.
|
||||||
|
|
||||||
|
Send bug reports to <bug-automake@gnu.org>."
|
||||||
|
exit $?
|
||||||
|
;;
|
||||||
|
|
||||||
|
-v|--v|--ve|--ver|--vers|--versi|--versio|--version)
|
||||||
|
echo "missing $scriptversion (GNU Automake)"
|
||||||
|
exit $?
|
||||||
|
;;
|
||||||
|
|
||||||
|
-*)
|
||||||
|
echo 1>&2 "$0: unknown '$1' option"
|
||||||
|
echo 1>&2 "Try '$0 --help' for more information"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Run the given program, remember its exit status.
|
||||||
|
"$@"; st=$?
|
||||||
|
|
||||||
|
# If it succeeded, we are done.
|
||||||
|
test $st -eq 0 && exit 0
|
||||||
|
|
||||||
|
# Also exit now if we it failed (or wasn't found), and '--version' was
|
||||||
|
# passed; such an option is passed most likely to detect whether the
|
||||||
|
# program is present and works.
|
||||||
|
case $2 in --version|--help) exit $st;; esac
|
||||||
|
|
||||||
|
# Exit code 63 means version mismatch. This often happens when the user
|
||||||
|
# tries to use an ancient version of a tool on a file that requires a
|
||||||
|
# minimum version.
|
||||||
|
if test $st -eq 63; then
|
||||||
|
msg="probably too old"
|
||||||
|
elif test $st -eq 127; then
|
||||||
|
# Program was missing.
|
||||||
|
msg="missing on your system"
|
||||||
|
else
|
||||||
|
# Program was found and executed, but failed. Give up.
|
||||||
|
exit $st
|
||||||
|
fi
|
||||||
|
|
||||||
|
perl_URL=https://www.perl.org/
|
||||||
|
flex_URL=https://github.com/westes/flex
|
||||||
|
gnu_software_URL=https://www.gnu.org/software
|
||||||
|
|
||||||
|
program_details ()
|
||||||
|
{
|
||||||
|
case $1 in
|
||||||
|
aclocal|automake)
|
||||||
|
echo "The '$1' program is part of the GNU Automake package:"
|
||||||
|
echo "<$gnu_software_URL/automake>"
|
||||||
|
echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:"
|
||||||
|
echo "<$gnu_software_URL/autoconf>"
|
||||||
|
echo "<$gnu_software_URL/m4/>"
|
||||||
|
echo "<$perl_URL>"
|
||||||
|
;;
|
||||||
|
autoconf|autom4te|autoheader)
|
||||||
|
echo "The '$1' program is part of the GNU Autoconf package:"
|
||||||
|
echo "<$gnu_software_URL/autoconf/>"
|
||||||
|
echo "It also requires GNU m4 and Perl in order to run:"
|
||||||
|
echo "<$gnu_software_URL/m4/>"
|
||||||
|
echo "<$perl_URL>"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
give_advice ()
|
||||||
|
{
|
||||||
|
# Normalize program name to check for.
|
||||||
|
normalized_program=`echo "$1" | sed '
|
||||||
|
s/^gnu-//; t
|
||||||
|
s/^gnu//; t
|
||||||
|
s/^g//; t'`
|
||||||
|
|
||||||
|
printf '%s\n' "'$1' is $msg."
|
||||||
|
|
||||||
|
configure_deps="'configure.ac' or m4 files included by 'configure.ac'"
|
||||||
|
case $normalized_program in
|
||||||
|
autoconf*)
|
||||||
|
echo "You should only need it if you modified 'configure.ac',"
|
||||||
|
echo "or m4 files included by it."
|
||||||
|
program_details 'autoconf'
|
||||||
|
;;
|
||||||
|
autoheader*)
|
||||||
|
echo "You should only need it if you modified 'acconfig.h' or"
|
||||||
|
echo "$configure_deps."
|
||||||
|
program_details 'autoheader'
|
||||||
|
;;
|
||||||
|
automake*)
|
||||||
|
echo "You should only need it if you modified 'Makefile.am' or"
|
||||||
|
echo "$configure_deps."
|
||||||
|
program_details 'automake'
|
||||||
|
;;
|
||||||
|
aclocal*)
|
||||||
|
echo "You should only need it if you modified 'acinclude.m4' or"
|
||||||
|
echo "$configure_deps."
|
||||||
|
program_details 'aclocal'
|
||||||
|
;;
|
||||||
|
autom4te*)
|
||||||
|
echo "You might have modified some maintainer files that require"
|
||||||
|
echo "the 'autom4te' program to be rebuilt."
|
||||||
|
program_details 'autom4te'
|
||||||
|
;;
|
||||||
|
bison*|yacc*)
|
||||||
|
echo "You should only need it if you modified a '.y' file."
|
||||||
|
echo "You may want to install the GNU Bison package:"
|
||||||
|
echo "<$gnu_software_URL/bison/>"
|
||||||
|
;;
|
||||||
|
lex*|flex*)
|
||||||
|
echo "You should only need it if you modified a '.l' file."
|
||||||
|
echo "You may want to install the Fast Lexical Analyzer package:"
|
||||||
|
echo "<$flex_URL>"
|
||||||
|
;;
|
||||||
|
help2man*)
|
||||||
|
echo "You should only need it if you modified a dependency" \
|
||||||
|
"of a man page."
|
||||||
|
echo "You may want to install the GNU Help2man package:"
|
||||||
|
echo "<$gnu_software_URL/help2man/>"
|
||||||
|
;;
|
||||||
|
makeinfo*)
|
||||||
|
echo "You should only need it if you modified a '.texi' file, or"
|
||||||
|
echo "any other file indirectly affecting the aspect of the manual."
|
||||||
|
echo "You might want to install the Texinfo package:"
|
||||||
|
echo "<$gnu_software_URL/texinfo/>"
|
||||||
|
echo "The spurious makeinfo call might also be the consequence of"
|
||||||
|
echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might"
|
||||||
|
echo "want to install GNU make:"
|
||||||
|
echo "<$gnu_software_URL/make/>"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "You might have modified some files without having the proper"
|
||||||
|
echo "tools for further handling them. Check the 'README' file, it"
|
||||||
|
echo "often tells you about the needed prerequisites for installing"
|
||||||
|
echo "this package. You may also peek at any GNU archive site, in"
|
||||||
|
echo "case some other package contains this missing '$1' program."
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
give_advice "$1" | sed -e '1s/^/WARNING: /' \
|
||||||
|
-e '2,$s/^/ /' >&2
|
||||||
|
|
||||||
|
# Propagate the correct exit status (expected to be 127 for a program
|
||||||
|
# not found, 63 for a program that failed due to version mismatch).
|
||||||
|
exit $st
|
||||||
|
|
||||||
|
# Local variables:
|
||||||
|
# eval: (add-hook 'before-save-hook 'time-stamp)
|
||||||
|
# time-stamp-start: "scriptversion="
|
||||||
|
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||||
|
# time-stamp-time-zone: "UTC0"
|
||||||
|
# time-stamp-end: "; # UTC"
|
||||||
|
# End:
|
|
@ -0,0 +1,73 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "AudioInputAndroid.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "../../logging.h"
|
||||||
|
#include "JNIUtilities.h"
|
||||||
|
|
||||||
|
extern JavaVM* sharedJVM;
|
||||||
|
|
||||||
|
using namespace tgvoip;
|
||||||
|
using namespace tgvoip::audio;
|
||||||
|
|
||||||
|
jmethodID AudioInputAndroid::initMethod=NULL;
|
||||||
|
jmethodID AudioInputAndroid::releaseMethod=NULL;
|
||||||
|
jmethodID AudioInputAndroid::startMethod=NULL;
|
||||||
|
jmethodID AudioInputAndroid::stopMethod=NULL;
|
||||||
|
jmethodID AudioInputAndroid::getEnabledEffectsMaskMethod=NULL;
|
||||||
|
jclass AudioInputAndroid::jniClass=NULL;
|
||||||
|
|
||||||
|
AudioInputAndroid::AudioInputAndroid(){
|
||||||
|
jni::DoWithJNI([this](JNIEnv* env){
|
||||||
|
jmethodID ctor=env->GetMethodID(jniClass, "<init>", "(J)V");
|
||||||
|
jobject obj=env->NewObject(jniClass, ctor, (jlong)(intptr_t)this);
|
||||||
|
javaObject=env->NewGlobalRef(obj);
|
||||||
|
|
||||||
|
env->CallVoidMethod(javaObject, initMethod, 48000, 16, 1, 960*2);
|
||||||
|
enabledEffects=(unsigned int)env->CallIntMethod(javaObject, getEnabledEffectsMaskMethod);
|
||||||
|
});
|
||||||
|
running=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioInputAndroid::~AudioInputAndroid(){
|
||||||
|
{
|
||||||
|
MutexGuard guard(mutex);
|
||||||
|
jni::DoWithJNI([this](JNIEnv* env){
|
||||||
|
env->CallVoidMethod(javaObject, releaseMethod);
|
||||||
|
env->DeleteGlobalRef(javaObject);
|
||||||
|
javaObject=NULL;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioInputAndroid::Start(){
|
||||||
|
MutexGuard guard(mutex);
|
||||||
|
jni::DoWithJNI([this](JNIEnv* env){
|
||||||
|
failed=!env->CallBooleanMethod(javaObject, startMethod);
|
||||||
|
});
|
||||||
|
running=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioInputAndroid::Stop(){
|
||||||
|
MutexGuard guard(mutex);
|
||||||
|
running=false;
|
||||||
|
jni::DoWithJNI([this](JNIEnv* env){
|
||||||
|
env->CallVoidMethod(javaObject, stopMethod);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioInputAndroid::HandleCallback(JNIEnv* env, jobject buffer){
|
||||||
|
if(!running)
|
||||||
|
return;
|
||||||
|
unsigned char* buf=(unsigned char*) env->GetDirectBufferAddress(buffer);
|
||||||
|
size_t len=(size_t) env->GetDirectBufferCapacity(buffer);
|
||||||
|
InvokeCallback(buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int AudioInputAndroid::GetEnabledEffects(){
|
||||||
|
return enabledEffects;
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LIBTGVOIP_AUDIOINPUTANDROID_H
|
||||||
|
#define LIBTGVOIP_AUDIOINPUTANDROID_H
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
#include "../../audio/AudioInput.h"
|
||||||
|
#include "../../threading.h"
|
||||||
|
|
||||||
|
namespace tgvoip{ namespace audio{
|
||||||
|
class AudioInputAndroid : public AudioInput{
|
||||||
|
|
||||||
|
public:
|
||||||
|
AudioInputAndroid();
|
||||||
|
virtual ~AudioInputAndroid();
|
||||||
|
virtual void Start();
|
||||||
|
virtual void Stop();
|
||||||
|
void HandleCallback(JNIEnv* env, jobject buffer);
|
||||||
|
unsigned int GetEnabledEffects();
|
||||||
|
static jmethodID initMethod;
|
||||||
|
static jmethodID releaseMethod;
|
||||||
|
static jmethodID startMethod;
|
||||||
|
static jmethodID stopMethod;
|
||||||
|
static jmethodID getEnabledEffectsMaskMethod;
|
||||||
|
static jclass jniClass;
|
||||||
|
|
||||||
|
static constexpr unsigned int EFFECT_AEC=1;
|
||||||
|
static constexpr unsigned int EFFECT_NS=2;
|
||||||
|
|
||||||
|
private:
|
||||||
|
jobject javaObject;
|
||||||
|
bool running;
|
||||||
|
Mutex mutex;
|
||||||
|
unsigned int enabledEffects=0;
|
||||||
|
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
|
||||||
|
#endif //LIBTGVOIP_AUDIOINPUTANDROID_H
|
|
@ -0,0 +1,137 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "AudioInputOpenSLES.h"
|
||||||
|
#include "../../logging.h"
|
||||||
|
#include "OpenSLEngineWrapper.h"
|
||||||
|
|
||||||
|
#define CHECK_SL_ERROR(res, msg) if(res!=SL_RESULT_SUCCESS){ LOGE(msg); return; }
|
||||||
|
#define BUFFER_SIZE 960 // 20 ms
|
||||||
|
|
||||||
|
using namespace tgvoip;
|
||||||
|
using namespace tgvoip::audio;
|
||||||
|
|
||||||
|
unsigned int AudioInputOpenSLES::nativeBufferSize;
|
||||||
|
|
||||||
|
AudioInputOpenSLES::AudioInputOpenSLES(){
|
||||||
|
slEngine=OpenSLEngineWrapper::CreateEngine();
|
||||||
|
|
||||||
|
LOGI("Native buffer size is %u samples", nativeBufferSize);
|
||||||
|
if(nativeBufferSize<BUFFER_SIZE && BUFFER_SIZE % nativeBufferSize!=0){
|
||||||
|
LOGE("20ms is not divisible by native buffer size!!");
|
||||||
|
}else if(nativeBufferSize>BUFFER_SIZE && nativeBufferSize%BUFFER_SIZE!=0){
|
||||||
|
LOGE("native buffer size is not multiple of 20ms!!");
|
||||||
|
nativeBufferSize+=nativeBufferSize%BUFFER_SIZE;
|
||||||
|
}
|
||||||
|
if(nativeBufferSize==BUFFER_SIZE)
|
||||||
|
nativeBufferSize*=2;
|
||||||
|
LOGI("Adjusted native buffer size is %u", nativeBufferSize);
|
||||||
|
|
||||||
|
buffer=(int16_t*)calloc(BUFFER_SIZE, sizeof(int16_t));
|
||||||
|
nativeBuffer=(int16_t*)calloc((size_t) nativeBufferSize, sizeof(int16_t));
|
||||||
|
slRecorderObj=NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioInputOpenSLES::~AudioInputOpenSLES(){
|
||||||
|
//Stop();
|
||||||
|
(*slBufferQueue)->Clear(slBufferQueue);
|
||||||
|
(*slRecorderObj)->Destroy(slRecorderObj);
|
||||||
|
slRecorderObj=NULL;
|
||||||
|
slRecorder=NULL;
|
||||||
|
slBufferQueue=NULL;
|
||||||
|
slEngine=NULL;
|
||||||
|
OpenSLEngineWrapper::DestroyEngine();
|
||||||
|
free(buffer);
|
||||||
|
buffer=NULL;
|
||||||
|
free(nativeBuffer);
|
||||||
|
nativeBuffer=NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioInputOpenSLES::BufferCallback(SLAndroidSimpleBufferQueueItf bq, void *context){
|
||||||
|
((AudioInputOpenSLES*)context)->HandleSLCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioInputOpenSLES::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){
|
||||||
|
assert(slRecorderObj==NULL);
|
||||||
|
SLDataLocator_IODevice loc_dev = {SL_DATALOCATOR_IODEVICE,
|
||||||
|
SL_IODEVICE_AUDIOINPUT,
|
||||||
|
SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};
|
||||||
|
SLDataSource audioSrc = {&loc_dev, NULL};
|
||||||
|
SLDataLocator_AndroidSimpleBufferQueue loc_bq =
|
||||||
|
{SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1};
|
||||||
|
SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM, channels, sampleRate*1000,
|
||||||
|
SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
|
||||||
|
channels==2 ? (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT) : SL_SPEAKER_FRONT_CENTER, SL_BYTEORDER_LITTLEENDIAN};
|
||||||
|
SLDataSink audioSnk = {&loc_bq, &format_pcm};
|
||||||
|
|
||||||
|
const SLInterfaceID id[2] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION};
|
||||||
|
const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
|
||||||
|
SLresult result = (*slEngine)->CreateAudioRecorder(slEngine, &slRecorderObj, &audioSrc, &audioSnk, 2, id, req);
|
||||||
|
CHECK_SL_ERROR(result, "Error creating recorder");
|
||||||
|
|
||||||
|
SLAndroidConfigurationItf recorderConfig;
|
||||||
|
result = (*slRecorderObj)->GetInterface(slRecorderObj, SL_IID_ANDROIDCONFIGURATION, &recorderConfig);
|
||||||
|
SLint32 streamType = SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION;
|
||||||
|
result = (*recorderConfig)->SetConfiguration(recorderConfig, SL_ANDROID_KEY_RECORDING_PRESET, &streamType, sizeof(SLint32));
|
||||||
|
|
||||||
|
result=(*slRecorderObj)->Realize(slRecorderObj, SL_BOOLEAN_FALSE);
|
||||||
|
CHECK_SL_ERROR(result, "Error realizing recorder");
|
||||||
|
|
||||||
|
result=(*slRecorderObj)->GetInterface(slRecorderObj, SL_IID_RECORD, &slRecorder);
|
||||||
|
CHECK_SL_ERROR(result, "Error getting recorder interface");
|
||||||
|
|
||||||
|
result=(*slRecorderObj)->GetInterface(slRecorderObj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &slBufferQueue);
|
||||||
|
CHECK_SL_ERROR(result, "Error getting buffer queue");
|
||||||
|
|
||||||
|
result=(*slBufferQueue)->RegisterCallback(slBufferQueue, AudioInputOpenSLES::BufferCallback, this);
|
||||||
|
CHECK_SL_ERROR(result, "Error setting buffer queue callback");
|
||||||
|
|
||||||
|
(*slBufferQueue)->Enqueue(slBufferQueue, nativeBuffer, nativeBufferSize*sizeof(int16_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioInputOpenSLES::Start(){
|
||||||
|
SLresult result=(*slRecorder)->SetRecordState(slRecorder, SL_RECORDSTATE_RECORDING);
|
||||||
|
CHECK_SL_ERROR(result, "Error starting record");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioInputOpenSLES::Stop(){
|
||||||
|
SLresult result=(*slRecorder)->SetRecordState(slRecorder, SL_RECORDSTATE_STOPPED);
|
||||||
|
CHECK_SL_ERROR(result, "Error stopping record");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AudioInputOpenSLES::HandleSLCallback(){
|
||||||
|
//SLmillisecond pMsec = 0;
|
||||||
|
//(*slRecorder)->GetPosition(slRecorder, &pMsec);
|
||||||
|
//LOGI("Callback! pos=%lu", pMsec);
|
||||||
|
//InvokeCallback((unsigned char*)buffer, BUFFER_SIZE*sizeof(int16_t));
|
||||||
|
//fwrite(nativeBuffer, 1, nativeBufferSize*2, test);
|
||||||
|
|
||||||
|
if(nativeBufferSize==BUFFER_SIZE){
|
||||||
|
//LOGV("nativeBufferSize==BUFFER_SIZE");
|
||||||
|
InvokeCallback((unsigned char *) nativeBuffer, BUFFER_SIZE*sizeof(int16_t));
|
||||||
|
}else if(nativeBufferSize<BUFFER_SIZE){
|
||||||
|
//LOGV("nativeBufferSize<BUFFER_SIZE");
|
||||||
|
if(positionInBuffer>=BUFFER_SIZE){
|
||||||
|
InvokeCallback((unsigned char *) buffer, BUFFER_SIZE*sizeof(int16_t));
|
||||||
|
positionInBuffer=0;
|
||||||
|
}
|
||||||
|
memcpy(((unsigned char*)buffer)+positionInBuffer*2, nativeBuffer, (size_t)nativeBufferSize*2);
|
||||||
|
positionInBuffer+=nativeBufferSize;
|
||||||
|
}else if(nativeBufferSize>BUFFER_SIZE){
|
||||||
|
//LOGV("nativeBufferSize>BUFFER_SIZE");
|
||||||
|
for(unsigned int offset=0;offset<nativeBufferSize;offset+=BUFFER_SIZE){
|
||||||
|
InvokeCallback(((unsigned char *) nativeBuffer)+offset*2, BUFFER_SIZE*sizeof(int16_t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(*slBufferQueue)->Enqueue(slBufferQueue, nativeBuffer, nativeBufferSize*sizeof(int16_t));
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LIBTGVOIP_AUDIOINPUTOPENSLES_H
|
||||||
|
#define LIBTGVOIP_AUDIOINPUTOPENSLES_H
|
||||||
|
|
||||||
|
#include <SLES/OpenSLES.h>
|
||||||
|
#include <SLES/OpenSLES_Android.h>
|
||||||
|
|
||||||
|
#include "../../audio/AudioInput.h"
|
||||||
|
|
||||||
|
namespace tgvoip{ namespace audio{
|
||||||
|
class AudioInputOpenSLES : public AudioInput{
|
||||||
|
|
||||||
|
public:
|
||||||
|
AudioInputOpenSLES();
|
||||||
|
virtual ~AudioInputOpenSLES();
|
||||||
|
virtual void Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels);
|
||||||
|
virtual void Start();
|
||||||
|
virtual void Stop();
|
||||||
|
|
||||||
|
static unsigned int nativeBufferSize;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void BufferCallback(SLAndroidSimpleBufferQueueItf bq, void *context);
|
||||||
|
void HandleSLCallback();
|
||||||
|
SLEngineItf slEngine;
|
||||||
|
SLObjectItf slRecorderObj;
|
||||||
|
SLRecordItf slRecorder;
|
||||||
|
SLAndroidSimpleBufferQueueItf slBufferQueue;
|
||||||
|
int16_t* buffer;
|
||||||
|
int16_t* nativeBuffer;
|
||||||
|
size_t positionInBuffer;
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
|
||||||
|
#endif //LIBTGVOIP_AUDIOINPUTOPENSLES_H
|
|
@ -0,0 +1,107 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "AudioOutputAndroid.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "../../logging.h"
|
||||||
|
|
||||||
|
extern JavaVM* sharedJVM;
|
||||||
|
|
||||||
|
using namespace tgvoip;
|
||||||
|
using namespace tgvoip::audio;
|
||||||
|
|
||||||
|
jmethodID AudioOutputAndroid::initMethod=NULL;
|
||||||
|
jmethodID AudioOutputAndroid::releaseMethod=NULL;
|
||||||
|
jmethodID AudioOutputAndroid::startMethod=NULL;
|
||||||
|
jmethodID AudioOutputAndroid::stopMethod=NULL;
|
||||||
|
jclass AudioOutputAndroid::jniClass=NULL;
|
||||||
|
|
||||||
|
AudioOutputAndroid::AudioOutputAndroid(){
|
||||||
|
JNIEnv* env=NULL;
|
||||||
|
bool didAttach=false;
|
||||||
|
sharedJVM->GetEnv((void**) &env, JNI_VERSION_1_6);
|
||||||
|
if(!env){
|
||||||
|
sharedJVM->AttachCurrentThread(&env, NULL);
|
||||||
|
didAttach=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
jmethodID ctor=env->GetMethodID(jniClass, "<init>", "(J)V");
|
||||||
|
jobject obj=env->NewObject(jniClass, ctor, (jlong)(intptr_t)this);
|
||||||
|
javaObject=env->NewGlobalRef(obj);
|
||||||
|
|
||||||
|
env->CallVoidMethod(javaObject, initMethod, 48000, 16, 1, 960*2);
|
||||||
|
|
||||||
|
if(didAttach){
|
||||||
|
sharedJVM->DetachCurrentThread();
|
||||||
|
}
|
||||||
|
running=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioOutputAndroid::~AudioOutputAndroid(){
|
||||||
|
JNIEnv* env=NULL;
|
||||||
|
bool didAttach=false;
|
||||||
|
sharedJVM->GetEnv((void**) &env, JNI_VERSION_1_6);
|
||||||
|
if(!env){
|
||||||
|
sharedJVM->AttachCurrentThread(&env, NULL);
|
||||||
|
didAttach=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
env->CallVoidMethod(javaObject, releaseMethod);
|
||||||
|
env->DeleteGlobalRef(javaObject);
|
||||||
|
javaObject=NULL;
|
||||||
|
|
||||||
|
if(didAttach){
|
||||||
|
sharedJVM->DetachCurrentThread();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputAndroid::Start(){
|
||||||
|
JNIEnv* env=NULL;
|
||||||
|
bool didAttach=false;
|
||||||
|
sharedJVM->GetEnv((void**) &env, JNI_VERSION_1_6);
|
||||||
|
if(!env){
|
||||||
|
sharedJVM->AttachCurrentThread(&env, NULL);
|
||||||
|
didAttach=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
env->CallVoidMethod(javaObject, startMethod);
|
||||||
|
|
||||||
|
if(didAttach){
|
||||||
|
sharedJVM->DetachCurrentThread();
|
||||||
|
}
|
||||||
|
running=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputAndroid::Stop(){
|
||||||
|
running=false;
|
||||||
|
JNIEnv* env=NULL;
|
||||||
|
bool didAttach=false;
|
||||||
|
sharedJVM->GetEnv((void**) &env, JNI_VERSION_1_6);
|
||||||
|
if(!env){
|
||||||
|
sharedJVM->AttachCurrentThread(&env, NULL);
|
||||||
|
didAttach=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
env->CallVoidMethod(javaObject, stopMethod);
|
||||||
|
|
||||||
|
if(didAttach){
|
||||||
|
sharedJVM->DetachCurrentThread();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputAndroid::HandleCallback(JNIEnv* env, jbyteArray buffer){
|
||||||
|
if(!running)
|
||||||
|
return;
|
||||||
|
unsigned char* buf=(unsigned char*) env->GetByteArrayElements(buffer, NULL);
|
||||||
|
size_t len=(size_t) env->GetArrayLength(buffer);
|
||||||
|
InvokeCallback(buf, len);
|
||||||
|
env->ReleaseByteArrayElements(buffer, (jbyte *) buf, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool AudioOutputAndroid::IsPlaying(){
|
||||||
|
return running;
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LIBTGVOIP_AUDIOOUTPUTANDROID_H
|
||||||
|
#define LIBTGVOIP_AUDIOOUTPUTANDROID_H
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
#include "../../audio/AudioOutput.h"
|
||||||
|
|
||||||
|
namespace tgvoip{ namespace audio{
|
||||||
|
class AudioOutputAndroid : public AudioOutput{
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
AudioOutputAndroid();
|
||||||
|
virtual ~AudioOutputAndroid();
|
||||||
|
virtual void Start();
|
||||||
|
virtual void Stop();
|
||||||
|
virtual bool IsPlaying() override;
|
||||||
|
void HandleCallback(JNIEnv* env, jbyteArray buffer);
|
||||||
|
static jmethodID initMethod;
|
||||||
|
static jmethodID releaseMethod;
|
||||||
|
static jmethodID startMethod;
|
||||||
|
static jmethodID stopMethod;
|
||||||
|
static jclass jniClass;
|
||||||
|
|
||||||
|
private:
|
||||||
|
jobject javaObject;
|
||||||
|
bool running;
|
||||||
|
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
|
||||||
|
#endif //LIBTGVOIP_AUDIOOUTPUTANDROID_H
|
|
@ -0,0 +1,171 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include "AudioOutputOpenSLES.h"
|
||||||
|
#include "../../logging.h"
|
||||||
|
#include "../../VoIPController.h"
|
||||||
|
#include "OpenSLEngineWrapper.h"
|
||||||
|
#include "AudioInputAndroid.h"
|
||||||
|
|
||||||
|
#define CHECK_SL_ERROR(res, msg) if(res!=SL_RESULT_SUCCESS){ LOGE(msg); failed=true; return; }
|
||||||
|
#define BUFFER_SIZE 960 // 20 ms
|
||||||
|
|
||||||
|
using namespace tgvoip;
|
||||||
|
using namespace tgvoip::audio;
|
||||||
|
|
||||||
|
unsigned int AudioOutputOpenSLES::nativeBufferSize;
|
||||||
|
|
||||||
|
AudioOutputOpenSLES::AudioOutputOpenSLES(){
|
||||||
|
SLresult result;
|
||||||
|
slEngine=OpenSLEngineWrapper::CreateEngine();
|
||||||
|
|
||||||
|
const SLInterfaceID pOutputMixIDs[] = {};
|
||||||
|
const SLboolean pOutputMixRequired[] = {};
|
||||||
|
result = (*slEngine)->CreateOutputMix(slEngine, &slOutputMixObj, 0, pOutputMixIDs, pOutputMixRequired);
|
||||||
|
CHECK_SL_ERROR(result, "Error creating output mix");
|
||||||
|
|
||||||
|
result = (*slOutputMixObj)->Realize(slOutputMixObj, SL_BOOLEAN_FALSE);
|
||||||
|
CHECK_SL_ERROR(result, "Error realizing output mix");
|
||||||
|
|
||||||
|
LOGI("Native buffer size is %u samples", nativeBufferSize);
|
||||||
|
/*if(nativeBufferSize<BUFFER_SIZE && BUFFER_SIZE % nativeBufferSize!=0){
|
||||||
|
LOGE("20ms is not divisible by native buffer size!!");
|
||||||
|
nativeBufferSize=BUFFER_SIZE;
|
||||||
|
}else if(nativeBufferSize>BUFFER_SIZE && nativeBufferSize%BUFFER_SIZE!=0){
|
||||||
|
LOGE("native buffer size is not multiple of 20ms!!");
|
||||||
|
nativeBufferSize+=nativeBufferSize%BUFFER_SIZE;
|
||||||
|
}
|
||||||
|
LOGI("Adjusted native buffer size is %u", nativeBufferSize);*/
|
||||||
|
|
||||||
|
buffer=(int16_t*)calloc(BUFFER_SIZE, sizeof(int16_t));
|
||||||
|
nativeBuffer=(int16_t*)calloc((size_t) nativeBufferSize, sizeof(int16_t));
|
||||||
|
slPlayerObj=NULL;
|
||||||
|
remainingDataSize=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioOutputOpenSLES::~AudioOutputOpenSLES(){
|
||||||
|
if(!stopped)
|
||||||
|
Stop();
|
||||||
|
(*slBufferQueue)->Clear(slBufferQueue);
|
||||||
|
LOGV("destroy slPlayerObj");
|
||||||
|
(*slPlayerObj)->Destroy(slPlayerObj);
|
||||||
|
LOGV("destroy slOutputMixObj");
|
||||||
|
(*slOutputMixObj)->Destroy(slOutputMixObj);
|
||||||
|
OpenSLEngineWrapper::DestroyEngine();
|
||||||
|
free(buffer);
|
||||||
|
free(nativeBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AudioOutputOpenSLES::SetNativeBufferSize(unsigned int size){
|
||||||
|
AudioOutputOpenSLES::nativeBufferSize=size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputOpenSLES::BufferCallback(SLAndroidSimpleBufferQueueItf bq, void *context){
|
||||||
|
((AudioOutputOpenSLES*)context)->HandleSLCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputOpenSLES::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){
|
||||||
|
assert(slPlayerObj==NULL);
|
||||||
|
SLDataLocator_AndroidSimpleBufferQueue locatorBufferQueue =
|
||||||
|
{SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1};
|
||||||
|
SLDataFormat_PCM formatPCM = {SL_DATAFORMAT_PCM, channels, sampleRate*1000,
|
||||||
|
SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
|
||||||
|
channels==2 ? (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT) : SL_SPEAKER_FRONT_CENTER, SL_BYTEORDER_LITTLEENDIAN};
|
||||||
|
SLDataSource audioSrc = {&locatorBufferQueue, &formatPCM};
|
||||||
|
SLDataLocator_OutputMix locatorOutMix = {SL_DATALOCATOR_OUTPUTMIX, slOutputMixObj};
|
||||||
|
SLDataSink audioSnk = {&locatorOutMix, NULL};
|
||||||
|
|
||||||
|
const SLInterfaceID id[2] = {SL_IID_BUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION};
|
||||||
|
const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
|
||||||
|
SLresult result = (*slEngine)->CreateAudioPlayer(slEngine, &slPlayerObj, &audioSrc, &audioSnk, 2, id, req);
|
||||||
|
CHECK_SL_ERROR(result, "Error creating player");
|
||||||
|
|
||||||
|
|
||||||
|
SLAndroidConfigurationItf playerConfig;
|
||||||
|
result = (*slPlayerObj)->GetInterface(slPlayerObj, SL_IID_ANDROIDCONFIGURATION, &playerConfig);
|
||||||
|
SLint32 streamType = SL_ANDROID_STREAM_VOICE;
|
||||||
|
result = (*playerConfig)->SetConfiguration(playerConfig, SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32));
|
||||||
|
|
||||||
|
|
||||||
|
result=(*slPlayerObj)->Realize(slPlayerObj, SL_BOOLEAN_FALSE);
|
||||||
|
CHECK_SL_ERROR(result, "Error realizing player");
|
||||||
|
|
||||||
|
result=(*slPlayerObj)->GetInterface(slPlayerObj, SL_IID_PLAY, &slPlayer);
|
||||||
|
CHECK_SL_ERROR(result, "Error getting player interface");
|
||||||
|
|
||||||
|
result=(*slPlayerObj)->GetInterface(slPlayerObj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &slBufferQueue);
|
||||||
|
CHECK_SL_ERROR(result, "Error getting buffer queue");
|
||||||
|
|
||||||
|
result=(*slBufferQueue)->RegisterCallback(slBufferQueue, AudioOutputOpenSLES::BufferCallback, this);
|
||||||
|
CHECK_SL_ERROR(result, "Error setting buffer queue callback");
|
||||||
|
|
||||||
|
(*slBufferQueue)->Enqueue(slBufferQueue, nativeBuffer, nativeBufferSize*sizeof(int16_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AudioOutputOpenSLES::IsPhone(){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputOpenSLES::EnableLoudspeaker(bool enabled){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputOpenSLES::Start(){
|
||||||
|
stopped=false;
|
||||||
|
SLresult result=(*slPlayer)->SetPlayState(slPlayer, SL_PLAYSTATE_PLAYING);
|
||||||
|
CHECK_SL_ERROR(result, "Error starting player");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputOpenSLES::Stop(){
|
||||||
|
stopped=true;
|
||||||
|
LOGV("Stopping OpenSL output");
|
||||||
|
SLresult result=(*slPlayer)->SetPlayState(slPlayer, SL_PLAYSTATE_PAUSED);
|
||||||
|
CHECK_SL_ERROR(result, "Error starting player");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputOpenSLES::HandleSLCallback(){
|
||||||
|
/*if(stopped){
|
||||||
|
//LOGV("left HandleSLCallback early");
|
||||||
|
return;
|
||||||
|
}*/
|
||||||
|
//LOGV("before InvokeCallback");
|
||||||
|
if(!stopped){
|
||||||
|
while(remainingDataSize<nativeBufferSize*2){
|
||||||
|
assert(remainingDataSize+BUFFER_SIZE*2<10240);
|
||||||
|
InvokeCallback(remainingData+remainingDataSize, BUFFER_SIZE*2);
|
||||||
|
remainingDataSize+=BUFFER_SIZE*2;
|
||||||
|
}
|
||||||
|
memcpy(nativeBuffer, remainingData, nativeBufferSize*2);
|
||||||
|
remainingDataSize-=nativeBufferSize*2;
|
||||||
|
if(remainingDataSize>0)
|
||||||
|
memmove(remainingData, remainingData+nativeBufferSize*2, remainingDataSize);
|
||||||
|
//InvokeCallback((unsigned char *) nativeBuffer, nativeBufferSize*sizeof(int16_t));
|
||||||
|
}else{
|
||||||
|
memset(nativeBuffer, 0, nativeBufferSize*2);
|
||||||
|
}
|
||||||
|
|
||||||
|
(*slBufferQueue)->Enqueue(slBufferQueue, nativeBuffer, nativeBufferSize*sizeof(int16_t));
|
||||||
|
//LOGV("left HandleSLCallback");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool AudioOutputOpenSLES::IsPlaying(){
|
||||||
|
if(slPlayer){
|
||||||
|
uint32_t state;
|
||||||
|
(*slPlayer)->GetPlayState(slPlayer, &state);
|
||||||
|
return state==SL_PLAYSTATE_PLAYING;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float AudioOutputOpenSLES::GetLevel(){
|
||||||
|
return 0; // we don't use this anyway
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LIBTGVOIP_AUDIOOUTPUTOPENSLES_H
|
||||||
|
#define LIBTGVOIP_AUDIOOUTPUTOPENSLES_H
|
||||||
|
|
||||||
|
#include <SLES/OpenSLES.h>
|
||||||
|
#include <SLES/OpenSLES_Android.h>
|
||||||
|
|
||||||
|
#include "../../audio/AudioOutput.h"
|
||||||
|
|
||||||
|
namespace tgvoip{ namespace audio{
|
||||||
|
class AudioOutputOpenSLES : public AudioOutput{
|
||||||
|
public:
|
||||||
|
AudioOutputOpenSLES();
|
||||||
|
virtual ~AudioOutputOpenSLES();
|
||||||
|
virtual void Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels);
|
||||||
|
virtual bool IsPhone();
|
||||||
|
virtual void EnableLoudspeaker(bool enabled);
|
||||||
|
virtual void Start();
|
||||||
|
virtual void Stop();
|
||||||
|
virtual bool IsPlaying();
|
||||||
|
virtual float GetLevel();
|
||||||
|
|
||||||
|
static void SetNativeBufferSize(unsigned int size);
|
||||||
|
static unsigned int nativeBufferSize;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void BufferCallback(SLAndroidSimpleBufferQueueItf bq, void *context);
|
||||||
|
void HandleSLCallback();
|
||||||
|
SLEngineItf slEngine;
|
||||||
|
SLObjectItf slPlayerObj;
|
||||||
|
SLObjectItf slOutputMixObj;
|
||||||
|
SLPlayItf slPlayer;
|
||||||
|
SLAndroidSimpleBufferQueueItf slBufferQueue;
|
||||||
|
int16_t* buffer;
|
||||||
|
int16_t* nativeBuffer;
|
||||||
|
bool stopped;
|
||||||
|
unsigned char remainingData[10240];
|
||||||
|
size_t remainingDataSize;
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
|
||||||
|
#endif //LIBTGVOIP_AUDIOOUTPUTANDROID_H
|
|
@ -0,0 +1,66 @@
|
||||||
|
//
|
||||||
|
// Created by Grishka on 15.08.2018.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LIBTGVOIP_JNIUTILITIES_H
|
||||||
|
#define LIBTGVOIP_JNIUTILITIES_H
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <jni.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string>
|
||||||
|
#include "../../Buffers.h"
|
||||||
|
|
||||||
|
extern JavaVM* sharedJVM;
|
||||||
|
|
||||||
|
namespace tgvoip{
|
||||||
|
namespace jni{
|
||||||
|
|
||||||
|
inline void DoWithJNI(std::function<void(JNIEnv*)> f){
|
||||||
|
JNIEnv *env=NULL;
|
||||||
|
bool didAttach=false;
|
||||||
|
sharedJVM->GetEnv((void **) &env, JNI_VERSION_1_6);
|
||||||
|
if(!env){
|
||||||
|
sharedJVM->AttachCurrentThread(&env, NULL);
|
||||||
|
didAttach=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
f(env);
|
||||||
|
|
||||||
|
if(didAttach){
|
||||||
|
sharedJVM->DetachCurrentThread();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void AttachAndCallVoidMethod(jmethodID method, jobject obj, ...){
|
||||||
|
if(!method || !obj)
|
||||||
|
return;
|
||||||
|
va_list va;
|
||||||
|
va_start(va, obj);
|
||||||
|
DoWithJNI([&va, method, obj](JNIEnv* env){
|
||||||
|
env->CallVoidMethodV(obj, method, va);
|
||||||
|
});
|
||||||
|
va_end(va);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string JavaStringToStdString(JNIEnv* env, jstring jstr){
|
||||||
|
if(!jstr)
|
||||||
|
return "";
|
||||||
|
const char* jchars=env->GetStringUTFChars(jstr, NULL);
|
||||||
|
std::string str(jchars);
|
||||||
|
env->ReleaseStringUTFChars(jstr, jchars);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline jbyteArray BufferToByteArray(JNIEnv* env, Buffer& buf){
|
||||||
|
jbyteArray arr=env->NewByteArray((jsize)buf.Length());
|
||||||
|
jbyte* elements=env->GetByteArrayElements(arr, NULL);
|
||||||
|
memcpy(elements, *buf, buf.Length());
|
||||||
|
env->ReleaseByteArrayElements(arr, elements, 0);
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //LIBTGVOIP_JNIUTILITIES_H
|
|
@ -0,0 +1,48 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include "OpenSLEngineWrapper.h"
|
||||||
|
#include "../../logging.h"
|
||||||
|
|
||||||
|
#define CHECK_SL_ERROR(res, msg) if(res!=SL_RESULT_SUCCESS){ LOGE(msg); return NULL; }
|
||||||
|
|
||||||
|
using namespace tgvoip;
|
||||||
|
using namespace tgvoip::audio;
|
||||||
|
|
||||||
|
|
||||||
|
SLObjectItf OpenSLEngineWrapper::sharedEngineObj=NULL;
|
||||||
|
SLEngineItf OpenSLEngineWrapper::sharedEngine=NULL;
|
||||||
|
int OpenSLEngineWrapper::count=0;
|
||||||
|
|
||||||
|
void OpenSLEngineWrapper::DestroyEngine(){
|
||||||
|
count--;
|
||||||
|
LOGI("release: engine instance count %d", count);
|
||||||
|
if(count==0){
|
||||||
|
(*sharedEngineObj)->Destroy(sharedEngineObj);
|
||||||
|
sharedEngineObj=NULL;
|
||||||
|
sharedEngine=NULL;
|
||||||
|
}
|
||||||
|
LOGI("after release");
|
||||||
|
}
|
||||||
|
|
||||||
|
SLEngineItf OpenSLEngineWrapper::CreateEngine(){
|
||||||
|
count++;
|
||||||
|
if(sharedEngine)
|
||||||
|
return sharedEngine;
|
||||||
|
const SLInterfaceID pIDs[1] = {SL_IID_ENGINE};
|
||||||
|
const SLboolean pIDsRequired[1] = {SL_BOOLEAN_TRUE};
|
||||||
|
SLresult result = slCreateEngine(&sharedEngineObj, 0, NULL, 1, pIDs, pIDsRequired);
|
||||||
|
CHECK_SL_ERROR(result, "Error creating engine");
|
||||||
|
|
||||||
|
result=(*sharedEngineObj)->Realize(sharedEngineObj, SL_BOOLEAN_FALSE);
|
||||||
|
CHECK_SL_ERROR(result, "Error realizing engine");
|
||||||
|
|
||||||
|
result = (*sharedEngineObj)->GetInterface(sharedEngineObj, SL_IID_ENGINE, &sharedEngine);
|
||||||
|
CHECK_SL_ERROR(result, "Error getting engine interface");
|
||||||
|
return sharedEngine;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LIBTGVOIP_OPENSLENGINEWRAPPER_H
|
||||||
|
#define LIBTGVOIP_OPENSLENGINEWRAPPER_H
|
||||||
|
|
||||||
|
#include <SLES/OpenSLES.h>
|
||||||
|
#include <SLES/OpenSLES_Android.h>
|
||||||
|
|
||||||
|
namespace tgvoip{ namespace audio{
|
||||||
|
class OpenSLEngineWrapper{
|
||||||
|
public:
|
||||||
|
static SLEngineItf CreateEngine();
|
||||||
|
static void DestroyEngine();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static SLObjectItf sharedEngineObj;
|
||||||
|
static SLEngineItf sharedEngine;
|
||||||
|
static int count;
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
|
||||||
|
#endif //LIBTGVOIP_OPENSLENGINEWRAPPER_H
|
|
@ -0,0 +1,150 @@
|
||||||
|
//
|
||||||
|
// Created by Grishka on 12.08.2018.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "VideoRendererAndroid.h"
|
||||||
|
#include "JNIUtilities.h"
|
||||||
|
#include "../../PrivateDefines.h"
|
||||||
|
#include "../../logging.h"
|
||||||
|
|
||||||
|
using namespace tgvoip;
|
||||||
|
using namespace tgvoip::video;
|
||||||
|
|
||||||
|
jmethodID VideoRendererAndroid::resetMethod=NULL;
|
||||||
|
jmethodID VideoRendererAndroid::decodeAndDisplayMethod=NULL;
|
||||||
|
jmethodID VideoRendererAndroid::setStreamEnabledMethod=NULL;
|
||||||
|
jmethodID VideoRendererAndroid::setRotationMethod=NULL;
|
||||||
|
std::vector<uint32_t> VideoRendererAndroid::availableDecoders;
|
||||||
|
int VideoRendererAndroid::maxResolution;
|
||||||
|
|
||||||
|
extern JavaVM* sharedJVM;
|
||||||
|
|
||||||
|
VideoRendererAndroid::VideoRendererAndroid(jobject jobj) : queue(50){
|
||||||
|
this->jobj=jobj;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoRendererAndroid::~VideoRendererAndroid(){
|
||||||
|
running=false;
|
||||||
|
Request req{
|
||||||
|
Buffer(0),
|
||||||
|
Request::Type::Shutdown
|
||||||
|
};
|
||||||
|
queue.Put(std::move(req));
|
||||||
|
thread->Join();
|
||||||
|
delete thread;
|
||||||
|
/*decoderThread.Post([this]{
|
||||||
|
decoderEnv->DeleteGlobalRef(jobj);
|
||||||
|
});*/
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoRendererAndroid::Reset(uint32_t codec, unsigned int width, unsigned int height, std::vector<Buffer> &_csd){
|
||||||
|
csd.clear();
|
||||||
|
for(Buffer& b:_csd){
|
||||||
|
csd.push_back(Buffer::CopyOf(b));
|
||||||
|
}
|
||||||
|
this->codec=codec;
|
||||||
|
this->width=width;
|
||||||
|
this->height=height;
|
||||||
|
Request req{
|
||||||
|
Buffer(0),
|
||||||
|
Request::Type::ResetDecoder
|
||||||
|
};
|
||||||
|
queue.Put(std::move(req));
|
||||||
|
Request req2{
|
||||||
|
Buffer(0),
|
||||||
|
Request::Type::UpdateStreamState
|
||||||
|
};
|
||||||
|
queue.Put(std::move(req2));
|
||||||
|
|
||||||
|
if(!thread){
|
||||||
|
thread=new Thread(std::bind(&VideoRendererAndroid::RunThread, this));
|
||||||
|
thread->Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoRendererAndroid::DecodeAndDisplay(Buffer frame, uint32_t pts){
|
||||||
|
/*decoderThread.Post(std::bind([this](Buffer frame){
|
||||||
|
}, std::move(frame)));*/
|
||||||
|
//LOGV("2 before decode %u", (unsigned int)frame.Length());
|
||||||
|
Request req{
|
||||||
|
std::move(frame),
|
||||||
|
Request::Type::DecodeFrame
|
||||||
|
};
|
||||||
|
queue.Put(std::move(req));
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoRendererAndroid::RunThread(){
|
||||||
|
JNIEnv* env;
|
||||||
|
sharedJVM->AttachCurrentThread(&env, NULL);
|
||||||
|
|
||||||
|
constexpr size_t bufferSize=200*1024;
|
||||||
|
unsigned char* buf=reinterpret_cast<unsigned char*>(malloc(bufferSize));
|
||||||
|
jobject jbuf=env->NewDirectByteBuffer(buf, bufferSize);
|
||||||
|
uint16_t lastSetRotation=0;
|
||||||
|
|
||||||
|
while(running){
|
||||||
|
//LOGV("before get from queue");
|
||||||
|
Request request=std::move(queue.GetBlocking());
|
||||||
|
//LOGV("1 before decode %u", (unsigned int)request.Length());
|
||||||
|
if(request.type==Request::Type::Shutdown){
|
||||||
|
LOGI("Shutting down video decoder thread");
|
||||||
|
break;
|
||||||
|
}else if(request.type==Request::Type::DecodeFrame){
|
||||||
|
if(request.buffer.Length()>bufferSize){
|
||||||
|
LOGE("Frame data is too long (%u, max %u)", (int) request.buffer.Length(), (int) bufferSize);
|
||||||
|
}else{
|
||||||
|
if(lastSetRotation!=rotation){
|
||||||
|
lastSetRotation=rotation;
|
||||||
|
env->CallVoidMethod(jobj, setRotationMethod, (jint)rotation);
|
||||||
|
}
|
||||||
|
memcpy(buf, *request.buffer, request.buffer.Length());
|
||||||
|
env->CallVoidMethod(jobj, decodeAndDisplayMethod, jbuf, (jint) request.buffer.Length(), 0);
|
||||||
|
}
|
||||||
|
}else if(request.type==Request::Type::ResetDecoder){
|
||||||
|
jobjectArray jcsd=NULL;
|
||||||
|
if(!csd.empty()){
|
||||||
|
jcsd=env->NewObjectArray((jsize)csd.size(), env->FindClass("[B"), NULL);
|
||||||
|
jsize i=0;
|
||||||
|
for(Buffer& b:csd){
|
||||||
|
env->SetObjectArrayElement(jcsd, i, jni::BufferToByteArray(env, b));
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::string codecStr="";
|
||||||
|
switch(codec){
|
||||||
|
case CODEC_AVC:
|
||||||
|
codecStr="video/avc";
|
||||||
|
break;
|
||||||
|
case CODEC_HEVC:
|
||||||
|
codecStr="video/hevc";
|
||||||
|
break;
|
||||||
|
case CODEC_VP8:
|
||||||
|
codecStr="video/x-vnd.on2.vp8";
|
||||||
|
break;
|
||||||
|
case CODEC_VP9:
|
||||||
|
codecStr="video/x-vnd.on2.vp9";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
env->CallVoidMethod(jobj, resetMethod, env->NewStringUTF(codecStr.c_str()), (jint)width, (jint)height, jcsd);
|
||||||
|
}else if(request.type==Request::Type::UpdateStreamState){
|
||||||
|
env->CallVoidMethod(jobj, setStreamEnabledMethod, streamEnabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(buf);
|
||||||
|
sharedJVM->DetachCurrentThread();
|
||||||
|
LOGI("==== decoder thread exiting ====");
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoRendererAndroid::SetStreamEnabled(bool enabled){
|
||||||
|
LOGI("Video stream state: %d", enabled);
|
||||||
|
streamEnabled=enabled;
|
||||||
|
Request req{
|
||||||
|
Buffer(0),
|
||||||
|
Request::Type::UpdateStreamState
|
||||||
|
};
|
||||||
|
queue.Put(std::move(req));
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoRendererAndroid::SetRotation(uint16_t rotation){
|
||||||
|
this->rotation=rotation;
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
//
|
||||||
|
// Created by Grishka on 12.08.2018.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LIBTGVOIP_VIDEORENDERERANDROID_H
|
||||||
|
#define LIBTGVOIP_VIDEORENDERERANDROID_H
|
||||||
|
|
||||||
|
#include "../../video/VideoRenderer.h"
|
||||||
|
#include "../../MessageThread.h"
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
#include "../../BlockingQueue.h"
|
||||||
|
|
||||||
|
namespace tgvoip{
|
||||||
|
namespace video{
|
||||||
|
class VideoRendererAndroid : public VideoRenderer{
|
||||||
|
public:
|
||||||
|
VideoRendererAndroid(jobject jobj);
|
||||||
|
virtual ~VideoRendererAndroid();
|
||||||
|
virtual void Reset(uint32_t codec, unsigned int width, unsigned int height, std::vector<Buffer>& csd) override;
|
||||||
|
virtual void DecodeAndDisplay(Buffer frame, uint32_t pts) override;
|
||||||
|
virtual void SetStreamEnabled(bool enabled) override;
|
||||||
|
virtual void SetRotation(uint16_t rotation) override;
|
||||||
|
|
||||||
|
static jmethodID resetMethod;
|
||||||
|
static jmethodID decodeAndDisplayMethod;
|
||||||
|
static jmethodID setStreamEnabledMethod;
|
||||||
|
static jmethodID setRotationMethod;
|
||||||
|
static std::vector<uint32_t> availableDecoders;
|
||||||
|
static int maxResolution;
|
||||||
|
private:
|
||||||
|
struct Request{
|
||||||
|
enum Type{
|
||||||
|
DecodeFrame,
|
||||||
|
ResetDecoder,
|
||||||
|
UpdateStreamState,
|
||||||
|
Shutdown
|
||||||
|
};
|
||||||
|
|
||||||
|
Buffer buffer;
|
||||||
|
Type type;
|
||||||
|
};
|
||||||
|
void RunThread();
|
||||||
|
Thread* thread=NULL;
|
||||||
|
bool running=true;
|
||||||
|
BlockingQueue<Request> queue;
|
||||||
|
std::vector<Buffer> csd;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
bool streamEnabled=true;
|
||||||
|
uint32_t codec;
|
||||||
|
uint16_t rotation=0;
|
||||||
|
jobject jobj;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //LIBTGVOIP_VIDEORENDERERANDROID_H
|
|
@ -0,0 +1,88 @@
|
||||||
|
//
|
||||||
|
// Created by Grishka on 12.08.2018.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "VideoSourceAndroid.h"
|
||||||
|
#include "JNIUtilities.h"
|
||||||
|
#include "../../logging.h"
|
||||||
|
#include "../../PrivateDefines.h"
|
||||||
|
|
||||||
|
using namespace tgvoip;
|
||||||
|
using namespace tgvoip::video;
|
||||||
|
|
||||||
|
extern JavaVM* sharedJVM;
|
||||||
|
|
||||||
|
std::vector<uint32_t> VideoSourceAndroid::availableEncoders;
|
||||||
|
|
||||||
|
VideoSourceAndroid::VideoSourceAndroid(jobject jobj) : javaObject(jobj){
|
||||||
|
jni::DoWithJNI([&](JNIEnv* env){
|
||||||
|
jclass cls=env->GetObjectClass(javaObject);
|
||||||
|
startMethod=env->GetMethodID(cls, "start", "()V");
|
||||||
|
stopMethod=env->GetMethodID(cls, "stop", "()V");
|
||||||
|
prepareEncoderMethod=env->GetMethodID(cls, "prepareEncoder", "(Ljava/lang/String;I)V");
|
||||||
|
requestKeyFrameMethod=env->GetMethodID(cls, "requestKeyFrame", "()V");
|
||||||
|
setBitrateMethod=env->GetMethodID(cls, "setBitrate", "(I)V");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoSourceAndroid::~VideoSourceAndroid(){
|
||||||
|
jni::DoWithJNI([this](JNIEnv* env){
|
||||||
|
env->DeleteGlobalRef(javaObject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSourceAndroid::Start(){
|
||||||
|
jni::DoWithJNI([this](JNIEnv* env){
|
||||||
|
env->CallVoidMethod(javaObject, startMethod);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSourceAndroid::Stop(){
|
||||||
|
jni::DoWithJNI([this](JNIEnv* env){
|
||||||
|
env->CallVoidMethod(javaObject, stopMethod);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSourceAndroid::SendFrame(Buffer frame, uint32_t flags){
|
||||||
|
callback(frame, flags, rotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSourceAndroid::SetStreamParameters(std::vector<Buffer> csd, unsigned int width, unsigned int height){
|
||||||
|
LOGD("Video stream parameters: %d x %d", width, height);
|
||||||
|
this->width=width;
|
||||||
|
this->height=height;
|
||||||
|
this->csd=std::move(csd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSourceAndroid::Reset(uint32_t codec, int maxResolution){
|
||||||
|
jni::DoWithJNI([&](JNIEnv* env){
|
||||||
|
std::string codecStr="";
|
||||||
|
switch(codec){
|
||||||
|
case CODEC_AVC:
|
||||||
|
codecStr="video/avc";
|
||||||
|
break;
|
||||||
|
case CODEC_HEVC:
|
||||||
|
codecStr="video/hevc";
|
||||||
|
break;
|
||||||
|
case CODEC_VP8:
|
||||||
|
codecStr="video/x-vnd.on2.vp8";
|
||||||
|
break;
|
||||||
|
case CODEC_VP9:
|
||||||
|
codecStr="video/x-vnd.on2.vp9";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
env->CallVoidMethod(javaObject, prepareEncoderMethod, env->NewStringUTF(codecStr.c_str()), maxResolution);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSourceAndroid::RequestKeyFrame(){
|
||||||
|
jni::DoWithJNI([this](JNIEnv* env){
|
||||||
|
env->CallVoidMethod(javaObject, requestKeyFrameMethod);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSourceAndroid::SetBitrate(uint32_t bitrate){
|
||||||
|
jni::DoWithJNI([&](JNIEnv* env){
|
||||||
|
env->CallVoidMethod(javaObject, setBitrateMethod, (jint)bitrate);
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
//
|
||||||
|
// Created by Grishka on 12.08.2018.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LIBTGVOIP_VIDEOSOURCEANDROID_H
|
||||||
|
#define LIBTGVOIP_VIDEOSOURCEANDROID_H
|
||||||
|
|
||||||
|
#include "../../video/VideoSource.h"
|
||||||
|
#include "../../Buffers.h"
|
||||||
|
#include <jni.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace tgvoip{
|
||||||
|
namespace video{
|
||||||
|
class VideoSourceAndroid : public VideoSource{
|
||||||
|
public:
|
||||||
|
VideoSourceAndroid(jobject jobj);
|
||||||
|
virtual ~VideoSourceAndroid();
|
||||||
|
virtual void Start() override;
|
||||||
|
virtual void Stop() override;
|
||||||
|
virtual void Reset(uint32_t codec, int maxResolution) override;
|
||||||
|
void SendFrame(Buffer frame, uint32_t flags);
|
||||||
|
void SetStreamParameters(std::vector<Buffer> csd, unsigned int width, unsigned int height);
|
||||||
|
virtual void RequestKeyFrame() override;
|
||||||
|
virtual void SetBitrate(uint32_t bitrate) override;
|
||||||
|
|
||||||
|
static std::vector<uint32_t> availableEncoders;
|
||||||
|
private:
|
||||||
|
jobject javaObject;
|
||||||
|
jmethodID prepareEncoderMethod;
|
||||||
|
jmethodID startMethod;
|
||||||
|
jmethodID stopMethod;
|
||||||
|
jmethodID requestKeyFrameMethod;
|
||||||
|
jmethodID setBitrateMethod;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif //LIBTGVOIP_VIDEOSOURCEANDROID_H
|
|
@ -0,0 +1,73 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include "AudioUnitIO.h"
|
||||||
|
#include "AudioInputAudioUnit.h"
|
||||||
|
#include "../../logging.h"
|
||||||
|
|
||||||
|
#define BUFFER_SIZE 960
|
||||||
|
|
||||||
|
using namespace tgvoip;
|
||||||
|
using namespace tgvoip::audio;
|
||||||
|
|
||||||
|
AudioInputAudioUnit::AudioInputAudioUnit(std::string deviceID, AudioUnitIO* io){
|
||||||
|
remainingDataSize=0;
|
||||||
|
isRecording=false;
|
||||||
|
this->io=io;
|
||||||
|
#if TARGET_OS_OSX
|
||||||
|
io->SetCurrentDevice(true, deviceID);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioInputAudioUnit::~AudioInputAudioUnit(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioInputAudioUnit::Start(){
|
||||||
|
isRecording=true;
|
||||||
|
io->EnableInput(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioInputAudioUnit::Stop(){
|
||||||
|
isRecording=false;
|
||||||
|
io->EnableInput(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioInputAudioUnit::HandleBufferCallback(AudioBufferList *ioData){
|
||||||
|
int i;
|
||||||
|
for(i=0;i<ioData->mNumberBuffers;i++){
|
||||||
|
AudioBuffer buf=ioData->mBuffers[i];
|
||||||
|
#if TARGET_OS_OSX
|
||||||
|
assert(remainingDataSize+buf.mDataByteSize/2<10240);
|
||||||
|
float* src=reinterpret_cast<float*>(buf.mData);
|
||||||
|
int16_t* dst=reinterpret_cast<int16_t*>(remainingData+remainingDataSize);
|
||||||
|
for(int j=0;j<buf.mDataByteSize/4;j++){
|
||||||
|
dst[j]=(int16_t)(src[j]*INT16_MAX);
|
||||||
|
}
|
||||||
|
remainingDataSize+=buf.mDataByteSize/2;
|
||||||
|
#else
|
||||||
|
assert(remainingDataSize+buf.mDataByteSize<10240);
|
||||||
|
memcpy(remainingData+remainingDataSize, buf.mData, buf.mDataByteSize);
|
||||||
|
remainingDataSize+=buf.mDataByteSize;
|
||||||
|
#endif
|
||||||
|
while(remainingDataSize>=BUFFER_SIZE*2){
|
||||||
|
InvokeCallback((unsigned char*)remainingData, BUFFER_SIZE*2);
|
||||||
|
remainingDataSize-=BUFFER_SIZE*2;
|
||||||
|
if(remainingDataSize>0){
|
||||||
|
memmove(remainingData, remainingData+(BUFFER_SIZE*2), remainingDataSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if TARGET_OS_OSX
|
||||||
|
void AudioInputAudioUnit::SetCurrentDevice(std::string deviceID){
|
||||||
|
io->SetCurrentDevice(true, deviceID);
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,38 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LIBTGVOIP_AUDIOINPUTAUDIOUNIT_H
|
||||||
|
#define LIBTGVOIP_AUDIOINPUTAUDIOUNIT_H
|
||||||
|
|
||||||
|
#include <AudioUnit/AudioUnit.h>
|
||||||
|
#include "../../audio/AudioInput.h"
|
||||||
|
#include "../../utils.h"
|
||||||
|
|
||||||
|
namespace tgvoip{ namespace audio{
|
||||||
|
class AudioUnitIO;
|
||||||
|
|
||||||
|
class AudioInputAudioUnit : public AudioInput{
|
||||||
|
|
||||||
|
public:
|
||||||
|
TGVOIP_DISALLOW_COPY_AND_ASSIGN(AudioInputAudioUnit);
|
||||||
|
AudioInputAudioUnit(std::string deviceID, AudioUnitIO* io);
|
||||||
|
virtual ~AudioInputAudioUnit();
|
||||||
|
virtual void Start();
|
||||||
|
virtual void Stop();
|
||||||
|
void HandleBufferCallback(AudioBufferList* ioData);
|
||||||
|
#if TARGET_OS_OSX
|
||||||
|
virtual void SetCurrentDevice(std::string deviceID);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned char remainingData[10240];
|
||||||
|
size_t remainingDataSize;
|
||||||
|
bool isRecording;
|
||||||
|
AudioUnitIO* io;
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
|
||||||
|
#endif //LIBTGVOIP_AUDIOINPUTAUDIOUNIT_H
|
|
@ -0,0 +1,308 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "AudioInputAudioUnitOSX.h"
|
||||||
|
#include "../../logging.h"
|
||||||
|
#include "../../audio/Resampler.h"
|
||||||
|
#include "../../VoIPController.h"
|
||||||
|
|
||||||
|
#define BUFFER_SIZE 960
|
||||||
|
#define CHECK_AU_ERROR(res, msg) if(res!=noErr){ LOGE("input: " msg": OSStatus=%d", (int)res); failed=true; return; }
|
||||||
|
|
||||||
|
#define kOutputBus 0
|
||||||
|
#define kInputBus 1
|
||||||
|
|
||||||
|
using namespace tgvoip;
|
||||||
|
using namespace tgvoip::audio;
|
||||||
|
|
||||||
|
AudioInputAudioUnitLegacy::AudioInputAudioUnitLegacy(std::string deviceID) : AudioInput(deviceID){
|
||||||
|
remainingDataSize=0;
|
||||||
|
isRecording=false;
|
||||||
|
|
||||||
|
inBufferList.mBuffers[0].mData=malloc(10240);
|
||||||
|
inBufferList.mBuffers[0].mDataByteSize=10240;
|
||||||
|
inBufferList.mNumberBuffers=1;
|
||||||
|
|
||||||
|
OSStatus status;
|
||||||
|
AudioComponentDescription inputDesc={
|
||||||
|
.componentType = kAudioUnitType_Output, .componentSubType = kAudioUnitSubType_HALOutput, .componentFlags = 0, .componentFlagsMask = 0,
|
||||||
|
.componentManufacturer = kAudioUnitManufacturer_Apple
|
||||||
|
};
|
||||||
|
AudioComponent component=AudioComponentFindNext(NULL, &inputDesc);
|
||||||
|
status=AudioComponentInstanceNew(component, &unit);
|
||||||
|
CHECK_AU_ERROR(status, "Error creating AudioUnit");
|
||||||
|
|
||||||
|
UInt32 flag=0;
|
||||||
|
status = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flag, sizeof(flag));
|
||||||
|
CHECK_AU_ERROR(status, "Error enabling AudioUnit output");
|
||||||
|
flag=1;
|
||||||
|
status = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag));
|
||||||
|
CHECK_AU_ERROR(status, "Error enabling AudioUnit input");
|
||||||
|
|
||||||
|
SetCurrentDevice(deviceID);
|
||||||
|
|
||||||
|
CFRunLoopRef theRunLoop = NULL;
|
||||||
|
AudioObjectPropertyAddress propertyAddress = { kAudioHardwarePropertyRunLoop,
|
||||||
|
kAudioObjectPropertyScopeGlobal,
|
||||||
|
kAudioObjectPropertyElementMaster };
|
||||||
|
status = AudioObjectSetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
|
||||||
|
|
||||||
|
propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
|
||||||
|
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
|
||||||
|
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
|
||||||
|
AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propertyAddress, AudioInputAudioUnitLegacy::DefaultDeviceChangedCallback, this);
|
||||||
|
|
||||||
|
AURenderCallbackStruct callbackStruct;
|
||||||
|
callbackStruct.inputProc = AudioInputAudioUnitLegacy::BufferCallback;
|
||||||
|
callbackStruct.inputProcRefCon=this;
|
||||||
|
status = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, kInputBus, &callbackStruct, sizeof(callbackStruct));
|
||||||
|
CHECK_AU_ERROR(status, "Error setting input buffer callback");
|
||||||
|
status=AudioUnitInitialize(unit);
|
||||||
|
CHECK_AU_ERROR(status, "Error initializing unit");
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioInputAudioUnitLegacy::~AudioInputAudioUnitLegacy(){
|
||||||
|
AudioObjectPropertyAddress propertyAddress;
|
||||||
|
propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
|
||||||
|
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
|
||||||
|
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
|
||||||
|
AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propertyAddress, AudioInputAudioUnitLegacy::DefaultDeviceChangedCallback, this);
|
||||||
|
|
||||||
|
AudioUnitUninitialize(unit);
|
||||||
|
AudioComponentInstanceDispose(unit);
|
||||||
|
free(inBufferList.mBuffers[0].mData);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioInputAudioUnitLegacy::Start(){
|
||||||
|
isRecording=true;
|
||||||
|
OSStatus status=AudioOutputUnitStart(unit);
|
||||||
|
CHECK_AU_ERROR(status, "Error starting AudioUnit");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioInputAudioUnitLegacy::Stop(){
|
||||||
|
isRecording=false;
|
||||||
|
OSStatus status=AudioOutputUnitStart(unit);
|
||||||
|
CHECK_AU_ERROR(status, "Error stopping AudioUnit");
|
||||||
|
}
|
||||||
|
|
||||||
|
OSStatus AudioInputAudioUnitLegacy::BufferCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData){
|
||||||
|
AudioInputAudioUnitLegacy* input=(AudioInputAudioUnitLegacy*) inRefCon;
|
||||||
|
input->inBufferList.mBuffers[0].mDataByteSize=10240;
|
||||||
|
AudioUnitRender(input->unit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, &input->inBufferList);
|
||||||
|
input->HandleBufferCallback(&input->inBufferList);
|
||||||
|
return noErr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioInputAudioUnitLegacy::HandleBufferCallback(AudioBufferList *ioData){
|
||||||
|
int i;
|
||||||
|
for(i=0;i<ioData->mNumberBuffers;i++){
|
||||||
|
AudioBuffer buf=ioData->mBuffers[i];
|
||||||
|
size_t len=buf.mDataByteSize;
|
||||||
|
if(hardwareSampleRate!=48000){
|
||||||
|
len=tgvoip::audio::Resampler::Convert((int16_t*)buf.mData, (int16_t*)(remainingData+remainingDataSize), buf.mDataByteSize/2, (10240-(buf.mDataByteSize+remainingDataSize))/2, 48000, hardwareSampleRate)*2;
|
||||||
|
}else{
|
||||||
|
assert(remainingDataSize+buf.mDataByteSize<10240);
|
||||||
|
memcpy(remainingData+remainingDataSize, buf.mData, buf.mDataByteSize);
|
||||||
|
}
|
||||||
|
remainingDataSize+=len;
|
||||||
|
while(remainingDataSize>=BUFFER_SIZE*2){
|
||||||
|
InvokeCallback((unsigned char*)remainingData, BUFFER_SIZE*2);
|
||||||
|
remainingDataSize-=BUFFER_SIZE*2;
|
||||||
|
if(remainingDataSize>0){
|
||||||
|
memmove(remainingData, remainingData+(BUFFER_SIZE*2), remainingDataSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AudioInputAudioUnitLegacy::EnumerateDevices(std::vector<AudioInputDevice>& devs){
|
||||||
|
AudioObjectPropertyAddress propertyAddress = {
|
||||||
|
kAudioHardwarePropertyDevices,
|
||||||
|
kAudioObjectPropertyScopeGlobal,
|
||||||
|
kAudioObjectPropertyElementMaster
|
||||||
|
};
|
||||||
|
|
||||||
|
UInt32 dataSize = 0;
|
||||||
|
OSStatus status = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize);
|
||||||
|
if(kAudioHardwareNoError != status) {
|
||||||
|
LOGE("AudioObjectGetPropertyDataSize (kAudioHardwarePropertyDevices) failed: %i", status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt32 deviceCount = (UInt32)(dataSize / sizeof(AudioDeviceID));
|
||||||
|
|
||||||
|
|
||||||
|
AudioDeviceID *audioDevices = (AudioDeviceID*)(malloc(dataSize));
|
||||||
|
|
||||||
|
status = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize, audioDevices);
|
||||||
|
if(kAudioHardwareNoError != status) {
|
||||||
|
LOGE("AudioObjectGetPropertyData (kAudioHardwarePropertyDevices) failed: %i", status);
|
||||||
|
free(audioDevices);
|
||||||
|
audioDevices = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Iterate through all the devices and determine which are input-capable
|
||||||
|
propertyAddress.mScope = kAudioDevicePropertyScopeInput;
|
||||||
|
for(UInt32 i = 0; i < deviceCount; ++i) {
|
||||||
|
// Query device UID
|
||||||
|
CFStringRef deviceUID = NULL;
|
||||||
|
dataSize = sizeof(deviceUID);
|
||||||
|
propertyAddress.mSelector = kAudioDevicePropertyDeviceUID;
|
||||||
|
status = AudioObjectGetPropertyData(audioDevices[i], &propertyAddress, 0, NULL, &dataSize, &deviceUID);
|
||||||
|
if(kAudioHardwareNoError != status) {
|
||||||
|
LOGE("AudioObjectGetPropertyData (kAudioDevicePropertyDeviceUID) failed: %i", status);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query device name
|
||||||
|
CFStringRef deviceName = NULL;
|
||||||
|
dataSize = sizeof(deviceName);
|
||||||
|
propertyAddress.mSelector = kAudioDevicePropertyDeviceNameCFString;
|
||||||
|
status = AudioObjectGetPropertyData(audioDevices[i], &propertyAddress, 0, NULL, &dataSize, &deviceName);
|
||||||
|
if(kAudioHardwareNoError != status) {
|
||||||
|
LOGE("AudioObjectGetPropertyData (kAudioDevicePropertyDeviceNameCFString) failed: %i", status);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine if the device is an input device (it is an input device if it has input channels)
|
||||||
|
dataSize = 0;
|
||||||
|
propertyAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
|
||||||
|
status = AudioObjectGetPropertyDataSize(audioDevices[i], &propertyAddress, 0, NULL, &dataSize);
|
||||||
|
if(kAudioHardwareNoError != status) {
|
||||||
|
LOGE("AudioObjectGetPropertyDataSize (kAudioDevicePropertyStreamConfiguration) failed: %i", status);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioBufferList *bufferList = (AudioBufferList*)(malloc(dataSize));
|
||||||
|
|
||||||
|
status = AudioObjectGetPropertyData(audioDevices[i], &propertyAddress, 0, NULL, &dataSize, bufferList);
|
||||||
|
if(kAudioHardwareNoError != status || 0 == bufferList->mNumberBuffers) {
|
||||||
|
if(kAudioHardwareNoError != status)
|
||||||
|
LOGE("AudioObjectGetPropertyData (kAudioDevicePropertyStreamConfiguration) failed: %i", status);
|
||||||
|
free(bufferList);
|
||||||
|
bufferList = NULL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(bufferList);
|
||||||
|
bufferList = NULL;
|
||||||
|
|
||||||
|
AudioInputDevice dev;
|
||||||
|
char buf[1024];
|
||||||
|
CFStringGetCString(deviceName, buf, 1024, kCFStringEncodingUTF8);
|
||||||
|
dev.displayName=std::string(buf);
|
||||||
|
CFStringGetCString(deviceUID, buf, 1024, kCFStringEncodingUTF8);
|
||||||
|
dev.id=std::string(buf);
|
||||||
|
if(dev.id.rfind("VPAUAggregateAudioDevice-0x")==0)
|
||||||
|
continue;
|
||||||
|
devs.push_back(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(audioDevices);
|
||||||
|
audioDevices = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioInputAudioUnitLegacy::SetCurrentDevice(std::string deviceID){
|
||||||
|
UInt32 size=sizeof(AudioDeviceID);
|
||||||
|
AudioDeviceID inputDevice=0;
|
||||||
|
OSStatus status;
|
||||||
|
|
||||||
|
if(deviceID=="default"){
|
||||||
|
AudioObjectPropertyAddress propertyAddress;
|
||||||
|
propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
|
||||||
|
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
|
||||||
|
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
|
||||||
|
UInt32 propsize = sizeof(AudioDeviceID);
|
||||||
|
status = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &propsize, &inputDevice);
|
||||||
|
CHECK_AU_ERROR(status, "Error getting default input device");
|
||||||
|
}else{
|
||||||
|
AudioObjectPropertyAddress propertyAddress = {
|
||||||
|
kAudioHardwarePropertyDevices,
|
||||||
|
kAudioObjectPropertyScopeGlobal,
|
||||||
|
kAudioObjectPropertyElementMaster
|
||||||
|
};
|
||||||
|
UInt32 dataSize = 0;
|
||||||
|
status = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize);
|
||||||
|
CHECK_AU_ERROR(status, "Error getting devices size");
|
||||||
|
UInt32 deviceCount = (UInt32)(dataSize / sizeof(AudioDeviceID));
|
||||||
|
AudioDeviceID audioDevices[deviceCount];
|
||||||
|
status = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize, audioDevices);
|
||||||
|
CHECK_AU_ERROR(status, "Error getting device list");
|
||||||
|
for(UInt32 i = 0; i < deviceCount; ++i) {
|
||||||
|
// Query device UID
|
||||||
|
CFStringRef deviceUID = NULL;
|
||||||
|
dataSize = sizeof(deviceUID);
|
||||||
|
propertyAddress.mSelector = kAudioDevicePropertyDeviceUID;
|
||||||
|
status = AudioObjectGetPropertyData(audioDevices[i], &propertyAddress, 0, NULL, &dataSize, &deviceUID);
|
||||||
|
CHECK_AU_ERROR(status, "Error getting device uid");
|
||||||
|
char buf[1024];
|
||||||
|
CFStringGetCString(deviceUID, buf, 1024, kCFStringEncodingUTF8);
|
||||||
|
if(deviceID==buf){
|
||||||
|
LOGV("Found device for id %s", buf);
|
||||||
|
inputDevice=audioDevices[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!inputDevice){
|
||||||
|
LOGW("Requested device not found, using default");
|
||||||
|
SetCurrentDevice("default");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
status =AudioUnitSetProperty(unit,
|
||||||
|
kAudioOutputUnitProperty_CurrentDevice,
|
||||||
|
kAudioUnitScope_Global,
|
||||||
|
kInputBus,
|
||||||
|
&inputDevice,
|
||||||
|
size);
|
||||||
|
CHECK_AU_ERROR(status, "Error setting input device");
|
||||||
|
|
||||||
|
AudioStreamBasicDescription hardwareFormat;
|
||||||
|
size=sizeof(hardwareFormat);
|
||||||
|
status=AudioUnitGetProperty(unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kInputBus, &hardwareFormat, &size);
|
||||||
|
CHECK_AU_ERROR(status, "Error getting hardware format");
|
||||||
|
hardwareSampleRate=hardwareFormat.mSampleRate;
|
||||||
|
|
||||||
|
AudioStreamBasicDescription desiredFormat={
|
||||||
|
.mSampleRate=hardwareFormat.mSampleRate, .mFormatID=kAudioFormatLinearPCM, .mFormatFlags=kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked | kAudioFormatFlagsNativeEndian,
|
||||||
|
.mFramesPerPacket=1, .mChannelsPerFrame=1, .mBitsPerChannel=16, .mBytesPerPacket=2, .mBytesPerFrame=2
|
||||||
|
};
|
||||||
|
|
||||||
|
status=AudioUnitSetProperty(unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &desiredFormat, sizeof(desiredFormat));
|
||||||
|
CHECK_AU_ERROR(status, "Error setting format");
|
||||||
|
|
||||||
|
LOGD("Switched capture device, new sample rate %d", hardwareSampleRate);
|
||||||
|
|
||||||
|
this->currentDevice=deviceID;
|
||||||
|
|
||||||
|
AudioObjectPropertyAddress propertyAddress = {
|
||||||
|
kAudioDevicePropertyBufferFrameSize,
|
||||||
|
kAudioObjectPropertyScopeGlobal,
|
||||||
|
kAudioObjectPropertyElementMaster
|
||||||
|
};
|
||||||
|
size=4;
|
||||||
|
UInt32 bufferFrameSize;
|
||||||
|
status=AudioObjectGetPropertyData(inputDevice, &propertyAddress, 0, NULL, &size, &bufferFrameSize);
|
||||||
|
if(status==noErr){
|
||||||
|
estimatedDelay=bufferFrameSize/48;
|
||||||
|
LOGD("CoreAudio buffer size for output device is %u frames (%u ms)", bufferFrameSize, estimatedDelay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OSStatus AudioInputAudioUnitLegacy::DefaultDeviceChangedCallback(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inClientData){
|
||||||
|
LOGV("System default input device changed");
|
||||||
|
AudioInputAudioUnitLegacy* self=(AudioInputAudioUnitLegacy*)inClientData;
|
||||||
|
if(self->currentDevice=="default"){
|
||||||
|
self->SetCurrentDevice(self->currentDevice);
|
||||||
|
}
|
||||||
|
return noErr;
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LIBTGVOIP_AUDIOINPUTAUDIOUNIT_OSX_H
|
||||||
|
#define LIBTGVOIP_AUDIOINPUTAUDIOUNIT_OSX_H
|
||||||
|
|
||||||
|
#include <AudioUnit/AudioUnit.h>
|
||||||
|
#import <AudioToolbox/AudioToolbox.h>
|
||||||
|
#import <CoreAudio/CoreAudio.h>
|
||||||
|
#include "../../audio/AudioInput.h"
|
||||||
|
|
||||||
|
namespace tgvoip{ namespace audio{
|
||||||
|
class AudioInputAudioUnitLegacy : public AudioInput{
|
||||||
|
|
||||||
|
public:
|
||||||
|
AudioInputAudioUnitLegacy(std::string deviceID);
|
||||||
|
virtual ~AudioInputAudioUnitLegacy();
|
||||||
|
virtual void Start();
|
||||||
|
virtual void Stop();
|
||||||
|
void HandleBufferCallback(AudioBufferList* ioData);
|
||||||
|
static void EnumerateDevices(std::vector<AudioInputDevice>& devs);
|
||||||
|
virtual void SetCurrentDevice(std::string deviceID);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static OSStatus BufferCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData);
|
||||||
|
static OSStatus DefaultDeviceChangedCallback(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inClientData);
|
||||||
|
unsigned char remainingData[10240];
|
||||||
|
size_t remainingDataSize;
|
||||||
|
bool isRecording;
|
||||||
|
AudioUnit unit;
|
||||||
|
AudioBufferList inBufferList;
|
||||||
|
int hardwareSampleRate;
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
|
||||||
|
#endif //LIBTGVOIP_AUDIOINPUTAUDIOUNIT_OSX_H
|
|
@ -0,0 +1,84 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include "AudioOutputAudioUnit.h"
|
||||||
|
#include "../../logging.h"
|
||||||
|
#include "AudioUnitIO.h"
|
||||||
|
|
||||||
|
#define BUFFER_SIZE 960
|
||||||
|
|
||||||
|
using namespace tgvoip;
|
||||||
|
using namespace tgvoip::audio;
|
||||||
|
|
||||||
|
AudioOutputAudioUnit::AudioOutputAudioUnit(std::string deviceID, AudioUnitIO* io){
|
||||||
|
isPlaying=false;
|
||||||
|
remainingDataSize=0;
|
||||||
|
this->io=io;
|
||||||
|
#if TARGET_OS_OSX
|
||||||
|
io->SetCurrentDevice(false, deviceID);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioOutputAudioUnit::~AudioOutputAudioUnit(){
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputAudioUnit::Start(){
|
||||||
|
isPlaying=true;
|
||||||
|
io->EnableOutput(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputAudioUnit::Stop(){
|
||||||
|
isPlaying=false;
|
||||||
|
io->EnableOutput(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AudioOutputAudioUnit::IsPlaying(){
|
||||||
|
return isPlaying;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputAudioUnit::HandleBufferCallback(AudioBufferList *ioData){
|
||||||
|
int i;
|
||||||
|
for(i=0;i<ioData->mNumberBuffers;i++){
|
||||||
|
AudioBuffer buf=ioData->mBuffers[i];
|
||||||
|
if(!isPlaying){
|
||||||
|
memset(buf.mData, 0, buf.mDataByteSize);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#if TARGET_OS_OSX
|
||||||
|
unsigned int k;
|
||||||
|
while(remainingDataSize<buf.mDataByteSize/2){
|
||||||
|
assert(remainingDataSize+BUFFER_SIZE*2<sizeof(remainingData));
|
||||||
|
InvokeCallback(remainingData+remainingDataSize, BUFFER_SIZE*2);
|
||||||
|
remainingDataSize+=BUFFER_SIZE*2;
|
||||||
|
}
|
||||||
|
float* dst=reinterpret_cast<float*>(buf.mData);
|
||||||
|
int16_t* src=reinterpret_cast<int16_t*>(remainingData);
|
||||||
|
for(k=0;k<buf.mDataByteSize/4;k++){
|
||||||
|
dst[k]=src[k]/(float)INT16_MAX;
|
||||||
|
}
|
||||||
|
remainingDataSize-=buf.mDataByteSize/2;
|
||||||
|
memmove(remainingData, remainingData+buf.mDataByteSize/2, remainingDataSize);
|
||||||
|
#else
|
||||||
|
while(remainingDataSize<buf.mDataByteSize){
|
||||||
|
assert(remainingDataSize+BUFFER_SIZE*2<sizeof(remainingData));
|
||||||
|
InvokeCallback(remainingData+remainingDataSize, BUFFER_SIZE*2);
|
||||||
|
remainingDataSize+=BUFFER_SIZE*2;
|
||||||
|
}
|
||||||
|
memcpy(buf.mData, remainingData, buf.mDataByteSize);
|
||||||
|
remainingDataSize-=buf.mDataByteSize;
|
||||||
|
memmove(remainingData, remainingData+buf.mDataByteSize, remainingDataSize);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if TARGET_OS_OSX
|
||||||
|
void AudioOutputAudioUnit::SetCurrentDevice(std::string deviceID){
|
||||||
|
io->SetCurrentDevice(false, deviceID);
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,38 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LIBTGVOIP_AUDIOOUTPUTAUDIOUNIT_H
|
||||||
|
#define LIBTGVOIP_AUDIOOUTPUTAUDIOUNIT_H
|
||||||
|
|
||||||
|
#include <AudioUnit/AudioUnit.h>
|
||||||
|
#include "../../audio/AudioOutput.h"
|
||||||
|
#include "../../utils.h"
|
||||||
|
|
||||||
|
namespace tgvoip{ namespace audio{
|
||||||
|
class AudioUnitIO;
|
||||||
|
|
||||||
|
class AudioOutputAudioUnit : public AudioOutput{
|
||||||
|
public:
|
||||||
|
TGVOIP_DISALLOW_COPY_AND_ASSIGN(AudioOutputAudioUnit);
|
||||||
|
AudioOutputAudioUnit(std::string deviceID, AudioUnitIO* io);
|
||||||
|
virtual ~AudioOutputAudioUnit();
|
||||||
|
virtual void Start();
|
||||||
|
virtual void Stop();
|
||||||
|
virtual bool IsPlaying();
|
||||||
|
void HandleBufferCallback(AudioBufferList* ioData);
|
||||||
|
#if TARGET_OS_OSX
|
||||||
|
virtual void SetCurrentDevice(std::string deviceID);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool isPlaying;
|
||||||
|
unsigned char remainingData[10240];
|
||||||
|
size_t remainingDataSize;
|
||||||
|
AudioUnitIO* io;
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
|
||||||
|
#endif //LIBTGVOIP_AUDIOOUTPUTAUDIOUNIT_H
|
|
@ -0,0 +1,365 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
#include "AudioOutputAudioUnitOSX.h"
|
||||||
|
#include "../../logging.h"
|
||||||
|
#include "../../VoIPController.h"
|
||||||
|
|
||||||
|
#define BUFFER_SIZE 960
|
||||||
|
#define CHECK_AU_ERROR(res, msg) if(res!=noErr){ LOGE("output: " msg": OSStatus=%d", (int)res); return; }
|
||||||
|
|
||||||
|
#define kOutputBus 0
|
||||||
|
#define kInputBus 1
|
||||||
|
|
||||||
|
using namespace tgvoip;
|
||||||
|
using namespace tgvoip::audio;
|
||||||
|
|
||||||
|
AudioOutputAudioUnitLegacy::AudioOutputAudioUnitLegacy(std::string deviceID){
|
||||||
|
remainingDataSize=0;
|
||||||
|
isPlaying=false;
|
||||||
|
sysDevID=0;
|
||||||
|
|
||||||
|
OSStatus status;
|
||||||
|
AudioComponentDescription inputDesc={
|
||||||
|
.componentType = kAudioUnitType_Output, .componentSubType = kAudioUnitSubType_HALOutput, .componentFlags = 0, .componentFlagsMask = 0,
|
||||||
|
.componentManufacturer = kAudioUnitManufacturer_Apple
|
||||||
|
};
|
||||||
|
AudioComponent component=AudioComponentFindNext(NULL, &inputDesc);
|
||||||
|
status=AudioComponentInstanceNew(component, &unit);
|
||||||
|
CHECK_AU_ERROR(status, "Error creating AudioUnit");
|
||||||
|
|
||||||
|
UInt32 flag=1;
|
||||||
|
status = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flag, sizeof(flag));
|
||||||
|
CHECK_AU_ERROR(status, "Error enabling AudioUnit output");
|
||||||
|
flag=0;
|
||||||
|
status = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag));
|
||||||
|
CHECK_AU_ERROR(status, "Error enabling AudioUnit input");
|
||||||
|
|
||||||
|
char model[128];
|
||||||
|
memset(model, 0, sizeof(model));
|
||||||
|
size_t msize=sizeof(model);
|
||||||
|
int mres=sysctlbyname("hw.model", model, &msize, NULL, 0);
|
||||||
|
if(mres==0){
|
||||||
|
LOGV("Mac model: %s", model);
|
||||||
|
isMacBookPro=(strncmp("MacBookPro", model, 10)==0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetCurrentDevice(deviceID);
|
||||||
|
|
||||||
|
CFRunLoopRef theRunLoop = NULL;
|
||||||
|
AudioObjectPropertyAddress propertyAddress = { kAudioHardwarePropertyRunLoop,
|
||||||
|
kAudioObjectPropertyScopeGlobal,
|
||||||
|
kAudioObjectPropertyElementMaster };
|
||||||
|
status = AudioObjectSetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
|
||||||
|
|
||||||
|
propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
|
||||||
|
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
|
||||||
|
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
|
||||||
|
AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propertyAddress, AudioOutputAudioUnitLegacy::DefaultDeviceChangedCallback, this);
|
||||||
|
|
||||||
|
AudioStreamBasicDescription desiredFormat={
|
||||||
|
.mSampleRate=/*hardwareFormat.mSampleRate*/48000, .mFormatID=kAudioFormatLinearPCM, .mFormatFlags=kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked | kAudioFormatFlagsNativeEndian,
|
||||||
|
.mFramesPerPacket=1, .mChannelsPerFrame=1, .mBitsPerChannel=16, .mBytesPerPacket=2, .mBytesPerFrame=2
|
||||||
|
};
|
||||||
|
|
||||||
|
status=AudioUnitSetProperty(unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &desiredFormat, sizeof(desiredFormat));
|
||||||
|
CHECK_AU_ERROR(status, "Error setting format");
|
||||||
|
|
||||||
|
AURenderCallbackStruct callbackStruct;
|
||||||
|
callbackStruct.inputProc = AudioOutputAudioUnitLegacy::BufferCallback;
|
||||||
|
callbackStruct.inputProcRefCon=this;
|
||||||
|
status = AudioUnitSetProperty(unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, kOutputBus, &callbackStruct, sizeof(callbackStruct));
|
||||||
|
CHECK_AU_ERROR(status, "Error setting input buffer callback");
|
||||||
|
status=AudioUnitInitialize(unit);
|
||||||
|
CHECK_AU_ERROR(status, "Error initializing unit");
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioOutputAudioUnitLegacy::~AudioOutputAudioUnitLegacy(){
|
||||||
|
AudioObjectPropertyAddress propertyAddress;
|
||||||
|
propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
|
||||||
|
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
|
||||||
|
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
|
||||||
|
AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propertyAddress, AudioOutputAudioUnitLegacy::DefaultDeviceChangedCallback, this);
|
||||||
|
|
||||||
|
AudioObjectPropertyAddress dataSourceProp={
|
||||||
|
kAudioDevicePropertyDataSource,
|
||||||
|
kAudioDevicePropertyScopeOutput,
|
||||||
|
kAudioObjectPropertyElementMaster
|
||||||
|
};
|
||||||
|
if(isMacBookPro && sysDevID && AudioObjectHasProperty(sysDevID, &dataSourceProp)){
|
||||||
|
AudioObjectRemovePropertyListener(sysDevID, &dataSourceProp, AudioOutputAudioUnitLegacy::DefaultDeviceChangedCallback, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioUnitUninitialize(unit);
|
||||||
|
AudioComponentInstanceDispose(unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputAudioUnitLegacy::Start(){
|
||||||
|
isPlaying=true;
|
||||||
|
OSStatus status=AudioOutputUnitStart(unit);
|
||||||
|
CHECK_AU_ERROR(status, "Error starting AudioUnit");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputAudioUnitLegacy::Stop(){
|
||||||
|
isPlaying=false;
|
||||||
|
OSStatus status=AudioOutputUnitStart(unit);
|
||||||
|
CHECK_AU_ERROR(status, "Error stopping AudioUnit");
|
||||||
|
}
|
||||||
|
|
||||||
|
OSStatus AudioOutputAudioUnitLegacy::BufferCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData){
|
||||||
|
AudioOutputAudioUnitLegacy* input=(AudioOutputAudioUnitLegacy*) inRefCon;
|
||||||
|
input->HandleBufferCallback(ioData);
|
||||||
|
return noErr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AudioOutputAudioUnitLegacy::IsPlaying(){
|
||||||
|
return isPlaying;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputAudioUnitLegacy::HandleBufferCallback(AudioBufferList *ioData){
|
||||||
|
int i;
|
||||||
|
for(i=0;i<ioData->mNumberBuffers;i++){
|
||||||
|
AudioBuffer buf=ioData->mBuffers[i];
|
||||||
|
if(!isPlaying){
|
||||||
|
memset(buf.mData, 0, buf.mDataByteSize);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while(remainingDataSize<buf.mDataByteSize){
|
||||||
|
assert(remainingDataSize+BUFFER_SIZE*2<10240);
|
||||||
|
InvokeCallback(remainingData+remainingDataSize, BUFFER_SIZE*2);
|
||||||
|
remainingDataSize+=BUFFER_SIZE*2;
|
||||||
|
}
|
||||||
|
memcpy(buf.mData, remainingData, buf.mDataByteSize);
|
||||||
|
remainingDataSize-=buf.mDataByteSize;
|
||||||
|
memmove(remainingData, remainingData+buf.mDataByteSize, remainingDataSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AudioOutputAudioUnitLegacy::EnumerateDevices(std::vector<AudioOutputDevice>& devs){
|
||||||
|
AudioObjectPropertyAddress propertyAddress = {
|
||||||
|
kAudioHardwarePropertyDevices,
|
||||||
|
kAudioObjectPropertyScopeGlobal,
|
||||||
|
kAudioObjectPropertyElementMaster
|
||||||
|
};
|
||||||
|
|
||||||
|
UInt32 dataSize = 0;
|
||||||
|
OSStatus status = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize);
|
||||||
|
if(kAudioHardwareNoError != status) {
|
||||||
|
LOGE("AudioObjectGetPropertyDataSize (kAudioHardwarePropertyDevices) failed: %i", status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt32 deviceCount = (UInt32)(dataSize / sizeof(AudioDeviceID));
|
||||||
|
|
||||||
|
|
||||||
|
AudioDeviceID *audioDevices = (AudioDeviceID*)(malloc(dataSize));
|
||||||
|
|
||||||
|
status = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize, audioDevices);
|
||||||
|
if(kAudioHardwareNoError != status) {
|
||||||
|
LOGE("AudioObjectGetPropertyData (kAudioHardwarePropertyDevices) failed: %i", status);
|
||||||
|
free(audioDevices);
|
||||||
|
audioDevices = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Iterate through all the devices and determine which are input-capable
|
||||||
|
propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
|
||||||
|
for(UInt32 i = 0; i < deviceCount; ++i) {
|
||||||
|
// Query device UID
|
||||||
|
CFStringRef deviceUID = NULL;
|
||||||
|
dataSize = sizeof(deviceUID);
|
||||||
|
propertyAddress.mSelector = kAudioDevicePropertyDeviceUID;
|
||||||
|
status = AudioObjectGetPropertyData(audioDevices[i], &propertyAddress, 0, NULL, &dataSize, &deviceUID);
|
||||||
|
if(kAudioHardwareNoError != status) {
|
||||||
|
LOGE("AudioObjectGetPropertyData (kAudioDevicePropertyDeviceUID) failed: %i", status);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query device name
|
||||||
|
CFStringRef deviceName = NULL;
|
||||||
|
dataSize = sizeof(deviceName);
|
||||||
|
propertyAddress.mSelector = kAudioDevicePropertyDeviceNameCFString;
|
||||||
|
status = AudioObjectGetPropertyData(audioDevices[i], &propertyAddress, 0, NULL, &dataSize, &deviceName);
|
||||||
|
if(kAudioHardwareNoError != status) {
|
||||||
|
LOGE("AudioObjectGetPropertyData (kAudioDevicePropertyDeviceNameCFString) failed: %i", status);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine if the device is an input device (it is an input device if it has input channels)
|
||||||
|
dataSize = 0;
|
||||||
|
propertyAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
|
||||||
|
status = AudioObjectGetPropertyDataSize(audioDevices[i], &propertyAddress, 0, NULL, &dataSize);
|
||||||
|
if(kAudioHardwareNoError != status) {
|
||||||
|
LOGE("AudioObjectGetPropertyDataSize (kAudioDevicePropertyStreamConfiguration) failed: %i", status);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioBufferList *bufferList = (AudioBufferList*)(malloc(dataSize));
|
||||||
|
|
||||||
|
status = AudioObjectGetPropertyData(audioDevices[i], &propertyAddress, 0, NULL, &dataSize, bufferList);
|
||||||
|
if(kAudioHardwareNoError != status || 0 == bufferList->mNumberBuffers) {
|
||||||
|
if(kAudioHardwareNoError != status)
|
||||||
|
LOGE("AudioObjectGetPropertyData (kAudioDevicePropertyStreamConfiguration) failed: %i", status);
|
||||||
|
free(bufferList);
|
||||||
|
bufferList = NULL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(bufferList);
|
||||||
|
bufferList = NULL;
|
||||||
|
|
||||||
|
AudioOutputDevice dev;
|
||||||
|
char buf[1024];
|
||||||
|
CFStringGetCString(deviceName, buf, 1024, kCFStringEncodingUTF8);
|
||||||
|
dev.displayName=std::string(buf);
|
||||||
|
CFStringGetCString(deviceUID, buf, 1024, kCFStringEncodingUTF8);
|
||||||
|
dev.id=std::string(buf);
|
||||||
|
if(dev.id.rfind("VPAUAggregateAudioDevice-0x")==0)
|
||||||
|
continue;
|
||||||
|
devs.push_back(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(audioDevices);
|
||||||
|
audioDevices = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputAudioUnitLegacy::SetCurrentDevice(std::string deviceID){
|
||||||
|
UInt32 size=sizeof(AudioDeviceID);
|
||||||
|
AudioDeviceID outputDevice=0;
|
||||||
|
OSStatus status;
|
||||||
|
AudioObjectPropertyAddress dataSourceProp={
|
||||||
|
kAudioDevicePropertyDataSource,
|
||||||
|
kAudioDevicePropertyScopeOutput,
|
||||||
|
kAudioObjectPropertyElementMaster
|
||||||
|
};
|
||||||
|
|
||||||
|
if(isMacBookPro && sysDevID && AudioObjectHasProperty(sysDevID, &dataSourceProp)){
|
||||||
|
AudioObjectRemovePropertyListener(sysDevID, &dataSourceProp, AudioOutputAudioUnitLegacy::DefaultDeviceChangedCallback, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(deviceID=="default"){
|
||||||
|
AudioObjectPropertyAddress propertyAddress;
|
||||||
|
propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
|
||||||
|
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
|
||||||
|
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
|
||||||
|
UInt32 propsize = sizeof(AudioDeviceID);
|
||||||
|
status = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &propsize, &outputDevice);
|
||||||
|
CHECK_AU_ERROR(status, "Error getting default input device");
|
||||||
|
}else{
|
||||||
|
AudioObjectPropertyAddress propertyAddress = {
|
||||||
|
kAudioHardwarePropertyDevices,
|
||||||
|
kAudioObjectPropertyScopeGlobal,
|
||||||
|
kAudioObjectPropertyElementMaster
|
||||||
|
};
|
||||||
|
UInt32 dataSize = 0;
|
||||||
|
status = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize);
|
||||||
|
CHECK_AU_ERROR(status, "Error getting devices size");
|
||||||
|
UInt32 deviceCount = (UInt32)(dataSize / sizeof(AudioDeviceID));
|
||||||
|
AudioDeviceID audioDevices[deviceCount];
|
||||||
|
status = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize, audioDevices);
|
||||||
|
CHECK_AU_ERROR(status, "Error getting device list");
|
||||||
|
for(UInt32 i = 0; i < deviceCount; ++i) {
|
||||||
|
// Query device UID
|
||||||
|
CFStringRef deviceUID = NULL;
|
||||||
|
dataSize = sizeof(deviceUID);
|
||||||
|
propertyAddress.mSelector = kAudioDevicePropertyDeviceUID;
|
||||||
|
status = AudioObjectGetPropertyData(audioDevices[i], &propertyAddress, 0, NULL, &dataSize, &deviceUID);
|
||||||
|
CHECK_AU_ERROR(status, "Error getting device uid");
|
||||||
|
char buf[1024];
|
||||||
|
CFStringGetCString(deviceUID, buf, 1024, kCFStringEncodingUTF8);
|
||||||
|
if(deviceID==buf){
|
||||||
|
LOGV("Found device for id %s", buf);
|
||||||
|
outputDevice=audioDevices[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!outputDevice){
|
||||||
|
LOGW("Requested device not found, using default");
|
||||||
|
SetCurrentDevice("default");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
status =AudioUnitSetProperty(unit,
|
||||||
|
kAudioOutputUnitProperty_CurrentDevice,
|
||||||
|
kAudioUnitScope_Global,
|
||||||
|
kOutputBus,
|
||||||
|
&outputDevice,
|
||||||
|
size);
|
||||||
|
CHECK_AU_ERROR(status, "Error setting output device");
|
||||||
|
|
||||||
|
AudioStreamBasicDescription hardwareFormat;
|
||||||
|
size=sizeof(hardwareFormat);
|
||||||
|
status=AudioUnitGetProperty(unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kOutputBus, &hardwareFormat, &size);
|
||||||
|
CHECK_AU_ERROR(status, "Error getting hardware format");
|
||||||
|
hardwareSampleRate=hardwareFormat.mSampleRate;
|
||||||
|
|
||||||
|
AudioStreamBasicDescription desiredFormat={
|
||||||
|
.mSampleRate=48000, .mFormatID=kAudioFormatLinearPCM, .mFormatFlags=kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked | kAudioFormatFlagsNativeEndian,
|
||||||
|
.mFramesPerPacket=1, .mChannelsPerFrame=1, .mBitsPerChannel=16, .mBytesPerPacket=2, .mBytesPerFrame=2
|
||||||
|
};
|
||||||
|
|
||||||
|
status=AudioUnitSetProperty(unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &desiredFormat, sizeof(desiredFormat));
|
||||||
|
CHECK_AU_ERROR(status, "Error setting format");
|
||||||
|
|
||||||
|
LOGD("Switched playback device, new sample rate %d", hardwareSampleRate);
|
||||||
|
|
||||||
|
this->currentDevice=deviceID;
|
||||||
|
sysDevID=outputDevice;
|
||||||
|
|
||||||
|
AudioObjectPropertyAddress propertyAddress = {
|
||||||
|
kAudioDevicePropertyBufferFrameSize,
|
||||||
|
kAudioObjectPropertyScopeGlobal,
|
||||||
|
kAudioObjectPropertyElementMaster
|
||||||
|
};
|
||||||
|
size=4;
|
||||||
|
UInt32 bufferFrameSize;
|
||||||
|
status=AudioObjectGetPropertyData(outputDevice, &propertyAddress, 0, NULL, &size, &bufferFrameSize);
|
||||||
|
if(status==noErr){
|
||||||
|
estimatedDelay=bufferFrameSize/48;
|
||||||
|
LOGD("CoreAudio buffer size for output device is %u frames (%u ms)", bufferFrameSize, estimatedDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isMacBookPro){
|
||||||
|
if(AudioObjectHasProperty(outputDevice, &dataSourceProp)){
|
||||||
|
UInt32 dataSource;
|
||||||
|
size=4;
|
||||||
|
AudioObjectGetPropertyData(outputDevice, &dataSourceProp, 0, NULL, &size, &dataSource);
|
||||||
|
SetPanRight(dataSource=='ispk');
|
||||||
|
AudioObjectAddPropertyListener(outputDevice, &dataSourceProp, AudioOutputAudioUnitLegacy::DefaultDeviceChangedCallback, this);
|
||||||
|
}else{
|
||||||
|
SetPanRight(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputAudioUnitLegacy::SetPanRight(bool panRight){
|
||||||
|
LOGI("%sabling pan right on macbook pro", panRight ? "En" : "Dis");
|
||||||
|
int32_t channelMap[]={panRight ? -1 : 0, 0};
|
||||||
|
OSStatus status=AudioUnitSetProperty(unit, kAudioOutputUnitProperty_ChannelMap, kAudioUnitScope_Global, kOutputBus, channelMap, sizeof(channelMap));
|
||||||
|
CHECK_AU_ERROR(status, "Error setting channel map");
|
||||||
|
}
|
||||||
|
|
||||||
|
OSStatus AudioOutputAudioUnitLegacy::DefaultDeviceChangedCallback(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inClientData){
|
||||||
|
AudioOutputAudioUnitLegacy* self=(AudioOutputAudioUnitLegacy*)inClientData;
|
||||||
|
if(inAddresses[0].mSelector==kAudioHardwarePropertyDefaultOutputDevice){
|
||||||
|
LOGV("System default input device changed");
|
||||||
|
if(self->currentDevice=="default"){
|
||||||
|
self->SetCurrentDevice(self->currentDevice);
|
||||||
|
}
|
||||||
|
}else if(inAddresses[0].mSelector==kAudioDevicePropertyDataSource){
|
||||||
|
UInt32 dataSource;
|
||||||
|
UInt32 size=4;
|
||||||
|
AudioObjectGetPropertyData(inObjectID, inAddresses, 0, NULL, &size, &dataSource);
|
||||||
|
self->SetPanRight(dataSource=='ispk');
|
||||||
|
}
|
||||||
|
return noErr;
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LIBTGVOIP_AUDIOOUTPUTAUDIOUNIT_OSX_H
|
||||||
|
#define LIBTGVOIP_AUDIOOUTPUTAUDIOUNIT_OSX_H
|
||||||
|
|
||||||
|
#include <AudioUnit/AudioUnit.h>
|
||||||
|
#import <AudioToolbox/AudioToolbox.h>
|
||||||
|
#import <CoreAudio/CoreAudio.h>
|
||||||
|
#include "../../audio/AudioOutput.h"
|
||||||
|
|
||||||
|
namespace tgvoip{ namespace audio{
|
||||||
|
class AudioOutputAudioUnitLegacy : public AudioOutput{
|
||||||
|
|
||||||
|
public:
|
||||||
|
AudioOutputAudioUnitLegacy(std::string deviceID);
|
||||||
|
virtual ~AudioOutputAudioUnitLegacy();
|
||||||
|
virtual void Start();
|
||||||
|
virtual void Stop();
|
||||||
|
virtual bool IsPlaying();
|
||||||
|
void HandleBufferCallback(AudioBufferList* ioData);
|
||||||
|
static void EnumerateDevices(std::vector<AudioOutputDevice>& devs);
|
||||||
|
virtual void SetCurrentDevice(std::string deviceID);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static OSStatus BufferCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData);
|
||||||
|
static OSStatus DefaultDeviceChangedCallback(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inClientData);
|
||||||
|
void SetPanRight(bool panRight);
|
||||||
|
unsigned char remainingData[10240];
|
||||||
|
size_t remainingDataSize;
|
||||||
|
bool isPlaying;
|
||||||
|
AudioUnit unit;
|
||||||
|
int hardwareSampleRate;
|
||||||
|
bool isMacBookPro;
|
||||||
|
AudioDeviceID sysDevID;
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
|
||||||
|
#endif //LIBTGVOIP_AUDIOOUTPUTAUDIOUNIT_OSX_H
|
|
@ -0,0 +1,321 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "AudioUnitIO.h"
|
||||||
|
#include "AudioInputAudioUnit.h"
|
||||||
|
#include "AudioOutputAudioUnit.h"
|
||||||
|
#include "../../logging.h"
|
||||||
|
#include "../../VoIPController.h"
|
||||||
|
#include "../../VoIPServerConfig.h"
|
||||||
|
|
||||||
|
#define CHECK_AU_ERROR(res, msg) if(res!=noErr){ LOGE(msg": OSStatus=%d", (int)res); failed=true; return; }
|
||||||
|
#define BUFFER_SIZE 960 // 20 ms
|
||||||
|
|
||||||
|
#if TARGET_OS_OSX
|
||||||
|
#define INPUT_BUFFER_SIZE 20480
|
||||||
|
#else
|
||||||
|
#define INPUT_BUFFER_SIZE 10240
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define kOutputBus 0
|
||||||
|
#define kInputBus 1
|
||||||
|
|
||||||
|
#if TARGET_OS_OSX && !defined(TGVOIP_NO_OSX_PRIVATE_API)
|
||||||
|
extern "C" {
|
||||||
|
OSStatus AudioDeviceDuck(AudioDeviceID inDevice,
|
||||||
|
Float32 inDuckedLevel,
|
||||||
|
const AudioTimeStamp* __nullable inStartTime,
|
||||||
|
Float32 inRampDuration) __attribute__((weak_import));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace tgvoip;
|
||||||
|
using namespace tgvoip::audio;
|
||||||
|
|
||||||
|
AudioUnitIO::AudioUnitIO(std::string inputDeviceID, std::string outputDeviceID){
|
||||||
|
input=NULL;
|
||||||
|
output=NULL;
|
||||||
|
inputEnabled=false;
|
||||||
|
outputEnabled=false;
|
||||||
|
failed=false;
|
||||||
|
started=false;
|
||||||
|
inBufferList.mBuffers[0].mData=malloc(INPUT_BUFFER_SIZE);
|
||||||
|
inBufferList.mBuffers[0].mDataByteSize=INPUT_BUFFER_SIZE;
|
||||||
|
inBufferList.mNumberBuffers=1;
|
||||||
|
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
|
DarwinSpecific::ConfigureAudioSession();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
OSStatus status;
|
||||||
|
AudioComponentDescription desc;
|
||||||
|
AudioComponent inputComponent;
|
||||||
|
desc.componentType = kAudioUnitType_Output;
|
||||||
|
desc.componentSubType = kAudioUnitSubType_VoiceProcessingIO;
|
||||||
|
desc.componentFlags = 0;
|
||||||
|
desc.componentFlagsMask = 0;
|
||||||
|
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||||
|
inputComponent = AudioComponentFindNext(NULL, &desc);
|
||||||
|
status = AudioComponentInstanceNew(inputComponent, &unit);
|
||||||
|
|
||||||
|
UInt32 flag=1;
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
|
status = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flag, sizeof(flag));
|
||||||
|
CHECK_AU_ERROR(status, "Error enabling AudioUnit output");
|
||||||
|
status = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag));
|
||||||
|
CHECK_AU_ERROR(status, "Error enabling AudioUnit input");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
|
flag=ServerConfig::GetSharedInstance()->GetBoolean("use_ios_vpio_agc", true) ? 1 : 0;
|
||||||
|
#else
|
||||||
|
flag=ServerConfig::GetSharedInstance()->GetBoolean("use_osx_vpio_agc", true) ? 1 : 0;
|
||||||
|
#endif
|
||||||
|
status=AudioUnitSetProperty(unit, kAUVoiceIOProperty_VoiceProcessingEnableAGC, kAudioUnitScope_Global, kInputBus, &flag, sizeof(flag));
|
||||||
|
CHECK_AU_ERROR(status, "Error disabling AGC");
|
||||||
|
|
||||||
|
AudioStreamBasicDescription audioFormat;
|
||||||
|
audioFormat.mSampleRate = 48000;
|
||||||
|
audioFormat.mFormatID = kAudioFormatLinearPCM;
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
|
audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked | kAudioFormatFlagsNativeEndian;
|
||||||
|
audioFormat.mBitsPerChannel = 16;
|
||||||
|
audioFormat.mBytesPerPacket = 2;
|
||||||
|
audioFormat.mBytesPerFrame = 2;
|
||||||
|
#else // OS X
|
||||||
|
audioFormat.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked | kAudioFormatFlagsNativeEndian;
|
||||||
|
audioFormat.mBitsPerChannel = 32;
|
||||||
|
audioFormat.mBytesPerPacket = 4;
|
||||||
|
audioFormat.mBytesPerFrame = 4;
|
||||||
|
#endif
|
||||||
|
audioFormat.mFramesPerPacket = 1;
|
||||||
|
audioFormat.mChannelsPerFrame = 1;
|
||||||
|
|
||||||
|
status = AudioUnitSetProperty(unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &audioFormat, sizeof(audioFormat));
|
||||||
|
CHECK_AU_ERROR(status, "Error setting output format");
|
||||||
|
status = AudioUnitSetProperty(unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &audioFormat, sizeof(audioFormat));
|
||||||
|
CHECK_AU_ERROR(status, "Error setting input format");
|
||||||
|
|
||||||
|
AURenderCallbackStruct callbackStruct;
|
||||||
|
|
||||||
|
callbackStruct.inputProc = AudioUnitIO::BufferCallback;
|
||||||
|
callbackStruct.inputProcRefCon = this;
|
||||||
|
status = AudioUnitSetProperty(unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, kOutputBus, &callbackStruct, sizeof(callbackStruct));
|
||||||
|
CHECK_AU_ERROR(status, "Error setting output buffer callback");
|
||||||
|
status = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, kInputBus, &callbackStruct, sizeof(callbackStruct));
|
||||||
|
CHECK_AU_ERROR(status, "Error setting input buffer callback");
|
||||||
|
|
||||||
|
#if TARGET_OS_OSX
|
||||||
|
CFRunLoopRef theRunLoop = NULL;
|
||||||
|
AudioObjectPropertyAddress propertyAddress = { kAudioHardwarePropertyRunLoop,
|
||||||
|
kAudioObjectPropertyScopeGlobal,
|
||||||
|
kAudioObjectPropertyElementMaster };
|
||||||
|
status = AudioObjectSetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
|
||||||
|
|
||||||
|
propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
|
||||||
|
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
|
||||||
|
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
|
||||||
|
AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propertyAddress, AudioUnitIO::DefaultDeviceChangedCallback, this);
|
||||||
|
propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
|
||||||
|
AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propertyAddress, AudioUnitIO::DefaultDeviceChangedCallback, this);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
input=new AudioInputAudioUnit(inputDeviceID, this);
|
||||||
|
output=new AudioOutputAudioUnit(outputDeviceID, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioUnitIO::~AudioUnitIO(){
|
||||||
|
#if TARGET_OS_OSX
|
||||||
|
AudioObjectPropertyAddress propertyAddress;
|
||||||
|
propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
|
||||||
|
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
|
||||||
|
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
|
||||||
|
AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propertyAddress, AudioUnitIO::DefaultDeviceChangedCallback, this);
|
||||||
|
propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
|
||||||
|
AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propertyAddress, AudioUnitIO::DefaultDeviceChangedCallback, this);
|
||||||
|
#endif
|
||||||
|
AudioOutputUnitStop(unit);
|
||||||
|
AudioUnitUninitialize(unit);
|
||||||
|
AudioComponentInstanceDispose(unit);
|
||||||
|
free(inBufferList.mBuffers[0].mData);
|
||||||
|
delete input;
|
||||||
|
delete output;
|
||||||
|
}
|
||||||
|
|
||||||
|
OSStatus AudioUnitIO::BufferCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData){
|
||||||
|
((AudioUnitIO*)inRefCon)->BufferCallback(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
|
||||||
|
return noErr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioUnitIO::BufferCallback(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 bus, UInt32 numFrames, AudioBufferList *ioData){
|
||||||
|
if(bus==kOutputBus){
|
||||||
|
if(output && outputEnabled){
|
||||||
|
output->HandleBufferCallback(ioData);
|
||||||
|
}else{
|
||||||
|
memset(ioData->mBuffers[0].mData, 0, ioData->mBuffers[0].mDataByteSize);
|
||||||
|
}
|
||||||
|
}else if(bus==kInputBus){
|
||||||
|
inBufferList.mBuffers[0].mDataByteSize=INPUT_BUFFER_SIZE;
|
||||||
|
AudioUnitRender(unit, ioActionFlags, inTimeStamp, bus, numFrames, &inBufferList);
|
||||||
|
if(input && inputEnabled){
|
||||||
|
input->HandleBufferCallback(&inBufferList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioUnitIO::EnableInput(bool enabled){
|
||||||
|
inputEnabled=enabled;
|
||||||
|
StartIfNeeded();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioUnitIO::EnableOutput(bool enabled){
|
||||||
|
outputEnabled=enabled;
|
||||||
|
StartIfNeeded();
|
||||||
|
#if TARGET_OS_OSX && !defined(TGVOIP_NO_OSX_PRIVATE_API)
|
||||||
|
if(actualDuckingEnabled!=duckingEnabled){
|
||||||
|
actualDuckingEnabled=duckingEnabled;
|
||||||
|
AudioDeviceDuck(currentOutputDeviceID, duckingEnabled ? 0.177828f : 1.0f, NULL, 0.1f);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioUnitIO::StartIfNeeded(){
|
||||||
|
if(started)
|
||||||
|
return;
|
||||||
|
started=true;
|
||||||
|
OSStatus status = AudioUnitInitialize(unit);
|
||||||
|
CHECK_AU_ERROR(status, "Error initializing AudioUnit");
|
||||||
|
status=AudioOutputUnitStart(unit);
|
||||||
|
CHECK_AU_ERROR(status, "Error starting AudioUnit");
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioInput* AudioUnitIO::GetInput(){
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioOutput* AudioUnitIO::GetOutput(){
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if TARGET_OS_OSX
|
||||||
|
OSStatus AudioUnitIO::DefaultDeviceChangedCallback(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inClientData){
|
||||||
|
AudioUnitIO* self=(AudioUnitIO*)inClientData;
|
||||||
|
if(inAddresses[0].mSelector==kAudioHardwarePropertyDefaultOutputDevice){
|
||||||
|
LOGV("System default output device changed");
|
||||||
|
if(self->currentOutputDevice=="default"){
|
||||||
|
self->SetCurrentDevice(false, self->currentOutputDevice);
|
||||||
|
}
|
||||||
|
}else if(inAddresses[0].mSelector==kAudioHardwarePropertyDefaultInputDevice){
|
||||||
|
LOGV("System default input device changed");
|
||||||
|
if(self->currentInputDevice=="default"){
|
||||||
|
self->SetCurrentDevice(true, self->currentInputDevice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return noErr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioUnitIO::SetCurrentDevice(bool input, std::string deviceID){
|
||||||
|
LOGV("Setting current %sput device: %s", input ? "in" : "out", deviceID.c_str());
|
||||||
|
if(started){
|
||||||
|
AudioOutputUnitStop(unit);
|
||||||
|
AudioUnitUninitialize(unit);
|
||||||
|
}
|
||||||
|
UInt32 size=sizeof(AudioDeviceID);
|
||||||
|
AudioDeviceID device=0;
|
||||||
|
OSStatus status;
|
||||||
|
|
||||||
|
if(deviceID=="default"){
|
||||||
|
AudioObjectPropertyAddress propertyAddress;
|
||||||
|
propertyAddress.mSelector = input ? kAudioHardwarePropertyDefaultInputDevice : kAudioHardwarePropertyDefaultOutputDevice;
|
||||||
|
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
|
||||||
|
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
|
||||||
|
UInt32 propsize = sizeof(AudioDeviceID);
|
||||||
|
status = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &propsize, &device);
|
||||||
|
CHECK_AU_ERROR(status, "Error getting default device");
|
||||||
|
}else{
|
||||||
|
AudioObjectPropertyAddress propertyAddress = {
|
||||||
|
kAudioHardwarePropertyDevices,
|
||||||
|
kAudioObjectPropertyScopeGlobal,
|
||||||
|
kAudioObjectPropertyElementMaster
|
||||||
|
};
|
||||||
|
UInt32 dataSize = 0;
|
||||||
|
status = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize);
|
||||||
|
CHECK_AU_ERROR(status, "Error getting devices size");
|
||||||
|
UInt32 deviceCount = (UInt32)(dataSize / sizeof(AudioDeviceID));
|
||||||
|
AudioDeviceID audioDevices[deviceCount];
|
||||||
|
status = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize, audioDevices);
|
||||||
|
CHECK_AU_ERROR(status, "Error getting device list");
|
||||||
|
for(UInt32 i = 0; i < deviceCount; ++i) {
|
||||||
|
// Query device UID
|
||||||
|
CFStringRef deviceUID = NULL;
|
||||||
|
dataSize = sizeof(deviceUID);
|
||||||
|
propertyAddress.mSelector = kAudioDevicePropertyDeviceUID;
|
||||||
|
status = AudioObjectGetPropertyData(audioDevices[i], &propertyAddress, 0, NULL, &dataSize, &deviceUID);
|
||||||
|
CHECK_AU_ERROR(status, "Error getting device uid");
|
||||||
|
char buf[1024];
|
||||||
|
CFStringGetCString(deviceUID, buf, 1024, kCFStringEncodingUTF8);
|
||||||
|
if(deviceID==buf){
|
||||||
|
LOGV("Found device for id %s", buf);
|
||||||
|
device=audioDevices[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!device){
|
||||||
|
LOGW("Requested device not found, using default");
|
||||||
|
SetCurrentDevice(input, "default");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
status=AudioUnitSetProperty(unit,
|
||||||
|
kAudioOutputUnitProperty_CurrentDevice,
|
||||||
|
kAudioUnitScope_Global,
|
||||||
|
input ? kInputBus : kOutputBus,
|
||||||
|
&device,
|
||||||
|
size);
|
||||||
|
CHECK_AU_ERROR(status, "Error setting input device");
|
||||||
|
|
||||||
|
if(input)
|
||||||
|
currentInputDevice=deviceID;
|
||||||
|
else
|
||||||
|
currentOutputDevice=deviceID;
|
||||||
|
|
||||||
|
/*AudioObjectPropertyAddress propertyAddress = {
|
||||||
|
kAudioDevicePropertyBufferFrameSize,
|
||||||
|
kAudioObjectPropertyScopeGlobal,
|
||||||
|
kAudioObjectPropertyElementMaster
|
||||||
|
};
|
||||||
|
size=4;
|
||||||
|
UInt32 bufferFrameSize;
|
||||||
|
status=AudioObjectGetPropertyData(device, &propertyAddress, 0, NULL, &size, &bufferFrameSize);
|
||||||
|
if(status==noErr){
|
||||||
|
estimatedDelay=bufferFrameSize/48;
|
||||||
|
LOGD("CoreAudio buffer size for device is %u frames (%u ms)", bufferFrameSize, estimatedDelay);
|
||||||
|
}*/
|
||||||
|
if(started){
|
||||||
|
started=false;
|
||||||
|
StartIfNeeded();
|
||||||
|
}
|
||||||
|
if(!input){
|
||||||
|
currentOutputDeviceID=device;
|
||||||
|
}
|
||||||
|
LOGV("Set current %sput device done", input ? "in" : "out");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioUnitIO::SetDuckingEnabled(bool enabled){
|
||||||
|
duckingEnabled=enabled;
|
||||||
|
#ifndef TGVOIP_NO_OSX_PRIVATE_API
|
||||||
|
if(outputEnabled && duckingEnabled!=actualDuckingEnabled){
|
||||||
|
actualDuckingEnabled=enabled;
|
||||||
|
AudioDeviceDuck(currentOutputDeviceID, enabled ? 0.177828f : 1.0f, NULL, 0.1f);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,55 @@
|
||||||
|
//
|
||||||
|
// libtgvoip is free and unencumbered public domain software.
|
||||||
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||||
|
// you should have received with this source code distribution.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LIBTGVOIP_AUDIOUNITIO_H
|
||||||
|
#define LIBTGVOIP_AUDIOUNITIO_H
|
||||||
|
|
||||||
|
#include <AudioUnit/AudioUnit.h>
|
||||||
|
#include <AudioToolbox/AudioToolbox.h>
|
||||||
|
#include "../../threading.h"
|
||||||
|
#include <string>
|
||||||
|
#include "../../audio/AudioIO.h"
|
||||||
|
|
||||||
|
namespace tgvoip{ namespace audio{
|
||||||
|
class AudioInputAudioUnit;
|
||||||
|
class AudioOutputAudioUnit;
|
||||||
|
|
||||||
|
class AudioUnitIO : public AudioIO{
|
||||||
|
public:
|
||||||
|
AudioUnitIO(std::string inputDeviceID, std::string outputDeviceID);
|
||||||
|
~AudioUnitIO();
|
||||||
|
void EnableInput(bool enabled);
|
||||||
|
void EnableOutput(bool enabled);
|
||||||
|
virtual AudioInput* GetInput();
|
||||||
|
virtual AudioOutput* GetOutput();
|
||||||
|
#if TARGET_OS_OSX
|
||||||
|
void SetCurrentDevice(bool input, std::string deviceID);
|
||||||
|
void SetDuckingEnabled(bool enabled);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
static OSStatus BufferCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData);
|
||||||
|
void BufferCallback(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 bus, UInt32 numFrames, AudioBufferList* ioData);
|
||||||
|
void StartIfNeeded();
|
||||||
|
#if TARGET_OS_OSX
|
||||||
|
static OSStatus DefaultDeviceChangedCallback(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inClientData);
|
||||||
|
std::string currentInputDevice;
|
||||||
|
std::string currentOutputDevice;
|
||||||
|
bool duckingEnabled=true;
|
||||||
|
bool actualDuckingEnabled=true;
|
||||||
|
AudioDeviceID currentOutputDeviceID;
|
||||||
|
#endif
|
||||||
|
AudioComponentInstance unit;
|
||||||
|
AudioInputAudioUnit* input;
|
||||||
|
AudioOutputAudioUnit* output;
|
||||||
|
AudioBufferList inBufferList;
|
||||||
|
bool inputEnabled;
|
||||||
|
bool outputEnabled;
|
||||||
|
bool started;
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
|
||||||
|
#endif /* LIBTGVOIP_AUDIOUNITIO_H */
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue