agora-python-server-sdk 2.4.0__tar.gz → 2.4.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agora_python_server_sdk-2.4.0/agora_python_server_sdk.egg-info → agora_python_server_sdk-2.4.2}/PKG-INFO +63 -26
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/README.md +62 -25
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/__init__.py +33 -5
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/_ctypes_handle/_ctypes_data.py +46 -1
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/_ctypes_handle/_rtc_connection_observer.py +1 -1
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/agora_base.py +13 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/agora_service.py +11 -5
- agora_python_server_sdk-2.4.2/agora/rtc/external_audio_processor.py +222 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/local_user.py +20 -5
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/rtc_connection.py +89 -5
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2/agora_python_server_sdk.egg-info}/PKG-INFO +63 -26
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora_python_server_sdk.egg-info/SOURCES.txt +1 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/setup.py +14 -1
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/MANIFEST.in +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/__init__.py +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/_ctypes_handle/_audio_frame_observer.py +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/_ctypes_handle/_local_user_observer.py +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/_ctypes_handle/_video_encoded_frame_observer.py +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/_ctypes_handle/_video_frame_observer.py +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/_utils/globals.py +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/agora_parameter.py +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/audio_encoded_frame_sender.py +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/audio_frame_observer.py +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/audio_pcm_data_sender.py +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/audio_sessionctrl.py +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/audio_vad_manager.py +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/local_audio_track.py +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/local_user_observer.py +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/local_video_track.py +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/media_node_factory.py +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/remote_audio_track.py +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/remote_video_track.py +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/rtc_connection_observer.py +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/utils/audio_consumer.py +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/utils/vad_dump.py +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/video_encoded_frame_observer.py +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/video_encoded_image_sender.py +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/video_frame_observer.py +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/video_frame_sender.py +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/voice_detection.py +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtm/__init__.py +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtm/_ctypes_handle/_ctypes_data.py +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtm/rtm_base.py +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtm/rtm_client.py +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtm/rtm_event_handler.py +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora_python_server_sdk.egg-info/dependency_links.txt +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora_python_server_sdk.egg-info/top_level.txt +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/pyproject.toml +0 -0
- {agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: agora_python_server_sdk
|
|
3
|
-
Version: 2.4.
|
|
3
|
+
Version: 2.4.2
|
|
4
4
|
Summary: A Python SDK for Agora Server
|
|
5
5
|
Home-page: https://github.com/AgoraIO-Extensions/Agora-Python-Server-SDK
|
|
6
6
|
Classifier: Intended Audience :: Developers
|
|
@@ -38,7 +38,7 @@ Description-Content-Type: text/markdown
|
|
|
38
38
|
# Required Operating Systems and Python Versions
|
|
39
39
|
- Supported Linux versions:
|
|
40
40
|
- Ubuntu 18.04 LTS and above
|
|
41
|
-
- CentOS
|
|
41
|
+
- CentOS 8.0 and above
|
|
42
42
|
|
|
43
43
|
- Supported Mac versions:
|
|
44
44
|
- MacOS 13 and above(only for coding and testing)
|
|
@@ -57,40 +57,77 @@ pip install agora_python_server_sdk
|
|
|
57
57
|
- Download and unzip [test_data.zip](https://download.agora.io/demo/test/test_data_202408221437.zip) to the Agora-Python-Server-SDK directory.
|
|
58
58
|
|
|
59
59
|
## Executing Test Script
|
|
60
|
-
|
|
60
|
+
for linux os, should set env to :/site_packages/agora/agora_sdk/, like:
|
|
61
61
|
export LD_LIBRARY_PATH=/site_packages/agora/agora_sdk/
|
|
62
62
|
```
|
|
63
63
|
python agora_rtc/examples/example_audio_pcm_send.py --appId=xxx --channelId=xxx --userId=xxx --audioFile=./test_data/demo.pcm --sampleRate=16000 --numOfChannels=1
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
# Change log
|
|
67
|
-
## 2025.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
67
|
+
## 2025.12.29 Release Version 2.4.2
|
|
68
|
+
|
|
69
|
+
- Added **incremental send mode** support.
|
|
70
|
+
- New `connection::send_intra_request` API, allowing you to initiate an intra request to remote users and trigger them to send a key frame.
|
|
71
|
+
|
|
72
|
+
#### Example usage of incremental send mode
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
# Configure incremental send parameters
|
|
76
|
+
publish_config.send_external_audio_parameters = SendExternalAudioParameters(
|
|
77
|
+
enabled=True,
|
|
78
|
+
send_ms=2000,
|
|
79
|
+
send_speed=2,
|
|
80
|
+
deliver_mute_data_for_fake_adm=False
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Create an RTC connection
|
|
84
|
+
connection = agora_service.create_rtc_connection(con_config, publish_config)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
## 2025.12.17 Release 2.4.1
|
|
90
|
+
|
|
91
|
+
- Updated RTC SDK to version 154.
|
|
92
|
+
- Added support for VAD (Voice Activity Detection) for external audio sources, including:
|
|
93
|
+
- **Background voice removal**
|
|
94
|
+
- **Noise suppression**
|
|
95
|
+
- **Echo cancellation**
|
|
96
|
+
- **Automatic gain control**
|
|
97
|
+
- **Other 3A algorithms**
|
|
98
|
+
All supported via the `external_Audio_Processor`.
|
|
99
|
+
- Modified LocalUser return values for clearer SDK error code distinction.
|
|
100
|
+
- Added `example_external_Audio_Processor.py` to demonstrate external audio data processing.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## 2025.11.18 Release 2.4.0
|
|
105
|
+
|
|
106
|
+
- Updated SDK to APM version: `4.4.32/1025`
|
|
107
|
+
- Added configuration support to enable/disable APM.
|
|
108
|
+
- Updated methods in `setup.py` and `__init__.py`, including version/URL and MD5 processing.
|
|
109
|
+
- Completed main code pipeline (**further testing recommended**).
|
|
110
|
+
- Added RTM support: one SDK now supports both RTC and RTM.
|
|
111
|
+
|
|
112
|
+
**To-Do Items:**
|
|
113
|
+
- [x] Add VAD algorithm update
|
|
114
|
+
- [x] Add vad_dump modifications
|
|
115
|
+
- [x] Add APM algorithm VAD switch support
|
|
116
|
+
- [x] Add VAD configuration parameter setting
|
|
117
|
+
- [x] Download and check for MD5 mismatch on every download
|
|
118
|
+
|
|
119
|
+
**Notes:**
|
|
120
|
+
- **APM features** include server-side echo cancellation, noise suppression, automatic gain control, background voice removal, and more.
|
|
121
|
+
- Typically, AEC/AINS/AGC, etc., are handled on the client; server-side activation is only necessary for specific requirements.
|
|
122
|
+
- If you need to enable APM, please contact Agora technical support.
|
|
123
|
+
|
|
124
|
+
**Usage Examples:**
|
|
125
|
+
- [RTC usage example](https://github.com/AgoraIO-Extensions/Agora-Python-Server-SDK/tree/main/agora_rtc/examples)
|
|
126
|
+
- [RTM usage example](https://github.com/AgoraIO-Extensions/Agora-Python-Server-SDK/tree/main/agora_rtc/rtm_examples)
|
|
86
127
|
# 2025.11.07 release 2.3.3
|
|
87
128
|
-- update: to support rtm
|
|
88
129
|
-- adjust sdk's directory structure
|
|
89
130
|
-- change requests to urllib
|
|
90
|
-
# 2025.10.23 release 2.3.2: support rtc and rtm in one package
|
|
91
|
-
-- update: to support rtm.can support both rtc and rtm in one package.
|
|
92
|
-
-- adjust sdk's directory structure
|
|
93
|
-
-- update rtc sdk
|
|
94
131
|
# 2025.10.09 release 2.3.1
|
|
95
132
|
-- update arm64 rtc sdk:Fixed a JNI referencing issue in the previous arm64 build. This issue only outputs logs to the console and does not affect functionality.
|
|
96
133
|
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
# Required Operating Systems and Python Versions
|
|
24
24
|
- Supported Linux versions:
|
|
25
25
|
- Ubuntu 18.04 LTS and above
|
|
26
|
-
- CentOS
|
|
26
|
+
- CentOS 8.0 and above
|
|
27
27
|
|
|
28
28
|
- Supported Mac versions:
|
|
29
29
|
- MacOS 13 and above(only for coding and testing)
|
|
@@ -42,40 +42,77 @@ pip install agora_python_server_sdk
|
|
|
42
42
|
- Download and unzip [test_data.zip](https://download.agora.io/demo/test/test_data_202408221437.zip) to the Agora-Python-Server-SDK directory.
|
|
43
43
|
|
|
44
44
|
## Executing Test Script
|
|
45
|
-
|
|
45
|
+
for linux os, should set env to :/site_packages/agora/agora_sdk/, like:
|
|
46
46
|
export LD_LIBRARY_PATH=/site_packages/agora/agora_sdk/
|
|
47
47
|
```
|
|
48
48
|
python agora_rtc/examples/example_audio_pcm_send.py --appId=xxx --channelId=xxx --userId=xxx --audioFile=./test_data/demo.pcm --sampleRate=16000 --numOfChannels=1
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
# Change log
|
|
52
|
-
## 2025.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
52
|
+
## 2025.12.29 Release Version 2.4.2
|
|
53
|
+
|
|
54
|
+
- Added **incremental send mode** support.
|
|
55
|
+
- New `connection::send_intra_request` API, allowing you to initiate an intra request to remote users and trigger them to send a key frame.
|
|
56
|
+
|
|
57
|
+
#### Example usage of incremental send mode
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
# Configure incremental send parameters
|
|
61
|
+
publish_config.send_external_audio_parameters = SendExternalAudioParameters(
|
|
62
|
+
enabled=True,
|
|
63
|
+
send_ms=2000,
|
|
64
|
+
send_speed=2,
|
|
65
|
+
deliver_mute_data_for_fake_adm=False
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# Create an RTC connection
|
|
69
|
+
connection = agora_service.create_rtc_connection(con_config, publish_config)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
## 2025.12.17 Release 2.4.1
|
|
75
|
+
|
|
76
|
+
- Updated RTC SDK to version 154.
|
|
77
|
+
- Added support for VAD (Voice Activity Detection) for external audio sources, including:
|
|
78
|
+
- **Background voice removal**
|
|
79
|
+
- **Noise suppression**
|
|
80
|
+
- **Echo cancellation**
|
|
81
|
+
- **Automatic gain control**
|
|
82
|
+
- **Other 3A algorithms**
|
|
83
|
+
All supported via the `external_Audio_Processor`.
|
|
84
|
+
- Modified LocalUser return values for clearer SDK error code distinction.
|
|
85
|
+
- Added `example_external_Audio_Processor.py` to demonstrate external audio data processing.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 2025.11.18 Release 2.4.0
|
|
90
|
+
|
|
91
|
+
- Updated SDK to APM version: `4.4.32/1025`
|
|
92
|
+
- Added configuration support to enable/disable APM.
|
|
93
|
+
- Updated methods in `setup.py` and `__init__.py`, including version/URL and MD5 processing.
|
|
94
|
+
- Completed main code pipeline (**further testing recommended**).
|
|
95
|
+
- Added RTM support: one SDK now supports both RTC and RTM.
|
|
96
|
+
|
|
97
|
+
**To-Do Items:**
|
|
98
|
+
- [x] Add VAD algorithm update
|
|
99
|
+
- [x] Add vad_dump modifications
|
|
100
|
+
- [x] Add APM algorithm VAD switch support
|
|
101
|
+
- [x] Add VAD configuration parameter setting
|
|
102
|
+
- [x] Download and check for MD5 mismatch on every download
|
|
103
|
+
|
|
104
|
+
**Notes:**
|
|
105
|
+
- **APM features** include server-side echo cancellation, noise suppression, automatic gain control, background voice removal, and more.
|
|
106
|
+
- Typically, AEC/AINS/AGC, etc., are handled on the client; server-side activation is only necessary for specific requirements.
|
|
107
|
+
- If you need to enable APM, please contact Agora technical support.
|
|
108
|
+
|
|
109
|
+
**Usage Examples:**
|
|
110
|
+
- [RTC usage example](https://github.com/AgoraIO-Extensions/Agora-Python-Server-SDK/tree/main/agora_rtc/examples)
|
|
111
|
+
- [RTM usage example](https://github.com/AgoraIO-Extensions/Agora-Python-Server-SDK/tree/main/agora_rtc/rtm_examples)
|
|
71
112
|
# 2025.11.07 release 2.3.3
|
|
72
113
|
-- update: to support rtm
|
|
73
114
|
-- adjust sdk's directory structure
|
|
74
115
|
-- change requests to urllib
|
|
75
|
-
# 2025.10.23 release 2.3.2: support rtc and rtm in one package
|
|
76
|
-
-- update: to support rtm.can support both rtc and rtm in one package.
|
|
77
|
-
-- adjust sdk's directory structure
|
|
78
|
-
-- update rtc sdk
|
|
79
116
|
# 2025.10.09 release 2.3.1
|
|
80
117
|
-- update arm64 rtc sdk:Fixed a JNI referencing issue in the previous arm64 build. This issue only outputs logs to the console and does not affect functionality.
|
|
81
118
|
|
|
@@ -95,7 +95,16 @@ def report_progress(blocknum, blocksize, totalsize):
|
|
|
95
95
|
downloaded = blocknum * blocksize
|
|
96
96
|
print(f"\rDownloading: ----{downloaded} bytes-----", end='', flush=True)
|
|
97
97
|
|
|
98
|
-
|
|
98
|
+
def _get_url_from_version_file(path: str):
|
|
99
|
+
if not os.path.exists(path):
|
|
100
|
+
return ""
|
|
101
|
+
try:
|
|
102
|
+
with open(path, 'r') as f:
|
|
103
|
+
lines = f.readline()
|
|
104
|
+
return lines
|
|
105
|
+
except Exception as e:
|
|
106
|
+
logger.error(f"Failed to read version file: {e}")
|
|
107
|
+
return ""
|
|
99
108
|
def _check_download_and_extract_sdk():
|
|
100
109
|
agora_service_path = os.path.dirname(os.path.abspath(__file__))
|
|
101
110
|
# change from dir like: /home/xxx/agora_rtc/agora/rtc/agora_sdk to
|
|
@@ -125,12 +134,20 @@ def _check_download_and_extract_sdk():
|
|
|
125
134
|
#20251110 Fusion version: with apm filter
|
|
126
135
|
mac_sdk="https://download.agora.io/sdk/release/agora_sdk_mac_v4.4.30_25869_FULL_20251030_1836_953684-aed.zip"
|
|
127
136
|
linux_sdk = "https://download.agora.io/sdk/release/agora_rtc_sdk_x86_64-linux-gnu-v4.4.32.150_26715_SERVER_20251030_1807-aed.zip"
|
|
137
|
+
|
|
138
|
+
linux_sdk = "https://download.agora.io/sdk/release/agora_rtc_sdk_x86_64-linux-gnu-Agora_Native_SDK_for_Linux_x64_zhourui_26895_SERVER_20251121_1628_987405_20251021_1427-3a.zip"
|
|
139
|
+
mac_sdk="https://download.agora.io/sdk/release/agora_sdk_mac_Agora_Native_SDK_for_Mac_zhourui_26101_FULL_20251121_2135_987705_20251021_1427-3a.zip"
|
|
140
|
+
|
|
141
|
+
linux_sdk="https://download.agora.io/sdk/release/agora_rtc_sdk_x86_64-linux-gnu-v4.4.32.154_26982_SERVER_20251210_1745_994155_20251021_1427-3a.zip"
|
|
142
|
+
mac_sdk="https://download.agora.io/sdk/release/agora_sdk_mac_v4.4.32.154_26308_FULL_20251210_1756_994156_20251021_1427-3a.zip"
|
|
143
|
+
|
|
144
|
+
|
|
128
145
|
|
|
129
146
|
|
|
130
147
|
linux_libfile_path = os.path.join(sdk_library_dir, "libagora_rtc_sdk.so")
|
|
131
148
|
mac_libfile_path = os.path.join(sdk_library_dir, "libAgoraRtcKit.dylib")
|
|
132
|
-
linux_md5 = "
|
|
133
|
-
mac_md5 = "
|
|
149
|
+
linux_md5 = "e89043f0db667c207d9308d3515a67ed"
|
|
150
|
+
mac_md5 = "f63e8af2047a53643a1ceb0a4b24b802"
|
|
134
151
|
|
|
135
152
|
#rtc_md5 = "7031dd10d1681cd88fd89d68c5b54282"
|
|
136
153
|
url = linux_sdk
|
|
@@ -156,6 +173,7 @@ def _check_download_and_extract_sdk():
|
|
|
156
173
|
url = "https://download.agora.io/sdk/release/Agora-RTC-aarch64-linux-gnu-v4.4.32-20251009_145437-921455_20251023_1538.zip"
|
|
157
174
|
rtc_md5 = "5c002f25d2b381e353082da4f835b4f2"
|
|
158
175
|
|
|
176
|
+
'''
|
|
159
177
|
is_file_exist = os.path.exists(rtc_libfile_path)
|
|
160
178
|
if is_file_exist:
|
|
161
179
|
md5_value = get_file_md5(rtc_libfile_path)
|
|
@@ -163,8 +181,15 @@ def _check_download_and_extract_sdk():
|
|
|
163
181
|
md5_value = ""
|
|
164
182
|
if md5_value == rtc_md5:
|
|
165
183
|
return
|
|
184
|
+
'''
|
|
185
|
+
is_file_exist = os.path.exists(rtc_libfile_path)
|
|
186
|
+
version_url = _get_url_from_version_file(os.path.join(sdk_root_dir, "version.txt"))
|
|
187
|
+
if version_url == url and is_file_exist:
|
|
188
|
+
return
|
|
189
|
+
|
|
190
|
+
#change from md5 check to url check
|
|
166
191
|
|
|
167
|
-
logger.error(f"missing agora sdk, now download it, please wait for a while...: {rtc_libfile_path} {
|
|
192
|
+
logger.error(f"missing agora sdk, now download it, please wait for a while...: {rtc_libfile_path} {version_url} {url} {is_file_exist}")
|
|
168
193
|
if os.path.exists(sdk_library_dir):
|
|
169
194
|
os.system(f"rm -rf {sdk_library_dir}")
|
|
170
195
|
os.makedirs(sdk_library_dir, exist_ok=True)
|
|
@@ -184,6 +209,9 @@ def _check_download_and_extract_sdk():
|
|
|
184
209
|
|
|
185
210
|
if os.path.exists(zip_path):
|
|
186
211
|
os.remove(zip_path)
|
|
212
|
+
#write version url to version.txt
|
|
213
|
+
with open(os.path.join(sdk_root_dir, "version.txt"), "w") as f:
|
|
214
|
+
f.write(url)
|
|
187
215
|
logger.error("download done, continue...")
|
|
188
216
|
|
|
189
217
|
|
|
@@ -207,5 +235,5 @@ if not os.path.exists(rtc_libfile_path):
|
|
|
207
235
|
logger.error(f"library {rtc_libfile_path} not found")
|
|
208
236
|
sys.exit(1)
|
|
209
237
|
|
|
210
|
-
#
|
|
238
|
+
# Explicitly export these variables to ensure that submodules can import them
|
|
211
239
|
__all__ = ['sdk_library_dir', 'sdk_rtc_dir', 'sdk_rtm_dir', 'sdk_root_dir']
|
|
@@ -1368,4 +1368,49 @@ class CapabilitiesItemMapInner(ctypes.Structure):
|
|
|
1368
1368
|
return CapabilitiesItemMap(
|
|
1369
1369
|
item=self.item,
|
|
1370
1370
|
size=self.size
|
|
1371
|
-
)
|
|
1371
|
+
)
|
|
1372
|
+
|
|
1373
|
+
class AudioSinkWantesInner(ctypes.Structure):
|
|
1374
|
+
_fields_ = [
|
|
1375
|
+
("samples_per_sec", ctypes.c_int),
|
|
1376
|
+
("channels", ctypes.c_uint32),
|
|
1377
|
+
]
|
|
1378
|
+
pass
|
|
1379
|
+
class AudioLabelInner(ctypes.Structure):
|
|
1380
|
+
_fields_ = [
|
|
1381
|
+
("far_filed_flag", ctypes.c_int),
|
|
1382
|
+
("rms", ctypes.c_int),
|
|
1383
|
+
("voice_prob", ctypes.c_int),
|
|
1384
|
+
("music_prob", ctypes.c_int),
|
|
1385
|
+
("pitch", ctypes.c_int),
|
|
1386
|
+
]
|
|
1387
|
+
class AudioPcmFrameInner(ctypes.Structure):
|
|
1388
|
+
_fields_ = [
|
|
1389
|
+
("capture_timestamp", ctypes.c_uint32),
|
|
1390
|
+
("samples_per_channel", ctypes.c_uint32),
|
|
1391
|
+
("sample_rate_hz", ctypes.c_int),
|
|
1392
|
+
("num_channels", ctypes.c_uint32),
|
|
1393
|
+
("bytes_per_sample", ctypes.c_uint32),
|
|
1394
|
+
("data", ctypes.c_int16*3840),
|
|
1395
|
+
("audio_label", AudioLabelInner),
|
|
1396
|
+
]
|
|
1397
|
+
def get(self):
|
|
1398
|
+
return AudioFrame(
|
|
1399
|
+
type=0,
|
|
1400
|
+
samples_per_channel=self.samples_per_channel,
|
|
1401
|
+
bytes_per_sample=self.bytes_per_sample,
|
|
1402
|
+
channels=self.num_channels,
|
|
1403
|
+
samples_per_sec=self.sample_rate_hz,
|
|
1404
|
+
buffer=bytearray(ctypes.string_at(self.data, self.samples_per_channel * self.bytes_per_sample * self.num_channels)),
|
|
1405
|
+
render_time_ms=self.capture_timestamp,
|
|
1406
|
+
avsync_type=0,
|
|
1407
|
+
presentation_ms=0,
|
|
1408
|
+
audio_track_number=0,
|
|
1409
|
+
rtp_timestamp=0,
|
|
1410
|
+
far_field_flag=self.audio_label.far_filed_flag,
|
|
1411
|
+
rms=self.audio_label.rms,
|
|
1412
|
+
voice_prob=self.audio_label.voice_prob,
|
|
1413
|
+
music_prob=self.audio_label.music_prob,
|
|
1414
|
+
pitch=self.audio_label.pitch
|
|
1415
|
+
)
|
|
1416
|
+
pass
|
|
@@ -288,7 +288,7 @@ class CapabilitiesObserverInner(ctypes.Structure):
|
|
|
288
288
|
self.on_capabilities_changed = ON_CAPABILITIES_CHANGED_CALLBACK(self._on_capabilities_changed)
|
|
289
289
|
|
|
290
290
|
def _on_capabilities_changed(self, agora_capabilities_observer, ptr_caps_inner, size):
|
|
291
|
-
|
|
291
|
+
logger.debug(f"ConnCB _on_capabilities_changed: {agora_capabilities_observer}, {ptr_caps_inner}, {size}")
|
|
292
292
|
|
|
293
293
|
# 正确解析C指针数组
|
|
294
294
|
# 方法1: 使用ctypes.cast将指针转换为数组
|
|
@@ -427,6 +427,18 @@ class SenderOptions:
|
|
|
427
427
|
cc_mode: TCcMode = TCcMode.CC_ENABLED
|
|
428
428
|
codec_type: VideoCodecType = VideoCodecType.VIDEO_CODEC_H264
|
|
429
429
|
|
|
430
|
+
'''
|
|
431
|
+
note: DeliverMuteDataForFakeAdm can only set to rtc engine level, can not
|
|
432
|
+
set to connection level
|
|
433
|
+
so if once a connection has set to true, wihich will affect all the connections,
|
|
434
|
+
'''
|
|
435
|
+
@dataclass(kw_only=True)
|
|
436
|
+
class SendExternalAudioParameters:
|
|
437
|
+
enabled: bool = False
|
|
438
|
+
send_ms: int = 0
|
|
439
|
+
send_speed: int = 0
|
|
440
|
+
deliver_mute_data_for_fake_adm: bool = False
|
|
441
|
+
|
|
430
442
|
|
|
431
443
|
@dataclass(kw_only=True)
|
|
432
444
|
class RtcConnectionPublishConfig:
|
|
@@ -437,6 +449,7 @@ class RtcConnectionPublishConfig:
|
|
|
437
449
|
audio_publish_type: AudioPublishType = AudioPublishType.AUDIO_PUBLISH_TYPE_PCM
|
|
438
450
|
video_publish_type: VideoPublishType = VideoPublishType.VIDEO_PUBLISH_TYPE_NONE
|
|
439
451
|
video_encoded_image_sender_options: 'SenderOptions' = field(default_factory=SenderOptions)
|
|
452
|
+
send_external_audio_parameters: 'SendExternalAudioParameters | None' = None
|
|
440
453
|
|
|
441
454
|
@dataclass(kw_only=True)
|
|
442
455
|
class VideoSubscriptionOptions:
|
|
@@ -118,6 +118,12 @@ class AgoraService:
|
|
|
118
118
|
result = agora_service_enable_extension(self.service_handle, cprovider, cgenerator, ctrak, 1)
|
|
119
119
|
if result != 0:
|
|
120
120
|
logger.error(f"Failed to enable audio processing remote playback filter. Error code: {result}")
|
|
121
|
+
if config.enable_apm:
|
|
122
|
+
generator = "audio_processing_pcm_source"
|
|
123
|
+
cgenerator = generator.encode('utf-8')
|
|
124
|
+
result = agora_service_enable_extension(self.service_handle, cprovider, cgenerator, ctrak, 1)
|
|
125
|
+
if result != 0:
|
|
126
|
+
logger.error(f"Failed to enable audio processing pcm source filter. Error code: {result}")
|
|
121
127
|
|
|
122
128
|
#versio 2.2.0 for callback when muted
|
|
123
129
|
if config.should_callbck_when_muted > 0:
|
|
@@ -180,11 +186,11 @@ class AgoraService:
|
|
|
180
186
|
return RTCConnection(self, con_config, publish_config)
|
|
181
187
|
|
|
182
188
|
# createCustomAudioTrackPcm: creatae a custom audio track from pcm data sender
|
|
183
|
-
def _create_custom_audio_track_pcm(self, audio_pcm_data_sender: AudioPcmDataSender, scenario: AudioScenarioType) -> LocalAudioTrack:
|
|
189
|
+
def _create_custom_audio_track_pcm(self, audio_pcm_data_sender: AudioPcmDataSender, scenario: AudioScenarioType, is_extra_audio: bool) -> LocalAudioTrack:
|
|
184
190
|
if not self.inited:
|
|
185
191
|
logger.error("AgoraService is not initialized. Please call initialize() first.")
|
|
186
192
|
return None
|
|
187
|
-
if scenario == AudioScenarioType.AUDIO_SCENARIO_AI_SERVER:
|
|
193
|
+
if scenario == AudioScenarioType.AUDIO_SCENARIO_AI_SERVER and is_extra_audio == False:
|
|
188
194
|
custom_audio_track = agora_service_create_direct_custom_audio_track_pcm(self.service_handle, audio_pcm_data_sender.sender_handle)
|
|
189
195
|
else:
|
|
190
196
|
custom_audio_track = agora_service_create_custom_audio_track_pcm(self.service_handle, audio_pcm_data_sender.sender_handle)
|
|
@@ -192,10 +198,10 @@ class AgoraService:
|
|
|
192
198
|
return None
|
|
193
199
|
local_track = LocalAudioTrack(custom_audio_track)
|
|
194
200
|
#default for ai senario to set min delay to 10ms
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
local_track.set_max_buffer_audio_frame_number(100000)
|
|
201
|
+
|
|
202
|
+
local_track.set_max_buffer_audio_frame_number(100000)
|
|
198
203
|
#and set enable to true
|
|
204
|
+
local_track.set_send_delay_ms(10)
|
|
199
205
|
local_track.set_enabled(True)
|
|
200
206
|
return local_track
|
|
201
207
|
# mix_mode: MIX_ENABLED = 0, MIX_DISABLED = 1
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import ctypes
|
|
3
|
+
|
|
4
|
+
from .agora_base import *
|
|
5
|
+
from .agora_service import AgoraService, _set_filter_property_by_track, _enable_audio_filter_by_track
|
|
6
|
+
from .local_user import LocalUser
|
|
7
|
+
from .rtc_connection_observer import IRTCConnectionObserver
|
|
8
|
+
from ._ctypes_handle._audio_frame_observer import AudioFrameObserverInner
|
|
9
|
+
from .agora_parameter import AgoraParameter
|
|
10
|
+
from ._utils.globals import AgoraHandleInstanceMap
|
|
11
|
+
from ._ctypes_handle._rtc_connection_observer import RTCConnectionObserverInner, CapabilitiesObserverInner
|
|
12
|
+
from ._ctypes_handle._ctypes_data import *
|
|
13
|
+
from .utils.audio_consumer import PcmConsumeStats
|
|
14
|
+
from .voice_detection import AudioVadConfigV2, AudioVadV2
|
|
15
|
+
import logging
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class IAudioSinkObserver:
|
|
22
|
+
def on_processed_audio_frame(self,processor: 'ExternalAudioProcessor',frame: AudioFrame,vad_result_state:int,vad_result_data:bytearray):
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
agora_audio_sink_create = agora_lib.agora_audio_sink_create
|
|
26
|
+
agora_audio_sink_create.restype = ctypes.c_void_p
|
|
27
|
+
agora_audio_sink_create.argtypes = [ctypes.c_void_p]
|
|
28
|
+
|
|
29
|
+
agora_audio_track_add_audio_sink = agora_lib.agora_audio_track_add_audio_sink
|
|
30
|
+
agora_audio_track_add_audio_sink.restype = ctypes.c_int
|
|
31
|
+
agora_audio_track_add_audio_sink.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.POINTER(AudioSinkWantesInner)]
|
|
32
|
+
|
|
33
|
+
agora_audio_sink_destroy = agora_lib.agora_audio_sink_destroy
|
|
34
|
+
agora_audio_sink_destroy.restype = None
|
|
35
|
+
agora_audio_sink_destroy.argtypes = [ctypes.c_void_p]
|
|
36
|
+
|
|
37
|
+
#call back dec
|
|
38
|
+
ON_AUDIO_SINK_DATA_CALLBACK = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.POINTER(AudioPcmFrameInner))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class AudioSinkObserverInner(ctypes.Structure):
|
|
42
|
+
_fields_ = [
|
|
43
|
+
("on_audio_sink_data", ON_AUDIO_SINK_DATA_CALLBACK),
|
|
44
|
+
("user_data", ctypes.c_void_p),
|
|
45
|
+
]
|
|
46
|
+
def __init__(self, processor: 'ExternalAudioProcessor'):
|
|
47
|
+
self.on_audio_sink_data = ON_AUDIO_SINK_DATA_CALLBACK(self._on_audio_sink_data)
|
|
48
|
+
self._processor = processor
|
|
49
|
+
pass
|
|
50
|
+
def _on_audio_sink_data(self, handle: ctypes.c_void_p, audio_pcm_frame_inner_ptr: ctypes.POINTER(AudioPcmFrameInner)):
|
|
51
|
+
|
|
52
|
+
#validity check
|
|
53
|
+
if audio_pcm_frame_inner_ptr is None:
|
|
54
|
+
return -1000
|
|
55
|
+
|
|
56
|
+
#convert to aduioframe, and get user data
|
|
57
|
+
pcm_frame = audio_pcm_frame_inner_ptr.contents
|
|
58
|
+
|
|
59
|
+
#conver to audioframe
|
|
60
|
+
audio_frame = pcm_frame.get()
|
|
61
|
+
|
|
62
|
+
if self._processor:
|
|
63
|
+
self._processor._do_result_frame(audio_frame)
|
|
64
|
+
|
|
65
|
+
return 0
|
|
66
|
+
|
|
67
|
+
class AudioSink:
|
|
68
|
+
def __init__(self, audio_processor: 'ExternalAudioProcessor'):
|
|
69
|
+
self._audio_sink_observer_inner = None
|
|
70
|
+
self._c_sink = None
|
|
71
|
+
self._audio_processor = audio_processor
|
|
72
|
+
pass
|
|
73
|
+
def release(self):
|
|
74
|
+
if self._c_sink:
|
|
75
|
+
agora_audio_sink_destroy(ctypes.c_void_p(self._c_sink))
|
|
76
|
+
self._c_sink = None
|
|
77
|
+
self._audio_sink_observer_inner = None
|
|
78
|
+
self._audio_processor = None
|
|
79
|
+
pass
|
|
80
|
+
|
|
81
|
+
class ExternalAudioProcessor:
|
|
82
|
+
|
|
83
|
+
def __init__(self, engine: AgoraService):
|
|
84
|
+
self._pcm_sender = None
|
|
85
|
+
self._local_audio_track = None
|
|
86
|
+
self._engine = engine
|
|
87
|
+
self._is_initialized = False
|
|
88
|
+
#do create sender and track
|
|
89
|
+
self._pcm_sender = self._engine.media_node_factory.create_audio_pcm_data_sender()
|
|
90
|
+
self._local_audio_track = self._engine._create_custom_audio_track_pcm(self._pcm_sender, AudioScenarioType.AUDIO_SCENARIO_DEFAULT)
|
|
91
|
+
self._audio_sink = self._create_audio_sink()
|
|
92
|
+
self._vad_instance = None
|
|
93
|
+
self._observer = None
|
|
94
|
+
pass
|
|
95
|
+
def initialize(self, apm_config: APMConfig, out_sample_rate: int, out_channels: int, vad_config: AudioVadConfigV2, observer: IAudioSinkObserver)->int:
|
|
96
|
+
if self._is_initialized:
|
|
97
|
+
return 0
|
|
98
|
+
if apm_config is None and vad_config is None:
|
|
99
|
+
logger.error("[ExternalAudioProcessor] apm_config and vad_config are both None")
|
|
100
|
+
return -1000
|
|
101
|
+
if out_sample_rate <= 0 or out_channels <= 0:
|
|
102
|
+
logger.error("[ExternalAudioProcessor] out_sample_rate or out_channels is invalid")
|
|
103
|
+
return -1001
|
|
104
|
+
if observer is None:
|
|
105
|
+
logger.error("[ExternalAudioProcessor] observer is None")
|
|
106
|
+
return -1002
|
|
107
|
+
|
|
108
|
+
#set filer properties
|
|
109
|
+
ret = self._set_filter_properties(apm_config)
|
|
110
|
+
if ret < 0:
|
|
111
|
+
logger.info(f"[ExternalAudioProcessor] Failed to set filter properties, error: {ret}")
|
|
112
|
+
return ret
|
|
113
|
+
|
|
114
|
+
#add sink
|
|
115
|
+
ret = self._add_audio_sink(out_sample_rate, out_channels)
|
|
116
|
+
if ret < 0:
|
|
117
|
+
logger.info(f"[ExternalAudioProcessor] Failed to add audio sink, error: {ret}")
|
|
118
|
+
return ret
|
|
119
|
+
|
|
120
|
+
#set track properties
|
|
121
|
+
self._local_audio_track.set_send_delay_ms(10)
|
|
122
|
+
self._local_audio_track.set_max_buffer_audio_frame_number(100000)
|
|
123
|
+
self._local_audio_track.set_enabled(True)
|
|
124
|
+
|
|
125
|
+
self._observer = observer
|
|
126
|
+
|
|
127
|
+
# create vad instance
|
|
128
|
+
if vad_config is not None:
|
|
129
|
+
self._vad_instance = AudioVadV2(vad_config)
|
|
130
|
+
return ret
|
|
131
|
+
pass
|
|
132
|
+
|
|
133
|
+
def push_audio_pcm_data(self, data, sample_rate, channels, start_pts:int=0)->int:
|
|
134
|
+
ret = -1000
|
|
135
|
+
if self._pcm_sender is None:
|
|
136
|
+
return -1001
|
|
137
|
+
readLen = len(data)
|
|
138
|
+
bytes_per_frame_in_ms = (sample_rate / 1000) * 2 * channels
|
|
139
|
+
remainder = readLen % bytes_per_frame_in_ms
|
|
140
|
+
if remainder != 0:
|
|
141
|
+
return -1002
|
|
142
|
+
pack_num_in_ms = readLen // bytes_per_frame_in_ms
|
|
143
|
+
|
|
144
|
+
frame = PcmAudioFrame()
|
|
145
|
+
frame.data = data
|
|
146
|
+
frame.sample_rate = sample_rate
|
|
147
|
+
frame.number_of_channels = channels
|
|
148
|
+
frame.bytes_per_sample = 2
|
|
149
|
+
frame.timestamp = 0
|
|
150
|
+
frame.samples_per_channel = readLen // (channels * 2)
|
|
151
|
+
frame.present_time_ms = start_pts
|
|
152
|
+
|
|
153
|
+
ret = self._pcm_sender.send_audio_pcm_data(frame)
|
|
154
|
+
return ret
|
|
155
|
+
def _create_audio_sink(self)->AudioSink:
|
|
156
|
+
audio_sink = AudioSink(self)
|
|
157
|
+
audio_sink_observer_inner = AudioSinkObserverInner(self)
|
|
158
|
+
audio_sink._audio_sink_observer_inner = audio_sink_observer_inner
|
|
159
|
+
audio_sink._c_sink = agora_audio_sink_create(ctypes.byref(audio_sink_observer_inner))
|
|
160
|
+
if audio_sink._c_sink is None:
|
|
161
|
+
return None
|
|
162
|
+
|
|
163
|
+
return audio_sink
|
|
164
|
+
def _add_audio_sink(self, out_sample_rate: int, out_channels: int)->int:
|
|
165
|
+
if self._audio_sink is None or self._audio_sink._c_sink is None:
|
|
166
|
+
return -1000
|
|
167
|
+
|
|
168
|
+
# call to c api
|
|
169
|
+
wants = AudioSinkWantesInner(samples_per_sec=out_sample_rate, channels=out_channels)
|
|
170
|
+
ret = agora_audio_track_add_audio_sink(self._local_audio_track.track_handle, self._audio_sink._c_sink, ctypes.byref(wants))
|
|
171
|
+
return ret
|
|
172
|
+
pass
|
|
173
|
+
def _set_filter_properties(self, apm_config: APMConfig)->int:
|
|
174
|
+
if self._audio_sink is None or self._audio_sink._c_sink is None:
|
|
175
|
+
return -2000
|
|
176
|
+
if apm_config is None:
|
|
177
|
+
return 0
|
|
178
|
+
#call to c api
|
|
179
|
+
track_handle = self._local_audio_track.track_handle
|
|
180
|
+
ret = _enable_audio_filter_by_track(track_handle, "audio_processing_pcm_source", True, True)
|
|
181
|
+
if ret != 0:
|
|
182
|
+
logger.info(f"[ExternalAudioProcessor] Failed to enable audio filter by track, error: {ret}")
|
|
183
|
+
return -2003
|
|
184
|
+
|
|
185
|
+
ret = _set_filter_property_by_track(track_handle, "audio_processing_pcm_source", "apm_load_resource", "ains", True)
|
|
186
|
+
if ret != 0:
|
|
187
|
+
logger.info(f"[ExternalAudioProcessor] Failed to set filter property by track, error: {ret}")
|
|
188
|
+
return -2004
|
|
189
|
+
|
|
190
|
+
apm_config_json = apm_config._to_json_string()
|
|
191
|
+
if apm_config_json is None:
|
|
192
|
+
logger.info(f"[ExternalAudioProcessor] Failed to get apm config json")
|
|
193
|
+
return -2005
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
ret = _set_filter_property_by_track(track_handle, "audio_processing_pcm_source", "apm_config", apm_config_json, True)
|
|
197
|
+
if ret != 0:
|
|
198
|
+
logger.info(f"[ExternalAudioProcessor]Failed to set filter property by track, error: {ret}")
|
|
199
|
+
return -2006
|
|
200
|
+
logger.info(f"[ExternalAudioProcessor] apm configure json: {apm_config_json}")
|
|
201
|
+
|
|
202
|
+
if apm_config.enable_dump:
|
|
203
|
+
ret = _set_filter_property_by_track(track_handle, "audio_processing_pcm_source", "apm_dump", "true", True)
|
|
204
|
+
if ret != 0:
|
|
205
|
+
logger.info(f"[ExternalAudioProcessor] Failed to set filter property by track, error: {ret}")
|
|
206
|
+
return -2007
|
|
207
|
+
|
|
208
|
+
return ret
|
|
209
|
+
def _do_result_frame(self, audio_frame: AudioFrame):
|
|
210
|
+
print(f"ExternalAudioProcessor _do_result_frame: audio_frame voice_prob {audio_frame.voice_prob}, rms {audio_frame.rms}, pitch {audio_frame.pitch}, far_field_flag {audio_frame.far_field_flag}")
|
|
211
|
+
ret = 0
|
|
212
|
+
data = None
|
|
213
|
+
if self._vad_instance is not None:
|
|
214
|
+
ret, data = self._vad_instance.process(audio_frame)
|
|
215
|
+
print(f"ExternalAudioProcessor _do_result_frame: vad result ret {ret}, data length {len(data)}")
|
|
216
|
+
#do callback now
|
|
217
|
+
if self._observer is not None:
|
|
218
|
+
self._observer.on_processed_audio_frame(self, audio_frame, ret, data)
|
|
219
|
+
pass
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
|
|
@@ -230,7 +230,9 @@ agora_local_user_send_aduio_meta_data = agora_lib.agora_local_user_send_audio_me
|
|
|
230
230
|
agora_local_user_send_aduio_meta_data.restype = AGORA_API_C_INT
|
|
231
231
|
agora_local_user_send_aduio_meta_data.argtypes = [AGORA_HANDLE, ctypes.c_char_p, ctypes.c_size_t]
|
|
232
232
|
|
|
233
|
-
|
|
233
|
+
agora_local_user_send_intra_request = agora_lib.agora_local_user_send_intra_request
|
|
234
|
+
agora_local_user_send_intra_request.restype = AGORA_API_C_INT
|
|
235
|
+
agora_local_user_send_intra_request.argtypes = [AGORA_HANDLE, ctypes.c_char_p]
|
|
234
236
|
|
|
235
237
|
|
|
236
238
|
class LocalUser:
|
|
@@ -383,7 +385,7 @@ class LocalUser:
|
|
|
383
385
|
|
|
384
386
|
def subscribe_audio(self, user_id):
|
|
385
387
|
if user_id is None:
|
|
386
|
-
return -
|
|
388
|
+
return -1000
|
|
387
389
|
uid_str = user_id.encode('utf-8')
|
|
388
390
|
#ret = agora_local_user_subscribe_audio(self.user_handle, ctypes.create_string_buffer(uid_str))
|
|
389
391
|
# note:both ctypes.create_string_buffer and ctypes.c_char_p are all can change python's str to c_char_p
|
|
@@ -398,7 +400,7 @@ class LocalUser:
|
|
|
398
400
|
def unsubscribe_audio(self, user_id):
|
|
399
401
|
#validity check
|
|
400
402
|
if user_id is None:
|
|
401
|
-
return -
|
|
403
|
+
return -1000
|
|
402
404
|
uid_str = user_id.encode('utf-8')
|
|
403
405
|
ret = agora_local_user_unsubscribe_audio(self.user_handle, ctypes.c_char_p(uid_str))
|
|
404
406
|
if ret < 0:
|
|
@@ -517,7 +519,7 @@ class LocalUser:
|
|
|
517
519
|
|
|
518
520
|
def subscribe_video(self, user_id, options: VideoSubscriptionOptions):
|
|
519
521
|
if user_id is None:
|
|
520
|
-
return -
|
|
522
|
+
return -1000
|
|
521
523
|
uid_str = user_id.encode('utf-8')
|
|
522
524
|
|
|
523
525
|
|
|
@@ -540,7 +542,7 @@ class LocalUser:
|
|
|
540
542
|
|
|
541
543
|
def unsubscribe_video(self, user_id):
|
|
542
544
|
if user_id is None:
|
|
543
|
-
return -
|
|
545
|
+
return -1000
|
|
544
546
|
uid_str = user_id.encode('utf-8')
|
|
545
547
|
ret = agora_local_user_unsubscribe_video(self.user_handle, ctypes.c_char_p(uid_str))
|
|
546
548
|
if ret < 0:
|
|
@@ -628,3 +630,16 @@ class LocalUser:
|
|
|
628
630
|
ret = self.connection._set_apm_filter_properties(remote_audio_track_handle, user_id_str)
|
|
629
631
|
print(f"**********LocalUser _set_apm_filter_properties: {ret}")
|
|
630
632
|
return ret
|
|
633
|
+
pass
|
|
634
|
+
def _send_intra_request(self, remote_uid: str) -> int:
|
|
635
|
+
#validity check
|
|
636
|
+
if remote_uid is None:
|
|
637
|
+
return -1000
|
|
638
|
+
if self.user_handle is None:
|
|
639
|
+
return -1001
|
|
640
|
+
uid_str = remote_uid.encode('utf-8')
|
|
641
|
+
ret = agora_local_user_send_intra_request(self.user_handle, ctypes.c_char_p(uid_str))
|
|
642
|
+
if ret < 0:
|
|
643
|
+
logger.error("Failed to send intra request")
|
|
644
|
+
return ret
|
|
645
|
+
pass
|
|
@@ -83,6 +83,13 @@ agora_local_user_unregister_capabilities_observer = agora_lib.agora_local_user_u
|
|
|
83
83
|
agora_local_user_unregister_capabilities_observer.restype = AGORA_API_C_INT
|
|
84
84
|
agora_local_user_unregister_capabilities_observer.argtypes = [AGORA_HANDLE, AGORA_HANDLE]
|
|
85
85
|
|
|
86
|
+
agora_local_audio_track_set_total_extra_send_ms = agora_lib.agora_local_audio_track_set_total_extra_send_ms
|
|
87
|
+
agora_local_audio_track_set_total_extra_send_ms.restype = AGORA_API_C_INT
|
|
88
|
+
agora_local_audio_track_set_total_extra_send_ms.argtypes = [AGORA_HANDLE, ctypes.c_uint64]
|
|
89
|
+
|
|
90
|
+
#global variable
|
|
91
|
+
_is_deliver_mute_data_has_set: bool = False
|
|
92
|
+
|
|
86
93
|
class RTCConnection:
|
|
87
94
|
def __init__(self, service: AgoraService, conn_config: RTCConnConfig, publish_config: RtcConnectionPublishConfig) -> None:
|
|
88
95
|
self.conn_handle = None
|
|
@@ -90,6 +97,7 @@ class RTCConnection:
|
|
|
90
97
|
self.local_user = None
|
|
91
98
|
self.rtc_engine = service
|
|
92
99
|
self._con_observer = None
|
|
100
|
+
self._agora_parameter = None
|
|
93
101
|
#1 create conn_handle
|
|
94
102
|
self.conn_handle = agora_rtc_conn_create(self.rtc_engine.service_handle, ctypes.byref(RTCConnConfigInner.create(conn_config)))
|
|
95
103
|
if self.conn_handle is None:
|
|
@@ -98,6 +106,7 @@ class RTCConnection:
|
|
|
98
106
|
self.local_user_handle = agora_rtc_conn_get_local_user(self.conn_handle)
|
|
99
107
|
if self.local_user_handle:
|
|
100
108
|
self.local_user = LocalUser(self.local_user_handle, self)
|
|
109
|
+
self._agora_parameter = self._init_agora_parameter()
|
|
101
110
|
#keep publish_config
|
|
102
111
|
self.publish_config = publish_config
|
|
103
112
|
#and prepare track and sender for publish
|
|
@@ -107,6 +116,9 @@ class RTCConnection:
|
|
|
107
116
|
self._audio_encoded_sender = None
|
|
108
117
|
self._video_sender = None
|
|
109
118
|
self._video_encoded_sender = None
|
|
119
|
+
# for external audio parameters
|
|
120
|
+
self._send_external_audio_parameters = publish_config.send_external_audio_parameters
|
|
121
|
+
|
|
110
122
|
self._pcm_consume_stats = PcmConsumeStats()
|
|
111
123
|
self._prepare_publish_track_and_sender()
|
|
112
124
|
#3 set profile and scenario
|
|
@@ -123,12 +135,15 @@ class RTCConnection:
|
|
|
123
135
|
self._capabilities_observer_obj = None
|
|
124
136
|
if self.publish_config.audio_scenario == AudioScenarioType.AUDIO_SCENARIO_AI_SERVER:
|
|
125
137
|
self._register_capabilities_observer()
|
|
138
|
+
# for external audio parameters
|
|
139
|
+
self._set_send_external_send_frame_speed(self._send_external_audio_parameters)
|
|
126
140
|
|
|
127
141
|
def _prepare_publish_track_and_sender(self)->int:
|
|
128
142
|
if self.publish_config.is_publish_audio:
|
|
129
143
|
if self.publish_config.audio_publish_type == AudioPublishType.AUDIO_PUBLISH_TYPE_PCM:
|
|
130
144
|
self._audio_sender = self.rtc_engine.media_node_factory.create_audio_pcm_data_sender()
|
|
131
|
-
|
|
145
|
+
is_extra_audio = self._is_support_send_external_audio()
|
|
146
|
+
self._audio_track = self.rtc_engine._create_custom_audio_track_pcm(self._audio_sender, self.publish_config.audio_scenario, is_extra_audio)
|
|
132
147
|
elif self.publish_config.audio_publish_type == AudioPublishType.AUDIO_PUBLISH_TYPE_ENCODED_PCM:
|
|
133
148
|
self._audio_encoded_sender = self.rtc_engine.media_node_factory.create_audio_encoded_frame_sender()
|
|
134
149
|
self._audio_track = self.rtc_engine.create_custom_audio_track_encoded(self._audio_encoded_sender, 1)#mix_mode: MIX_ENABLED = 0, MIX_DISABLED = 1
|
|
@@ -203,11 +218,13 @@ class RTCConnection:
|
|
|
203
218
|
return ret
|
|
204
219
|
|
|
205
220
|
#
|
|
206
|
-
def
|
|
221
|
+
def _init_agora_parameter(self):
|
|
207
222
|
agora_parameter = agora_rtc_conn_get_agora_parameter(self.conn_handle)
|
|
208
223
|
if not agora_parameter:
|
|
209
224
|
return None
|
|
210
225
|
return AgoraParameter(agora_parameter)
|
|
226
|
+
def get_agora_parameter(self):
|
|
227
|
+
return self._agora_parameter
|
|
211
228
|
|
|
212
229
|
#
|
|
213
230
|
|
|
@@ -397,6 +414,11 @@ class RTCConnection:
|
|
|
397
414
|
frame.samples_per_channel = readLen // (channels * 2)
|
|
398
415
|
frame.present_time_ms = start_pts
|
|
399
416
|
|
|
417
|
+
#check if a new round or not. if new round should call _set_total_extra_send_ms()
|
|
418
|
+
is_new_round = self._pcm_consume_stats.is_new_round()
|
|
419
|
+
if is_new_round:
|
|
420
|
+
self._set_total_extra_send_ms()
|
|
421
|
+
|
|
400
422
|
ret = self._audio_sender.send_audio_pcm_data(frame)
|
|
401
423
|
self._pcm_consume_stats.add_pcm_data(readLen, sample_rate, channels)
|
|
402
424
|
return ret
|
|
@@ -431,8 +453,9 @@ class RTCConnection:
|
|
|
431
453
|
|
|
432
454
|
updated_track = None
|
|
433
455
|
delayed_del_track = None
|
|
456
|
+
is_extra_audio = False
|
|
434
457
|
if self._audio_sender:
|
|
435
|
-
updated_track = self.rtc_engine._create_custom_audio_track_pcm(self._audio_sender, scenario)
|
|
458
|
+
updated_track = self.rtc_engine._create_custom_audio_track_pcm(self._audio_sender, scenario, is_extra_audio)
|
|
436
459
|
elif self._audio_encoded_sender:
|
|
437
460
|
updated_track = self.rtc_engine.create_custom_audio_track_encoded(self._audio_encoded_sender, scenario)
|
|
438
461
|
|
|
@@ -440,6 +463,8 @@ class RTCConnection:
|
|
|
440
463
|
delayed_del_track = self._audio_track
|
|
441
464
|
self._audio_track = updated_track
|
|
442
465
|
self._audio_track.set_enabled(True)
|
|
466
|
+
self._audio_track.set_send_delay_ms(10)
|
|
467
|
+
self._audio_track.set_max_buffer_audio_frame_number(100000)
|
|
443
468
|
|
|
444
469
|
if delayed_del_track:
|
|
445
470
|
delayed_del_track.release()
|
|
@@ -467,10 +492,10 @@ class RTCConnection:
|
|
|
467
492
|
item_index = 0
|
|
468
493
|
for cap in capabilities:
|
|
469
494
|
item_index = 0
|
|
470
|
-
|
|
495
|
+
logger.debug(f"Capability[{index}] - Type: {cap.capability_type}")
|
|
471
496
|
index += 1
|
|
472
497
|
for item in cap.item_map.item:
|
|
473
|
-
|
|
498
|
+
logger.debug(f"Item[{item_index}] - ID: {item.id}, Name: {item.name}")
|
|
474
499
|
item_index += 1
|
|
475
500
|
if cap.capability_type == 19 and item.name and item.name.upper() == "SUPPORT":
|
|
476
501
|
fallback_scenario = False
|
|
@@ -541,5 +566,64 @@ class RTCConnection:
|
|
|
541
566
|
print(f"**********APM: to enable apm_dump, error: {ret}")
|
|
542
567
|
return ret
|
|
543
568
|
|
|
569
|
+
def _set_send_external_send_frame_speed(self, send_external_audio_parameters: SendExternalAudioParameters)->int:
|
|
570
|
+
ret = -1000
|
|
571
|
+
if send_external_audio_parameters == None or send_external_audio_parameters.enabled == False or send_external_audio_parameters.send_ms <= 0 or send_external_audio_parameters.send_speed <= 1:
|
|
572
|
+
return -1001
|
|
573
|
+
speed = send_external_audio_parameters.send_speed
|
|
574
|
+
if speed < 1:
|
|
575
|
+
speed = 1
|
|
576
|
+
if speed > 5:
|
|
577
|
+
speed = 5
|
|
578
|
+
#set send speed for fake adm to connection level
|
|
579
|
+
params = '{"che.audio.extra_send_frames_per_interval_for_fake_adm": %d}' % speed
|
|
580
|
+
|
|
581
|
+
if (self._agora_parameter is None):
|
|
582
|
+
return -1002
|
|
583
|
+
ret = self._agora_parameter.set_parameters(params)
|
|
584
|
+
|
|
544
585
|
|
|
586
|
+
# set deliver mute data for fake adm to service level and only once
|
|
587
|
+
self._set_deliver_mute_data_for_fake_adm(send_external_audio_parameters.deliver_mute_data_for_fake_adm)
|
|
588
|
+
|
|
589
|
+
return ret
|
|
590
|
+
pass
|
|
591
|
+
|
|
592
|
+
def _set_deliver_mute_data_for_fake_adm(self, deliver_mute_data_for_fake_adm: bool)->int:
|
|
593
|
+
ret = -1000
|
|
594
|
+
global _is_deliver_mute_data_has_set
|
|
595
|
+
if deliver_mute_data_for_fake_adm == False and _is_deliver_mute_data_has_set == False:
|
|
596
|
+
params = '{"che.audio.deliver_mute_data_for_fake_adm": false}'
|
|
597
|
+
rtc_parameter = self.rtc_engine.get_agora_parameter()
|
|
598
|
+
if rtc_parameter is not None:
|
|
599
|
+
ret = rtc_parameter.set_parameters(params)
|
|
600
|
+
_is_deliver_mute_data_has_set = True
|
|
601
|
+
return ret
|
|
602
|
+
def _is_support_send_external_audio(self)->bool:
|
|
603
|
+
ret = False
|
|
604
|
+
if ((self._send_external_audio_parameters is not None)
|
|
605
|
+
and (self._send_external_audio_parameters.enabled == True)
|
|
606
|
+
and (self._send_external_audio_parameters.send_ms > 0)
|
|
607
|
+
and (self._send_external_audio_parameters.send_speed > 1)):
|
|
608
|
+
ret = True
|
|
609
|
+
return ret
|
|
610
|
+
pass
|
|
611
|
+
#only valid after call this api
|
|
612
|
+
#and default call before each round
|
|
613
|
+
def _set_total_extra_send_ms(self)->int:
|
|
614
|
+
is_support = self._is_support_send_external_audio()
|
|
615
|
+
if is_support == False:
|
|
616
|
+
return 0
|
|
617
|
+
send_ms = self._send_external_audio_parameters.send_ms
|
|
618
|
+
ret = agora_local_audio_track_set_total_extra_send_ms(self._audio_track.track_handle, ctypes.c_uint64(send_ms))
|
|
619
|
+
|
|
620
|
+
return ret
|
|
621
|
+
pass
|
|
622
|
+
def send_intra_request(self, remote_uid: str) -> int:
|
|
623
|
+
ret = -1000
|
|
624
|
+
if self.local_user is None:
|
|
625
|
+
return -1001
|
|
626
|
+
ret = self.local_user._send_intra_request(remote_uid)
|
|
627
|
+
return ret
|
|
628
|
+
pass
|
|
545
629
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: agora_python_server_sdk
|
|
3
|
-
Version: 2.4.
|
|
3
|
+
Version: 2.4.2
|
|
4
4
|
Summary: A Python SDK for Agora Server
|
|
5
5
|
Home-page: https://github.com/AgoraIO-Extensions/Agora-Python-Server-SDK
|
|
6
6
|
Classifier: Intended Audience :: Developers
|
|
@@ -38,7 +38,7 @@ Description-Content-Type: text/markdown
|
|
|
38
38
|
# Required Operating Systems and Python Versions
|
|
39
39
|
- Supported Linux versions:
|
|
40
40
|
- Ubuntu 18.04 LTS and above
|
|
41
|
-
- CentOS
|
|
41
|
+
- CentOS 8.0 and above
|
|
42
42
|
|
|
43
43
|
- Supported Mac versions:
|
|
44
44
|
- MacOS 13 and above(only for coding and testing)
|
|
@@ -57,40 +57,77 @@ pip install agora_python_server_sdk
|
|
|
57
57
|
- Download and unzip [test_data.zip](https://download.agora.io/demo/test/test_data_202408221437.zip) to the Agora-Python-Server-SDK directory.
|
|
58
58
|
|
|
59
59
|
## Executing Test Script
|
|
60
|
-
|
|
60
|
+
for linux os, should set env to :/site_packages/agora/agora_sdk/, like:
|
|
61
61
|
export LD_LIBRARY_PATH=/site_packages/agora/agora_sdk/
|
|
62
62
|
```
|
|
63
63
|
python agora_rtc/examples/example_audio_pcm_send.py --appId=xxx --channelId=xxx --userId=xxx --audioFile=./test_data/demo.pcm --sampleRate=16000 --numOfChannels=1
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
# Change log
|
|
67
|
-
## 2025.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
67
|
+
## 2025.12.29 Release Version 2.4.2
|
|
68
|
+
|
|
69
|
+
- Added **incremental send mode** support.
|
|
70
|
+
- New `connection::send_intra_request` API, allowing you to initiate an intra request to remote users and trigger them to send a key frame.
|
|
71
|
+
|
|
72
|
+
#### Example usage of incremental send mode
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
# Configure incremental send parameters
|
|
76
|
+
publish_config.send_external_audio_parameters = SendExternalAudioParameters(
|
|
77
|
+
enabled=True,
|
|
78
|
+
send_ms=2000,
|
|
79
|
+
send_speed=2,
|
|
80
|
+
deliver_mute_data_for_fake_adm=False
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Create an RTC connection
|
|
84
|
+
connection = agora_service.create_rtc_connection(con_config, publish_config)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
## 2025.12.17 Release 2.4.1
|
|
90
|
+
|
|
91
|
+
- Updated RTC SDK to version 154.
|
|
92
|
+
- Added support for VAD (Voice Activity Detection) for external audio sources, including:
|
|
93
|
+
- **Background voice removal**
|
|
94
|
+
- **Noise suppression**
|
|
95
|
+
- **Echo cancellation**
|
|
96
|
+
- **Automatic gain control**
|
|
97
|
+
- **Other 3A algorithms**
|
|
98
|
+
All supported via the `external_Audio_Processor`.
|
|
99
|
+
- Modified LocalUser return values for clearer SDK error code distinction.
|
|
100
|
+
- Added `example_external_Audio_Processor.py` to demonstrate external audio data processing.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## 2025.11.18 Release 2.4.0
|
|
105
|
+
|
|
106
|
+
- Updated SDK to APM version: `4.4.32/1025`
|
|
107
|
+
- Added configuration support to enable/disable APM.
|
|
108
|
+
- Updated methods in `setup.py` and `__init__.py`, including version/URL and MD5 processing.
|
|
109
|
+
- Completed main code pipeline (**further testing recommended**).
|
|
110
|
+
- Added RTM support: one SDK now supports both RTC and RTM.
|
|
111
|
+
|
|
112
|
+
**To-Do Items:**
|
|
113
|
+
- [x] Add VAD algorithm update
|
|
114
|
+
- [x] Add vad_dump modifications
|
|
115
|
+
- [x] Add APM algorithm VAD switch support
|
|
116
|
+
- [x] Add VAD configuration parameter setting
|
|
117
|
+
- [x] Download and check for MD5 mismatch on every download
|
|
118
|
+
|
|
119
|
+
**Notes:**
|
|
120
|
+
- **APM features** include server-side echo cancellation, noise suppression, automatic gain control, background voice removal, and more.
|
|
121
|
+
- Typically, AEC/AINS/AGC, etc., are handled on the client; server-side activation is only necessary for specific requirements.
|
|
122
|
+
- If you need to enable APM, please contact Agora technical support.
|
|
123
|
+
|
|
124
|
+
**Usage Examples:**
|
|
125
|
+
- [RTC usage example](https://github.com/AgoraIO-Extensions/Agora-Python-Server-SDK/tree/main/agora_rtc/examples)
|
|
126
|
+
- [RTM usage example](https://github.com/AgoraIO-Extensions/Agora-Python-Server-SDK/tree/main/agora_rtc/rtm_examples)
|
|
86
127
|
# 2025.11.07 release 2.3.3
|
|
87
128
|
-- update: to support rtm
|
|
88
129
|
-- adjust sdk's directory structure
|
|
89
130
|
-- change requests to urllib
|
|
90
|
-
# 2025.10.23 release 2.3.2: support rtc and rtm in one package
|
|
91
|
-
-- update: to support rtm.can support both rtc and rtm in one package.
|
|
92
|
-
-- adjust sdk's directory structure
|
|
93
|
-
-- update rtc sdk
|
|
94
131
|
# 2025.10.09 release 2.3.1
|
|
95
132
|
-- update arm64 rtc sdk:Fixed a JNI referencing issue in the previous arm64 build. This issue only outputs logs to the console and does not affect functionality.
|
|
96
133
|
|
|
@@ -12,6 +12,7 @@ agora/rtc/audio_frame_observer.py
|
|
|
12
12
|
agora/rtc/audio_pcm_data_sender.py
|
|
13
13
|
agora/rtc/audio_sessionctrl.py
|
|
14
14
|
agora/rtc/audio_vad_manager.py
|
|
15
|
+
agora/rtc/external_audio_processor.py
|
|
15
16
|
agora/rtc/local_audio_track.py
|
|
16
17
|
agora/rtc/local_user.py
|
|
17
18
|
agora/rtc/local_user_observer.py
|
|
@@ -84,6 +84,16 @@ class CustomInstallCommand(install):
|
|
|
84
84
|
#20251110 Fusion version: with apm filter
|
|
85
85
|
mac_sdk="https://download.agora.io/sdk/release/agora_sdk_mac_v4.4.30_25869_FULL_20251030_1836_953684-aed.zip"
|
|
86
86
|
url = "https://download.agora.io/sdk/release/agora_rtc_sdk_x86_64-linux-gnu-v4.4.32.150_26715_SERVER_20251030_1807-aed.zip"
|
|
87
|
+
#date:20251124 update sdk for fix the issue of auto load so and auto load ains resource
|
|
88
|
+
url = "https://download.agora.io/sdk/release/agora_rtc_sdk_x86_64-linux-gnu-Agora_Native_SDK_for_Linux_x64_zhourui_26895_SERVER_20251121_1628_987405_20251021_1427-3a.zip"
|
|
89
|
+
mac_sdk="https://download.agora.io/sdk/release/agora_sdk_mac_Agora_Native_SDK_for_Mac_zhourui_26101_FULL_20251121_2135_987705_20251021_1427-3a.zip"
|
|
90
|
+
#added local track apm filter
|
|
91
|
+
#date:20251217 for incremental sending mode support
|
|
92
|
+
url="https://download.agora.io/sdk/release/agora_rtc_sdk_x86_64-linux-gnu-v4.4.32.154_26982_SERVER_20251210_1745_994155_20251021_1427-3a.zip"
|
|
93
|
+
mac_sdk="https://download.agora.io/sdk/release/agora_sdk_mac_v4.4.32.154_26308_FULL_20251210_1756_994156_20251021_1427-3a.zip"
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
87
97
|
if sys.platform == 'darwin':
|
|
88
98
|
url = mac_sdk
|
|
89
99
|
|
|
@@ -109,11 +119,14 @@ class CustomInstallCommand(install):
|
|
|
109
119
|
|
|
110
120
|
if os.path.exists(zip_path):
|
|
111
121
|
os.remove(zip_path)
|
|
122
|
+
#write version url to version.txt ,to replace the md5 check
|
|
123
|
+
with open(os.path.join(agora_service_path, "version.txt"), "w") as f:
|
|
124
|
+
f.write(url)
|
|
112
125
|
|
|
113
126
|
|
|
114
127
|
setup(
|
|
115
128
|
name='agora_python_server_sdk',
|
|
116
|
-
version='2.4.
|
|
129
|
+
version='2.4.2',
|
|
117
130
|
description='A Python SDK for Agora Server',
|
|
118
131
|
long_description=open('README.md').read(),
|
|
119
132
|
long_description_content_type='text/markdown',
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/agora_parameter.py
RENAMED
|
File without changes
|
|
File without changes
|
{agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/audio_frame_observer.py
RENAMED
|
File without changes
|
{agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/audio_pcm_data_sender.py
RENAMED
|
File without changes
|
{agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/audio_sessionctrl.py
RENAMED
|
File without changes
|
{agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/audio_vad_manager.py
RENAMED
|
File without changes
|
{agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/local_audio_track.py
RENAMED
|
File without changes
|
{agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/local_user_observer.py
RENAMED
|
File without changes
|
{agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/local_video_track.py
RENAMED
|
File without changes
|
{agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/media_node_factory.py
RENAMED
|
File without changes
|
{agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/remote_audio_track.py
RENAMED
|
File without changes
|
{agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/remote_video_track.py
RENAMED
|
File without changes
|
{agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/rtc_connection_observer.py
RENAMED
|
File without changes
|
{agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/utils/audio_consumer.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/video_frame_observer.py
RENAMED
|
File without changes
|
{agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/video_frame_sender.py
RENAMED
|
File without changes
|
{agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtc/voice_detection.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agora_python_server_sdk-2.4.0 → agora_python_server_sdk-2.4.2}/agora/rtm/rtm_event_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|