家庭监控系统实现(二) -- 树莓派定时转存文件功能及HTTP服务器准备

转载请注明出处:https://blog.csdn.net/mymottoissh/article/details/83590248

上一篇对开发需要的环境进行了准备。现在开始准备代码。前面我们说过,会在树莓派一端建立服务器,而Android会作为客户端进行连接。本篇针对的即是服务端的代码。具体来讲,服务端需要实现两个服务器,分别作为视频文件服务器和实时视频推送服务器。本篇实现的功能包括定时转存视频文件以及本地HTTP服务器的准备。

依照之前的设计思路,该服务器实现的功能是每隔3min,将采集到的视频流存储为文件。同时,该路径下启动一HTTP服务器供Android访问。那么可以这样设计,树莓派上电时,开启后台service。这个service有两个作用,一是需要实现定时转存文件功能。二是提供HTTP本地服务。考虑到后续需要实现的实时视频流的推送功能,HTTP服务至少需要提供一个GET处理方式。来确定客户端请求的是历史视频文件还是实时视频流,又或者是直接访问的文件。如果客户端请求的是历史视频文件,那么需要将所有视频文件的文件名返回给客户端供访问。如果请求的是实时视频流,则需要开启流推送。如果是直接访问的视频文件,还需要将文件传送给客户端。

为了实现上述功能,我们的main函数加载时,需要干两件事:

if __name__ == '__main__':
    #开启文件转存线程
    startRecording()
    #开启本地HTTP服务器
    server = MyVideoHttpServer.MyVideoHttpServer()
    server.setPort("127.0.0.1", 8080)
    server.setHandler(MyVideoHttpServer.MyVideoHttpHandler)
    server.start()

其实有了上一节对picamera的介绍,视频转存部分的代码,直接在例子的基础是改吧改吧就可以用了。

def getFileName():
    ts = time.localtime(time.time())
    return 'video_' + str(ts.tm_year) + '_' + str(ts.tm_mon) + '_' + str(ts.tm_mday) + '_' + str(ts.tm_hour) + '_' + str(ts.tm_min) + '_' + str(ts.tm_sec) + ".h264"

def recordFile():
    camera = picamera.PiCamera()
    camera.resolution = (640, 480)
    while True:
        camera.start_recording(getFileName())
        camera.wait_recording(180)
        camera.stop_recording()

def startRecording():
    trd = threading.Thread(target=recordFile, args=())
    trd.start()

对于HTTP的部分,直接上代码

import SocketServer
import BaseHTTPServer
import urlparse
import os
import socket
import shutil

class MyVideoHttpServer:
    handler = None
    ip = "127.0.0.1"
    port = 8080

    def setHandler(self, handler):
        self.handler = handler

    def setPort(self, addr="127.0.0.1", port=8080):
        self.ip = addr
        self.port = port

    def start(self):
        httpd = SocketServer.TCPServer(("", self.port), self.handler)
        httpd.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 )
        httpd.serve_forever()

class MyVideoHttpHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    def do_GET(self):
        print "get req received"
        print self.path
        path = urlparse.urlparse(self.path).path
        query = urlparse.urlparse(self.path).query
        print query

        if len(query) == 0:
            if len(path.split(".")) > 1 and (path.split(".")[1] == "h264" or path.split(".")[1] == "mp4"):
                try:
                    file = open("." + path, 'rb')
                    self.respFileHeaders(os.path.getsize("." + path))
                    shutil.copyfileobj(file, self.wfile)
                except:
                    print "file process error"
                finally:
                    file.close()
            else:
                self.respErr()
        else:
            params = dict()
            for p in query.split("&"):
                if p.find("=") > 0:
                    params[p.split("=")[0]] = p.split("=")[1]
            showMode = params.get("showMode")
            print showMode
            #1 hist
            if showMode == "1":
                print "history file mode"
                self.sendFileList()
            #2 air
            elif showMode == "0":
                print "air mode"
                self.startRtmpServer()
            else:
                self.respErr()

    def respTextHeaders(self, contentLen):
        self.send_response(200)
        self.send_header("Content-Type", "text/plain")
        self.send_header("Content-Length", contentLen)
        self.send_header("Transfer-Encoding", "utf-8")
        self.end_headers()

    def respFileHeaders(self, contentLen):
        self.send_response(200)
        self.send_header("Content-Type", "application/octet-stream")
        self.send_header("Content-Length", contentLen)
        self.end_headers()

    def respErr(self):
        self.send_response(404)
        self.end_headers()

    def sendFileList(self):
        fileList = ""
        for root, dirs, files in os.walk(os.curdir):
            if len(files) > 0:
                for f in files:
                    if len(f.split(".")) > 0 and f.split(".")[1] == "h264":
                        fileList += f
                        fileList += " "
        self.respTextHeaders(len(fileList))
        self.wfile.write(fileList)

    def startRtmpServer(self):
        self.respTextHeaders(2)
        self.wfile.write("OK")
        os.system("gst-launch-1.0 -v v4l2src device=/dev/video0 ! 'video/x-raw, width=640, height=480, framerate=30/1' ! queue ! videoconvert ! omxh264enc ! h264parse ! flvmux ! rtmpsink location='rtmp://192.168.0.102/live live=1' &")

重点说一下handler的实现。提供了doGet方法来处理get请求。

如果url中含有showMode的查询字段,那么如果showMode等于1表示历史文件列表请求,那么会给客户端返回当前目录下所有以.mp4和.h264结尾的文件名供前端访问。

如果showMode等于0,表示请求实时数据,此时会开启GStreamer进行视频采集并发送给rtmp服务器。关于rtmp的部署会在下一节记录。

如果请求路径中含有文件名,服务端会尝试打开该文件并返回给客户端。

如果以上情况均不是,则直接返回404。

其实关于上述实现,还有一个重要的缺陷。就是经过测试picamera和GStreamer同时访问一个摄像头资源会造成错误。这个问题需要在结合具体需求来进行权衡。本次实现只针对功能,所以暂且不去考虑这个问题。默认同一时间内仅有一个功能可以使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值