1. 程式人生 > >將android攝像頭視訊釋出到公網

將android攝像頭視訊釋出到公網

想用舊android手機作為監控,拍攝家裡的視訊。

1.首先需要 ip攝像頭 這款軟體。這款安卓軟體本身是在區域網中使用的,可以把手機變為攝像頭,也可以播放器他變為攝像頭的手機,他本身是啟動了web伺服器和流媒體伺服器,用來等待其他手機來連結攝像頭。

2.然後需要Qpython,這是在安卓手機上執行python指令碼的app,用他來執行python-ngrok指令碼,將開啟攝像頭本身手機的8081埠,也就是ip攝像頭開啟web應用的埠,對映到ngrok的一個子域名下,這樣可以在外網訪問ngrok,在方位到攝像頭主機的8081埠。

3.python-ngrok是一個Qpthon內網穿透指令碼,現在用於將本級的8081埠,對映到ngrok的一個子域名下。

工具上傳了

使用方法就是,先啟用Qpython,執行python-ngrok.py指令碼,在開啟IP攝像頭,點選 把手機變為攝像頭 ,在用另外一個手機,安卓ip攝像頭,新增一個url,這個url就是ngrok給我們分配的子域名。

其中python-ngrok.py內容如下

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# 建議Python 2.7.9 或 Python 3.4.2 以上執行
# 專案地址: https://github.com/hauntek/python-ngrok
# Version: v1.46
import socket
import ssl
import json
import struct
import random
import sys
import time
import logging
import threading

host = 'tunnel.qydev.com' # Ngrok伺服器地址
port = 4443 # 埠
bufsize = 1024 # 吞吐量

Tunnels = list() # 全域性渠道賦值

#這個怎樣用,還不知道
#body = dict()
#body['protocol'] = 'http'
#body['hostname'] = 'jingchudne'
#body['subdomain'] = ''
#body['rport'] = 0
#body['lhost'] = '127.0.0.1'
#body['lport'] = 80
#Tunnels.append(body) # 加入渠道佇列

#這裡是http端頭對映,意思就是,在ngrok下,建立一個jingchunde的子域,訪問這個子域,就能得到本級8081的埠內容。
body = dict()
body['protocol'] = 'http'
body['hostname'] = ''
body['subdomain'] = 'jingchunde'
body['rport'] = 0
body['lhost'] = '127.0.0.1'
body['lport'] = 8081
Tunnels.append(body) # 加入渠道佇列

#這裡是tcp埠對映
#body = dict()
#body['protocol'] = 'tcp'
#body['hostname'] = ''
#body['subdomain'] = ''
#body['rport'] = 55499
#body['lhost'] = '127.0.0.1'
#body['lport'] = 22
#Tunnels.append(body) # 加入渠道佇列

# 讀取配置檔案
if len(sys.argv) >= 2:
    file_object = open(sys.argv[1])
    try:
        all_the_text = file_object.read()
        config_object = json.loads(all_the_text)
        host = config_object["server"]["host"] # Ngrok伺服器地址
        port = int(config_object["server"]["port"]) # 埠
        bufsize = int(config_object["server"]["bufsize"]) # 吞吐量
        Tunnels = list() # 重置渠道賦值
        for Tunnel in config_object["client"]:
            body = dict()
            body['protocol'] = Tunnel["protocol"]
            body['hostname'] = Tunnel["hostname"]
            body['subdomain'] = Tunnel["subdomain"]
            body['rport'] = int(Tunnel["rport"])
            body['lhost'] = Tunnel["lhost"]
            body['lport'] = int(Tunnel["lport"])
            Tunnels.append(body) # 加入渠道佇列
        del all_the_text
        del config_object
    except Exception:
        # logger = logging.getLogger('%s' % 'config')
        # logger.error('The configuration file read failed')
        # exit(1)
        pass
    finally:
        file_object.close()

mainsocket = 0

ClientId = ''
pingtime = 0

def getloacladdr(Tunnels, Url):
    protocol = Url[0:Url.find(':')]
    hostname = Url[Url.find('//') + 2:]
    subdomain = hostname[0:hostname.find('.')]
    rport = Url[Url.rfind(':') + 1:]

    for tunnelinfo in Tunnels:
        if tunnelinfo.get('protocol') == protocol:
            if tunnelinfo.get('protocol') in ['http', 'https']:
                if tunnelinfo.get('hostname') == hostname:
                    return tunnelinfo
                if tunnelinfo.get('subdomain') == subdomain:
                    return tunnelinfo
            if tunnelinfo.get('protocol') == 'tcp':
                if tunnelinfo.get('rport') == int(rport):
                    return tunnelinfo

    return dict()

def dnsopen(host):
    try:
        ip = socket.gethostbyname(host)
    except socket.error:
        return False

    return ip

def connectremote(host, port):
    try:
        host = socket.gethostbyname(host)
        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        ssl_client = ssl.wrap_socket(client, ssl_version=ssl.PROTOCOL_SSLv23)
        ssl_client.connect((host, port))
        ssl_client.setblocking(1)
        logger = logging.getLogger('%s:%d' % ('Conn', ssl_client.fileno()))
        logger.debug('New connection to: %s:%d' % (host, port))
    except socket.error:
        return False

    return ssl_client

def connectlocal(localhost, localport):
    try:
        localhost = socket.gethostbyname(localhost)
        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        client.connect((localhost, localport))
        client.setblocking(1)
        logger = logging.getLogger('%s:%d' % ('Conn', client.fileno()))
        logger.debug('New connection to: %s:%d' % (localhost, localport))
    except socket.error:
        return False

    return client

def NgrokAuth():
    Payload = dict()
    Payload['ClientId'] = ''
    Payload['OS'] = 'darwin'
    Payload['Arch'] = 'amd64'
    Payload['Version'] = '2'
    Payload['MmVersion'] = '1.7'
    Payload['User'] = 'user'
    Payload['Password'] = ''
    body = dict()
    body['Type'] = 'Auth'
    body['Payload'] = Payload
    buffer = json.dumps(body)
    return(buffer)

def ReqTunnel(Protocol, Hostname, Subdomain, RemotePort):
    Payload = dict()
    Payload['ReqId'] = getRandChar(8)
    Payload['Protocol'] = Protocol
    Payload['Hostname'] = Hostname
    Payload['Subdomain'] = Subdomain
    Payload['HttpAuth'] = ''
    Payload['RemotePort'] = RemotePort
    body = dict()
    body['Type'] = 'ReqTunnel'
    body['Payload'] = Payload
    buffer = json.dumps(body)
    return(buffer)

def RegProxy(ClientId):
    Payload = dict()
    Payload['ClientId'] = ClientId
    body = dict()
    body['Type'] = 'RegProxy'
    body['Payload'] = Payload
    buffer = json.dumps(body)
    return(buffer)

def Ping():
    Payload = dict()
    body = dict()
    body['Type'] = 'Ping'
    body['Payload'] = Payload
    buffer = json.dumps(body)
    return(buffer)

def lentobyte(len):
    return struct.pack('<LL', len, 0)

def sendbuf(sock, buf, isblock = False):
    if isblock:
        sock.setblocking(1)
    sock.sendall(buf)
    if isblock:
        sock.setblocking(0)

def sendpack(sock, msg, isblock = False):
    if isblock:
        sock.setblocking(1)
    sock.sendall(lentobyte(len(msg)) + msg.encode('utf-8'))
    logger = logging.getLogger('%s:%d' % ('Send', sock.fileno()))
    logger.debug('Writing message: %s' % msg)
    if isblock:
        sock.setblocking(0)

def tolen(v):
    if len(v) == 8:
        return struct.unpack('<II', v)[0]
    return 0

def getRandChar(length):
    _chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"
    return ''.join(random.sample(_chars, length))

# 客戶端程式處理過程
def HKClient(sock, linkstate, type, tosock = None):
    global mainsocket
    global ClientId
    global pingtime
    recvbuf = bytes()
    while True:
        try:
            if linkstate == 0:
                if type == 1:
                    sendpack(sock, NgrokAuth(), False)
                    linkstate = 1
                if type == 2:
                    sendpack(sock, RegProxy(ClientId), False)
                    linkstate = 1
                if type == 3:
                    linkstate = 1

            recvbut = sock.recv(bufsize)
            if not recvbut: break

            if len(recvbut) > 0:
                if not recvbuf:
                    recvbuf = recvbut
                else:
                    recvbuf += recvbut

            if type == 1 or (type == 2 and linkstate == 1):
                lenbyte = tolen(recvbuf[0:8])
                if len(recvbuf) >= (8 + lenbyte):
                    buf = recvbuf[8:lenbyte + 8].decode('utf-8')
                    logger = logging.getLogger('%s:%d' % ('Recv', sock.fileno()))
                    logger.debug('Reading message with length: %d' % len(buf))
                    logger.debug('Read message: %s' % buf)
                    js = json.loads(buf)
                    if type == 1:
                        if js['Type'] == 'ReqProxy':
                            newsock = connectremote(host, port)
                            if newsock:
                                thread = threading.Thread(target = HKClient, args = (newsock, 0, 2))
                                thread.setDaemon(True)
                                thread.start()
                        if js['Type'] == 'AuthResp':
                            ClientId = js['Payload']['ClientId']
                            logger = logging.getLogger('%s' % 'client')
                            logger.info('Authenticated with server, client id: %s' % ClientId)
                            sendpack(sock, Ping())
                            pingtime = time.time()
                            for tunnelinfo in Tunnels:
                                # 註冊通道
                                sendpack(sock, ReqTunnel(tunnelinfo['protocol'], tunnelinfo['hostname'], tunnelinfo['subdomain'], tunnelinfo['rport']))
                        if js['Type'] == 'NewTunnel':
                            if js['Payload']['Error'] != '':
                                logger = logging.getLogger('%s' % 'client')
                                logger.error('Server failed to allocate tunnel: %s' % js['Payload']['Error'])
                                time.sleep(30)
                            else:
                                logger = logging.getLogger('%s' % 'client')
                                logger.info('Tunnel established at %s' % js['Payload']['Url'])
                    if type == 2:
                        if js['Type'] == 'StartProxy':
                            loacladdr = getloacladdr(Tunnels, js['Payload']['Url'])

                            newsock = connectlocal(loacladdr['lhost'], loacladdr['lport'])
                            if newsock:
                                thread = threading.Thread(target = HKClient, args = (newsock, 0, 3, sock))
                                thread.setDaemon(True)
                                thread.start()
                                tosock = newsock
                                linkstate = 2
                            else:
                                body = '<html><body style="background-color: #97a8b9"><div style="margin:auto; width:400px;padding: 20px 60px; background-color: #D3D3D3; border: 5px solid maroon;"><h2>Tunnel %s unavailable</h2><p>Unable to initiate connection to <strong>%s</strong>. This port is not yet available for web server.</p>'
                                html = body % (js['Payload']['Url'], loacladdr['lhost'] + ':' + str(loacladdr['lport']))
                                header = "HTTP/1.0 502 Bad Gateway" + "\r\n"
                                header += "Content-Type: text/html" + "\r\n"
                                header += "Content-Length: %d" + "\r\n"
                                header += "\r\n" + "%s"
                                buf = header % (len(html.encode('utf-8')), html)
                                sendbuf(sock, buf.encode('utf-8'))

                    if len(recvbuf) == (8 + lenbyte):
                        recvbuf = bytes()
                    else:
                        recvbuf = recvbuf[8 + lenbyte:]

            if type == 3 or (type == 2 and linkstate == 2):
                sendbuf(tosock, recvbuf)
                recvbuf = bytes()

        except socket.error:
            break

    if type == 1:
        mainsocket = False
    if type == 3:
        try:
            tosock.shutdown(socket.SHUT_WR)
        except socket.error:
            tosock.close()

    logger = logging.getLogger('%s:%d' % ('Close', sock.fileno()))
    logger.debug('Closing')
    sock.close()

# 客戶端程式初始化
if __name__ == '__main__':
    logging.basicConfig(level=logging.DEBUG, format='[%(asctime)s] [%(levelname)s] [%(name)s] %(message)s', datefmt='%Y/%m/%d %H:%M:%S')
    logger = logging.getLogger('%s' % 'client')
    logger.info('python-ngrok v1.46')
    while True:
        try:
            # 檢測控制連線是否連線.
            if mainsocket == False:
                ip = dnsopen(host)
                if ip == False:
                    logger = logging.getLogger('%s' % 'client')
                    logger.info('update dns')
                    time.sleep(10)
                    continue
                mainsocket = connectremote(ip, port)
                if mainsocket == False:
                    logger = logging.getLogger('%s' % 'client')
                    logger.info('connect failed...!')
                    time.sleep(10)
                    continue
                thread = threading.Thread(target = HKClient, args = (mainsocket, 0, 1))
                thread.setDaemon(True)
                thread.start()

            # 傳送心跳
            if pingtime + 20 < time.time() and pingtime != 0:
                sendpack(mainsocket, Ping())
                pingtime = time.time()

            time.sleep(1)

        except socket.error:
            pingtime = 0
        except KeyboardInterrupt:
            sys.exit()

一會把測試視訊傳上來