0.树莓派实现视频电话功能
树莓派python环境下可使用的模块有:pjsip,linphone,exosip2ctypesp,sipsimple
(1)pjsip(pjsua2+python)功能强大,但api较多,视频例子较少。主要是看到使用pjsip传输已经编码的视频,源码在github这篇文章,考虑到可以使用omx硬件编解码,但努力了几天,未成功。
(2)linphone有专门的raspberry的python模块,但没有实现omx硬件编解码,视频编码方式只有vp8,遗憾的放弃。
(3)exosip2ctypesp,是libexosip2的python封装,功能较简单,主要实现的是sip协议,rtp,编解码等未包含。根据exapmle中 代码修改,初步实现了树莓派sip客户端使用omx硬件编解码。依赖库太多了!!!
(4)sipsimple,没有安装成功。
-------------------------------------------------
1.安装python下的sip模块:exosip2ctypes
(1)sudo apt-get install libexosip2
(2)依赖库较多,需使用python虚拟环境。virtualenv
pip install virtualenv 安装虚拟环境
virtualenv venv 创建虚拟环境
source venv/bin/active 进入虚拟环境nm
(3)虚拟环境下,在源码包中pip install -r requirements-dev-lt_3.2.txt 安装依赖库,总之太多了。
(4)安装exosip2ctypes python setup.py install
2.修改exosip2ctypes源代码;
(1)错误1:context.py
def start(self, s=0, ms=50, event_executor=None):
self.logger.info('<0x%x>start: >>> s=%s, ms=%s', id(self), s, ms)
if self._is_running:
raise RuntimeError("Context loop already started.")
self._event_executor = event_executor or ThreadPoolExecutor() => self._event_executor = event_executor or ThreadPoolExecutor(2)
(2)错误2:context.py
class Context(BaseContext, LoggerMixin):
def __init__(self, event_callback=None): =>def __init__(self, event_callback=None,contact_address=None):
(3)错误3:message.py
class OsipMessage: => class OsipMessage(object):
3.树莓派代码
pc上安装ekiga软件与树莓派python程序建立视频通话:
(1)ekiga呼叫树莓派,先输入180回铃
(2)发送rtp视频编码: 这里使用了Camkit这个工具,github上有,感谢作者。需根据远端的sdp信息进行修改端口和IP地址。如果传过来的pt不为96,需修改rtppack这个文件。
(3)接受rtp视频编码:根据本端的sdp信息,创建sdp文件,使用omxplayer 播放这个sdp文件。
(4)输入200,建立链接。这时pc 上可以看到远端视频。
(5)问题1:omxplayer播放花屏,卡顿,不是带宽的问题,未知。
问题2:暂时没有实现树莓派音频采集/编码/打包/发送,下步再研究。
import sys
import logging
import logging.config
from exosip2ctypes import initialize, Context, call, EventType
logging.basicConfig(
level=logging.DEBUG, stream=sys.stdout,
format='%(asctime)-15s [%(threadName)-10s] [%(levelname)-7s] %(name)s - %(message)s'
)
latest_event = None
def on_exosip_event(context, evt):
global latest_event
latest_event = evt
if evt.type == EventType.call_invite:
print('**********************************************************************************')
logging.debug('[%s] on_call_invite', evt.did)
logging.debug('call-id: %s', evt.request.call_id)
# logging.debug("%s" % evt.request)
logging.debug('[%s] from: %s', evt.did, evt.request.from_)
logging.debug('[%s] allows: %s', evt.did, evt.request.allows)
logging.debug('[%s] contacts: %s', evt.did, evt.request.contacts)
for hname in ('User-Agent',):
logging.debug('[%s] header["%s"]: %s', evt.did, hname, evt.request.get_headers(hname))
print('**********************************************************************************')
logging.debug('body: %s', evt.request.bodies[0])
print('**********************************************************************************')
logging.debug('body: %s', evt.request.bodies)
print('**********************************************************************************')
elif evt.type == EventType.call_cancelled:
logging.debug('[%s] call_cancelled', evt.did)
elif evt.type == EventType.call_closed:
logging.debug('[%s] call_closed', evt.did)
initialize()
ctx = Context(event_callback=on_exosip_event)
# ctx.event_callback = on_exosip_event
#ctx.masquerade_contact('192.168.1.132', 5066)
print('listening...')
ctx.listen_on_address(address='192.168.1.132', port=5066)
print('starting...')
ctx.start()
print('started!')
while True:
s = sys.stdin.readline().strip().lower()
if s in ('q', 'quit'):
ctx.stop()
break
elif s == 'ack':
with ctx.lock:
ctx.call_send_ack(latest_event.did)
elif s in ('t', 'terminate'):
with ctx.lock:
ctx.call_terminate(latest_event.cid, latest_event.did)
elif s.isdigit():
status = int(s)
if status == 200:
with ctx.lock:
msg = call.Answer(ctx, latest_event.tid, 200)
msg.content_type = 'application/sdp'
msg.add_body(
"v=0\r\n"
"o=- 0 0 IN IP4 192.168.1.132\r\n"
"s=No Name\r\n"
"c=IN IP4 192.168.1.132\r\n"
"t=0 0\r\n"
"a=tool:libavformat 57.25.10\r\n"
"m=audio 54000 RTP/AVP 0 8 101\r\n"
"a=rtpmap:0 PCMU/8000\r\n"
"a=rtpmap:8 PCMA/8000\r\n"
"a=rtpmap:101 telephone-event/8000\r\n"
"m=video 4002 RTP/AVP 96\r\n"
"a=rtpmap:96 H264/90000\r\n"
)
# "a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z01AM5p0FCNCAAEEugA9CQEeMGVA,aO48gA==; profile-level-id=4D4033\r\n"
# "a=fmtp:96 packetization-mode=1; sprop-parameter-sets=J01AKakYDwBE/LgDUBAQG2wrXvfAQA==,KN4JyA==; profile-level-id=4D4029\r\n"
#./ffmpeg -re -i 1.h264 -vcodec copy -f rtp -payload_type 103 rtp://192.168.1.132:5056
#ffplay -vcodec h264_mmal
ctx.call_send_answer(answer=msg)
else:
with ctx.lock:
ctx.call_send_answer(latest_event.tid, status)