From f1ed8250dd2620caae597670805228d6ed074c4f Mon Sep 17 00:00:00 2001 From: HUI <15215604969@163.com> Date: Mon, 8 Jun 2020 15:48:24 +0800 Subject: [PATCH] init --- Annotations.java | 736 ++++++++++++++++++++++++++++++++++++++ BeanCovertor.kt | 171 +++++++++ Callback.kt | 20 ++ EnumCovertor.kt | 87 +++++ Extensions.kt | 135 +++++++ MediaObserver.kt | 40 +++ RtcChannel.kt | 188 ++++++++++ RtcChannelEventHandler.kt | 193 ++++++++++ RtcEngine.kt | 417 +++++++++++++++++++++ RtcEngineEventHandler.kt | 460 ++++++++++++++++++++++++ RtcSurfaceView.kt | 82 +++++ RtcTextureView.kt | 46 +++ 12 files changed, 2575 insertions(+) create mode 100644 Annotations.java create mode 100644 BeanCovertor.kt create mode 100644 Callback.kt create mode 100644 EnumCovertor.kt create mode 100644 Extensions.kt create mode 100644 MediaObserver.kt create mode 100644 RtcChannel.kt create mode 100644 RtcChannelEventHandler.kt create mode 100644 RtcEngine.kt create mode 100644 RtcEngineEventHandler.kt create mode 100644 RtcSurfaceView.kt create mode 100644 RtcTextureView.kt diff --git a/Annotations.java b/Annotations.java new file mode 100644 index 000000000..6e4dba31d --- /dev/null +++ b/Annotations.java @@ -0,0 +1,736 @@ +package io.agora.rtc.base; + +import androidx.annotation.IntDef; +import androidx.annotation.StringDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import io.agora.rtc.Constants; +import io.agora.rtc.IMetadataObserver; +import io.agora.rtc.video.BeautyOptions; + +public class Annotations { + + @IntDef({ + AgoraAudioCodecProfileType.LC_AAC, + AgoraAudioCodecProfileType.HE_AAC + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraAudioCodecProfileType { + int LC_AAC = 0; + int HE_AAC = 1; + } + + @IntDef({ + Constants.AUDIO_EQUALIZATION_BAND_31, + Constants.AUDIO_EQUALIZATION_BAND_62, + Constants.AUDIO_EQUALIZATION_BAND_125, + Constants.AUDIO_EQUALIZATION_BAND_250, + Constants.AUDIO_EQUALIZATION_BAND_500, + Constants.AUDIO_EQUALIZATION_BAND_1K, + Constants.AUDIO_EQUALIZATION_BAND_2K, + Constants.AUDIO_EQUALIZATION_BAND_4K, + Constants.AUDIO_EQUALIZATION_BAND_8K, + Constants.AUDIO_EQUALIZATION_BAND_16K + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraAudioEqualizationBandFrequency { + } + + @IntDef({ + Constants.LOCAL_AUDIO_STREAM_ERROR_OK, + Constants.LOCAL_AUDIO_STREAM_ERROR_FAILURE, + Constants.LOCAL_AUDIO_STREAM_ERROR_DEVICE_NO_PERMISSION, + Constants.LOCAL_AUDIO_STREAM_ERROR_DEVICE_BUSY, + Constants.LOCAL_AUDIO_STREAM_ERROR_CAPTURE_FAILURE, + Constants.LOCAL_AUDIO_STREAM_ERROR_ENCODE_FAILURE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraAudioLocalError { + } + + @IntDef({ + Constants.LOCAL_AUDIO_STREAM_STATE_STOPPED, + Constants.LOCAL_AUDIO_STREAM_STATE_CAPTURING, + Constants.LOCAL_AUDIO_STREAM_STATE_ENCODING, + Constants.LOCAL_AUDIO_STREAM_STATE_FAILED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraAudioLocalState { + } + + @IntDef({ + Constants.MEDIA_ENGINE_AUDIO_ERROR_MIXING_OPEN, + Constants.MEDIA_ENGINE_AUDIO_ERROR_MIXING_TOO_FREQUENT, + Constants.MEDIA_ENGINE_AUDIO_EVENT_MIXING_INTERRUPTED_EOF, + AgoraAudioMixingErrorCode.MEDIA_ENGINE_AUDIO_ERROR_OK + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraAudioMixingErrorCode { + int MEDIA_ENGINE_AUDIO_ERROR_OK = 0; + } + + @IntDef({ + Constants.MEDIA_ENGINE_AUDIO_EVENT_MIXING_PLAY, + Constants.MEDIA_ENGINE_AUDIO_EVENT_MIXING_PAUSED, + Constants.MEDIA_ENGINE_AUDIO_EVENT_MIXING_STOPPED, + Constants.MEDIA_ENGINE_AUDIO_EVENT_MIXING_ERROR + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraAudioMixingStateCode { + } + + @IntDef({ + Constants.AUDIO_ROUTE_DEFAULT, + Constants.AUDIO_ROUTE_HEADSET, + Constants.AUDIO_ROUTE_EARPIECE, + Constants.AUDIO_ROUTE_HEADSETNOMIC, + Constants.AUDIO_ROUTE_SPEAKERPHONE, + Constants.AUDIO_ROUTE_LOUDSPEAKER, + Constants.AUDIO_ROUTE_HEADSETBLUETOOTH + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraAudioOutputRouting { + } + + @IntDef({ + Constants.AUDIO_PROFILE_DEFAULT, + Constants.AUDIO_PROFILE_SPEECH_STANDARD, + Constants.AUDIO_PROFILE_MUSIC_STANDARD, + Constants.AUDIO_PROFILE_MUSIC_STANDARD_STEREO, + Constants.AUDIO_PROFILE_MUSIC_HIGH_QUALITY, + Constants.AUDIO_PROFILE_MUSIC_HIGH_QUALITY_STEREO + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraAudioProfile { + } + + @IntDef({ + Constants.RAW_AUDIO_FRAME_OP_MODE_READ_ONLY, + Constants.RAW_AUDIO_FRAME_OP_MODE_WRITE_ONLY, + Constants.RAW_AUDIO_FRAME_OP_MODE_READ_WRITE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraAudioRawFrameOperationMode { + } + + @IntDef({ + Constants.AUDIO_RECORDING_QUALITY_LOW, + Constants.AUDIO_RECORDING_QUALITY_MEDIUM, + Constants.AUDIO_RECORDING_QUALITY_HIGH + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraAudioRecordingQuality { + } + + @IntDef({ + Constants.REMOTE_AUDIO_STATE_STOPPED, + Constants.REMOTE_AUDIO_STATE_STARTING, + Constants.REMOTE_AUDIO_STATE_DECODING, + Constants.REMOTE_AUDIO_STATE_FROZEN, + Constants.REMOTE_AUDIO_STATE_FAILED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraAudioRemoteState { + } + + @IntDef({ + Constants.REMOTE_AUDIO_REASON_INTERNAL, + Constants.REMOTE_AUDIO_REASON_NETWORK_CONGESTION, + Constants.REMOTE_AUDIO_REASON_NETWORK_RECOVERY, + Constants.REMOTE_AUDIO_REASON_LOCAL_MUTED, + Constants.REMOTE_AUDIO_REASON_LOCAL_UNMUTED, + Constants.REMOTE_AUDIO_REASON_REMOTE_MUTED, + Constants.REMOTE_AUDIO_REASON_REMOTE_UNMUTED, + Constants.REMOTE_AUDIO_REASON_REMOTE_OFFLINE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraAudioRemoteStateReason { + } + + @IntDef({ + Constants.AUDIO_REVERB_OFF, + Constants.AUDIO_REVERB_POPULAR, + Constants.AUDIO_REVERB_RNB, + Constants.AUDIO_REVERB_ROCK, + Constants.AUDIO_REVERB_HIPHOP, + Constants.AUDIO_REVERB_VOCAL_CONCERT, + Constants.AUDIO_REVERB_KTV, + Constants.AUDIO_REVERB_STUDIO + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraAudioReverbPreset { + } + + @IntDef({ + Constants.AUDIO_REVERB_DRY_LEVEL, + Constants.AUDIO_REVERB_WET_LEVEL, + Constants.AUDIO_REVERB_ROOM_SIZE, + Constants.AUDIO_REVERB_WET_DELAY, + Constants.AUDIO_REVERB_STRENGTH, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraAudioReverbType { + } + + @IntDef({ + AgoraAudioSampleRateType.TYPE_32000, + AgoraAudioSampleRateType.TYPE_44100, + AgoraAudioSampleRateType.TYPE_48000 + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraAudioSampleRateType { + int TYPE_32000 = 32000; + int TYPE_44100 = 44100; + int TYPE_48000 = 48000; + } + + @IntDef({ + Constants.AUDIO_SCENARIO_DEFAULT, + Constants.AUDIO_SCENARIO_CHATROOM_ENTERTAINMENT, + Constants.AUDIO_SCENARIO_EDUCATION, + Constants.AUDIO_SCENARIO_GAME_STREAMING, + Constants.AUDIO_SCENARIO_SHOWROOM, + Constants.AUDIO_SCENARIO_CHATROOM_GAMING + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraAudioScenario { + } + + @IntDef({ + Constants.VOICE_CHANGER_OFF, + Constants.VOICE_CHANGER_OLDMAN, + Constants.VOICE_CHANGER_BABYBOY, + Constants.VOICE_CHANGER_BABYGIRL, + Constants.VOICE_CHANGER_ZHUBAJIE, + Constants.VOICE_CHANGER_ETHEREAL, + Constants.VOICE_CHANGER_HULK + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraAudioVoiceChanger { + } + + @IntDef({ + AgoraCameraCaptureOutputPreference.CAPTURER_OUTPUT_PREFERENCE_AUTO, + AgoraCameraCaptureOutputPreference.CAPTURER_OUTPUT_PREFERENCE_PERFORMANCE, + AgoraCameraCaptureOutputPreference.CAPTURER_OUTPUT_PREFERENCE_PREVIEW + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraCameraCaptureOutputPreference { + int CAPTURER_OUTPUT_PREFERENCE_AUTO = 0; + int CAPTURER_OUTPUT_PREFERENCE_PERFORMANCE = 1; + int CAPTURER_OUTPUT_PREFERENCE_PREVIEW = 2; + } + + @IntDef({ + AgoraCameraDirection.CAMERA_REAR, + AgoraCameraDirection.CAMERA_FRONT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraCameraDirection { + int CAMERA_REAR = 0; + int CAMERA_FRONT = 1; + } + + @IntDef({ + Constants.RELAY_OK, + Constants.RELAY_ERROR_SERVER_ERROR_RESPONSE, + Constants.RELAY_ERROR_SERVER_NO_RESPONSE, + Constants.RELAY_ERROR_NO_RESOURCE_AVAILABLE, + Constants.RELAY_ERROR_FAILED_JOIN_SRC, + Constants.RELAY_ERROR_FAILED_JOIN_DEST, + Constants.RELAY_ERROR_FAILED_PACKET_RECEIVED_FROM_SRC, + Constants.RELAY_ERROR_FAILED_PACKET_SENT_TO_DEST, + Constants.RELAY_ERROR_SERVER_CONNECTION_LOST, + Constants.RELAY_ERROR_INTERNAL_ERROR, + Constants.RELAY_ERROR_SRC_TOKEN_EXPIRED, + Constants.RELAY_ERROR_DEST_TOKEN_EXPIRED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraChannelMediaRelayError { + } + + @IntDef({ + Constants.RELAY_EVENT_NETWORK_DISCONNECTED, + Constants.RELAY_EVENT_NETWORK_CONNECTED, + Constants.RELAY_EVENT_PACKET_JOINED_SRC_CHANNEL, + Constants.RELAY_EVENT_PACKET_JOINED_DEST_CHANNEL, + Constants.RELAY_EVENT_PACKET_SENT_TO_DEST_CHANNEL, + Constants.RELAY_EVENT_PACKET_RECEIVED_VIDEO_FROM_SRC, + Constants.RELAY_EVENT_PACKET_RECEIVED_AUDIO_FROM_SRC, + Constants.RELAY_EVENT_PACKET_UPDATE_DEST_CHANNEL, + Constants.RELAY_EVENT_PACKET_UPDATE_DEST_CHANNEL_REFUSED, + Constants.RELAY_EVENT_PACKET_UPDATE_DEST_CHANNEL_NOT_CHANGE, + Constants.RELAY_EVENT_PACKET_UPDATE_DEST_CHANNEL_IS_NULL, + Constants.RELAY_EVENT_VIDEO_PROFILE_UPDATE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraChannelMediaRelayEvent { + } + + @IntDef({ + Constants.RELAY_STATE_IDLE, + Constants.RELAY_STATE_CONNECTING, + Constants.RELAY_STATE_RUNNING, + Constants.RELAY_STATE_FAILURE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraChannelMediaRelayState { + } + + @IntDef({ + Constants.CHANNEL_PROFILE_COMMUNICATION, + Constants.CHANNEL_PROFILE_LIVE_BROADCASTING, + Constants.CHANNEL_PROFILE_GAME + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraChannelProfile { + } + + @IntDef({ + Constants.CLIENT_ROLE_BROADCASTER, + Constants.CLIENT_ROLE_AUDIENCE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraClientRole { + } + + @IntDef({ + Constants.CONNECTION_CHANGED_CONNECTING, + Constants.CONNECTION_CHANGED_JOIN_SUCCESS, + Constants.CONNECTION_CHANGED_INTERRUPTED, + Constants.CONNECTION_CHANGED_BANNED_BY_SERVER, + Constants.CONNECTION_CHANGED_JOIN_FAILED, + Constants.CONNECTION_CHANGED_LEAVE_CHANNEL, + Constants.CONNECTION_CHANGED_INVALID_APP_ID, + Constants.CONNECTION_CHANGED_INVALID_CHANNEL_NAME, + Constants.CONNECTION_CHANGED_INVALID_TOKEN, + Constants.CONNECTION_CHANGED_TOKEN_EXPIRED, + Constants.CONNECTION_CHANGED_REJECTED_BY_SERVER, + Constants.CONNECTION_CHANGED_SETTING_PROXY_SERVER, + Constants.CONNECTION_CHANGED_RENEW_TOKEN, + Constants.CONNECTION_CHANGED_CLIENT_IP_ADDRESS_CHANGED, + Constants.CONNECTION_CHANGED_KEEP_ALIVE_TIMEOUT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraConnectionChangedReason { + } + + @IntDef({ + Constants.CONNECTION_STATE_DISCONNECTED, + Constants.CONNECTION_STATE_CONNECTING, + Constants.CONNECTION_STATE_CONNECTED, + Constants.CONNECTION_STATE_RECONNECTING, + Constants.CONNECTION_STATE_FAILED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraConnectionStateType { + } + + @IntDef({ + AgoraDegradationPreference.MAINTAIN_QUALITY, + AgoraDegradationPreference.MAINTAIN_FRAMERATE, + AgoraDegradationPreference.MAINTAIN_BALANCED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraDegradationPreference { + int MAINTAIN_QUALITY = 0; + int MAINTAIN_FRAMERATE = 1; + int MAINTAIN_BALANCED = 2; + } + + @StringDef({ + AgoraEncryptionMode.AES128XTS, + AgoraEncryptionMode.AES256XTS, + AgoraEncryptionMode.AES128ECB + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraEncryptionMode { + String AES128XTS = "aes-128-xts"; + String AES256XTS = "aes-256-xts"; + String AES128ECB = "aes-128-ecb"; + } + + @IntDef({ + Constants.ERR_OK, + Constants.ERR_FAILED, + Constants.ERR_INVALID_ARGUMENT, + Constants.ERR_NOT_READY, + Constants.ERR_NOT_SUPPORTED, + Constants.ERR_REFUSED, + Constants.ERR_BUFFER_TOO_SMALL, + Constants.ERR_NOT_INITIALIZED, + Constants.ERR_NO_PERMISSION, + Constants.ERR_TIMEDOUT, + Constants.ERR_CANCELED, + Constants.ERR_TOO_OFTEN, + Constants.ERR_BIND_SOCKET, + Constants.ERR_NET_DOWN, + Constants.ERR_NET_NOBUFS, + Constants.ERR_JOIN_CHANNEL_REJECTED, + Constants.ERR_LEAVE_CHANNEL_REJECTED, + Constants.ERR_ALREADY_IN_USE, + Constants.ERR_INVALID_APP_ID, + Constants.ERR_INVALID_CHANNEL_NAME, + Constants.ERR_TOKEN_EXPIRED, + Constants.ERR_INVALID_TOKEN, + Constants.ERR_CONNECTION_INTERRUPTED, + Constants.ERR_CONNECTION_LOST, + Constants.ERR_NOT_IN_CHANNEL, + Constants.ERR_SIZE_TOO_LARGE, + Constants.ERR_BITRATE_LIMIT, + Constants.ERR_TOO_MANY_DATA_STREAMS, + Constants.ERR_DECRYPTION_FAILED, + Constants.ERR_CLIENT_IS_BANNED_BY_SERVER, + Constants.ERR_WATERMARK_PARAM, + Constants.ERR_WATERMARK_PATH, + Constants.ERR_WATERMARK_PNG, + Constants.ERR_WATERMARKR_INFO, + Constants.ERR_WATERMARK_ARGB, + Constants.ERR_WATERMARK_READ, + Constants.ERR_ENCRYPTED_STREAM_NOT_ALLOWED_PUBLISHED, + Constants.ERR_INVALID_USER_ACCOUNT, + Constants.ERR_PUBLISH_STREAM_CDN_ERROR, + Constants.ERR_PUBLISH_STREAM_NUM_REACH_LIMIT, + Constants.ERR_PUBLISH_STREAM_NOT_AUTHORIZED, + Constants.ERR_PUBLISH_STREAM_INTERNAL_SERVER_ERROR, + Constants.ERR_PUBLISH_STREAM_NOT_FOUND, + Constants.ERR_PUBLISH_STREAM_FORMAT_NOT_SUPPORTED, + Constants.ERR_LOAD_MEDIA_ENGINE, + Constants.ERR_START_CALL, + Constants.ERR_START_CAMERA, + Constants.ERR_START_VIDEO_RENDER, + Constants.ERR_ADM_GENERAL_ERROR, + Constants.ERR_ADM_JAVA_RESOURCE, + Constants.ERR_ADM_SAMPLE_RATE, + Constants.ERR_ADM_INIT_PLAYOUT, + Constants.ERR_ADM_START_PLAYOUT, + Constants.ERR_ADM_STOP_PLAYOUT, + Constants.ERR_ADM_INIT_RECORDING, + Constants.ERR_ADM_START_RECORDING, + Constants.ERR_ADM_STOP_RECORDING, + Constants.ERR_ADM_RUNTIME_PLAYOUT_ERROR, + Constants.ERR_ADM_RUNTIME_RECORDING_ERROR, + Constants.ERR_ADM_RECORD_AUDIO_FAILED, + Constants.ERR_ADM_INIT_LOOPBACK, + Constants.ERR_ADM_START_LOOPBACK, + Constants.ERR_AUDIO_BT_SCO_FAILED, + Constants.ERR_ADM_NO_RECORDING_DEVICE, + Constants.ERR_ADM_NO_PLAYOUT_DEVICE, + Constants.ERR_VDM_CAMERA_NOT_AUTHORIZED, + Constants.ERR_VCM_UNKNOWN_ERROR, + Constants.ERR_VCM_ENCODER_INIT_ERROR, + Constants.ERR_VCM_ENCODER_ENCODE_ERROR, + Constants.ERR_VCM_ENCODER_SET_ERROR + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraErrorCode { + } + + @IntDef({ + Constants.INJECT_STREAM_STATUS_START_SUCCESS, + Constants.INJECT_STREAM_STATUS_START_ALREADY_EXISTS, + Constants.INJECT_STREAM_STATUS_START_UNAUTHORIZED, + Constants.INJECT_STREAM_STATUS_START_TIMEDOUT, + Constants.INJECT_STREAM_STATUS_START_FAILED, + Constants.INJECT_STREAM_STATUS_STOP_SUCCESS, + Constants.INJECT_STREAM_STATUS_STOP_NOT_FOUND, + Constants.INJECT_STREAM_STATUS_STOP_UNAUTHORIZED, + Constants.INJECT_STREAM_STATUS_STOP_TIMEDOUT, + Constants.INJECT_STREAM_STATUS_STOP_FAILED, + Constants.INJECT_STREAM_STATUS_BROKEN + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraInjectStreamStatus { + } + + @IntDef({ + Constants.LASTMILE_PROBE_RESULT_COMPLETE, + Constants.LASTMILE_PROBE_RESULT_INCOMPLETE_NO_BWE, + Constants.LASTMILE_PROBE_RESULT_UNAVAILABLE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraLastmileProbeResultState { + } + + @IntDef({ + BeautyOptions.LIGHTENING_CONTRAST_LOW, + BeautyOptions.LIGHTENING_CONTRAST_NORMAL, + BeautyOptions.LIGHTENING_CONTRAST_HIGH + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraLighteningContrastLevel { + } + + @IntDef({ + Constants.LOCAL_VIDEO_STREAM_ERROR_OK, + Constants.LOCAL_VIDEO_STREAM_ERROR_FAILURE, + Constants.LOCAL_VIDEO_STREAM_ERROR_DEVICE_NO_PERMISSION, + Constants.LOCAL_VIDEO_STREAM_ERROR_DEVICE_BUSY, + Constants.LOCAL_VIDEO_STREAM_ERROR_CAPTURE_FAILURE, + Constants.LOCAL_VIDEO_STREAM_ERROR_ENCODE_FAILURE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraLocalVideoStreamError { + } + + @IntDef({ + Constants.LOCAL_VIDEO_STREAM_STATE_STOPPED, + Constants.LOCAL_VIDEO_STREAM_STATE_CAPTURING, + Constants.LOCAL_VIDEO_STREAM_STATE_ENCODING, + Constants.LOCAL_VIDEO_STREAM_STATE_FAILED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraLocalVideoStreamState { + } + + @IntDef({ + Constants.LOG_FILTER_OFF, + Constants.LOG_FILTER_DEBUG, + Constants.LOG_FILTER_INFO, + Constants.LOG_FILTER_WARNING, + Constants.LOG_FILTER_ERROR, + Constants.LOG_FILTER_CRITICAL + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraLogFilter { + } + + @IntDef({ + Constants.MEDIA_TYPE_NONE, + Constants.MEDIA_TYPE_AUDIO_ONLY, + Constants.MEDIA_TYPE_VIDEO_ONLY, + Constants.MEDIA_TYPE_AUDIO_AND_VIDEO + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraMediaType { + } + + @IntDef({ + IMetadataObserver.UNKNOWN_METADATA, + IMetadataObserver.VIDEO_METADATA + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraMetadataType { + } + + @IntDef({ + Constants.QUALITY_UNKNOWN, + Constants.QUALITY_EXCELLENT, + Constants.QUALITY_GOOD, + Constants.QUALITY_POOR, + Constants.QUALITY_BAD, + Constants.QUALITY_VBAD, + Constants.QUALITY_DOWN, + Constants.QUALITY_UNSUPPORTED, + Constants.QUALITY_DETECTING + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraNetworkQuality { + } + + @IntDef({ + Constants.NETWORK_TYPE_UNKNOWN, + Constants.NETWORK_TYPE_DISCONNECTED, + Constants.NETWORK_TYPE_LAN, + Constants.NETWORK_TYPE_WIFI, + Constants.NETWORK_TYPE_MOBILE_2G, + Constants.NETWORK_TYPE_MOBILE_3G, + Constants.NETWORK_TYPE_MOBILE_4G + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraNetworkType { + } + + @IntDef({ + Constants.RTMP_STREAM_PUBLISH_ERROR_OK, + Constants.RTMP_STREAM_PUBLISH_ERROR_INVALID_ARGUMENT, + Constants.RTMP_STREAM_PUBLISH_ERROR_ENCRYPTED_STREAM_NOT_ALLOWED, + Constants.RTMP_STREAM_PUBLISH_ERROR_CONNECTION_TIMEOUT, + Constants.RTMP_STREAM_PUBLISH_ERROR_INTERNAL_SERVER_ERROR, + Constants.RTMP_STREAM_PUBLISH_ERROR_RTMP_SERVER_ERROR, + Constants.RTMP_STREAM_PUBLISH_ERROR_TOO_OFTEN, + Constants.RTMP_STREAM_PUBLISH_ERROR_REACH_LIMIT, + Constants.RTMP_STREAM_PUBLISH_ERROR_NOT_AUTHORIZED, + Constants.RTMP_STREAM_PUBLISH_ERROR_STREAM_NOT_FOUND, + Constants.RTMP_STREAM_PUBLISH_ERROR_FORMAT_NOT_SUPPORTED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraRtmpStreamingErrorCode { + } + + @IntDef({ + Constants.RTMP_STREAM_PUBLISH_STATE_IDLE, + Constants.RTMP_STREAM_PUBLISH_STATE_CONNECTING, + Constants.RTMP_STREAM_PUBLISH_STATE_RUNNING, + Constants.RTMP_STREAM_PUBLISH_STATE_RECOVERING, + Constants.RTMP_STREAM_PUBLISH_STATE_FAILURE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraRtmpStreamingState { + } + + @IntDef({ + Constants.STREAM_FALLBACK_OPTION_DISABLED, + Constants.STREAM_FALLBACK_OPTION_VIDEO_STREAM_LOW, + Constants.STREAM_FALLBACK_OPTION_AUDIO_ONLY + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraStreamFallbackOptions { + } + + @IntDef({ + Constants.USER_OFFLINE_QUIT, + Constants.USER_OFFLINE_DROPPED, + Constants.USER_OFFLINE_BECOME_AUDIENCE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraUserOfflineReason { + } + + @IntDef({ + Constants.USER_PRIORITY_HIGH, + Constants.USER_PRIORITY_NORANL + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraUserPriority { + } + + @IntDef({ + AgoraVideoCodecProfileType.BASELINE, + AgoraVideoCodecProfileType.MAIN, + AgoraVideoCodecProfileType.HIGH + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraVideoCodecProfileType { + int BASELINE = 66; + int MAIN = 77; + int HIGH = 100; + } + + @IntDef({ + AgoraVideoFrameRate.FRAME_RATE_FPS_1, + AgoraVideoFrameRate.FRAME_RATE_FPS_7, + AgoraVideoFrameRate.FRAME_RATE_FPS_10, + AgoraVideoFrameRate.FRAME_RATE_FPS_15, + AgoraVideoFrameRate.FRAME_RATE_FPS_24, + AgoraVideoFrameRate.FRAME_RATE_FPS_30 + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraVideoFrameRate { + int FRAME_RATE_FPS_1 = 1; + int FRAME_RATE_FPS_7 = 7; + int FRAME_RATE_FPS_10 = 10; + int FRAME_RATE_FPS_15 = 15; + int FRAME_RATE_FPS_24 = 24; + int FRAME_RATE_FPS_30 = 30; + } + + @IntDef({ + Constants.VIDEO_MIRROR_MODE_AUTO, + Constants.VIDEO_MIRROR_MODE_ENABLED, + Constants.VIDEO_MIRROR_MODE_DISABLED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraVideoMirrorMode { + } + + @IntDef({ + AgoraVideoOutputOrientationMode.ORIENTATION_MODE_ADAPTIVE, + AgoraVideoOutputOrientationMode.ORIENTATION_MODE_FIXED_LANDSCAPE, + AgoraVideoOutputOrientationMode.ORIENTATION_MODE_FIXED_PORTRAIT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraVideoOutputOrientationMode { + int ORIENTATION_MODE_ADAPTIVE = 0; + int ORIENTATION_MODE_FIXED_LANDSCAPE = 1; + int ORIENTATION_MODE_FIXED_PORTRAIT = 2; + } + + @IntDef({ + Constants.ADAPT_NONE, + Constants.ADAPT_UP_BANDWIDTH, + Constants.ADAPT_DOWN_BANDWIDTH + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraVideoQualityAdaptIndication { + int ORIENTATION_MODE_ADAPTIVE = 0; + int ORIENTATION_MODE_FIXED_LANDSCAPE = 1; + int ORIENTATION_MODE_FIXED_PORTRAIT = 2; + } + + @IntDef({ + Constants.REMOTE_VIDEO_STATE_STOPPED, + Constants.REMOTE_VIDEO_STATE_STARTING, + Constants.REMOTE_VIDEO_STATE_DECODING, + Constants.REMOTE_VIDEO_STATE_FROZEN, + Constants.REMOTE_VIDEO_STATE_FAILED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraVideoRemoteState { + } + + @IntDef({ + Constants.REMOTE_VIDEO_STATE_REASON_INTERNAL, + Constants.REMOTE_VIDEO_STATE_REASON_NETWORK_CONGESTION, + Constants.REMOTE_VIDEO_STATE_REASON_NETWORK_RECOVERY, + Constants.REMOTE_VIDEO_STATE_REASON_LOCAL_MUTED, + Constants.REMOTE_VIDEO_STATE_REASON_LOCAL_UNMUTED, + Constants.REMOTE_VIDEO_STATE_REASON_REMOTE_MUTED, + Constants.REMOTE_VIDEO_STATE_REASON_REMOTE_UNMUTED, + Constants.REMOTE_VIDEO_STATE_REASON_REMOTE_OFFLINE, + Constants.REMOTE_VIDEO_STATE_REASON_AUDIO_FALLBACK, + Constants.REMOTE_VIDEO_STATE_REASON_AUDIO_FALLBACK_RECOVERY + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraVideoRemoteStateReason { + } + + @IntDef({ + Constants.RENDER_MODE_HIDDEN, + Constants.RENDER_MODE_FIT, + Constants.RENDER_MODE_ADAPTIVE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraVideoRenderMode { + } + + @IntDef({ + Constants.VIDEO_STREAM_HIGH, + Constants.VIDEO_STREAM_LOW + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraVideoStreamType { + } + + @IntDef({ + Constants.WARN_INVALID_VIEW, + Constants.WARN_INIT_VIDEO, + Constants.WARN_PENDING, + Constants.WARN_NO_AVAILABLE_CHANNEL, + Constants.WARN_LOOKUP_CHANNEL_TIMEOUT, + Constants.WARN_LOOKUP_CHANNEL_REJECTED, + Constants.WARN_OPEN_CHANNEL_TIMEOUT, + Constants.WARN_OPEN_CHANNEL_REJECTED, + Constants.WARN_SWITCH_LIVE_VIDEO_TIMEOUT, + Constants.WARN_SET_CLIENT_ROLE_TIMEOUT, + Constants.WARN_SET_CLIENT_ROLE_NOT_AUTHORIZED, + Constants.WARN_OPEN_CHANNEL_INVALID_TICKET, + Constants.WARN_OPEN_CHANNEL_TRY_NEXT_VOS, + Constants.WARN_AUDIO_MIXING_OPEN_ERROR, + Constants.WARN_ADM_RUNTIME_PLAYOUT_WARNING, + Constants.WARN_ADM_RUNTIME_RECORDING_WARNING, + Constants.WARN_ADM_RECORD_AUDIO_SILENCE, + Constants.WARN_ADM_CALL_INTERRUPTION, + Constants.WARN_ADM_RECORD_AUDIO_LOWLEVEL, + Constants.WARN_ADM_PLAYOUT_AUDIO_LOWLEVEL, + Constants.WARN_ADM_RECORD_IS_OCCUPIED, + Constants.WARN_APM_HOWLING, + Constants.WARN_ADM_GLITCH_STATE, + Constants.WARN_ADM_IMPROPER_SETTINGS + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AgoraWarningCode { + } +} diff --git a/BeanCovertor.kt b/BeanCovertor.kt new file mode 100644 index 000000000..dd88db158 --- /dev/null +++ b/BeanCovertor.kt @@ -0,0 +1,171 @@ +package io.agora.rtc.base + +import android.graphics.Color +import io.agora.rtc.internal.LastmileProbeConfig +import io.agora.rtc.live.LiveInjectStreamConfig +import io.agora.rtc.live.LiveTranscoding +import io.agora.rtc.live.LiveTranscoding.TranscodingUser +import io.agora.rtc.models.ChannelMediaOptions +import io.agora.rtc.video.* + +fun mapToVideoDimensions(map: Map<*, *>): VideoEncoderConfiguration.VideoDimensions { + return VideoEncoderConfiguration.VideoDimensions().apply { + (map["width"] as? Number)?.let { width = it.toInt() } + (map["height"] as? Number)?.let { height = it.toInt() } + } +} + +fun mapToVideoEncoderConfiguration(map: Map<*, *>): VideoEncoderConfiguration { + return VideoEncoderConfiguration().apply { + (map["dimensions"] as? Map<*, *>)?.let { dimensions = mapToVideoDimensions(it) } + (map["frameRate"] as? Number)?.let { frameRate = it.toInt() } + (map["minFrameRate"] as? Number)?.let { minFrameRate = it.toInt() } + (map["bitrate"] as? Number)?.let { bitrate = it.toInt() } + (map["minBitrate"] as? Number)?.let { minBitrate = it.toInt() } + (map["orientationMode"] as? Number)?.let { orientationMode = intToOrientationMode(it.toInt()) } + (map["degradationPrefer"] as? Number)?.let { degradationPrefer = intToDegradationPreference(it.toInt()) } + (map["mirrorMode"] as? Number)?.let { mirrorMode = it.toInt() } + } +} + +fun mapToBeautyOptions(map: Map<*, *>): BeautyOptions { + return BeautyOptions().apply { + (map["lighteningContrastLevel"] as? Number)?.let { lighteningContrastLevel = it.toInt() } + (map["lighteningLevel"] as? Number)?.let { lighteningLevel = it.toFloat() } + (map["smoothnessLevel"] as? Number)?.let { smoothnessLevel = it.toFloat() } + (map["rednessLevel"] as? Number)?.let { rednessLevel = it.toFloat() } + } +} + +fun mapToAgoraImage(map: Map<*, *>): AgoraImage { + return AgoraImage().apply { + (map["url"] as? String)?.let { url = it } + (map["x"] as? Number)?.let { x = it.toInt() } + (map["y"] as? Number)?.let { y = it.toInt() } + (map["width"] as? Number)?.let { width = it.toInt() } + (map["height"] as? Number)?.let { height = it.toInt() } + } +} + +fun mapToTranscodingUser(map: Map<*, *>): TranscodingUser { + return TranscodingUser().apply { + (map["uid"] as? Number)?.let { uid = it.toInt() } + (map["x"] as? Number)?.let { x = it.toInt() } + (map["y"] as? Number)?.let { y = it.toInt() } + (map["width"] as? Number)?.let { width = it.toInt() } + (map["height"] as? Number)?.let { height = it.toInt() } + (map["zOrder"] as? Number)?.let { zOrder = it.toInt() } + (map["alpha"] as? Number)?.let { alpha = it.toFloat() } + (map["audioChannel"] as? Number)?.let { audioChannel = it.toInt() } + } +} + +fun mapToColor(map: Map<*, *>): Int { + return Color.rgb( + (map["red"] as Number).toInt(), + (map["green"] as Number).toInt(), + (map["blue"] as Number).toInt() + ) +} + +fun mapToLiveTranscoding(map: Map<*, *>): LiveTranscoding { + return LiveTranscoding().apply { + (map["width"] as? Number)?.let { width = it.toInt() } + (map["height"] as? Number)?.let { height = it.toInt() } + (map["videoBitrate"] as? Number)?.let { videoBitrate = it.toInt() } + (map["videoFramerate"] as? Number)?.let { videoFramerate = it.toInt() } + (map["lowLatency"] as? Boolean)?.let { lowLatency = it } + (map["videoGop"] as? Number)?.let { videoGop = it.toInt() } + (map["watermark"] as? Map<*, *>)?.let { watermark = mapToAgoraImage(it) } + (map["backgroundImage"] as? Map<*, *>)?.let { backgroundImage = mapToAgoraImage(it) } + (map["audioSampleRate"] as? Number)?.let { audioSampleRate = intToLiveTranscodingAudioSampleRate(it.toInt()) } + (map["audioBitrate"] as? Number)?.let { audioBitrate = it.toInt() } + (map["audioChannels"] as? Number)?.let { audioChannels = it.toInt() } + (map["audioCodecProfile"] as? Number)?.let { audioCodecProfile = intToAudioCodecProfile(it.toInt()) } + (map["videoCodecProfile"] as? Number)?.let { videoCodecProfile = intToVideoCodecProfile(it.toInt()) } + (map["backgroundColor"] as? Map<*, *>)?.let { backgroundColor = mapToColor(it) } + (map["userConfigExtraInfo"] as? String)?.let { userConfigExtraInfo = it } + (map["transcodingUsers"] as? List<*>)?.let { list -> + list.forEach { item -> + (item as? Map<*, *>)?.let { + addUser(mapToTranscodingUser(it)) + } + } + } + } +} + +fun mapToChannelMediaInfo(map: Map<*, *>): ChannelMediaInfo { + return ChannelMediaInfo( + map["channelName"] as String, + map["token"] as String, + (map["uid"] as Number).toInt() + ) +} + +fun mapToChannelMediaRelayConfiguration(map: Map<*, *>): ChannelMediaRelayConfiguration { + return ChannelMediaRelayConfiguration().apply { + (map["srcInfo"] as? Map<*, *>)?.let { setSrcChannelInfo(mapToChannelMediaInfo(it)) } + (map["destInfos"] as? List<*>)?.let { list -> + list.forEach { item -> + (item as? Map<*, *>)?.let { + val info = mapToChannelMediaInfo(it) + setDestChannelInfo(info.channelName, info) + } + } + } + } +} + +fun mapToLastmileProbeConfig(map: Map<*, *>): LastmileProbeConfig { + return LastmileProbeConfig().apply { + (map["probeUplink"] as? Boolean)?.let { probeUplink = it } + (map["probeDownlink"] as? Boolean)?.let { probeDownlink = it } + (map["expectedUplinkBitrate"] as? Number)?.let { expectedUplinkBitrate = it.toInt() } + (map["expectedDownlinkBitrate"] as? Number)?.let { expectedUplinkBitrate = it.toInt() } + } +} + +fun mapToRectangle(map: Map<*, *>): WatermarkOptions.Rectangle { + return WatermarkOptions.Rectangle().apply { + (map["x"] as? Number)?.let { x = it.toInt() } + (map["y"] as? Number)?.let { y = it.toInt() } + (map["width"] as? Number)?.let { width = it.toInt() } + (map["height"] as? Number)?.let { height = it.toInt() } + } +} + +fun mapToWatermarkOptions(map: Map<*, *>): WatermarkOptions { + return WatermarkOptions().apply { + (map["visibleInPreview"] as? Boolean)?.let { visibleInPreview = it } + (map["positionInLandscapeMode"] as? Map<*, *>)?.let { positionInLandscapeMode = mapToRectangle(it) } + (map["positionInPortraitMode"] as? Map<*, *>)?.let { positionInPortraitMode = mapToRectangle(it) } + } +} + +fun mapToLiveInjectStreamConfig(map: Map<*, *>): LiveInjectStreamConfig { + return LiveInjectStreamConfig().apply { + (map["width"] as? Number)?.let { width = it.toInt() } + (map["height"] as? Number)?.let { height = it.toInt() } + (map["videoGop"] as? Number)?.let { videoGop = it.toInt() } + (map["videoFramerate"] as? Number)?.let { videoFramerate = it.toInt() } + (map["videoBitrate"] as? Number)?.let { videoBitrate = it.toInt() } + (map["audioSampleRate"] as? Number)?.let { audioSampleRate = intToLiveInjectStreamConfigAudioSampleRate(it.toInt()) } + (map["audioBitrate"] as? Number)?.let { audioBitrate = it.toInt() } + (map["audioChannels"] as? Number)?.let { audioChannels = it.toInt() } + } +} + +fun mapToCameraCapturerConfiguration(map: Map<*, *>): CameraCapturerConfiguration { + return CameraCapturerConfiguration( + intToCapturerOutputPreference(map["preference"] as Int), + intToCameraDirection(map["cameraDirection"] as Int) + ) +} + +fun mapToChannelMediaOptions(map: Map<*, *>): ChannelMediaOptions { + return ChannelMediaOptions().apply { + (map["autoSubscribeAudio"] as? Boolean)?.let { autoSubscribeAudio = it } + (map["autoSubscribeVideo"] as? Boolean)?.let { autoSubscribeVideo = it } + } +} diff --git a/Callback.kt b/Callback.kt new file mode 100644 index 000000000..affe4c5b8 --- /dev/null +++ b/Callback.kt @@ -0,0 +1,20 @@ +package io.agora.rtc.base + +import io.agora.rtc.Constants +import io.agora.rtc.RtcEngine +import kotlin.math.abs + +abstract class Callback { + fun code(code: Int?) { + val newCode = code ?: Constants.ERR_NOT_INITIALIZED + if (newCode == 0) { + success(null) + } else if (newCode < 0) { + failure(newCode.toString(), RtcEngine.getErrorDescription(abs(newCode))) + } + } + + abstract fun success(data: T?) + + abstract fun failure(code: String, message: String) +} diff --git a/EnumCovertor.kt b/EnumCovertor.kt new file mode 100644 index 000000000..b44665390 --- /dev/null +++ b/EnumCovertor.kt @@ -0,0 +1,87 @@ +package io.agora.rtc.base + +import io.agora.rtc.live.LiveInjectStreamConfig +import io.agora.rtc.live.LiveTranscoding +import io.agora.rtc.video.CameraCapturerConfiguration +import io.agora.rtc.video.VideoEncoderConfiguration + +fun intToFrameRate(@Annotations.AgoraVideoFrameRate intValue: Int): VideoEncoderConfiguration.FRAME_RATE { + for (value in VideoEncoderConfiguration.FRAME_RATE.values()) { + if (value.value == intValue) { + return value + } + } + throw RuntimeException("VideoEncoderConfiguration.FRAME_RATE not contains $intValue") +} + +fun intToOrientationMode(@Annotations.AgoraVideoOutputOrientationMode intValue: Int): VideoEncoderConfiguration.ORIENTATION_MODE { + for (value in VideoEncoderConfiguration.ORIENTATION_MODE.values()) { + if (value.value == intValue) { + return value + } + } + throw RuntimeException("VideoEncoderConfiguration.ORIENTATION_MODE not contains $intValue") +} + +fun intToDegradationPreference(@Annotations.AgoraDegradationPreference intValue: Int): VideoEncoderConfiguration.DEGRADATION_PREFERENCE { + for (value in VideoEncoderConfiguration.DEGRADATION_PREFERENCE.values()) { + if (value.value == intValue) { + return value + } + } + throw RuntimeException("VideoEncoderConfiguration.DEGRADATION_PREFERENCE not contains $intValue") +} + +fun intToLiveTranscodingAudioSampleRate(@Annotations.AgoraAudioSampleRateType intValue: Int): LiveTranscoding.AudioSampleRateType { + for (value in LiveTranscoding.AudioSampleRateType.values()) { + if (LiveTranscoding.AudioSampleRateType.getValue(value) == intValue) { + return value + } + } + throw RuntimeException("LiveTranscoding.AudioSampleRateType not contains $intValue") +} + +fun intToLiveInjectStreamConfigAudioSampleRate(@Annotations.AgoraAudioSampleRateType intValue: Int): LiveInjectStreamConfig.AudioSampleRateType { + for (value in LiveInjectStreamConfig.AudioSampleRateType.values()) { + if (LiveInjectStreamConfig.AudioSampleRateType.getValue(value) == intValue) { + return value + } + } + throw RuntimeException("LiveInjectStreamConfig.AudioSampleRateType not contains $intValue") +} + +fun intToAudioCodecProfile(@Annotations.AgoraAudioCodecProfileType intValue: Int): LiveTranscoding.AudioCodecProfileType { + for (value in LiveTranscoding.AudioCodecProfileType.values()) { + if (LiveTranscoding.AudioCodecProfileType.getValue(value) == intValue) { + return value + } + } + throw RuntimeException("LiveTranscoding.AudioCodecProfileType not contains $intValue") +} + +fun intToVideoCodecProfile(@Annotations.AgoraVideoCodecProfileType intValue: Int): LiveTranscoding.VideoCodecProfileType { + for (value in LiveTranscoding.VideoCodecProfileType.values()) { + if (LiveTranscoding.VideoCodecProfileType.getValue(value) == intValue) { + return value + } + } + throw RuntimeException("LiveTranscoding.VideoCodecProfileType not contains $intValue") +} + +fun intToCapturerOutputPreference(@Annotations.AgoraCameraCaptureOutputPreference intValue: Int): CameraCapturerConfiguration.CAPTURER_OUTPUT_PREFERENCE { + for (value in CameraCapturerConfiguration.CAPTURER_OUTPUT_PREFERENCE.values()) { + if (value.value == intValue) { + return value + } + } + throw RuntimeException("CameraCapturerConfiguration.CAPTURER_OUTPUT_PREFERENCE not contains $intValue") +} + +fun intToCameraDirection(@Annotations.AgoraCameraDirection intValue: Int): CameraCapturerConfiguration.CAMERA_DIRECTION { + for (value in CameraCapturerConfiguration.CAMERA_DIRECTION.values()) { + if (value.value == intValue) { + return value + } + } + throw RuntimeException("CameraCapturerConfiguration.CAMERA_DIRECTION not contains $intValue") +} diff --git a/Extensions.kt b/Extensions.kt new file mode 100644 index 000000000..6066ceac6 --- /dev/null +++ b/Extensions.kt @@ -0,0 +1,135 @@ +package io.agora.rtc.base + +import android.graphics.Rect +import io.agora.rtc.IRtcEngineEventHandler.* +import io.agora.rtc.models.UserInfo + +fun UserInfo.toMap(): Map { + return hashMapOf( + "uid" to uid, + "userAccount" to userAccount + ) +} + +fun LocalAudioStats.toMap(): Map { + return hashMapOf( + "numChannels" to numChannels, + "sentSampleRate" to sentSampleRate, + "sentBitrate" to sentBitrate + ) +} + +fun RtcStats.toMap(): Map { + return hashMapOf( + "totalDuration" to totalDuration, + "txBytes" to txBytes, + "rxBytes" to rxBytes, + "txAudioBytes" to txAudioBytes, + "txVideoBytes" to txVideoBytes, + "rxAudioBytes" to rxAudioBytes, + "rxVideoBytes" to rxVideoBytes, + "txKBitRate" to txKBitRate, + "rxKBitRate" to rxKBitRate, + "txAudioKBitRate" to txAudioKBitRate, + "rxAudioKBitRate" to rxAudioKBitRate, + "txVideoKBitRate" to txVideoKBitRate, + "rxVideoKBitRate" to rxVideoKBitRate, + "users" to users, + "lastmileDelay" to lastmileDelay, + "txPacketLossRate" to txPacketLossRate, + "rxPacketLossRate" to rxPacketLossRate, + "cpuTotalUsage" to cpuTotalUsage, + "cpuAppUsage" to cpuAppUsage, + "gatewayRtt" to gatewayRtt, + "memoryAppUsageRatio" to memoryAppUsageRatio, + "memoryTotalUsageRatio" to memoryTotalUsageRatio, + "memoryAppUsageInKbytes" to memoryAppUsageInKbytes + ) +} + +fun Rect.toMap(): Map { + return hashMapOf( + "left" to left, + "top" to top, + "right" to right, + "bottom" to bottom + ) +} + +fun RemoteAudioStats.toMap(): Map { + return hashMapOf( + "uid" to uid, + "quality" to quality, + "networkTransportDelay" to networkTransportDelay, + "jitterBufferDelay" to jitterBufferDelay, + "audioLossRate" to audioLossRate, + "numChannels" to numChannels, + "receivedSampleRate" to receivedSampleRate, + "receivedBitrate" to receivedBitrate, + "totalFrozenTime" to totalFrozenTime, + "frozenRate" to frozenRate + ) +} + +fun LocalVideoStats.toMap(): Map { + return hashMapOf( + "sentBitrate" to sentBitrate, + "sentFrameRate" to sentFrameRate, + "encoderOutputFrameRate" to encoderOutputFrameRate, + "rendererOutputFrameRate" to rendererOutputFrameRate, + "targetBitrate" to targetBitrate, + "targetFrameRate" to targetFrameRate, + "qualityAdaptIndication" to qualityAdaptIndication, + "encodedBitrate" to encodedBitrate, + "encodedFrameWidth" to encodedFrameWidth, + "encodedFrameHeight" to encodedFrameHeight, + "encodedFrameCount" to encodedFrameCount, + "codecType" to codecType + ) +} + +fun RemoteVideoStats.toMap(): Map { + return hashMapOf( + "uid" to uid, + "delay" to delay, + "width" to width, + "height" to height, + "receivedBitrate" to receivedBitrate, + "decoderOutputFrameRate" to decoderOutputFrameRate, + "rendererOutputFrameRate" to rendererOutputFrameRate, + "packetLossRate" to packetLossRate, + "rxStreamType" to rxStreamType, + "totalFrozenTime" to totalFrozenTime, + "frozenRate" to frozenRate + ) +} + +fun AudioVolumeInfo.toMap(): Map { + return hashMapOf( + "uid" to uid, + "volume" to volume, + "vad" to vad, + "channelId" to channelId + ) +} + +fun Array.toMapList(): List> { + return List(size) { this[it].toMap() } +} + +fun LastmileProbeResult.LastmileProbeOneWayResult.toMap(): Map { + return hashMapOf( + "packetLossRate" to packetLossRate, + "jitter" to jitter, + "availableBandwidth" to availableBandwidth + ) +} + +fun LastmileProbeResult.toMap(): Map { + return hashMapOf( + "state" to state, + "rtt" to rtt, + "uplinkReport" to uplinkReport.toMap(), + "downlinkReport" to downlinkReport.toMap() + ) +} diff --git a/MediaObserver.kt b/MediaObserver.kt new file mode 100644 index 000000000..f7b61bd18 --- /dev/null +++ b/MediaObserver.kt @@ -0,0 +1,40 @@ +package io.agora.rtc.base + +import androidx.annotation.IntRange +import io.agora.rtc.IMetadataObserver +import java.util.* +import java.util.concurrent.atomic.AtomicInteger + +class MediaObserver( + private val emit: (methodName: String, data: Map?) -> Unit +) : IMetadataObserver { + private var maxMetadataSize = AtomicInteger(0) + private var metadataList = Collections.synchronizedList(mutableListOf()) + + fun addMetadata(metadata: String) { + metadataList.add(metadata) + } + + fun setMaxMetadataSize(@IntRange(from = 0, to = 1024) size: Int) { + maxMetadataSize.set(size) + } + + override fun onReadyToSendMetadata(timeStampMs: Long): ByteArray? { + if (metadataList.size > 0) { + return metadataList.removeAt(0).toByteArray() + } + return null + } + + override fun getMaxMetadataSize(): Int { + return maxMetadataSize.get() + } + + override fun onMetadataReceived(buffer: ByteArray, uid: Int, timeStampMs: Long) { + emit("MetadataReceived", hashMapOf( + "buffer" to String(buffer), + "uid" to uid, + "timeStampMs" to timeStampMs + )) + } +} diff --git a/RtcChannel.kt b/RtcChannel.kt new file mode 100644 index 000000000..b73477be4 --- /dev/null +++ b/RtcChannel.kt @@ -0,0 +1,188 @@ +package io.agora.rtc.base + +import androidx.annotation.FloatRange +import androidx.annotation.IntRange +import io.agora.rtc.Constants +import io.agora.rtc.IMetadataObserver +import io.agora.rtc.RtcChannel +import io.agora.rtc.RtcEngine +import java.util.* + +interface RtcChannelInterface : + RtcChannelManager.RtcAudioInterface, + RtcChannelManager.RtcVideoInterface, + RtcChannelManager.RtcVoicePositionInterface, + RtcChannelManager.RtcPublishStreamInterface, + RtcChannelManager.RtcMediaRelayInterface, + RtcChannelManager.RtcDualStreamInterface, + RtcChannelManager.RtcFallbackInterface, + RtcChannelManager.RtcMediaMetadataInterface, + RtcChannelManager.RtcEncryptionInterface, + RtcChannelManager.RtcInjectStreamInterface, + RtcChannelManager.RtcStreamMessageInterface { + fun create(channelId: String, callback: Callback?) + + fun destroy(channelId: String, callback: Callback?) + + fun setClientRole(channelId: String, @Annotations.AgoraClientRole role: Int, callback: Callback?) + + fun joinChannel(channelId: String, token: String?, optionalInfo: String?, optionalUid: Int, options: Map, callback: Callback?) + + fun joinChannelWithUserAccount(channelId: String, token: String?, userAccount: String, options: Map, callback: Callback?) + + fun leaveChannel(channelId: String, callback: Callback?) + + fun renewToken(channelId: String, token: String, callback: Callback?) + + fun getConnectionState(channelId: String, callback: Callback?) + + fun publish(channelId: String, callback: Callback?) + + fun unpublish(channelId: String, callback: Callback?) + + fun getCallId(channelId: String, callback: Callback?) +} + +class RtcChannelManager { + private val rtcChannelMap = Collections.synchronizedMap(mutableMapOf()) + private val mediaObserverMap = Collections.synchronizedMap(mutableMapOf()) + + fun create(engine: RtcEngine, channelId: String, emit: (methodName: String, data: Map?) -> Unit) { + engine.createRtcChannel(channelId)?.let { + it.setRtcChannelEventHandler(RtcChannelEventHandler { methodName, data -> emit(methodName, data) }) + rtcChannelMap[channelId] = it + } + } + + fun destroy(channelId: String): Int { + this[channelId]?.let { + val res = it.destroy() + if (res == 0) rtcChannelMap.remove(channelId) + return@destroy res + } + return Constants.ERR_NOT_INITIALIZED + } + + fun release() { + rtcChannelMap.forEach { it.value.destroy() } + rtcChannelMap.clear() + mediaObserverMap.clear() + } + + operator fun get(channelId: String): RtcChannel? { + return rtcChannelMap[channelId] + } + + fun registerMediaMetadataObserver(channelId: String, emit: (methodName: String, data: Map?) -> Unit): Int { + this[channelId]?.let { + val mediaObserver = MediaObserver { methodName, data -> + emit(methodName, data?.toMutableMap()?.apply { put("channelId", channelId) }) + } + val res = it.registerMediaMetadataObserver(mediaObserver, IMetadataObserver.VIDEO_METADATA) + if (res == 0) mediaObserverMap[channelId] = mediaObserver + return@registerMediaMetadataObserver res + } + return Constants.ERR_NOT_INITIALIZED + } + + fun unregisterMediaMetadataObserver(channelId: String): Int { + this[channelId]?.let { + val res = it.registerMediaMetadataObserver(null, IMetadataObserver.VIDEO_METADATA) + if (res == 0) mediaObserverMap.remove(channelId) + return@unregisterMediaMetadataObserver res + } + return Constants.ERR_NOT_INITIALIZED + } + + fun setMaxMetadataSize(channelId: String, @IntRange(from = 0, to = 1024) size: Int): Int { + mediaObserverMap[channelId]?.let { + it.maxMetadataSize = size + return@setMaxMetadataSize 0 + } + return Constants.ERR_NOT_INITIALIZED + } + + fun addMetadata(channelId: String, metadata: String): Int { + mediaObserverMap[channelId]?.let { + it.addMetadata(metadata) + return@addMetadata 0 + } + return Constants.ERR_NOT_INITIALIZED + } + + interface RtcAudioInterface { + fun adjustUserPlaybackSignalVolume(channelId: String, uid: Int, @IntRange(from = 0, to = 100) volume: Int, callback: Callback?) + + fun muteRemoteAudioStream(channelId: String, uid: Int, muted: Boolean, callback: Callback?) + + fun muteAllRemoteAudioStreams(channelId: String, muted: Boolean, callback: Callback?) + + fun setDefaultMuteAllRemoteAudioStreams(channelId: String, muted: Boolean, callback: Callback?) + } + + interface RtcVideoInterface { + fun muteRemoteVideoStream(channelId: String, uid: Int, muted: Boolean, callback: Callback?) + + fun muteAllRemoteVideoStreams(channelId: String, muted: Boolean, callback: Callback?) + + fun setDefaultMuteAllRemoteVideoStreams(channelId: String, muted: Boolean, callback: Callback?) + } + + interface RtcVoicePositionInterface { + fun setRemoteVoicePosition(channelId: String, uid: Int, @FloatRange(from = -1.0, to = 1.0) pan: Double, @FloatRange(from = 0.0, to = 100.0) gain: Double, callback: Callback?) + } + + interface RtcPublishStreamInterface { + fun setLiveTranscoding(channelId: String, transcoding: Map, callback: Callback?) + + fun addPublishStreamUrl(channelId: String, url: String, transcodingEnabled: Boolean, callback: Callback?) + + fun removePublishStreamUrl(channelId: String, url: String, callback: Callback?) + } + + interface RtcMediaRelayInterface { + fun startChannelMediaRelay(channelId: String, channelMediaRelayConfiguration: Map, callback: Callback?) + + fun updateChannelMediaRelay(channelId: String, channelMediaRelayConfiguration: Map, callback: Callback?) + + fun stopChannelMediaRelay(channelId: String, callback: Callback?) + } + + interface RtcDualStreamInterface { + fun setRemoteVideoStreamType(channelId: String, uid: Int, @Annotations.AgoraVideoStreamType streamType: Int, callback: Callback?) + + fun setRemoteDefaultVideoStreamType(channelId: String, @Annotations.AgoraVideoStreamType streamType: Int, callback: Callback?) + } + + interface RtcFallbackInterface { + fun setRemoteUserPriority(channelId: String, uid: Int, @Annotations.AgoraUserPriority userPriority: Int, callback: Callback?) + } + + interface RtcMediaMetadataInterface { + fun registerMediaMetadataObserver(channelId: String, callback: Callback?) + + fun unregisterMediaMetadataObserver(channelId: String, callback: Callback?) + + fun setMaxMetadataSize(channelId: String, @IntRange(from = 0, to = 1024) size: Int, callback: Callback?) + + fun sendMetadata(channelId: String, metadata: String, callback: Callback?) + } + + interface RtcEncryptionInterface { + fun setEncryptionSecret(channelId: String, secret: String, callback: Callback?) + + fun setEncryptionMode(channelId: String, @Annotations.AgoraEncryptionMode encryptionMode: String, callback: Callback?) + } + + interface RtcInjectStreamInterface { + fun addInjectStreamUrl(channelId: String, url: String, config: Map, callback: Callback?) + + fun removeInjectStreamUrl(channelId: String, url: String, callback: Callback?) + } + + interface RtcStreamMessageInterface { + fun createDataStream(channelId: String, reliable: Boolean, ordered: Boolean, callback: Callback?) + + fun sendStreamMessage(channelId: String, streamId: Int, message: String, callback: Callback?) + } +} diff --git a/RtcChannelEventHandler.kt b/RtcChannelEventHandler.kt new file mode 100644 index 000000000..5bdf2cee6 --- /dev/null +++ b/RtcChannelEventHandler.kt @@ -0,0 +1,193 @@ +package io.agora.rtc.base + +import androidx.annotation.IntRange +import io.agora.rtc.IRtcChannelEventHandler +import io.agora.rtc.IRtcEngineEventHandler +import io.agora.rtc.RtcChannel + +class RtcChannelEventHandler( + private val emitter: (methodName: String, data: Map?) -> Unit +) : IRtcChannelEventHandler() { + companion object { + const val PREFIX = "io.agora.rtc." + val EVENTS = hashMapOf( + "Warning" to "Warning", + "Error" to "Error", + "JoinChannelSuccess" to "JoinChannelSuccess", + "RejoinChannelSuccess" to "RejoinChannelSuccess", + "LeaveChannel" to "LeaveChannel", + "ClientRoleChanged" to "ClientRoleChanged", + "UserJoined" to "UserJoined", + "UserOffline" to "UserOffline", + "ConnectionStateChanged" to "ConnectionStateChanged", + "ConnectionLost" to "ConnectionLost", + "TokenPrivilegeWillExpire" to "TokenPrivilegeWillExpire", + "RequestToken" to "RequestToken", + "ActiveSpeaker" to "ActiveSpeaker", + "VideoSizeChanged" to "VideoSizeChanged", + "RemoteVideoStateChanged" to "RemoteVideoStateChanged", + "RemoteAudioStateChanged" to "RemoteAudioStateChanged", + "LocalPublishFallbackToAudioOnly" to "LocalPublishFallbackToAudioOnly", + "RemoteSubscribeFallbackToAudioOnly" to "RemoteSubscribeFallbackToAudioOnly", + "RtcStats" to "RtcStats", + "NetworkQuality" to "NetworkQuality", + "RemoteVideoStats" to "RemoteVideoStats", + "RemoteAudioStats" to "RemoteAudioStats", + "RtmpStreamingStateChanged" to "RtmpStreamingStateChanged", + "TranscodingUpdated" to "TranscodingUpdated", + "StreamInjectedStatus" to "StreamInjectedStatus", + "StreamMessage" to "StreamMessage", + "StreamMessageError" to "StreamMessageError", + "ChannelMediaRelayStateChanged" to "ChannelMediaRelayStateChanged", + "ChannelMediaRelayEvent" to "ChannelMediaRelayEvent", + "MetadataReceived" to "MetadataReceived" + ) + } + + private fun callback(methodName: String, channel: RtcChannel, vararg data: Any?) { + emitter(methodName, hashMapOf( + "channelId" to channel.channelId(), + "data" to data.toList() + )) + } + + override fun onChannelWarning(rtcChannel: RtcChannel, @Annotations.AgoraWarningCode warn: Int) { + super.onChannelWarning(rtcChannel, warn) + callback("Warning", rtcChannel, warn) + } + + override fun onChannelError(rtcChannel: RtcChannel, @Annotations.AgoraErrorCode err: Int) { + super.onChannelError(rtcChannel, err) + callback("Error", rtcChannel, err) + } + + override fun onJoinChannelSuccess(rtcChannel: RtcChannel, uid: Int, elapsed: Int) { + super.onJoinChannelSuccess(rtcChannel, uid, elapsed) + callback("JoinChannelSuccess", rtcChannel, uid, elapsed) + } + + override fun onRejoinChannelSuccess(rtcChannel: RtcChannel, uid: Int, elapsed: Int) { + super.onRejoinChannelSuccess(rtcChannel, uid, elapsed) + callback("RejoinChannelSuccess", rtcChannel, uid, elapsed) + } + + override fun onLeaveChannel(rtcChannel: RtcChannel, stats: IRtcEngineEventHandler.RtcStats?) { + super.onLeaveChannel(rtcChannel, stats) + callback("LeaveChannel", rtcChannel, stats?.toMap()) + } + + override fun onClientRoleChanged(rtcChannel: RtcChannel, @Annotations.AgoraClientRole oldRole: Int, @Annotations.AgoraClientRole newRole: Int) { + super.onClientRoleChanged(rtcChannel, oldRole, newRole) + callback("ClientRoleChanged", rtcChannel, oldRole, newRole) + } + + override fun onUserJoined(rtcChannel: RtcChannel, uid: Int, elapsed: Int) { + super.onUserJoined(rtcChannel, uid, elapsed) + callback("UserJoined", rtcChannel, uid, elapsed) + } + + override fun onUserOffline(rtcChannel: RtcChannel, uid: Int, @Annotations.AgoraUserOfflineReason reason: Int) { + super.onUserOffline(rtcChannel, uid, reason) + callback("UserOffline", rtcChannel, uid, reason) + } + + override fun onConnectionStateChanged(rtcChannel: RtcChannel, @Annotations.AgoraConnectionStateType state: Int, @Annotations.AgoraConnectionChangedReason reason: Int) { + super.onConnectionStateChanged(rtcChannel, state, reason) + callback("ConnectionStateChanged", rtcChannel, state, reason) + } + + override fun onConnectionLost(rtcChannel: RtcChannel) { + super.onConnectionLost(rtcChannel) + callback("ConnectionLost", rtcChannel) + } + + override fun onTokenPrivilegeWillExpire(rtcChannel: RtcChannel, token: String?) { + super.onTokenPrivilegeWillExpire(rtcChannel, token) + callback("TokenPrivilegeWillExpire", rtcChannel, token) + } + + override fun onRequestToken(rtcChannel: RtcChannel) { + super.onRequestToken(rtcChannel) + callback("RequestToken", rtcChannel) + } + + override fun onActiveSpeaker(rtcChannel: RtcChannel, uid: Int) { + super.onActiveSpeaker(rtcChannel, uid) + callback("ActiveSpeaker", rtcChannel, uid) + } + + override fun onVideoSizeChanged(rtcChannel: RtcChannel, uid: Int, width: Int, height: Int, @IntRange(from = 0, to = 360) rotation: Int) { + super.onVideoSizeChanged(rtcChannel, uid, width, height, rotation) + callback("VideoSizeChanged", rtcChannel, uid, width, height, rotation) + } + + override fun onRemoteVideoStateChanged(rtcChannel: RtcChannel, uid: Int, @Annotations.AgoraVideoRemoteState state: Int, @Annotations.AgoraVideoRemoteStateReason reason: Int, elapsed: Int) { + super.onRemoteVideoStateChanged(rtcChannel, uid, state, reason, elapsed) + callback("RemoteVideoStateChanged", rtcChannel, uid, state, reason, elapsed) + } + + override fun onRemoteAudioStateChanged(rtcChannel: RtcChannel, uid: Int, @Annotations.AgoraAudioRemoteState state: Int, @Annotations.AgoraAudioRemoteStateReason reason: Int, elapsed: Int) { + super.onRemoteAudioStateChanged(rtcChannel, uid, state, reason, elapsed) + callback("RemoteAudioStateChanged", rtcChannel, uid, state, reason, elapsed) + } + + override fun onRemoteSubscribeFallbackToAudioOnly(rtcChannel: RtcChannel, uid: Int, isFallbackOrRecover: Boolean) { + super.onRemoteSubscribeFallbackToAudioOnly(rtcChannel, uid, isFallbackOrRecover) + callback("RemoteSubscribeFallbackToAudioOnly", rtcChannel, uid, isFallbackOrRecover) + } + + override fun onRtcStats(rtcChannel: RtcChannel, stats: IRtcEngineEventHandler.RtcStats?) { + super.onRtcStats(rtcChannel, stats) + callback("RtcStats", rtcChannel, stats?.toMap()) + } + + override fun onNetworkQuality(rtcChannel: RtcChannel, uid: Int, @Annotations.AgoraNetworkQuality txQuality: Int, @Annotations.AgoraNetworkQuality rxQuality: Int) { + super.onNetworkQuality(rtcChannel, uid, txQuality, rxQuality) + callback("NetworkQuality", rtcChannel, uid, txQuality, rxQuality) + } + + override fun onRemoteVideoStats(rtcChannel: RtcChannel, stats: IRtcEngineEventHandler.RemoteVideoStats?) { + super.onRemoteVideoStats(rtcChannel, stats) + callback("RemoteVideoStats", rtcChannel, stats?.toMap()) + } + + override fun onRemoteAudioStats(rtcChannel: RtcChannel, stats: IRtcEngineEventHandler.RemoteAudioStats?) { + super.onRemoteAudioStats(rtcChannel, stats) + callback("RemoteAudioStats", rtcChannel, stats?.toMap()) + } + + override fun onRtmpStreamingStateChanged(rtcChannel: RtcChannel, url: String?, @Annotations.AgoraRtmpStreamingState state: Int, @Annotations.AgoraRtmpStreamingErrorCode errCode: Int) { + super.onRtmpStreamingStateChanged(rtcChannel, url, state, errCode) + callback("RtmpStreamingStateChanged", rtcChannel, url, state, errCode) + } + + override fun onTranscodingUpdated(rtcChannel: RtcChannel) { + super.onTranscodingUpdated(rtcChannel) + callback("TranscodingUpdated", rtcChannel) + } + + override fun onStreamInjectedStatus(rtcChannel: RtcChannel, url: String?, uid: Int, @Annotations.AgoraInjectStreamStatus status: Int) { + super.onStreamInjectedStatus(rtcChannel, url, uid, status) + callback("StreamInjectedStatus", rtcChannel, url, uid, status) + } + + override fun onStreamMessage(rtcChannel: RtcChannel, uid: Int, streamId: Int, data: ByteArray?) { + super.onStreamMessage(rtcChannel, uid, streamId, data) + callback("StreamMessage", rtcChannel, uid, streamId, data?.let { String(it, Charsets.UTF_8) }) + } + + override fun onStreamMessageError(rtcChannel: RtcChannel, uid: Int, streamId: Int, @Annotations.AgoraErrorCode error: Int, missed: Int, cached: Int) { + super.onStreamMessageError(rtcChannel, uid, streamId, error, missed, cached) + callback("StreamMessageError", rtcChannel, uid, streamId, error, missed, cached) + } + + override fun onChannelMediaRelayStateChanged(rtcChannel: RtcChannel, @Annotations.AgoraChannelMediaRelayState state: Int, @Annotations.AgoraChannelMediaRelayError code: Int) { + super.onChannelMediaRelayStateChanged(rtcChannel, state, code) + callback("ChannelMediaRelayStateChanged", rtcChannel, state, code) + } + + override fun onChannelMediaRelayEvent(rtcChannel: RtcChannel, @Annotations.AgoraChannelMediaRelayEvent code: Int) { + super.onChannelMediaRelayEvent(rtcChannel, code) + callback("ChannelMediaRelayEvent", rtcChannel, code) + } +} diff --git a/RtcEngine.kt b/RtcEngine.kt new file mode 100644 index 000000000..bd1150b33 --- /dev/null +++ b/RtcEngine.kt @@ -0,0 +1,417 @@ +package io.agora.rtc.base + +import android.content.Context +import androidx.annotation.FloatRange +import androidx.annotation.IntRange +import io.agora.rtc.Constants +import io.agora.rtc.IMetadataObserver +import io.agora.rtc.RtcEngine +import io.agora.rtc.RtcEngineEx +import io.agora.rtc.models.UserInfo + +interface RtcEngineInterface : + RtcEngineManager.RtcUserInfoInterface, + RtcEngineManager.RtcAudioInterface, + RtcEngineManager.RtcVideoInterface, + RtcEngineManager.RtcAudioMixingInterface, + RtcEngineManager.RtcAudioEffectInterface, + RtcEngineManager.RtcVoiceChangerInterface, + RtcEngineManager.RtcVoicePositionInterface, + RtcEngineManager.RtcPublishStreamInterface, + RtcEngineManager.RtcMediaRelayInterface, + RtcEngineManager.RtcAudioRouteInterface, + RtcEngineManager.RtcEarMonitoringInterface, + RtcEngineManager.RtcDualStreamInterface, + RtcEngineManager.RtcFallbackInterface, + RtcEngineManager.RtcTestInterface, + RtcEngineManager.RtcMediaMetadataInterface, + RtcEngineManager.RtcWatermarkInterface, + RtcEngineManager.RtcEncryptionInterface, + RtcEngineManager.RtcAudioRecorderInterface, + RtcEngineManager.RtcInjectStreamInterface, + RtcEngineManager.RtcCameraInterface, + RtcEngineManager.RtcStreamMessageInterface { + fun create(appId: String, callback: Callback?) + + fun destroy(callback: Callback?) + + fun setChannelProfile(@Annotations.AgoraChannelProfile profile: Int, callback: Callback?) + + fun setClientRole(@Annotations.AgoraClientRole role: Int, callback: Callback?) + + fun joinChannel(token: String?, channelName: String, optionalInfo: String?, optionalUid: Int, callback: Callback?) + + fun switchChannel(token: String?, channelName: String, callback: Callback?) + + fun leaveChannel(callback: Callback?) + + fun renewToken(token: String, callback: Callback?) + + @Deprecated("") + fun enableWebSdkInteroperability(enabled: Boolean, callback: Callback?) + + fun getConnectionState(callback: Callback?) + + fun getCallId(callback: Callback?) + + fun rate(callId: String, @IntRange(from = 1, to = 5) rating: Int, description: String?, callback: Callback?) + + fun complain(callId: String, description: String, callback: Callback?) + + fun setLogFile(filePath: String, callback: Callback?) + + fun setLogFilter(@Annotations.AgoraLogFilter filter: Int, callback: Callback?) + + fun setLogFileSize(fileSizeInKBytes: Int, callback: Callback?) + + fun setParameters(parameters: String, callback: Callback?) +} + +class RtcEngineManager { + private var engine: RtcEngine? = null + private var mediaObserver: MediaObserver? = null + + fun create(context: Context, appId: String, emit: (methodName: String, data: Map?) -> Unit) { + engine = RtcEngineEx.create(context, appId, RtcEngineEventHandler { methodName, data -> + emit(methodName, data) + }) + (engine as? RtcEngineEx)?.setAppType(8) + } + + fun destroy() { + RtcEngine.destroy() + engine = null + } + + fun release() { + destroy() + mediaObserver = null + } + + fun engine(): RtcEngine? { + return engine + } + + fun getUserInfoByUserAccount(userAccount: String): Map? { + engine?.let { + val userInfo = UserInfo() + it.getUserInfoByUserAccount(userAccount, userInfo) + return@getUserInfoByUserAccount userInfo.toMap() + } + return null + } + + fun getUserInfoByUid(uid: Int): Map? { + engine?.let { + val userInfo = UserInfo() + it.getUserInfoByUid(uid, userInfo) + return@getUserInfoByUid userInfo.toMap() + } + return null + } + + fun registerMediaMetadataObserver(emit: (methodName: String, data: Map?) -> Unit): Int { + engine?.let { + val mediaObserver = MediaObserver { methodName, data -> + emit(methodName, data) + } + val res = it.registerMediaMetadataObserver(mediaObserver, IMetadataObserver.VIDEO_METADATA) + if (res == 0) this.mediaObserver = mediaObserver + return@registerMediaMetadataObserver res + } + return Constants.ERR_NOT_INITIALIZED + } + + fun unregisterMediaMetadataObserver(): Int { + engine?.let { + val res = it.registerMediaMetadataObserver(null, IMetadataObserver.VIDEO_METADATA) + if (res == 0) mediaObserver = null + return@unregisterMediaMetadataObserver res + } + return Constants.ERR_NOT_INITIALIZED + } + + fun setMaxMetadataSize(@IntRange(from = 0, to = 1024) size: Int): Int { + mediaObserver?.let { + it.maxMetadataSize = size + return@setMaxMetadataSize 0 + } + return Constants.ERR_NOT_INITIALIZED + } + + fun addMetadata(metadata: String): Int { + mediaObserver?.let { + it.addMetadata(metadata) + return@addMetadata 0 + } + return Constants.ERR_NOT_INITIALIZED + } + + fun createDataStream(reliable: Boolean, ordered: Boolean): Int { + engine?.let { + return@createDataStream it.createDataStream(reliable, ordered) + } + return Constants.ERR_NOT_INITIALIZED + } + + fun sendStreamMessage(streamId: Int, message: String): Int { + engine?.let { + return@sendStreamMessage it.sendStreamMessage(streamId, message.toByteArray()) + } + return Constants.ERR_NOT_INITIALIZED + } + + interface RtcUserInfoInterface { + fun registerLocalUserAccount(appId: String, userAccount: String, callback: Callback?) + + fun joinChannelWithUserAccount(token: String?, channelName: String, userAccount: String, callback: Callback?) + + fun getUserInfoByUserAccount(userAccount: String, callback: Callback?) + + fun getUserInfoByUid(uid: Int, callback: Callback?) + } + + interface RtcAudioInterface { + fun enableAudio(callback: Callback?) + + fun disableAudio(callback: Callback?) + + fun setAudioProfile(@Annotations.AgoraAudioProfile profile: Int, @Annotations.AgoraAudioScenario scenario: Int, callback: Callback?) + + fun adjustRecordingSignalVolume(@IntRange(from = 0, to = 400) volume: Int, callback: Callback?) + + fun adjustUserPlaybackSignalVolume(uid: Int, @IntRange(from = 0, to = 100) volume: Int, callback: Callback?) + + fun adjustPlaybackSignalVolume(@IntRange(from = 0, to = 400) volume: Int, callback: Callback?) + + fun enableLocalAudio(enabled: Boolean, callback: Callback?) + + fun muteLocalAudioStream(muted: Boolean, callback: Callback?) + + fun muteRemoteAudioStream(uid: Int, muted: Boolean, callback: Callback?) + + fun muteAllRemoteAudioStreams(muted: Boolean, callback: Callback?) + + fun setDefaultMuteAllRemoteAudioStreams(muted: Boolean, callback: Callback?) + + fun enableAudioVolumeIndication(interval: Int, @IntRange(from = 0, to = 10) smooth: Int, report_vad: Boolean, callback: Callback?) + } + + interface RtcVideoInterface { + fun enableVideo(callback: Callback?) + + fun disableVideo(callback: Callback?) + + fun setVideoEncoderConfiguration(config: Map, callback: Callback?) + + fun enableLocalVideo(enabled: Boolean, callback: Callback?) + + fun muteLocalVideoStream(muted: Boolean, callback: Callback?) + + fun muteRemoteVideoStream(uid: Int, muted: Boolean, callback: Callback?) + + fun muteAllRemoteVideoStreams(muted: Boolean, callback: Callback?) + + fun setDefaultMuteAllRemoteVideoStreams(muted: Boolean, callback: Callback?) + + fun setBeautyEffectOptions(enabled: Boolean, options: Map, callback: Callback?) + } + + interface RtcAudioMixingInterface { + fun startAudioMixing(filePath: String, loopback: Boolean, replace: Boolean, cycle: Int, callback: Callback?) + + fun stopAudioMixing(callback: Callback?) + + fun pauseAudioMixing(callback: Callback?) + + fun resumeAudioMixing(callback: Callback?) + + fun adjustAudioMixingVolume(@IntRange(from = 0, to = 100) volume: Int, callback: Callback?) + + fun adjustAudioMixingPlayoutVolume(@IntRange(from = 0, to = 400) volume: Int, callback: Callback?) + + fun adjustAudioMixingPublishVolume(@IntRange(from = 0, to = 100) volume: Int, callback: Callback?) + + fun getAudioMixingPlayoutVolume(callback: Callback?) + + fun getAudioMixingPublishVolume(callback: Callback?) + + fun getAudioMixingDuration(callback: Callback?) + + fun getAudioMixingCurrentPosition(callback: Callback?) + + fun setAudioMixingPosition(pos: Int, callback: Callback?) + } + + interface RtcAudioEffectInterface { + fun getEffectsVolume(callback: Callback?) + + fun setEffectsVolume(@FloatRange(from = 0.0, to = 100.0) volume: Double, callback: Callback?) + + fun setVolumeOfEffect(soundId: Int, @FloatRange(from = 0.0, to = 100.0) volume: Double, callback: Callback?) + + fun playEffect(soundId: Int, filePath: String, loopCount: Int, @FloatRange(from = 0.5, to = 2.0) pitch: Double, @FloatRange(from = -1.0, to = 1.0) pan: Double, @FloatRange(from = 0.0, to = 100.0) gain: Double, publish: Boolean, callback: Callback?) + + fun stopEffect(soundId: Int, callback: Callback?) + + fun stopAllEffects(callback: Callback?) + + fun preloadEffect(soundId: Int, filePath: String, callback: Callback?) + + fun unloadEffect(soundId: Int, callback: Callback?) + + fun pauseEffect(soundId: Int, callback: Callback?) + + fun pauseAllEffects(callback: Callback?) + + fun resumeEffect(soundId: Int, callback: Callback?) + + fun resumeAllEffects(callback: Callback?) + } + + interface RtcVoiceChangerInterface { + fun setLocalVoiceChanger(@Annotations.AgoraAudioVoiceChanger voiceChanger: Int, callback: Callback?) + + fun setLocalVoiceReverbPreset(@Annotations.AgoraAudioReverbPreset preset: Int, callback: Callback?) + + fun setLocalVoicePitch(@FloatRange(from = 0.5, to = 2.0) pitch: Double, callback: Callback?) + + fun setLocalVoiceEqualization(@IntRange(from = 0, to = 9) bandFrequency: Int, @IntRange(from = -15, to = 15) bandGain: Int, callback: Callback?) + + fun setLocalVoiceReverb(@Annotations.AgoraAudioReverbType reverbKey: Int, value: Int, callback: Callback?) + } + + interface RtcVoicePositionInterface { + fun enableSoundPositionIndication(enabled: Boolean, callback: Callback?) + + fun setRemoteVoicePosition(uid: Int, @FloatRange(from = -1.0, to = 1.0) pan: Double, @FloatRange(from = 0.0, to = 100.0) gain: Double, callback: Callback?) + } + + interface RtcPublishStreamInterface { + fun setLiveTranscoding(transcoding: Map, callback: Callback?) + + fun addPublishStreamUrl(url: String, transcodingEnabled: Boolean, callback: Callback?) + + fun removePublishStreamUrl(url: String, callback: Callback?) + } + + interface RtcMediaRelayInterface { + fun startChannelMediaRelay(channelMediaRelayConfiguration: Map, callback: Callback?) + + fun updateChannelMediaRelay(channelMediaRelayConfiguration: Map, callback: Callback?) + + fun stopChannelMediaRelay(callback: Callback?) + } + + interface RtcAudioRouteInterface { + fun setDefaultAudioRoutetoSpeakerphone(defaultToSpeaker: Boolean, callback: Callback?) + + fun setEnableSpeakerphone(enabled: Boolean, callback: Callback?) + + fun isSpeakerphoneEnabled(callback: Callback?) + } + + interface RtcEarMonitoringInterface { + fun enableInEarMonitoring(enabled: Boolean, callback: Callback?) + + fun setInEarMonitoringVolume(@IntRange(from = 0, to = 100) volume: Int, callback: Callback?) + } + + interface RtcDualStreamInterface { + fun enableDualStreamMode(enabled: Boolean, callback: Callback?) + + fun setRemoteVideoStreamType(uid: Int, @Annotations.AgoraVideoStreamType streamType: Int, callback: Callback?) + + fun setRemoteDefaultVideoStreamType(@Annotations.AgoraVideoStreamType streamType: Int, callback: Callback?) + } + + interface RtcFallbackInterface { + fun setLocalPublishFallbackOption(@Annotations.AgoraStreamFallbackOptions option: Int, callback: Callback?) + + fun setRemoteSubscribeFallbackOption(@Annotations.AgoraStreamFallbackOptions option: Int, callback: Callback?) + + fun setRemoteUserPriority(uid: Int, @Annotations.AgoraUserPriority userPriority: Int, callback: Callback?) + } + + interface RtcTestInterface { + fun startEchoTest(@IntRange(from = 2, to = 10) intervalInSeconds: Int, callback: Callback?) + + fun stopEchoTest(callback: Callback?) + + fun enableLastmileTest(callback: Callback?) + + fun disableLastmileTest(callback: Callback?) + + fun startLastmileProbeTest(config: Map, callback: Callback?) + + fun stopLastmileProbeTest(callback: Callback?) + } + + interface RtcMediaMetadataInterface { + fun registerMediaMetadataObserver(callback: Callback?) + + fun unregisterMediaMetadataObserver(callback: Callback?) + + fun setMaxMetadataSize(@IntRange(from = 0, to = 1024) size: Int, callback: Callback?) + + fun sendMetadata(metadata: String, callback: Callback?) + } + + interface RtcWatermarkInterface { + fun addVideoWatermark(watermarkUrl: String, options: Map, callback: Callback?) + + fun clearVideoWatermarks(callback: Callback?) + } + + interface RtcEncryptionInterface { + fun setEncryptionSecret(secret: String, callback: Callback?) + + fun setEncryptionMode(@Annotations.AgoraEncryptionMode encryptionMode: String, callback: Callback?) + } + + interface RtcAudioRecorderInterface { + fun startAudioRecording(filePath: String, sampleRate: Int, @Annotations.AgoraAudioRecordingQuality quality: Int, callback: Callback?) + + fun stopAudioRecording(callback: Callback?) + } + + interface RtcInjectStreamInterface { + fun addInjectStreamUrl(url: String, config: Map, callback: Callback?) + + fun removeInjectStreamUrl(url: String, callback: Callback?) + } + + interface RtcCameraInterface { + fun switchCamera(callback: Callback?) + + fun isCameraZoomSupported(callback: Callback?) + + fun isCameraTorchSupported(callback: Callback?) + + fun isCameraFocusSupported(callback: Callback?) + + fun isCameraExposurePositionSupported(callback: Callback?) + + fun isCameraAutoFocusFaceModeSupported(callback: Callback?) + + fun setCameraZoomFactor(@FloatRange(from = 1.0) factor: Float, callback: Callback?) + + fun getCameraMaxZoomFactor(callback: Callback?) + + fun setCameraFocusPositionInPreview(positionX: Float, positionY: Float, callback: Callback?) + + fun setCameraExposurePosition(positionXinView: Float, positionYinView: Float, callback: Callback?) + + fun setCameraTorchOn(isOn: Boolean, callback: Callback?) + + fun setCameraAutoFocusFaceModeEnabled(enabled: Boolean, callback: Callback?) + + fun setCameraCapturerConfiguration(config: Map, callback: Callback?) + } + + interface RtcStreamMessageInterface { + fun createDataStream(reliable: Boolean, ordered: Boolean, callback: Callback?) + + fun sendStreamMessage(streamId: Int, message: String, callback: Callback?) + } +} diff --git a/RtcEngineEventHandler.kt b/RtcEngineEventHandler.kt new file mode 100644 index 000000000..abb7897eb --- /dev/null +++ b/RtcEngineEventHandler.kt @@ -0,0 +1,460 @@ +package io.agora.rtc.base + +import android.graphics.Rect +import androidx.annotation.IntRange +import io.agora.rtc.IRtcEngineEventHandler +import io.agora.rtc.models.UserInfo + +class RtcEngineEventHandler( + private val emitter: (methodName: String, data: Map?) -> Unit +) : IRtcEngineEventHandler() { + companion object { + const val PREFIX = "io.agora.rtc." + val EVENTS = hashMapOf( + "Warning" to "Warning", + "Error" to "Error", + "ApiCallExecuted" to "ApiCallExecuted", + "JoinChannelSuccess" to "JoinChannelSuccess", + "RejoinChannelSuccess" to "RejoinChannelSuccess", + "LeaveChannel" to "LeaveChannel", + "LocalUserRegistered" to "LocalUserRegistered", + "UserInfoUpdated" to "UserInfoUpdated", + "ClientRoleChanged" to "ClientRoleChanged", + "UserJoined" to "UserJoined", + "UserOffline" to "UserOffline", + "ConnectionStateChanged" to "ConnectionStateChanged", + "NetworkTypeChanged" to "NetworkTypeChanged", + "ConnectionLost" to "ConnectionLost", + "TokenPrivilegeWillExpire" to "TokenPrivilegeWillExpire", + "RequestToken" to "RequestToken", + "AudioVolumeIndication" to "AudioVolumeIndication", + "ActiveSpeaker" to "ActiveSpeaker", + "FirstLocalAudioFrame" to "FirstLocalAudioFrame", + "FirstLocalVideoFrame" to "FirstLocalVideoFrame", + "UserMuteVideo" to "UserMuteVideo", + "VideoSizeChanged" to "VideoSizeChanged", + "RemoteVideoStateChanged" to "RemoteVideoStateChanged", + "LocalVideoStateChanged" to "LocalVideoStateChanged", + "RemoteAudioStateChanged" to "RemoteAudioStateChanged", + "LocalAudioStateChanged" to "LocalAudioStateChanged", + "LocalPublishFallbackToAudioOnly" to "LocalPublishFallbackToAudioOnly", + "RemoteSubscribeFallbackToAudioOnly" to "RemoteSubscribeFallbackToAudioOnly", + "AudioRouteChanged" to "AudioRouteChanged", + "CameraFocusAreaChanged" to "CameraFocusAreaChanged", + "CameraExposureAreaChanged" to "CameraExposureAreaChanged", + "RtcStats" to "RtcStats", + "LastmileQuality" to "LastmileQuality", + "NetworkQuality" to "NetworkQuality", + "LastmileProbeResult" to "LastmileProbeResult", + "LocalVideoStats" to "LocalVideoStats", + "LocalAudioStats" to "LocalAudioStats", + "RemoteVideoStats" to "RemoteVideoStats", + "RemoteAudioStats" to "RemoteAudioStats", + "AudioMixingFinished" to "AudioMixingFinished", + "AudioMixingStateChanged" to "AudioMixingStateChanged", + "AudioEffectFinished" to "AudioEffectFinished", + "RtmpStreamingStateChanged" to "RtmpStreamingStateChanged", + "TranscodingUpdated" to "TranscodingUpdated", + "StreamInjectedStatus" to "StreamInjectedStatus", + "StreamMessage" to "StreamMessage", + "StreamMessageError" to "StreamMessageError", + "MediaEngineLoadSuccess" to "MediaEngineLoadSuccess", + "MediaEngineStartCallSuccess" to "MediaEngineStartCallSuccess", + "ChannelMediaRelayStateChanged" to "ChannelMediaRelayStateChanged", + "ChannelMediaRelayEvent" to "ChannelMediaRelayEvent", + "FirstRemoteVideoFrame" to "FirstRemoteVideoFrame", + "FirstRemoteAudioFrame" to "FirstRemoteAudioFrame", + "FirstRemoteAudioDecoded" to "FirstRemoteAudioDecoded", + "UserMuteAudio" to "UserMuteAudio", + "StreamPublished" to "StreamPublished", + "StreamUnpublished" to "StreamUnpublished", + "RemoteAudioTransportStats" to "RemoteAudioTransportStats", + "RemoteVideoTransportStats" to "RemoteVideoTransportStats", + "UserEnableVideo" to "UserEnableVideo", + "UserEnableLocalVideo" to "UserEnableLocalVideo", + "FirstRemoteVideoDecoded" to "FirstRemoteVideoDecoded", + "MicrophoneEnabled" to "MicrophoneEnabled", + "ConnectionInterrupted" to "ConnectionInterrupted", + "ConnectionBanned" to "ConnectionBanned", + "AudioQuality" to "AudioQuality", + "CameraReady" to "CameraReady", + "VideoStopped" to "VideoStopped", + "MetadataReceived" to "MetadataReceived" + ) + } + + private fun callback(methodName: String, vararg data: Any?) { + emitter(methodName, hashMapOf("data" to data.toList())) + } + + override fun onWarning(@Annotations.AgoraWarningCode warn: Int) { + super.onWarning(warn) + callback("Warning", warn) + } + + override fun onError(@Annotations.AgoraErrorCode err: Int) { + super.onError(err) + callback("Error", err) + } + + override fun onApiCallExecuted(@Annotations.AgoraErrorCode error: Int, api: String?, result: String?) { + super.onApiCallExecuted(error, api, result) + callback("ApiCallExecuted", error, api, result) + } + + override fun onJoinChannelSuccess(channel: String?, uid: Int, elapsed: Int) { + super.onJoinChannelSuccess(channel, uid, elapsed) + callback("JoinChannelSuccess", channel, uid, elapsed) + } + + override fun onRejoinChannelSuccess(channel: String?, uid: Int, elapsed: Int) { + super.onRejoinChannelSuccess(channel, uid, elapsed) + callback("RejoinChannelSuccess", uid, elapsed) + } + + override fun onLeaveChannel(stats: RtcStats?) { + super.onLeaveChannel(stats) + callback("LeaveChannel", stats?.toMap()) + } + + override fun onLocalUserRegistered(uid: Int, userAccount: String?) { + super.onLocalUserRegistered(uid, userAccount) + callback("LocalUserRegistered", uid, userAccount) + } + + override fun onUserInfoUpdated(uid: Int, userInfo: UserInfo?) { + super.onUserInfoUpdated(uid, userInfo) + callback("UserInfoUpdated", uid, userInfo?.toMap()) + } + + override fun onClientRoleChanged(@Annotations.AgoraClientRole oldRole: Int, @Annotations.AgoraClientRole newRole: Int) { + super.onClientRoleChanged(oldRole, newRole) + callback("ClientRoleChanged", oldRole, newRole) + } + + override fun onUserJoined(uid: Int, elapsed: Int) { + super.onUserJoined(uid, elapsed) + callback("UserJoined", uid, elapsed) + } + + override fun onUserOffline(uid: Int, @Annotations.AgoraUserOfflineReason reason: Int) { + super.onUserOffline(uid, reason) + callback("UserOffline", uid, reason) + } + + override fun onConnectionStateChanged(@Annotations.AgoraConnectionStateType state: Int, @Annotations.AgoraConnectionChangedReason reason: Int) { + super.onConnectionStateChanged(state, reason) + callback("ConnectionStateChanged", state, reason) + } + + override fun onNetworkTypeChanged(@Annotations.AgoraNetworkType type: Int) { + super.onNetworkTypeChanged(type) + callback("NetworkTypeChanged", type) + } + + override fun onConnectionLost() { + super.onConnectionLost() + callback("ConnectionLost") + } + + override fun onTokenPrivilegeWillExpire(token: String?) { + super.onTokenPrivilegeWillExpire(token) + callback("TokenPrivilegeWillExpire", token) + } + + override fun onRequestToken() { + super.onRequestToken() + callback("RequestToken") + } + + override fun onAudioVolumeIndication(speakers: Array?, @IntRange(from = 0, to = 255) totalVolume: Int) { + super.onAudioVolumeIndication(speakers, totalVolume) + callback("AudioVolumeIndication", speakers?.toMapList(), totalVolume) + } + + override fun onActiveSpeaker(uid: Int) { + super.onActiveSpeaker(uid) + callback("ActiveSpeaker", uid) + } + + override fun onFirstLocalAudioFrame(elapsed: Int) { + super.onFirstLocalAudioFrame(elapsed) + callback("FirstLocalAudioFrame", elapsed) + } + + override fun onFirstLocalVideoFrame(width: Int, height: Int, elapsed: Int) { + super.onFirstLocalVideoFrame(width, height, elapsed) + callback("FirstLocalVideoFrame", width, height, elapsed) + } + + @Deprecated("", ReplaceWith("onRemoteVideoStateChanged")) + override fun onUserMuteVideo(uid: Int, muted: Boolean) { + super.onUserMuteVideo(uid, muted) + callback("UserMuteVideo", uid, muted) + } + + override fun onVideoSizeChanged(uid: Int, width: Int, height: Int, @IntRange(from = 0, to = 360) rotation: Int) { + super.onVideoSizeChanged(uid, width, height, rotation) + callback("VideoSizeChanged", uid, width, height, rotation) + } + + override fun onRemoteVideoStateChanged(uid: Int, @Annotations.AgoraVideoRemoteState state: Int, @Annotations.AgoraVideoRemoteStateReason reason: Int, elapsed: Int) { + super.onRemoteVideoStateChanged(uid, state, reason, elapsed) + callback("RemoteVideoStateChanged", uid, state, reason, elapsed) + } + + override fun onLocalVideoStateChanged(@Annotations.AgoraLocalVideoStreamState localVideoState: Int, @Annotations.AgoraLocalVideoStreamError error: Int) { + super.onLocalVideoStateChanged(localVideoState, error) + callback("LocalVideoStateChanged", localVideoState, error) + } + + override fun onRemoteAudioStateChanged(uid: Int, @Annotations.AgoraAudioRemoteState state: Int, @Annotations.AgoraAudioRemoteStateReason reason: Int, elapsed: Int) { + super.onRemoteAudioStateChanged(uid, state, reason, elapsed) + callback("RemoteAudioStateChanged", uid, state, reason, elapsed) + } + + override fun onLocalAudioStateChanged(@Annotations.AgoraAudioLocalState state: Int, @Annotations.AgoraAudioLocalError error: Int) { + super.onLocalAudioStateChanged(state, error) + callback("LocalAudioStateChanged", state, error) + } + + override fun onLocalPublishFallbackToAudioOnly(isFallbackOrRecover: Boolean) { + super.onLocalPublishFallbackToAudioOnly(isFallbackOrRecover) + callback("LocalPublishFallbackToAudioOnly", isFallbackOrRecover) + } + + override fun onRemoteSubscribeFallbackToAudioOnly(uid: Int, isFallbackOrRecover: Boolean) { + super.onRemoteSubscribeFallbackToAudioOnly(uid, isFallbackOrRecover) + callback("RemoteSubscribeFallbackToAudioOnly", uid, isFallbackOrRecover) + } + + override fun onAudioRouteChanged(@Annotations.AgoraAudioOutputRouting routing: Int) { + super.onAudioRouteChanged(routing) + callback("AudioRouteChanged", routing) + } + + override fun onCameraFocusAreaChanged(rect: Rect?) { + super.onCameraFocusAreaChanged(rect) + callback("CameraFocusAreaChanged", rect?.toMap()) + } + + override fun onCameraExposureAreaChanged(rect: Rect?) { + super.onCameraExposureAreaChanged(rect) + callback("CameraExposureAreaChanged", rect?.toMap()) + } + + override fun onRtcStats(stats: RtcStats?) { + super.onRtcStats(stats) + callback("RtcStats", stats?.toMap()) + } + + override fun onLastmileQuality(@Annotations.AgoraNetworkQuality quality: Int) { + super.onLastmileQuality(quality) + callback("LastmileQuality", quality) + } + + override fun onNetworkQuality(uid: Int, @Annotations.AgoraNetworkQuality txQuality: Int, @Annotations.AgoraNetworkQuality rxQuality: Int) { + super.onNetworkQuality(uid, txQuality, rxQuality) + callback("NetworkQuality", uid, txQuality, rxQuality) + } + + override fun onLastmileProbeResult(result: LastmileProbeResult?) { + super.onLastmileProbeResult(result) + callback("LastmileProbeResult", result?.toMap()) + } + + @Deprecated("", ReplaceWith("onLocalVideoStats")) + override fun onLocalVideoStat(sentBitrate: Int, sentFrameRate: Int) { + super.onLocalVideoStat(sentBitrate, sentFrameRate) + // TODO Not in iOS + } + + override fun onLocalVideoStats(stats: LocalVideoStats?) { + super.onLocalVideoStats(stats) + callback("LocalVideoStats", stats?.toMap()) + } + + override fun onLocalAudioStats(stats: LocalAudioStats?) { + super.onLocalAudioStats(stats) + callback("LocalAudioStats", stats?.toMap()) + } + + @Deprecated("", ReplaceWith("onRemoteVideoStats")) + override fun onRemoteVideoStat(uid: Int, delay: Int, receivedBitrate: Int, receivedFrameRate: Int) { + super.onRemoteVideoStat(uid, delay, receivedBitrate, receivedFrameRate) + // TODO Not in iOS + } + + override fun onRemoteVideoStats(stats: RemoteVideoStats?) { + super.onRemoteVideoStats(stats) + callback("RemoteVideoStats", stats?.toMap()) + } + + override fun onRemoteAudioStats(stats: RemoteAudioStats?) { + super.onRemoteAudioStats(stats) + callback("RemoteAudioStats", stats?.toMap()) + } + + @Deprecated("", ReplaceWith("onAudioMixingStateChanged")) + override fun onAudioMixingFinished() { + super.onAudioMixingFinished() + callback("AudioMixingFinished") + } + + override fun onAudioMixingStateChanged(@Annotations.AgoraAudioMixingStateCode state: Int, @Annotations.AgoraAudioMixingErrorCode errorCode: Int) { + super.onAudioMixingStateChanged(state, errorCode) + callback("AudioMixingStateChanged", state, errorCode) + } + + override fun onAudioEffectFinished(soundId: Int) { + super.onAudioEffectFinished(soundId) + callback("AudioEffectFinished", soundId) + } + + override fun onRtmpStreamingStateChanged(url: String?, @Annotations.AgoraRtmpStreamingState state: Int, @Annotations.AgoraRtmpStreamingErrorCode errCode: Int) { + super.onRtmpStreamingStateChanged(url, state, errCode) + callback("RtmpStreamingStateChanged", url, state, errCode) + } + + override fun onTranscodingUpdated() { + super.onTranscodingUpdated() + callback("TranscodingUpdated") + } + + override fun onStreamInjectedStatus(url: String?, uid: Int, @Annotations.AgoraInjectStreamStatus status: Int) { + super.onStreamInjectedStatus(url, uid, status) + callback("StreamInjectedStatus", url, uid, status) + } + + override fun onStreamMessage(uid: Int, streamId: Int, data: ByteArray?) { + super.onStreamMessage(uid, streamId, data) + callback("StreamMessage", uid, streamId, data?.let { String(it, Charsets.UTF_8) }) + } + + override fun onStreamMessageError(uid: Int, streamId: Int, @Annotations.AgoraErrorCode error: Int, missed: Int, cached: Int) { + super.onStreamMessageError(uid, streamId, error, missed, cached) + callback("StreamMessageError", uid, streamId, error, missed, cached) + } + + override fun onMediaEngineLoadSuccess() { + super.onMediaEngineLoadSuccess() + callback("MediaEngineLoadSuccess") + } + + override fun onMediaEngineStartCallSuccess() { + super.onMediaEngineStartCallSuccess() + callback("MediaEngineStartCallSuccess") + } + + override fun onChannelMediaRelayStateChanged(@Annotations.AgoraChannelMediaRelayState state: Int, @Annotations.AgoraChannelMediaRelayError code: Int) { + super.onChannelMediaRelayStateChanged(state, code) + callback("ChannelMediaRelayStateChanged", state, code) + } + + override fun onChannelMediaRelayEvent(@Annotations.AgoraChannelMediaRelayEvent code: Int) { + super.onChannelMediaRelayEvent(code) + callback("ChannelMediaRelayEvent", code) + } + + @Deprecated("", ReplaceWith("onRemoteVideoStateChanged")) + override fun onFirstRemoteVideoFrame(uid: Int, width: Int, height: Int, elapsed: Int) { + super.onFirstRemoteVideoFrame(uid, width, height, elapsed) + callback("FirstRemoteVideoFrame", uid, width, height, elapsed) + } + + @Deprecated("", ReplaceWith("onRemoteAudioStateChanged")) + override fun onFirstRemoteAudioFrame(uid: Int, elapsed: Int) { + super.onFirstRemoteAudioFrame(uid, elapsed) + callback("FirstRemoteAudioFrame", uid, elapsed) + } + + @Deprecated("", ReplaceWith("onRemoteAudioStateChanged")) + override fun onFirstRemoteAudioDecoded(uid: Int, elapsed: Int) { + super.onFirstRemoteAudioDecoded(uid, elapsed) + callback("FirstRemoteAudioDecoded", uid, elapsed) + } + + @Deprecated("", ReplaceWith("onRemoteAudioStateChanged")) + override fun onUserMuteAudio(uid: Int, muted: Boolean) { + super.onUserMuteAudio(uid, muted) + callback("UserMuteAudio", uid, muted) + } + + @Deprecated("", ReplaceWith("onRtmpStreamingStateChanged")) + override fun onStreamPublished(url: String?, @Annotations.AgoraErrorCode error: Int) { + super.onStreamPublished(url, error) + callback("StreamPublished", url, error) + } + + @Deprecated("", ReplaceWith("onRtmpStreamingStateChanged")) + override fun onStreamUnpublished(url: String?) { + super.onStreamUnpublished(url) + callback("StreamUnpublished", url) + } + + @Deprecated("", ReplaceWith("onRemoteAudioStats")) + override fun onRemoteAudioTransportStats(uid: Int, delay: Int, lost: Int, rxKBitRate: Int) { + super.onRemoteAudioTransportStats(uid, delay, lost, rxKBitRate) + callback("RemoteAudioTransportStats", uid, delay, lost, rxKBitRate) + } + + @Deprecated("", ReplaceWith("onRemoteVideoStats")) + override fun onRemoteVideoTransportStats(uid: Int, delay: Int, lost: Int, rxKBitRate: Int) { + super.onRemoteVideoTransportStats(uid, delay, lost, rxKBitRate) + callback("RemoteVideoTransportStats", uid, delay, lost, rxKBitRate) + } + + @Deprecated("", ReplaceWith("onRemoteVideoStateChanged")) + override fun onUserEnableVideo(uid: Int, enabled: Boolean) { + super.onUserEnableVideo(uid, enabled) + callback("UserEnableVideo", uid, enabled) + } + + @Deprecated("", ReplaceWith("onRemoteVideoStateChanged")) + override fun onUserEnableLocalVideo(uid: Int, enabled: Boolean) { + super.onUserEnableLocalVideo(uid, enabled) + callback("UserEnableLocalVideo", uid, enabled) + } + + @Deprecated("", ReplaceWith("onRemoteVideoStateChanged")) + override fun onFirstRemoteVideoDecoded(uid: Int, width: Int, height: Int, elapsed: Int) { + super.onFirstRemoteVideoDecoded(uid, width, height, elapsed) + callback("FirstRemoteVideoDecoded", uid, width, height, elapsed) + } + + @Deprecated("", ReplaceWith("onLocalAudioStateChanged")) + override fun onMicrophoneEnabled(enabled: Boolean) { + super.onMicrophoneEnabled(enabled) + callback("MicrophoneEnabled", enabled) + } + + @Deprecated("", ReplaceWith("onConnectionStateChanged")) + override fun onConnectionInterrupted() { + super.onConnectionInterrupted() + callback("ConnectionInterrupted") + } + + @Deprecated("", ReplaceWith("onConnectionStateChanged")) + override fun onConnectionBanned() { + super.onConnectionBanned() + callback("ConnectionBanned") + } + + @Deprecated("", ReplaceWith("onRemoteAudioStats")) + override fun onAudioQuality(uid: Int, @Annotations.AgoraNetworkQuality quality: Int, delay: Short, lost: Short) { + super.onAudioQuality(uid, quality, delay, lost) + callback("AudioQuality", uid, quality, delay, lost) + } + + @Deprecated("", ReplaceWith("onLocalVideoStateChanged")) + override fun onCameraReady() { + super.onCameraReady() + callback("CameraReady") + } + + @Deprecated("", ReplaceWith("onLocalVideoStateChanged")) + override fun onVideoStopped() { + super.onVideoStopped() + callback("VideoStopped") + } +} diff --git a/RtcSurfaceView.kt b/RtcSurfaceView.kt new file mode 100644 index 000000000..f7495ea5d --- /dev/null +++ b/RtcSurfaceView.kt @@ -0,0 +1,82 @@ +package io.agora.rtc.base + +import android.content.Context +import android.view.SurfaceView +import android.widget.FrameLayout +import io.agora.rtc.RtcEngine +import io.agora.rtc.video.VideoCanvas + +class RtcSurfaceView( + context: Context +) : FrameLayout(context) { + private var surface: SurfaceView = RtcEngine.CreateRendererView(context) + private var canvas: VideoCanvas + + init { + canvas = VideoCanvas(surface) + addView(surface) + } + + fun setZOrderMediaOverlay(isMediaOverlay: Boolean) { + try { + removeView(surface) + surface.setZOrderMediaOverlay(isMediaOverlay) + addView(surface) + } catch (e: Exception) { + e.printStackTrace() + } + } + + fun setZOrderOnTop(onTop: Boolean) { + try { + removeView(surface) + surface.setZOrderOnTop(onTop) + addView(surface) + } catch (e: Exception) { + e.printStackTrace() + } + } + + fun setRenderMode(engine: RtcEngine, @Annotations.AgoraVideoRenderMode renderMode: Int) { + canvas.renderMode = renderMode + if (canvas.uid == 0) { + engine.setLocalRenderMode(canvas.renderMode, canvas.mirrorMode) + } else { + engine.setRemoteRenderMode(canvas.uid, canvas.renderMode, canvas.mirrorMode) + } + } + + fun setChannelId(engine: RtcEngine, channelId: String) { + canvas.channelId = channelId + if (canvas.uid == 0) { + engine.setupLocalVideo(canvas) + } else { + engine.setupRemoteVideo(canvas) + } + } + + fun setMirrorMode(engine: RtcEngine, @Annotations.AgoraVideoMirrorMode mirrorMode: Int) { + canvas.mirrorMode = mirrorMode + if (canvas.uid == 0) { + engine.setLocalRenderMode(canvas.renderMode, canvas.mirrorMode) + } else { + engine.setRemoteRenderMode(canvas.uid, canvas.renderMode, canvas.mirrorMode) + } + } + + fun setUid(engine: RtcEngine, uid: Int) { + canvas.uid = uid + if (canvas.uid == 0) { + engine.setupLocalVideo(canvas) + } else { + engine.setupRemoteVideo(canvas) + } + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + val width: Int = MeasureSpec.getSize(widthMeasureSpec) + val height: Int = MeasureSpec.getSize(heightMeasureSpec) + surface.layout(0, 0, width, height) + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + } +} diff --git a/RtcTextureView.kt b/RtcTextureView.kt new file mode 100644 index 000000000..e787d2dd8 --- /dev/null +++ b/RtcTextureView.kt @@ -0,0 +1,46 @@ +package io.agora.rtc.base + +import android.content.Context +import android.widget.FrameLayout +import io.agora.rtc.RtcEngine +import io.agora.rtc.mediaio.AgoraTextureView +import io.agora.rtc.mediaio.MediaIO + +class RtcTextureView( + context: Context +) : FrameLayout(context) { + private var texture: AgoraTextureView = AgoraTextureView(context) + private var uid: Int = 0 + + init { + texture.init(null) + texture.setBufferType(MediaIO.BufferType.BYTE_ARRAY) + texture.setPixelFormat(MediaIO.PixelFormat.I420) + addView(texture) + } + + fun setMirror(engine: RtcEngine, mirror: Boolean) { + texture.setMirror(mirror) + setupVideo(engine) + } + + fun setUid(engine: RtcEngine, uid: Int) { + this.uid = uid + setupVideo(engine) + } + + private fun setupVideo(engine: RtcEngine) { + if (uid == 0) { + engine.setLocalVideoRenderer(texture) + } else { + engine.setRemoteVideoRenderer(uid, texture) + } + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + val width: Int = MeasureSpec.getSize(widthMeasureSpec) + val height: Int = MeasureSpec.getSize(heightMeasureSpec) + texture.layout(0, 0, width, height) + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + } +}