diff --git a/CHANGELOGS.md b/CHANGELOGS.md index a4793aa763..4b5160f2cf 100644 --- a/CHANGELOGS.md +++ b/CHANGELOGS.md @@ -1,5 +1,6 @@ sunnypilot - 0.9.2.x (2023-02-22) ======================== +* Dashcam Viewer (native & screen recordings) via Browser support thanks to actuallylemoncurd, AlexandreSato, ntegan1, and royjr! * Honda Clarity 2018-22 support thanks to mcallbosco, vanillagorillaa and wirelessnet2! sunnypilot - Version Latest (2023-02-22) diff --git a/system/dashcamviewer/README.md b/system/dashcamviewer/README.md index 4b2574ac9b..10d035ed68 100644 --- a/system/dashcamviewer/README.md +++ b/system/dashcamviewer/README.md @@ -1,5 +1,5 @@ # Dashcam Viewer -View dashcam footage by connecting to the comma device via the same network, with your mobile device or PC. Big thanks to [actuallylemoncurd](https://github.com/actuallylemoncurd), [AlexandreSato](https://github.com/alexandreSato), [ntegan1](https://github.com/ntegan1), and [royjr](https://github.com/royjr). +View dashcam footage and screen recordings by connecting to the comma device via the same network, with your mobile device or PC. Big thanks to [actuallylemoncurd](https://github.com/actuallylemoncurd), [AlexandreSato](https://github.com/alexandreSato), [ntegan1](https://github.com/ntegan1), and [royjr](https://github.com/royjr). -The network can be set up by Wi-Fi, mobile hotspot, or tethering on the comma device. Navigate to http://tici:5000/ OR ipAddress:5000 to access the dashcam footage. +The network can be set up by Wi-Fi, mobile hotspot, or tethering on the comma device. Navigate to http://tici:5050/ OR http://ipAddress:5050 to access the dashcam footage. diff --git a/system/dashcamviewer/dashcam_viewer.py b/system/dashcamviewer/dashcam_viewer.py old mode 100644 new mode 100755 index 9686bfe1e8..42c824cf4f --- a/system/dashcamviewer/dashcam_viewer.py +++ b/system/dashcamviewer/dashcam_viewer.py @@ -1,6 +1,8 @@ #!/usr/bin/env python3 -from flask import Flask, render_template, Response, request -from system.dashcamviewer.helpers import * +import os +import system.dashcamviewer.helpers as dashcam +from flask import Flask, render_template, Response, request, send_from_directory +from system.loggerd.config import ROOT as REALDATA app = Flask(__name__) @@ -14,10 +16,10 @@ def index_page(): def full(cameratype, route): chunk_size = 1024 * 512 # 5KiB file_name = cameratype + (".ts" if cameratype == "qcamera" else ".hevc") - vidlist = "|".join(ROOT + "/" + segment + "/" + file_name for segment in segments_in_route(route)) + vidlist = "|".join(REALDATA + "/" + segment + "/" + file_name for segment in dashcam.segments_in_route(route)) def generate_buffered_stream(): - with ffmpeg_mp4_concat_wrap_process_builder(vidlist, cameratype, chunk_size) as process: + with dashcam.ffmpeg_mp4_concat_wrap_process_builder(vidlist, cameratype, chunk_size) as process: for chunk in iter(lambda: process.stdout.read(chunk_size), b""): yield bytes(chunk) return Response(generate_buffered_stream(), status=200, mimetype='video/mp4') @@ -25,17 +27,16 @@ def full(cameratype, route): @app.route("/footage//") def fcamera(cameratype, segment): - if not is_valid_segment(segment): - return "invalid segment" - file_name = ROOT + "/" + segment + "/" + cameratype + (".ts" if cameratype == "qcamera" else ".hevc") - - return Response(ffmpeg_mp4_wrap_process_builder(file_name).stdout.read(), status=200, mimetype='video/mp4') + if not dashcam.is_valid_segment(segment): + return render_template("error.html", error="invalid segment") + file_name = REALDATA + "/" + segment + "/" + cameratype + (".ts" if cameratype == "qcamera" else ".hevc") + return Response(dashcam.ffmpeg_mp4_wrap_process_builder(file_name).stdout.read(), status=200, mimetype='video/mp4') @app.route("/footage/") def route(route): if len(route) != 20: - return "route not found" + return render_template("error.html", error="route not found") if str(request.query_string) == "b''": query_segment = str("0") @@ -46,85 +47,48 @@ def route(route): links = "" segments = "" - for segment in segments_in_route(route): + for segment in dashcam.segments_in_route(route): links += ""+segment+"
" segments += "'"+segment+"'," - return """ - - - - Dashcam Footage - - -
- -

- current segment: -
- current view: -
- download full route """ + query_type + """ -

- back to routes -

- qcamera - - fcamera - - dcamera - - ecamera -

- """+links+""" -
- - - -""" + return render_template("route.html", route=route, query_type=query_type, links=links, segments=segments, query_segment=query_segment) @app.route("/footage") -def index(): - result = """ - - - - - Dashcam Footage - -

Back to landing page
""" - for route in all_routes(): - result += ""+route+"
" - result += """
""" - return result +def footage(): + return render_template("footage.html", rows=dashcam.all_routes()) + + +@app.route("/screenrecords") +def screenrecords(): + rows = dashcam.all_screenrecords() + if not rows: + return render_template("error.html", error="no screenrecords found at:

" + dashcam.SCREENRECORD_PATH) + return render_template("screenrecords.html", rows=rows, clip=rows[0]) + + +@app.route("/screenrecords/") +def screenrecord(clip): + return render_template("screenrecords.html", rows=dashcam.all_screenrecords(), clip=clip) + + +@app.route("/screenrecords/play/pipe/") +def videoscreenrecord(file): + file_name = dashcam.SCREENRECORD_PATH + file + return Response(dashcam.ffplay_mp4_wrap_process_builder(file_name).stdout.read(), status=200, mimetype='video/mp4') + + +@app.route("/screenrecords/download/") +def download_file(clip): + return send_from_directory(dashcam.SCREENRECORD_PATH, clip, as_attachment=True) + + +@app.route("/about") +def about(): + return render_template("about.html") def main(): - app.run(host="0.0.0.0") + app.run(host="0.0.0.0", port=5050) if __name__ == '__main__': diff --git a/system/dashcamviewer/helpers.py b/system/dashcamviewer/helpers.py index 7e0ce2ce9f..17fab408de 100644 --- a/system/dashcamviewer/helpers.py +++ b/system/dashcamviewer/helpers.py @@ -1,13 +1,26 @@ import os import subprocess -from system.loggerd.config import ROOT +from pathlib import Path +from system.hardware import PC +from system.loggerd.config import ROOT as REALDATA from system.loggerd.uploader import listdir_by_creation from tools.lib.route import SegmentName +# path to sunnypilot screen recordings +if PC: + SCREENRECORD_PATH = os.path.join(str(Path.home()), ".comma", "media", "0", "videos", "") +else: + SCREENRECORD_PATH = "/data/media/0/videos/" + + + +def all_screenrecords(): + return sorted(listdir_by_creation(SCREENRECORD_PATH), reverse=True) + def is_valid_segment(segment): try: - segment_to_segment_name(ROOT, segment) + segment_to_segment_name(REALDATA, segment) return True except AssertionError: return False @@ -20,9 +33,9 @@ def segment_to_segment_name(data_dir, segment): def all_segment_names(): segments = [] - for segment in listdir_by_creation(ROOT): + for segment in listdir_by_creation(REALDATA): try: - segments.append(segment_to_segment_name(ROOT, segment)) + segments.append(segment_to_segment_name(REALDATA, segment)) except AssertionError: pass return segments @@ -89,3 +102,16 @@ def ffmpeg_mp4_wrap_process_builder(filename): return subprocess.Popen( command_line, stdout=subprocess.PIPE ) + + +def ffplay_mp4_wrap_process_builder(file_name): + command_line = ["ffmpeg"] + command_line += ["-i", file_name] + command_line += ["-c", "copy"] + command_line += ["-map", "0"] + command_line += ["-f", "mp4"] + command_line += ["-movflags", "empty_moov"] + command_line += ["-"] + return subprocess.Popen( + command_line, stdout=subprocess.PIPE + ) diff --git a/system/dashcamviewer/static/favicon.ico b/system/dashcamviewer/static/favicon.ico deleted file mode 100644 index d6b869f84c..0000000000 --- a/system/dashcamviewer/static/favicon.ico +++ /dev/null @@ -1 +0,0 @@ -/data/openpilot/selfdrive/assets/img_spinner_comma.png \ No newline at end of file diff --git a/system/dashcamviewer/static/favicon.ico b/system/dashcamviewer/static/favicon.ico new file mode 120000 index 0000000000..2f24c5828e --- /dev/null +++ b/system/dashcamviewer/static/favicon.ico @@ -0,0 +1 @@ +../../../selfdrive/assets/img_spinner_comma.png \ No newline at end of file diff --git a/system/dashcamviewer/templates/about.html b/system/dashcamviewer/templates/about.html new file mode 100644 index 0000000000..281b37965a --- /dev/null +++ b/system/dashcamviewer/templates/about.html @@ -0,0 +1,18 @@ +{% extends "layout.html" %} + +{% block title %} + About +{% endblock %} + +{% block main %} +
+ About +

+
+ Special thanks to:

+ ntegan1
+ royjr
+ AlexandreSato
+ actuallylemoncurd +
+{% endblock %} diff --git a/system/dashcamviewer/templates/error.html b/system/dashcamviewer/templates/error.html new file mode 100644 index 0000000000..ebb182e4c6 --- /dev/null +++ b/system/dashcamviewer/templates/error.html @@ -0,0 +1,11 @@ +{% extends "layout.html" %} + +{% block title %} + About +{% endblock %} + +{% block main %} +
Oopss +



+ {{ error | safe }} +{% endblock %} diff --git a/system/dashcamviewer/templates/footage.html b/system/dashcamviewer/templates/footage.html new file mode 100644 index 0000000000..f2124382a0 --- /dev/null +++ b/system/dashcamviewer/templates/footage.html @@ -0,0 +1,14 @@ +{% extends "layout.html" %} + +{% block title %} + Dashcam Routes +{% endblock %} + +{% block main %} +
+ Dashcam Routes +

+ {% for row in rows %} + {{ row }}
+ {% endfor %} +{% endblock %} diff --git a/system/dashcamviewer/templates/index.html b/system/dashcamviewer/templates/index.html index 4704dbb244..638b6ae204 100644 --- a/system/dashcamviewer/templates/index.html +++ b/system/dashcamviewer/templates/index.html @@ -1,65 +1,13 @@ - - - - - remote door locker - - - - -
-
Viem DashCam Footage
-
- - +{% block main %} +
+ Dashcam Viewer +

+ View Dashcam Footage
+
View Screen Recordings
+{% endblock %} diff --git a/system/dashcamviewer/templates/layout.html b/system/dashcamviewer/templates/layout.html new file mode 100644 index 0000000000..6ba6fa9d43 --- /dev/null +++ b/system/dashcamviewer/templates/layout.html @@ -0,0 +1,37 @@ + + + + + + + + + + sunnypilot: {% block title %}{% endblock %} + + + +
{% block main %}{% endblock %}
+ + diff --git a/system/dashcamviewer/templates/route.html b/system/dashcamviewer/templates/route.html new file mode 100644 index 0000000000..5ebdefb39e --- /dev/null +++ b/system/dashcamviewer/templates/route.html @@ -0,0 +1,56 @@ +{% extends "layout.html" %} + +{% block title %} + Dashcam Segments +{% endblock %} + + +{% block main %} +{% autoescape false %} +
+ Dashcam Segments (one per minute) +

+ +

+ current segment: +
+ current view: +
+ download full route {{ query_type }} +

+ qcamera - + fcamera - + dcamera - + ecamera +

+ {{ links }} + +{% endautoescape %} +{% endblock %} diff --git a/system/dashcamviewer/templates/screenrecords.html b/system/dashcamviewer/templates/screenrecords.html new file mode 100644 index 0000000000..922f5bbd41 --- /dev/null +++ b/system/dashcamviewer/templates/screenrecords.html @@ -0,0 +1,30 @@ +{% extends "layout.html" %} + +{% block title %} + Screen Recordings +{% endblock %} + +{% block main %} +
+ Screen Recordings +

+ +

+ current view: +
+ download: {{ clip }}

+ + {% for row in rows %} + {{ row }}
+ {% endfor %} +{% endblock %}