{"id":19976374,"url":"https://github.com/agoraio-extensions/agora-python-server-sdk","last_synced_at":"2025-05-04T02:34:53.287Z","repository":{"id":252385575,"uuid":"840279662","full_name":"AgoraIO-Extensions/Agora-Python-Server-SDK","owner":"AgoraIO-Extensions","description":null,"archived":false,"fork":false,"pushed_at":"2024-11-07T02:48:37.000Z","size":489,"stargazers_count":5,"open_issues_count":2,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-11-07T03:31:21.494Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AgoraIO-Extensions.png","metadata":{"files":{"readme":"README.cn.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-08-09T10:58:21.000Z","updated_at":"2024-11-04T16:29:15.000Z","dependencies_parsed_at":"2024-08-26T06:57:37.816Z","dependency_job_id":"fe83401d-9968-4622-bd46-e0aaa582781b","html_url":"https://github.com/AgoraIO-Extensions/Agora-Python-Server-SDK","commit_stats":null,"previous_names":["agoraio-extensions/agora-python-server-sdk"],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AgoraIO-Extensions%2FAgora-Python-Server-SDK","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AgoraIO-Extensions%2FAgora-Python-Server-SDK/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AgoraIO-Extensions%2FAgora-Python-Server-SDK/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AgoraIO-Extensions%2FAgora-Python-Server-SDK/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AgoraIO-Extensions","download_url":"https://codeload.github.com/AgoraIO-Extensions/Agora-Python-Server-SDK/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224381414,"owners_count":17301825,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-13T03:23:26.934Z","updated_at":"2025-05-04T02:34:52.791Z","avatar_url":"https://github.com/AgoraIO-Extensions.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# 注意\n- 这是一个 Python SDK 封装的 Agora RTC SDK。\n- 支持Linux和Mac平台。\n- examples只是作为非常简单的演示，不建议在生产环境中使用。\n\n# 非常重要的通知 !!!\n- 一个进程只能有一个实例\n- 一个实例，可以有多个connection\n- 所有的observer或者是回调中，都不能在调用sdk自身的api，也不能在回调中做cpu耗时的工作，数据拷贝是可以的。\n\n# 所需的操作系统和 Python 版本\n- 支持的 Linux 版本：\n  - Ubuntu 18.04 LTS 及以上\n  - CentOS 7.0 及以上\n  \n- 支持的 Mac 版本（仅支持开发测试）：\n  - MacOS 13 及以上\n\n- Python 版本：\n  - Python 3.10及以上\n\n\n# 使用Agora-Python-Server-SDK\n```\npip install agora_python_server_sdk\n```\n\n# 运行examples\n\n## 准备测试数据\n- 下载并解压 [test_data.zip](https://download.agora.io/demo/test/test_data_202408221437.zip) 到Agora-Python-Server-SDK目录\n\n## 执行测试脚本\n```\npython agora_rtc/examples/example_audio_pcm_send.py --appId=xxx --channelId=xxx --userId=xxx --audioFile=./test_data/demo.pcm --sampleRate=16000 --numOfChannels=1\n```\n\n# 更新日志\n## todo：\n## 2025.04.28 发布 2.2.4\n-- 更新：更新rtc sdk 到版本4.4.32\n## 2025.04.14 发布 2.2.3\n-- Fix: \n  -- 修复了enable_encryption 在salt 处理中的一个bug\n  -- 更新在enable_encryption 中的逻辑，当enable为0的时候，不做处理\n## 2025.04.10 发布 2.2.2\n-- Add:\n  - push_video_encoded_file.py: 支持推送mp4文件；支持推送h264编码的h.264文件；其中里面有从mp4文件的avformat转换为raw 264流的annex B格式的转换。\n-- Add：增加set_log_file_filter函数，可以设置日志的过滤级别\n-- 增加arm64版本的支持，但目前该版本不支持audio label 算法！所以在arm64 下不支持！\n-- 修改loalauidostats, local video stats, local audio stats, remote video stats, remote audio stats, \n-- 增加：connnection::agora_rtc_conn_enable_encryption \n-- 增加：connectionObserver::on_encryption_error (but not working for now, need to fix in the next monthly version 4.4.32)\n## 2025.02.26 发布 2.2.1\n-- sdk 更新：\n  -- sdk更新到20250102版本，优化main线程参考群：？？？\n-- AudioConsumer修改：==\u003e支持业务上能感知对TTS数据的播放状态，包括：开始播放、结束播放；从而可以推送播放状态信息给app端，完成业务诉求 \n  -- AudioConsumer的buffer大小修改为100ms，原来是180ms；ok\n  -- AudioConsumer::_samples_per_channel 在立体声下计算错误的bu g\n  -- AudioConsumer 新增加一个函数：is_push_to_rtc_completed 表示是否已经推送完成？》\n  -- 验证方式：app层启动一个timer，定时的去consume，当数据为空的时候，就通过datastream发送结束的消息给app端，app端收到消息后，渲染出来；通过对app 录制视频来判断文字和语音的同步性；？？？需要测试android·端和iOS端和web 端\n-- 增加：支持私有化的接口 ？？\n## 2025.01.07 发布 2.2.0\n-- 更新：\n  -- 更新sdk 版本，从4.4.30更新到4.4.31 ok\n-- 增加：serviceconfigure\n  -- 增加domain_limit ok\n  -- 增加should_callback_when_muted: ok\n  -- ExternalVideoFrame::增加colorspacetype，支持虚拟人场景下的纯色背景的编码：ok\n-- 增加：\n  -- AudioMetaData接口:localuser::send_audio_meta_data ok\n  -- OnAudioMetaDataReceived 回调接localuserObserver::on_audio_meta_data_received, ok\n-- sample 修改\n## 2024.12.17 发布 2.1.7  \n--修改：\n  修改了LocalUser::sub/unsub audio/video中typeError的问题\n  将vad默认的stopRecogCount从30调整到50\n  修改sample_vad\n## 2024.12.09 发布 2.1.6\n-- 增加：\n  -增加了AduioVadManger，用来管理vad instance\n  -将vad 功能内置在sdk内部，开发者不在需要关注如何使用vad，只需要关注设置合适的参数就可以。参考： sample_audio_vad.py\n- 修改：\n  -- register_audio_frame_observer中，增加了2个参数，用于设置vad的参数，参考： sample_audio_vad.py\n  -- 在on_playback_audio_frame_before_mixing中，返回值增加了2个参数： vad_result_state 和 vad_result_bytearray。 state： \u003c 0 没有设置内部自动出来vad；0: nospeaking, 1: startspeakong; 2 speaking; 3 stopspeaking. vad_result_bytearray 在vad状态下返回的vad处理后的结果。\n  -- 如果启动了自动处理vad：\n    . 开发者需要用vad_result_bytearray来做后续的业务处理，比如发送给ASR/STT， 而不是用frame 来做处理\n  参考： sample_audio_vad.py\n- 优化：\n  -- 在推送pcm中，不在使用pacer，而是使用Audioconsumer 进行推送。\n- 更新：\n  --修改了和Pacer、vad有关的sample\n## 2024.12.03 发布 2.1.5\n- 修改: LocalUser/audioTrack：\n  - 当场景为chorus的时候，开发者不需要调用setsenddelayinms；\n  - 当场景为chorus的时候，开发者不需要调用track的setaudioscnario 为chorus\n  - NOTE： 可以降低开发者难度。在ai场景下，开发者只需要设置service为chorus就可以。\n- 增加：VadDump 类，在测试环境下可以协助排查vad的问题。但在线上环境中，不要开启\n- 移除: Vad V1版本，只保留v2 版本。参考voice_detection.py,sample_audio_vad.py\n- 增加： on_volume_indication 回调\n- 增加： on_remote_video_track_state_changed 回调\n- 更新：更新有关的samples：audioconsume, vad sample\n## 2024.11.15 发布 2.1.4\n- 修改videoFrame中的metadata的类型从str修改bytes类型，和c++保持一致；从而可以支持字节流；\n- 修改了内部对ExteranlVideoFrame的封装，从而支持字节流；对alpha编码的支持，做了逻辑判断，如果fill_alpha_buffer 为0 ，则不处理\n\n## 2024.11.11 发布 2.1.3\n- 增加了一个sample：example_jpeg_send.py 可以将jpeg文件或者jpeg 流 推送到频道中\n- 性能耗费参考example中的注释，可以简单总结为对1920*1080对jpeg文件，从读取文件到转换为RGBA bytearry，耗费在11ms\n## 2024.11.07 发布 2.1.2\n\n- 对AudioVolumeInfoInner 以及 AudioVolumeInfo 结构中的user_id更新为str 类型\n- 修复了_on_audio_volume_indication中回调的bug，原来只能回调一个；修改成可以回调speaker_number个 ():\n- 修复了IRTCLocalUserObserver::on_audio_volume_indication中回调的参数类型为list类型\n\n## 2024.10.29 发布 2.1.1\n\n- 添加V2版本的音频 VAD 接口及相应的示例。\n\n## 2024.10.24 发布 2.1.0\n\n- 修复了一些 bug\n\n### 常见用法Q\u0026A\n## serveice和进程的关系？\n- 一个进程只能有一个service，只能对service做一次初始化；\n- 一个service，只能有一个media_node_factory；\n- 一个service，可以有多个connection；\n- 在进程退出去的时候，再释放：media_node_factory.release() 和 service.release()\n\n## 如果对docker的使用是一个docker一个用户，用户的时候，启动docker，用户退出去的时候，就释放docker，那么应该这么来做？\n- 这个时候就在进程启动的时候，创建service/media_node_factory 和 connection；\n- 在进程退出的时候，释放service/media_node_factory 和 connedtion，这样就可以保证\n\n## 如果docker的使用是一个docker支持多个用户的时候，docker会长时间运行，应该怎么做？\n- 这个情况下，我们推荐用connection pool的概念\n- 在进程启动的时候，创建service/media_node_factory 和 connection pool（只是new connection，并不初始化）；\n- 当有用户进来的时候，就从connection pool中获取一个connection，然后初始化，执行 con.connect()并且设置好回调，然后加入频道；\n- 处理业务\n- 当用户退出的时候，con.disconnect()并释放跟随该conn 的audio/video track，observer等，但不调用con.release();然后将该con 放回connection pool中；\n- 在进程退出的时候，释放和 connedtion pool（对每一个con.release()\n释放 service/media_node_factory 和 connedtion pool（对每一个con.release()），这样就可以保证资源的释放和性能最优\n\n## VAD的使用\n# source code: voice_detection.py\n# sample code: example_audio_vad.py\n- 推荐用VAD V2版本，类为： AudioVadV2； 参考：voice_detection.py；\n\n- VAD 的使用： \n  - 1. 调用 _vad_instance.init(AudioVadConfigV2) 初始化vad实例.参考：voice_detection.py。 实例假如为： _vad_instance\n  - 2. 在audio_frame_observer::on_playback_audio_frame_before_mixing(audio_frame) 中:\n    - 1. 调用 vad模块的process:  state, bytes = _vad_instance.process(audio_frame)\n    - 2. 根据返回的state，判断state的值，并做相应的处理\n       - A. 如果state为 _vad_instance._vad_state_startspeaking，则表明当前“开始说话”，可以开始进行语音识别（STT/ASR）等操作。记住：一定要将返回的bytes 交给识别模块，而不是原始的audio_frame，否则会导致识别结果不正确。\n       - B. 如果state为 _vad_instance._vad_state_stopspeaking，则表明当前“停止说话”，可以停止语音识别（STT/ASR）等操作。记住：一定要将返回的bytes 交给识别模块，而不是原始的audio_frame，否则会导致识别结果不正确。\n       - C. 如果state为 _vad_instance._vad_state_speaking，则表明当前“说话中”，可以继续进行语音识别（STT/ASR）等操作。记住：一定要将返回的bytes 交给识别模块，而不是原始的audio_frame，否则会导致识别结果不正确。\n  备注：如果使用了vad模块，并且希望用vad模块进行语音识别（STT/ASR）等操作，那么一定要将返回的bytes 交给识别模块，而不是原始的audio_frame，否则会导致识别结果不正确。\n- 如何更好的排查VAD的问题：包含2个方面，配置和调试。\n  - 1. 确保vad模块的初始化参数正确，参考：voice_detection.py。\n  - 2. 在state,bytes = on_playback_audio_frame_before_mixing(audio_frame) 中，\n    - 1. 将audio_frame的data 的data 保存到本地文件，参考：example_audio_pcm_send.py。这个就是录制原始的音频数据。比如可以命名为：source_{time.time()*1000}.pcm\n    - 2. 保存每一次vad 处理的结果：\n      - A state==start_speaking的时候：新建一个二进制文件，比如命名为：vad_{time.time()*1000}.pcm，并将bytes 写入到文件中。\n      - B state==speaking的时候：将bytes 写入到文件中。\n      - C state==stop_speaking的时候：将bytes 写入到文件中。并关闭文件。\n  备注：这样就可以根据原始音频文件和vad处理后的音频文件，进行排查问题。生产环境的时候，可以关闭这个功能\n\n## 如何将TTS生成的音频推入到频道中？\n# source code: audio_consumer.py\n# sample code: example_audio_consumer.py\n\n## 如何释放资源？\n    localuser.unpublish_audio(audio_track)\n    localuser.unpublish_video(video_track)\n    audio_track.set_enabled(0)\n    video_track.set_enabled(0)\n\n    localuser.unregister_audio_frame_observer()\n    localuser.unregister_video_frame_observer()\n    localuser.unregister_local_user_observer()\n\n    connection.disconnect()\n    connection.unregister_observer()\n\n    localuser.release()\n    connection.release()\n\n    \n    audio_track.release()\n    video_track.release()\n    pcm_data_sender.release()\n    video_data_sender.release()\n    audio_consumer.release()\n\n    media_node_factory.release()\n    agora_service.release()\n    \n    #set to None\n    audio_track = None\n    video_track = None\n    audio_observer = None\n    video_observer = None\n    local_observer = None\n    localuser = None\n    connection = None\n    agora_service = None\n\n## 在AI场景下，如何做打断？\n- 打断的定义\n  在人机对话中，打断是指用户在对话过程中突然打断机器人的回答，要求机器人立即停止当前回答并转而回答用户的新问题。这个行为就叫打断\n- 打断触发的条件\n  打断根据不同的产品定义，一般有两种方式：\n  - 模式1：语音激励模式. 当检测到有用户说话，就执行打断策略，比如用户说话时，识别到用户说话，就执行打断策略。\n  - 模式2：ASR激励模式. 当检测到有用户说话、并且asr/STT的识别返回有结果的时候，就执行打断策略。\n- 不同打断策略的优点\n  - 1. 语音激励打断：\n    - 优点：\n    - 1. 减少用户等待时间，减少用户打断的概率。因为用户说话时，机器人会立即停止回答，用户不需要等待机器人回答完成。\n    - 缺点：\n    - 1. 因为是语音激励模式，有可能会被无意义的语音信号给打断，依赖于VAD判断的准确性。比如AI在回答的时候，如果有人敲击键盘，就可能触发语音激励，将AI打断。\n  -2 . ASR激励打断：\n    - 优点：\n    - 1. 降低用户打断的概率。因为用户说话时，asr/STT识别到用户说话，才会触发打断策略。\n    - 缺点：\n    - 1. 因为是asr/STT激励模式，需要将语音信号转换成文本，会增加打断的延迟。\n\n- 推荐模式\n  如果VAD能过滤掉非人声，只是在有人声的时候，才触发VAD判断，建议用语音激励模式；或者是对打断要求延迟敏感的时候，用改模式\n  如果对打断延迟不敏感，建议用ASR激励模式，因为ASR激励模式，可以过滤掉非人声，降低用户打断的概率。\n- 如何实现打断？打断需要做哪些操作？\n  定义：人机对话，通常可以理解为对话轮的方式来进行。比如用户问一个问题，机器人回答一个问题；然后用户再问一个问题，机器人再回答一个问题。这样的模式就是对话轮。我们假设给对话轮一个roundId,每轮对话，roundId+1。 一个对话轮包含了这样的3个阶段/组成部分：vad、asr、LLM、TTS、rtc推流。\n  1. vad： 是指人机对话的开始，通过vad识别出用户说话的开始和结束，然后根据用户说话的开始和结束，交给后续的ASR。\n  2. asr： 是指人机对话的识别阶段，通过asr识别出用户说的话，然后交给LLM。\n  3. LLM： 是指人机对话的生成阶段，LLM根据用户说的话，生成一个回答。\n  4. TTS： 是指人机对话的合成阶段，LLM根据生成的回答，合成一个音频。\n  5. rtc推流： 是指人机对话的推流阶段，将合成后的音频推流到rtc，然后机器人播放音频。\n\n  因此，所谓的打断，就是在（roundid+1）轮的时候，无论是用语音激励（VAD阶段触发）还是用ASR激励（就是在ASR识别出用户说的话）打断，都需要做如下的操作：\n  1. 停止当前轮roundID轮的LLM生成。\n  2. 停止当前轮roundID轮的TTS合成。\n  3. 停止当前轮roundID轮的RTC推流。\n   API调用参考：\n    a 调用:AudioConsumer.clear()；\n    b 调用:LocalAudioTrack.clear_sender_buffer()；\n    c 业务层：清除TTS返回来保留的数据（如果有）\n## LLM的结果什么时候交给TTS做合成？\n  LLM的结果是异步返回的，而且都是流式返回的。应该按照什么时机将LLM的结果交给TTS做合成呢？\n  需要考虑2个因素：\n  1. 无歧义、连续、流畅：确保TTS合成的语音是没有歧义、而且是完整、连续的。比如LLM返回的文本是：\"中间的首都是北京吗？\"如果我们给TTS的是：中  然后是：国首  然后是：是北   然后是：京吗？  这样合成会有歧义，因为\"中\"和\"国\"之间没有空格，\"首\"和\"是\"之间没有空格，\"京\"和\"吗\"之间没有空格。\n  2. 确保整个流程延迟最低。LLM 生成完成后，在交给TTS，这样的处理方式，合成的语音一定是没有歧义，而且是连续的。但延迟会很大，对用户体验不友好。\n  推荐的方案：\n    将有LLM返回数据的时候：\n    a LLM返回的结果存放在缓存中\n    b 对缓存中的数据做逆序扫描，找到最近的一个标点符号\n    c 将缓存中的数据，从头开始到最尾的一个标点符号截断，然后交给TTS做合成。\n    d 将截断后的数据，从缓存中删除。剩余的数据，移动到缓存头位置，继续等待LLM返回数据。\n\n## VAD配置参数的含义\nAgoraAudioVadConfigV2 属性\n属性名\t                 类型\t    描述\t                         默认值\t     取值范围\npreStartRecognizeCount\tint\t    开始说话状态前保存的音频帧数\t      16\t       [0, ]  \nstartRecognizeCount\t    int   \t判断是否开始说话状态的音频帧总数     30\t    [1, max]\nstopRecognizeCount\t    int\t    判断停止说话状态的音频帧数\t        50\t    [1, max]\nactivePercent\t          float\t  在 startRecognizeCount \n                                  帧中活跃帧的百分比\t            0.7\t    0.0, 1.0]\ninactivePercent       \tfloat\t  在 stopRecognizeCount\n                                 帧中非活跃帧的百分比\t             0.5     [0.0, 1.0]\nstartVoiceProb\t        int\t    音频帧是人声的概率\t              70\t    [0, 100]\nstopVoiceProb\t          int\t     音频帧是人声的概率\t               70\t    [0, 100]\nstartRmsThreshold\t      int\t     音频帧的能量分贝阈值\t            -50\t     [-100, 0]\nstopRmsThreshold\t      int\t    音频帧的能量分贝阈值            \t-50\t    [-100, 0]\n注意事项\n\nstartRmsThreshold 和 stopRmsThreshold:\n值越高，就需要说话人的声音相比周围环境的环境音的音量越大\n在安静环境中推荐使用默认值 -50。\n在嘈杂环境中可以调高到 -40 到 -30 之间，以减少误检。\n根据实际使用场景和音频特征进行微调可获得最佳效果。\n\nstopReecognizeCount: 反映在识别到非人声的情况下，需要等待多长时间才认为用户已经停止说话。可以用来控制说话人相邻语句的间隔，在该间隔内，VAD会将相邻的语句当作一段话。如果该值短，相邻语句就越容易被识别为2段话。通常推荐50～80。\n比如：下午好，[interval_between_sentences]北京有哪些好玩的地方？\n如果说话人语气之间的间隔interval_between_sentences 大于stopReecognizeCount，那么VAD就会将上述识别为2个vad：\nvad1: 下午好\nvad2: 北京有哪些好玩的地方？\n如果interval_between_sentences 小于 stopReecognizeCount，那么VAD就会将上述识别为1个vad：\nvad： 下午好，北京有哪些好玩的地方？\n\n如果对延迟敏感，可以调低该值，或者咨询研发，在降低该值的情况下，应该如何在应用层做处理，在保障延迟的情况下，还能确保语意的连续性，不会产生AI被敏感的打断的感觉。\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fagoraio-extensions%2Fagora-python-server-sdk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fagoraio-extensions%2Fagora-python-server-sdk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fagoraio-extensions%2Fagora-python-server-sdk/lists"}