Skip to content

Commit

Permalink
fix(camera): WebRTC and Streaming URL impl.
Browse files Browse the repository at this point in the history
  • Loading branch information
kakopappa committed Jul 7, 2023
1 parent 742f318 commit 5154491
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 38 deletions.
30 changes: 20 additions & 10 deletions examples/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,16 @@
import asyncio
import base64

APP_KEY = ''
APP_SECRET = ''
APP_KEY = ""
APP_SECRET = ""
CAMERA_ID = ''


def webrtc_offer(device_id, format, offer):
def get_webrtc_answer(device_id, offer):
sdp_offer = base64.b64decode(offer)
print('device_id: {} format: {} offer: {}'.format(
device_id, format, sdp_offer))
print('device_id: {} offer: {}'.format(device_id, offer))

mediamtx_url = "http://<mediamtx-hostname>:8889/<device>/whep" # PORT 8889 for WebRTC
# PORT 8889 for WebRTC. eg: for PiCam, use http://<mediamtx-hostname>:8889/cam/whep
mediamtx_url = "http://<mediamtx-hostname>:8889/<device>/whep"
headers = {"Content-Type": "application/sdp"}
response = requests.post(mediamtx_url, headers=headers, data=sdp_offer)

Expand All @@ -25,17 +24,28 @@ def webrtc_offer(device_id, format, offer):


def power_state(device_id, state):
print('device_id: {} state: {}'.format(device_id, state))
print('device_id: {} power state: {}'.format(device_id, state))
return True, state

def get_camera_stream_url(device_id, protocol):
# Google Home: RTSP protocol not supported. Requires a Chromecast TV or Google Nest Hub
# Alexa: RTSP url must be interleaved TCP on port 443 (for both RTP and RTSP) over TLS 1.2 port 443

print('device_id: {} protocol: {}'.format(device_id, protocol))

if protocol == "rstp":
return True, 'rtsp://rtspurl:443' # RSTP.
else:
return True, 'https://cph-p2p-msl.akamaized.net/hls/live/2000341/test/master.m3u8' # HLS

callbacks = {
SinricProConstants.WEBRTC_OFFER: webrtc_offer,
SinricProConstants.GET_WEBRTC_ANSWER: get_webrtc_answer,
SinricProConstants.GET_CAMERA_STREAM_URL: get_camera_stream_url,
SinricProConstants.SET_POWER_STATE: power_state
}

if __name__ == '__main__':
loop = asyncio.get_event_loop()
client = SinricPro(APP_KEY, [CAMERA_ID], callbacks,
enable_log=False, restore_states=False, secret_key=APP_SECRET)
enable_log=True, restore_states=False, secret_key=APP_SECRET)
loop.run_until_complete(client.connect())
29 changes: 22 additions & 7 deletions sinric/_callback_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from ._tv_contorller import TvController
from ._speaker_controller import SpeakerController
from ._mode_controller import ModeController
from ._webrtc_controller import WebRTCController
from ._camera_stream_controller import CameraStreamController
from ._lock_controller import LockStateController
from ._signature import Signature
from ._leaky_bucket import LeakyBucket
Expand All @@ -30,7 +30,7 @@
# noinspection PyBroadException
class CallBackHandler(PowerLevelController, PowerController, BrightnessController, ColorController, ColorTemperatureController,
ThermostateMode, RangeValueController, TemperatureController, TvController, SpeakerController,
LockStateController, ModeController, WebRTCController, Signature):
LockStateController, ModeController, CameraStreamController, Signature):
def __init__(self, callbacks, trace_bool, logger, enable_track=False, secret_key=""):
self.myHmac = None
self.secret_key = secret_key
Expand All @@ -50,7 +50,7 @@ def __init__(self, callbacks, trace_bool, logger, enable_track=False, secret_key
SpeakerController.__init__(self)
ModeController.__init__(self)
ColorTemperatureController.__init__(self, 0, [2200, 2700, 4000, 5500, 7000])
WebRTCController.__init__(self)
CameraStreamController.__init__(self)

self.callbacks = callbacks
self.logger = logger
Expand Down Expand Up @@ -175,8 +175,11 @@ def json_response(action, resp, data_dict, instance_id='') -> dict:
elif action == SinricProConstants.SET_LOCK_STATE:
await self._handle_set_lock_state(connection, udp_client, jsn, handle_response, json_response, action)

elif action == SinricProConstants.WEBRTC_OFFER:
await self._handle_webrtc_offer(connection, udp_client, jsn, handle_response, json_response, action)
elif action == SinricProConstants.GET_WEBRTC_ANSWER:
await self._handle_get_webrtc_answer(connection, udp_client, jsn, handle_response, json_response, action)

elif action == SinricProConstants.GET_CAMERA_STREAM_URL:
await self._handle_get_camera_stream_url(connection, udp_client, jsn, handle_response, json_response, action)

# Handle events

Expand Down Expand Up @@ -281,9 +284,21 @@ def json_response(action, resp, data_dict, instance_id='') -> dict:
self.logger.info('Sending push notification event')
await connection.send(dumps(jsn))

async def _handle_webrtc_offer(self, connection, udp_client, jsn, handle_response, json_response, action):
async def _handle_get_camera_stream_url(self, connection, udp_client, jsn, handle_response, json_response, action):
try:
resp, url = await self.get_camera_stream_url(jsn, self.callbacks.get(action))
response = json_response(action, resp, { "url": url })

if resp:
await handle_response(response, connection, udp_client)
except AssertionError:
self.logger.error("Signature verification failed for " + jsn.get('payload').get('action'))
except Exception as e:
self.logger.error(str(e))

async def _handle_get_webrtc_answer(self, connection, udp_client, jsn, handle_response, json_response, action):
try:
resp, value = await self.webrtc_offer(jsn, self.callbacks.get(action))
resp, value = await self.get_webrtc_answer(jsn, self.callbacks.get(action))
response = json_response(action, resp, { "answer": value })

if resp:
Expand Down
22 changes: 22 additions & 0 deletions sinric/_camera_stream_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"""
* Copyright (c) 2019-2023 Sinric. All rights reserved.
* Licensed under Creative Commons Attribution-Share Alike (CC BY-SA)
*
* This file is part of the Sinric Pro (https://github.com/sinricpro/)
"""

from ._sinricpro_constants import SinricProConstants

class CameraStreamController:

async def get_webrtc_answer(self, jsn, get_webrtc_answer_callback):
self.offer = jsn.get("payload").get("value").get("offer")

return get_webrtc_answer_callback(jsn.get("payload").get(SinricProConstants.DEVICEID),
self.offer)

async def get_camera_stream_url(self, jsn, get_camera_stream_url_callback):
self.protocol = jsn.get("payload").get("value").get("protocol")

return get_camera_stream_url_callback(jsn.get("payload").get(SinricProConstants.DEVICEID),
self.protocol)
5 changes: 3 additions & 2 deletions sinric/_sinricpro_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ class SinricProConstants(object):
RESET_BANDS = 'resetBands'
SET_MODE = 'setMode'
SET_LOCK_STATE = 'setLockState'
WEBRTC_OFFER = 'webrtcOffer'
GET_WEBRTC_ANSWER = 'getWebRTCAnswer'
GET_CAMERA_STREAM_URL = 'getCameraStreamUrl'
PUSH_NOTIFICATION = 'pushNotification'
MOTION = 'motion'
SET_CONTACT_STATE = 'setContactState'
Expand Down Expand Up @@ -57,7 +58,7 @@ class SinricProConstants(object):
NAME = 'name'
BANDS = 'bands'
THERMOSTATMODE = 'thermostatMode'
MODE: 'mode',
MODE = 'mode'


# values
Expand Down
4 changes: 2 additions & 2 deletions sinric/_sinricpro_websocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ async def connect(self): # Producer
'true' if self.restore_states else 'false')},
ping_interval=30000, ping_timeout=10000)
if self.connection.open:
self.logger.success(f"{'Connected :)'}")
self.logger.success("Connected :)")
timestamp = await self.connection.recv()
if (int(time()) - json.loads(timestamp).get('timestamp') > 60000):
self.logger.warning(f'Timestamp is not in sync. Please check your system time.')
self.logger.warning('Timestamp is not in sync. Please check your system time.')
return self.connection

async def receive_message(self, connection):
Expand Down
17 changes: 0 additions & 17 deletions sinric/_webrtc_controller.py

This file was deleted.

0 comments on commit 5154491

Please sign in to comment.