我正在基于pjsua2使用 python 开发一个 SIP 客户端。我有一个基于python 包装器调用的自定义 Call 类,并且我的代码能够获得活动连接。在我的 customonCallMediaState
上,我可以访问音频会议桥:
def onCallMediaState(self, prm):
"""
Manage call media state callbacks.
- Autoconnect audio
"""
ci = self.getInfo()
logger.info("onCallMediaState", media_size=ci.media.size())
self._print_call_info("onCallMediaState")
for media_index, media in enumerate(ci.media):
if media.type == pj.PJMEDIA_TYPE_AUDIO:
if ci.stateText == "CONFIRMED":
"""
It seems a bug with callbacks. CONFIRMED
is send at start and disconnect. So stop
record is manual, cannot use DISCONNECTD
"""
logger.info("Call CONFIRMED")
此时我可以media_index
用来录制或播放来自通话的音频。例如,对于录制:
def record_call(self, media_index):
"""
Record the audio incoming from call using default playback device
"""
record_media = pj.Endpoint_instance().audDevManager().getCaptureDevMedia()
audio_media = pj.AudioMedia.typecastFromMedia(self.getMedia(media_index))
port_id = audio_media.getPortId()
rx_level = audio_media.getRxLevel()
tx_level = audio_media.getTxLevel()
filename = "file.wav"
logger.info("Recording audio media", port_id=port_id, rx_level=rx_level, tx_level=tx_level)
self._recorder = pj.AudioMediaRecorder()
self._recorder.createRecorder(filename);
self._is_recording = True
record_media.startTransmit(self._recorder)
并创建了一个 file.wav。或使用默认音频设备:
def play_call(self, media_index):
"""
Play the audio incoming from call using default playback device
"""
playback_media = pj.Endpoint_instance().audDevManager().getPlaybackDevMedia()
audio_media = pj.AudioMedia.typecastFromMedia(self.getMedia(media_index))
port_id = audio_media.getPortId()
rx_level = audio_media.getRxLevel()
tx_level = audio_media.getTxLevel()
logger.info("Playing audio media", port_id=port_id, rx_level=rx_level, tx_level=tx_level)
audio_media.startTransmit(playback_media)
这两个示例都有效,根据PjSUA2 媒体文档和音频媒体文档,可以传输和接收音频、播放和录制 WAV。但根据pjsip 媒体端口文档,其他媒体也是可能的。似乎SWIG 包装器不支持它。
最后,我的问题是,是否可以在不记录 wav 文件的情况下管理内存中的音频帧?
我不想将音频写入硬盘,只是在内存中使用它,为此我需要直接原始数据。一种解决方法是编写一个块 wav 文件,并一个一个地读取它,但这是一个开销很大的肮脏解决方案。根据媒体流文档,可以获得回调,但我找不到如何在 Python 中执行此操作。在 python 包装器中不存在typedef void *MediaPort
试图绕过回调。