agora-python-server-sdk 2.4.1__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.
Files changed (49) hide show
  1. {agora_python_server_sdk-2.4.1/agora_python_server_sdk.egg-info → agora_python_server_sdk-2.4.2}/PKG-INFO +23 -1
  2. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/README.md +22 -0
  3. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/_ctypes_handle/_rtc_connection_observer.py +1 -1
  4. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/agora_base.py +13 -0
  5. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/agora_service.py +5 -5
  6. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/local_user.py +16 -1
  7. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/rtc_connection.py +89 -5
  8. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2/agora_python_server_sdk.egg-info}/PKG-INFO +23 -1
  9. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/setup.py +1 -1
  10. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/MANIFEST.in +0 -0
  11. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/__init__.py +0 -0
  12. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/__init__.py +0 -0
  13. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/_ctypes_handle/_audio_frame_observer.py +0 -0
  14. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/_ctypes_handle/_ctypes_data.py +0 -0
  15. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/_ctypes_handle/_local_user_observer.py +0 -0
  16. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/_ctypes_handle/_video_encoded_frame_observer.py +0 -0
  17. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/_ctypes_handle/_video_frame_observer.py +0 -0
  18. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/_utils/globals.py +0 -0
  19. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/agora_parameter.py +0 -0
  20. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/audio_encoded_frame_sender.py +0 -0
  21. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/audio_frame_observer.py +0 -0
  22. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/audio_pcm_data_sender.py +0 -0
  23. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/audio_sessionctrl.py +0 -0
  24. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/audio_vad_manager.py +0 -0
  25. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/external_audio_processor.py +0 -0
  26. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/local_audio_track.py +0 -0
  27. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/local_user_observer.py +0 -0
  28. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/local_video_track.py +0 -0
  29. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/media_node_factory.py +0 -0
  30. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/remote_audio_track.py +0 -0
  31. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/remote_video_track.py +0 -0
  32. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/rtc_connection_observer.py +0 -0
  33. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/utils/audio_consumer.py +0 -0
  34. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/utils/vad_dump.py +0 -0
  35. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/video_encoded_frame_observer.py +0 -0
  36. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/video_encoded_image_sender.py +0 -0
  37. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/video_frame_observer.py +0 -0
  38. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/video_frame_sender.py +0 -0
  39. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtc/voice_detection.py +0 -0
  40. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtm/__init__.py +0 -0
  41. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtm/_ctypes_handle/_ctypes_data.py +0 -0
  42. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtm/rtm_base.py +0 -0
  43. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtm/rtm_client.py +0 -0
  44. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora/rtm/rtm_event_handler.py +0 -0
  45. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora_python_server_sdk.egg-info/SOURCES.txt +0 -0
  46. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora_python_server_sdk.egg-info/dependency_links.txt +0 -0
  47. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/agora_python_server_sdk.egg-info/top_level.txt +0 -0
  48. {agora_python_server_sdk-2.4.1 → agora_python_server_sdk-2.4.2}/pyproject.toml +0 -0
  49. {agora_python_server_sdk-2.4.1 → 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.1
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
@@ -64,6 +64,28 @@ python agora_rtc/examples/example_audio_pcm_send.py --appId=xxx --channelId=xxx
64
64
  ```
65
65
 
66
66
  # Change log
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
+
67
89
  ## 2025.12.17 Release 2.4.1
68
90
 
69
91
  - Updated RTC SDK to version 154.
@@ -49,6 +49,28 @@ python agora_rtc/examples/example_audio_pcm_send.py --appId=xxx --channelId=xxx
49
49
  ```
50
50
 
51
51
  # Change log
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
+
52
74
  ## 2025.12.17 Release 2.4.1
53
75
 
54
76
  - Updated RTC SDK to version 154.
@@ -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
- print(f"ConnCB _on_capabilities_changed: {agora_capabilities_observer}, {ptr_caps_inner}, {size}")
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:
@@ -186,11 +186,11 @@ class AgoraService:
186
186
  return RTCConnection(self, con_config, publish_config)
187
187
 
188
188
  # createCustomAudioTrackPcm: creatae a custom audio track from pcm data sender
189
- 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:
190
190
  if not self.inited:
191
191
  logger.error("AgoraService is not initialized. Please call initialize() first.")
192
192
  return None
193
- if scenario == AudioScenarioType.AUDIO_SCENARIO_AI_SERVER:
193
+ if scenario == AudioScenarioType.AUDIO_SCENARIO_AI_SERVER and is_extra_audio == False:
194
194
  custom_audio_track = agora_service_create_direct_custom_audio_track_pcm(self.service_handle, audio_pcm_data_sender.sender_handle)
195
195
  else:
196
196
  custom_audio_track = agora_service_create_custom_audio_track_pcm(self.service_handle, audio_pcm_data_sender.sender_handle)
@@ -198,10 +198,10 @@ class AgoraService:
198
198
  return None
199
199
  local_track = LocalAudioTrack(custom_audio_track)
200
200
  #default for ai senario to set min delay to 10ms
201
- if scenario != AudioScenarioType.AUDIO_SCENARIO_AI_SERVER:
202
- local_track.set_send_delay_ms(10)
203
- local_track.set_max_buffer_audio_frame_number(100000)
201
+
202
+ local_track.set_max_buffer_audio_frame_number(100000)
204
203
  #and set enable to true
204
+ local_track.set_send_delay_ms(10)
205
205
  local_track.set_enabled(True)
206
206
  return local_track
207
207
  # mix_mode: MIX_ENABLED = 0, MIX_DISABLED = 1
@@ -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:
@@ -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
- self._audio_track = self.rtc_engine._create_custom_audio_track_pcm(self._audio_sender, self.publish_config.audio_scenario)
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 get_agora_parameter(self):
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
- print(f"Capability[{index}] - Type: {cap.capability_type}")
495
+ logger.debug(f"Capability[{index}] - Type: {cap.capability_type}")
471
496
  index += 1
472
497
  for item in cap.item_map.item:
473
- print(f"Item[{item_index}] - ID: {item.id}, Name: {item.name}")
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.1
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
@@ -64,6 +64,28 @@ python agora_rtc/examples/example_audio_pcm_send.py --appId=xxx --channelId=xxx
64
64
  ```
65
65
 
66
66
  # Change log
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
+
67
89
  ## 2025.12.17 Release 2.4.1
68
90
 
69
91
  - Updated RTC SDK to version 154.
@@ -126,7 +126,7 @@ class CustomInstallCommand(install):
126
126
 
127
127
  setup(
128
128
  name='agora_python_server_sdk',
129
- version='2.4.1',
129
+ version='2.4.2',
130
130
  description='A Python SDK for Agora Server',
131
131
  long_description=open('README.md').read(),
132
132
  long_description_content_type='text/markdown',