From c337aebae62ef5fdc691bc1a6273d63168a245c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=40jkccchen123?= <893824054@qq.com> Date: Thu, 24 Oct 2024 17:58:50 +0800 Subject: [PATCH] jk_20241024-17:58 --- carsrun/PiCamera_H264_Server.py | 67 ++ .../PiCamera_H264_Server.cpython-311.pyc | Bin 0 -> 4883 bytes .../PiCamera_H264_Server.cpython-39.pyc | Bin 0 -> 2379 bytes carsrun/__pycache__/base_ctrl.cpython-311.pyc | Bin 0 -> 16598 bytes carsrun/__pycache__/camera_pi.cpython-311.pyc | Bin 0 -> 2646 bytes carsrun/__pycache__/camera_pi.cpython-39.pyc | Bin 0 -> 1376 bytes carsrun/__pycache__/car.cpython-311.pyc | Bin 0 -> 10147 bytes carsrun/__pycache__/car.cpython-39.pyc | Bin 0 -> 4123 bytes carsrun/appCam.py | 136 +++ carsrun/apptest.py | 31 + carsrun/base_ctrl.py | 286 ++++++ carsrun/camera_pi.py | 61 ++ carsrun/car.py | 147 +++ carsrun/config.yaml | 107 +++ carsrun/qrcode.png | Bin 0 -> 48065 bytes carsrun/static/Decoder.js | 891 ++++++++++++++++++ carsrun/static/Player.js | 335 +++++++ carsrun/static/YUVCanvas.js | 551 +++++++++++ carsrun/static/avc.wasm | Bin 0 -> 132979 bytes carsrun/static/style.css | 9 + carsrun/templates/Decoder.js | 891 ++++++++++++++++++ carsrun/templates/Player.js | 335 +++++++ carsrun/templates/YUVCanvas.js | 551 +++++++++++ carsrun/templates/avc.wasm | Bin 0 -> 132979 bytes carsrun/templates/index-t.html | 174 ++++ carsrun/templates/index.html | 187 ++++ carsrun/templates/index2.html | 19 + carsrun/templates/index3.html | 34 + carsrun/templates/style.css | 9 + 29 files changed, 4821 insertions(+) create mode 100644 carsrun/PiCamera_H264_Server.py create mode 100644 carsrun/__pycache__/PiCamera_H264_Server.cpython-311.pyc create mode 100644 carsrun/__pycache__/PiCamera_H264_Server.cpython-39.pyc create mode 100644 carsrun/__pycache__/base_ctrl.cpython-311.pyc create mode 100644 carsrun/__pycache__/camera_pi.cpython-311.pyc create mode 100644 carsrun/__pycache__/camera_pi.cpython-39.pyc create mode 100644 carsrun/__pycache__/car.cpython-311.pyc create mode 100644 carsrun/__pycache__/car.cpython-39.pyc create mode 100644 carsrun/appCam.py create mode 100644 carsrun/apptest.py create mode 100644 carsrun/base_ctrl.py create mode 100644 carsrun/camera_pi.py create mode 100644 carsrun/car.py create mode 100644 carsrun/config.yaml create mode 100644 carsrun/qrcode.png create mode 100644 carsrun/static/Decoder.js create mode 100644 carsrun/static/Player.js create mode 100644 carsrun/static/YUVCanvas.js create mode 100644 carsrun/static/avc.wasm create mode 100644 carsrun/static/style.css create mode 100644 carsrun/templates/Decoder.js create mode 100644 carsrun/templates/Player.js create mode 100644 carsrun/templates/YUVCanvas.js create mode 100644 carsrun/templates/avc.wasm create mode 100644 carsrun/templates/index-t.html create mode 100644 carsrun/templates/index.html create mode 100644 carsrun/templates/index2.html create mode 100644 carsrun/templates/index3.html create mode 100644 carsrun/templates/style.css diff --git a/carsrun/PiCamera_H264_Server.py b/carsrun/PiCamera_H264_Server.py new file mode 100644 index 0000000..3950f23 --- /dev/null +++ b/carsrun/PiCamera_H264_Server.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +# vuquangtrong.github.io + +import io +import picamera +import time +from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer +from wsgiref.simple_server import make_server +from ws4py.websocket import WebSocket +from ws4py.server.wsgirefserver import WSGIServer, WebSocketWSGIHandler, WebSocketWSGIRequestHandler +from ws4py.server.wsgiutils import WebSocketWSGIApplication +from threading import Thread, Condition + + +class FrameBuffer(object): + def __init__(self): + self.frame = None + self.buffer = io.BytesIO() + self.condition = Condition() + + def write(self, buf): + if buf.startswith(b'\x00\x00\x00\x01'): + with self.condition: + self.buffer.seek(0) + self.buffer.write(buf) + self.buffer.truncate() + self.frame = self.buffer.getvalue() + self.condition.notify_all() + + +def stream(): + with picamera.PiCamera(resolution='640x480', framerate=24) as camera: + broadcasting = True + frame_buffer = FrameBuffer() + camera.start_recording(frame_buffer, format='h264', profile="baseline") + try: + WebSocketWSGIHandler.http_version = '1.1' + websocketd = make_server('', 9000, server_class=WSGIServer, + handler_class=WebSocketWSGIRequestHandler, + app=WebSocketWSGIApplication(handler_cls=WebSocket)) + websocketd.initialize_websockets_manager() + websocketd_thread = Thread(target=websocketd.serve_forever) + + httpd = ThreadingHTTPServer(('', 8000), SimpleHTTPRequestHandler) + httpd_thread = Thread(target=httpd.serve_forever) + + try: + websocketd_thread.start() + httpd_thread.start() + while broadcasting: + with frame_buffer.condition: + frame_buffer.condition.wait() + websocketd.manager.broadcast(frame_buffer.frame, binary=True) + except KeyboardInterrupt: + pass + finally: + websocketd.shutdown() + httpd.shutdown() + broadcasting = False + raise KeyboardInterrupt + except KeyboardInterrupt: + pass + finally: + camera.stop_recording() + +if __name__ == "__main__": + stream() diff --git a/carsrun/__pycache__/PiCamera_H264_Server.cpython-311.pyc b/carsrun/__pycache__/PiCamera_H264_Server.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..204dcf41ed1f927fd3aef2a9901811e2f5dc599d GIT binary patch literal 4883 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 literal 0 HcmV?d00001 diff --git a/carsrun/__pycache__/base_ctrl.cpython-311.pyc b/carsrun/__pycache__/base_ctrl.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e60b7c79c275d082a641afe45001ea74207b5b5b GIT binary patch literal 16598 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 YF%Qh=PdY1xEbNCCZpdN#&`ksUKUcx1>;M1& literal 0 HcmV?d00001 diff --git a/carsrun/__pycache__/camera_pi.cpython-311.pyc b/carsrun/__pycache__/camera_pi.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4ee0a895612f22800fc794a317b585c4985e6283 GIT binary patch literal 2646 zcmb7G%}*Og6rb5$FKc@j8%!_|2!qgI^>mfS{eZ8lCuqN9ZrIaGS3_*}ediC8Qt)XHe1^aT3!tlVs?~CRql3 zh7@KFDXhw^un4Wf=d`3x!B`;v@?nr!a7qokK}p z;ea-&g5sM)DZviEC{1He03l)t`L_QCvV_cCTT`A~aolu0 zhW#ne`dKu^xd&e06_8)`&g|ACAIRO&zr0Jks}Jq*1tYi(^5C>$uNiXZV3FX?6{gXO z##6r@R?t<*lJmvcu{gGPGo??cCXGzouM>ZOH>s;B#qv9&mYuNpYYwYPuJ8E_IA~hF zaUDWyai#{;XJk}0{~TMKp=QPn;&tr(v4P3lv^r2QRDEDXHEx@^{6IRT8$d`-qc3a)%WGO-2St5p4upN}p7Mu{YhvtKA>I9Qo}^rF(d-d$`&? z{BX3C*brMwiPvK9y4brW_Ep8cir8oKC=xB7sI(s22)C63kB1heva}%CtjmTS_}LNy z@nFmrQRFzRx8c6VVj;Q13e$p$&IHs9`aS8t0ss)hJFaG37u7;vm$iXLXpx8j5XSDNBC!03#uWb@77B`hwCjgt80W{>hnxf|9d`@>TYLjFo-eL)P z%>fWg_)2ri+6w5Zk;@cJEtj=?lj9jJZ~5-hL853S=XLe2rrvYj5DAjSXgRCt!hKUU zKL65cO{el^L09E;E^BJpLatyCC{u4+9Kl2Syez@g3)ytaRP{rkKqrM3=PWjzF)Y4r z5pS3vKW(w&xtu}b6@x|}qfV@!X|6xRI+peW&lrIi6ww>$P*pmzCdH~!%tjsluI14` zV<+ZEt71>>aPRzR=}LV7Op_w7#n`$STNC@MVt+;K-w?y4FBa~6pC~2f6K@Vh%gM!? z<(u=PwP?@l=-_&E@MZh&?X~kG0DK)oTL^cI;T4eC$ zsrAU|O5}8{t#jcLT;6uZ9st*@XBEn6!+K6dM6*Z2@<-723<207m%((=oE3MLo^H0ofI(=GIL`mna%9Z#P$ST zw5gJU=AcIuyXAHK9sCJysnSu?zj3!q);?j^7Zw3*LVJ z;NY~Kn}e=Ccxv34Z@Yu4oFAE&&yBq>78OWWb@jvURmkc^?d!6sFU>ocWo#tP*o0<0 zqnCR_Z65w(^`8P^0+${`?iI?WS{&WD7zn$meyR+VwiPVjh) zu#JE@3~VGPAmG3oa}b-*t|XhtQyoNR*o+u*I(kaQ9x#`ZI8AU3)tKYV{hHC;l4 zngQ~NV!3IaPgf(&v1MMa#%po~HnVKWG>}OrulZB*_2Cs?velMO(F%6kYiPOJdFyN! zGY4NO-p%gaCwg=y>@y(3cjwQJfcgUFHK$}T`G~lmJ_Ku=Ux8@!Hi>kR)11|ZK*HPK zAp94%Sm{E{I&+fKVz+ljUDVpNs%z~)r&}y=z87Ry4^1xQm-SGjkXhr}ruTKbD3a6T zrtXU5A}%|TSgNz;qBh?SP1*uSfz@poefr89_xTsaepPn9w?O9!b3x3?T|7x`zf&Kd+4uP|{?Bia QpA7LnM$$S2%o!&C6H!_+1poj5 literal 0 HcmV?d00001 diff --git a/carsrun/__pycache__/car.cpython-311.pyc b/carsrun/__pycache__/car.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..196df9554ac3cf023e516c4c4cc43622e1a45f0f GIT binary patch literal 10147 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 https://blog.miguelgrinberg.com/post/video-streaming-with-flask +# PiCam Local Web Server with Flask +# MJRoBot.org 19Jan18 + +from flask import Flask, render_template, Response, redirect, url_for, send_file, jsonify, request + +# Raspberry Pi camera module (requires picamera package) +#from camera_pi import Camera +from picamera2 import Picamera2 +import os +import time +from gevent import pywsgi +from car import CAR +#from PiCamera_H264_Server import stream +import threading +import cv2 +app = Flask(__name__, static_url_path='') + +def gen_frames(): # generate frame by frame from camera + picam2 = Picamera2() + picam2.configure(picam2.create_video_configuration(main={"format": 'XRGB8888', "size": (640, 480)})) + picam2.start() + while True: + # Capture frame-by-frame + frame = picam2.capture_array() # read the camera frame + frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) + ret, buffer = cv2.imencode('.jpg', frame) + frame = buffer.tobytes() + yield (b'--frame\r\n' + b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n') # concat frame one by one and show result + + +@app.route('/') +def index(): + return render_template('index-t.html') + +@app.route('/video_feed') +def video_feed(): + #Video streaming route. Put this in the src attribute of an img tag + return Response(gen_frames(), mimetype='multipart/x-mixed-replace; boundary=frame') + +# def gen(camera): +# """Video streaming generator function.""" +# while True: +# frame = camera.get_frame() +# yield (b'--frame\r\n' +# b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n') + +# @app.route('/capture') +# def capture(): +# pic = open("qrcode.png", "wb") +# frame = Camera().get_frame() +# pic.write(frame) +# return Response(b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n', +# mimetype='multipart/x-mixed-replace; boundary=frame') +# #return send_file("qrcode.png", mimetype='image/png') +# #return redirect(url_for('index')) + +# @app.route('/video_feed') +# def video_feed(): +# """Video streaming route. Put this in the src attribute of an img tag.""" +# return Response(gen(Camera()), +# mimetype='multipart/x-mixed-replace; boundary=frame') +car = CAR() +@app.route('/control/') +def control_index(): + word = """指令:\n + /led: 灯光闪烁\n + /led_light: 打开全部灯光\n + /led_dark: 关闭全部灯光\n + /stop(/Q): 小车停止运动\n + /forward(/W): 小车开始运动\n + /back(/S): 小车向后运动\n + /left(/A): 小车向左运动\n + /right(/D): 小车向右运动\n""" + print(word) + return word + +# def execute_forward_function(): +# url = "http://192.168.185.242:80/send_command" # 示例URL,实际使用时需要替换为正确的服务器地址 +# data = {"command": 'base -c {"T":1,"L":0.5,"R":0.5}'} # 请求体数据 + +# # 发送请求并打印返回结果 +# try: +# response = request.post(url, data=data) +# print(response.text) # 打印服务器返回的内容 +# except request.exceptions.RequestException as e: +# print(f"请求发生错误: {e}") +# print("执行前进功能") +# # 返回一些结果 +# return "前进功能已执行" + +# @app.route('/control/forward', methods=['GET']) +# def control_forward(): +# try: +# # 获取前端发送的数据 +# data = request.args + +# # 调试输出接收到的数据 +# print("接收到的数据:", data) + +# # 执行你的函数 +# result = execute_forward_function() # 确保这个函数是可以被调用的,如果有必要打印它的返回值 + +# # 返回JSON响应, result 需要确保是可json化的对象 +# return jsonify({ +# "resultCode": 200, +# "message": "请求成功", +# "data": result +# }) +# except Exception as e: +# print(f"发生错误: {e}") # 打印异常 +# return jsonify({"resultCode": 500, "message": "内部服务器错误"}), 500 # 返回500错误 + + + +@app.route('/control/') +def fun(info): + if hasattr(car, info): + getattr(car, info)() + return 'Run: '+info+'\n' + else: + return 'Error: '+info+' not be defined\n' + + + +if __name__ == '__main__': + app.run(host='0.0.0.0', port =80, debug=True, threaded=True) + # t = threading.Thread(target=stream) + # t.start() + # server = pywsgi.WSGIServer(('0.0.0.0', 80), app) + # server.serve_forever() diff --git a/carsrun/apptest.py b/carsrun/apptest.py new file mode 100644 index 0000000..89b9d9e --- /dev/null +++ b/carsrun/apptest.py @@ -0,0 +1,31 @@ +from flask import Flask, render_template, Response +from picamera2 import Picamera2 +import time +import cv2 + +app = Flask(__name__) + +def gen_frames(): # generate frame by frame from camera + picam2 = Picamera2() + picam2.configure(picam2.create_video_configuration(main={"format": 'XRGB8888', "size": (640, 480)})) + picam2.start() + while True: + # Capture frame-by-frame + frame = picam2.capture_array() # read the camera frame + frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) + ret, buffer = cv2.imencode('.jpg', frame) + frame = buffer.tobytes() + yield (b'--frame\r\n' + b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n') # concat frame one by one and show result + +@app.route('/') +def index(): + return render_template('index.html') + +@app.route('/video_feed') +def video_feed(): + #Video streaming route. Put this in the src attribute of an img tag + return Response(gen_frames(), mimetype='multipart/x-mixed-replace; boundary=frame') + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=5000, debug=True) diff --git a/carsrun/base_ctrl.py b/carsrun/base_ctrl.py new file mode 100644 index 0000000..2b25e9b --- /dev/null +++ b/carsrun/base_ctrl.py @@ -0,0 +1,286 @@ +import serial +import json +import queue +import threading +import yaml +import os +import time +import glob +import numpy as np + +curpath = os.path.realpath(__file__) +thisPath = os.path.dirname(curpath) +with open(thisPath + '/config.yaml', 'r') as yaml_file: + f = yaml.safe_load(yaml_file) + +class ReadLine: + def __init__(self, s): + self.buf = bytearray() + self.s = s + + self.sensor_data = [] + self.sensor_list = [] + try: + self.sensor_data_ser = serial.Serial(glob.glob('/dev/ttyUSB*')[0], 115200) + print("/dev/ttyUSB* connected succeed") + except: + self.sensor_data_ser = None + self.sensor_data_max_len = 51 + + try: + self.lidar_ser = serial.Serial(glob.glob('/dev/ttyACM*')[0], 230400, timeout=1) + print("/dev/ttyACM* connected succeed") + except: + self.lidar_ser = None + self.ANGLE_PER_FRAME = 12 + self.HEADER = 0x54 + self.lidar_angles = [] + self.lidar_distances = [] + self.lidar_angles_show = [] + self.lidar_distances_show = [] + self.last_start_angle = 0 + + def readline(self): + i = self.buf.find(b"\n") + if i >= 0: + r = self.buf[:i+1] + self.buf = self.buf[i+1:] + return r + while True: + i = max(1, min(512, self.s.in_waiting)) + data = self.s.read(i) + i = data.find(b"\n") + if i >= 0: + r = self.buf + data[:i+1] + self.buf[0:] = data[i+1:] + return r + else: + self.buf.extend(data) + + def clear_buffer(self): + self.s.reset_input_buffer() + + def read_sensor_data(self): + if self.sensor_data_ser == None: + return + + try: + buffer_clear = False + while self.sensor_data_ser.in_waiting > 0: + buffer_clear = True + sensor_readline = self.sensor_data_ser.readline() + if len(sensor_readline) <= self.sensor_data_max_len: + self.sensor_list.append(sensor_readline.decode('utf-8')[:-2]) + else: + self.sensor_list.append(sensor_readline.decode('utf-8')[:self.sensor_data_max_len]) + self.sensor_list.append(sensor_readline.decode('utf-8')[self.sensor_data_max_len:-2]) + if buffer_clear: + self.sensor_data = self.sensor_list.copy() + self.sensor_list.clear() + self.sensor_data_ser.reset_input_buffer() + except Exception as e: + print(f"[base_ctrl.read_sensor_data] error: {e}") + + def parse_lidar_frame(self, data): + # header = data[0] + # verlen = data[1] + # speed = data[3] << 8 | data[2] + start_angle = (data[5] << 8 | data[4]) * 0.01 + # print(start) + # end_angle = (data[43] << 8 | data[42]) * 0.01 + for i in range(0, self.ANGLE_PER_FRAME): + offset = 6 + i * 3 + distance = data[offset+1] << 8 | data[offset] + confidence = data[offset+2] + # lidar_angles.append(np.radians(start_angle + i * 0.167)) + self.lidar_angles.append(np.radians(start_angle + i * 0.83333 + 180)) + # lidar_angles.append(np.radians(start_angle + end_angle)) + self.lidar_distances.append(distance) + # end_angle = (data[43] << 8 | data[42]) * 0.01 + # timestamp = data[45] << 8 | data[44] + # crc = data[46] + return start_angle + + def lidar_data_recv(self): + if self.lidar_ser == None: + return + try: + while True: + self.header = self.lidar_ser.read(1) + if self.header == b'\x54': + # Read the rest of the data + data = self.header + self.lidar_ser.read(46) + hex_data = [int(hex(byte), 16) for byte in data] + start_angle = self.parse_lidar_frame(hex_data) + if self.last_start_angle > start_angle: + break + self.last_start_angle = start_angle + else: + self.lidar_ser.flushInput() + + self.last_start_angle = start_angle + self.lidar_angles_show = self.lidar_angles.copy() + self.lidar_distances_show = self.lidar_distances.copy() + self.lidar_angles.clear() + self.lidar_distances.clear() + except Exception as e: + print(f"[base_ctrl.lidar_data_recv] error: {e}") + self.lidar_ser = serial.Serial(glob.glob('/dev/ttyACM*')[0], 230400, timeout=1) + + +class BaseController: + + def __init__(self, uart_dev_set, buad_set): + self.ser = serial.Serial(uart_dev_set, buad_set, timeout=1) + self.rl = ReadLine(self.ser) + self.command_queue = queue.Queue() + self.command_thread = threading.Thread(target=self.process_commands, daemon=True) + self.command_thread.start() + + self.base_light_status = 0 + self.head_light_status = 0 + + self.data_buffer = None + self.base_data = None + + self.use_lidar = f['base_config']['use_lidar'] + self.extra_sensor = f['base_config']['extra_sensor'] + + + def feedback_data(self): + try: + while self.rl.s.in_waiting > 0: + self.data_buffer = json.loads(self.rl.readline().decode('utf-8')) + if 'T' in self.data_buffer: + self.base_data = self.data_buffer + self.data_buffer = None + if self.base_data["T"] == 1003: + print(self.base_data) + return self.base_data + self.rl.clear_buffer() + self.data_buffer = json.loads(self.rl.readline().decode('utf-8')) + self.base_data = self.data_buffer + return self.base_data + except Exception as e: + self.rl.clear_buffer() + print(f"[base_ctrl.feedback_data] error: {e}") + + + def on_data_received(self): + self.ser.reset_input_buffer() + data_read = json.loads(self.rl.readline().decode('utf-8')) + return data_read + + + def send_command(self, data): + self.command_queue.put(data) + + + def process_commands(self): + while True: + data = self.command_queue.get() + self.ser.write((json.dumps(data) + '\n').encode("utf-8")) + + + def base_json_ctrl(self, input_json): + self.send_command(input_json) + + + def gimbal_emergency_stop(self): + data = {"T":0} + self.send_command(data) + + + def base_speed_ctrl(self, input_left, input_right): + data = {"T":1,"L":input_left,"R":input_right} + self.send_command(data) + + + def gimbal_ctrl(self, input_x, input_y, input_speed, input_acceleration): + data = {"T":133,"X":input_x,"Y":input_y,"SPD":input_speed,"ACC":input_acceleration} + self.send_command(data) + + + def gimbal_base_ctrl(self, input_x, input_y, input_speed): + data = {"T":141,"X":input_x,"Y":input_y,"SPD":input_speed} + self.send_command(data) + + + def base_oled(self, input_line, input_text): + data = {"T":3,"lineNum":input_line,"Text":input_text} + self.send_command(data) + + + def base_default_oled(self): + data = {"T":-3} + self.send_command(data) + + + def bus_servo_id_set(self, old_id, new_id): + # data = {"T":54,"old":old_id,"new":new_id} + data = {"T":f['cmd_config']['cmd_set_servo_id'],"raw":old_id,"new":new_id} + self.send_command(data) + + + def bus_servo_torque_lock(self, input_id, input_status): + # data = {"T":55,"id":input_id,"status":input_status} + data = {"T":f['cmd_config']['cmd_servo_torque'],"id":input_id,"cmd":input_status} + self.send_command(data) + + + def bus_servo_mid_set(self, input_id): + # data = {"T":58,"id":input_id} + data = {"T":f['cmd_config']['cmd_set_servo_mid'],"id":input_id} + self.send_command(data) + + + def lights_ctrl(self, pwmA, pwmB): + data = {"T":132,"IO4":pwmA,"IO5":pwmB} + self.send_command(data) + self.base_light_status = pwmA + self.head_light_status = pwmB + + + def base_lights_ctrl(self): + if self.base_light_status != 0: + self.base_light_status = 0 + else: + self.base_light_status = 255 + self.lights_ctrl(self.base_light_status, self.head_light_status) + + def gimbal_dev_close(self): + self.ser.close() + + def breath_light(self, input_time): + breath_start_time = time.time() + while time.time() - breath_start_time < input_time: + for i in range(0, 128, 10): + self.lights_ctrl(i, 128-i) + time.sleep(0.1) + for i in range(0, 128, 10): + self.lights_ctrl(128-i, i) + time.sleep(0.1) + self.lights_ctrl(0, 0) + + +if __name__ == '__main__': + # RPi5 + base = BaseController('/dev/ttyAMA0', 115200) + + # RPi4B + # base = BaseController('/dev/serial0', 115200) + + # breath light for 15s + base.breath_light(15) + + # gimble ctrl, look forward + # x y spd acc + base.gimbal_ctrl(0, 0, 10, 0) + + # x(-180 ~ 180) + # x- look left + # x+ look right + + # y(-30 ~ 90) + # y- look down + # y+ look up \ No newline at end of file diff --git a/carsrun/camera_pi.py b/carsrun/camera_pi.py new file mode 100644 index 0000000..471ee3f --- /dev/null +++ b/carsrun/camera_pi.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# camera_pi.py +# +# +# +import time +import io +import threading +import picamera + + +class Camera(object): + thread = None # background thread that reads frames from camera + frame = None # current frame is stored here by background thread + last_access = 0 # time of last client access to the camera + + def initialize(self): + if Camera.thread is None: + # start background frame thread + Camera.thread = threading.Thread(target=self._thread) + Camera.thread.start() + + # wait until frames start to be available + while self.frame is None: + time.sleep(0) + + def get_frame(self): + Camera.last_access = time.time() + self.initialize() + return self.frame + + @classmethod + def _thread(cls): + with picamera.PiCamera() as camera: + # camera setup + camera.resolution = (320, 240) + camera.hflip = True + camera.vflip = True + + # let camera warm up + camera.start_preview() + time.sleep(2) + + stream = io.BytesIO() + for foo in camera.capture_continuous(stream, 'jpeg', + use_video_port=True): + # store frame + stream.seek(0) + cls.frame = stream.read() + + # reset stream for next frame + stream.seek(0) + stream.truncate() + + # if there hasn't been any clients asking for frames in + # the last 10 seconds stop the thread + if time.time() - cls.last_access > 10: + break + cls.thread = None diff --git a/carsrun/car.py b/carsrun/car.py new file mode 100644 index 0000000..c72f49a --- /dev/null +++ b/carsrun/car.py @@ -0,0 +1,147 @@ +#coding:utf-8 +import os +import time +import RPi.GPIO as GPIO +from base_ctrl import BaseController +####################################### +#############信号引脚定义############## +####################################### +GPIO.setmode(GPIO.BCM) +GPIO.setwarnings(False) +def is_raspberry_pi5(): + with open('/proc/cpuinfo', 'r') as file: + for line in file: + if 'Model' in line: + if 'Raspberry Pi 5' in line: + return True + else: + return False + +base = BaseController('/dev/ttyAMA0', 115200) + +class CAR: + def __init__(self): + self.LED0 = 10 #LED0的IO口定义 + self.LED1 = 9 #LED1的IO口定义 + self.LED2 = 25 #LED2的IO口定义 + self.ENA = 13 #//L298 使能A + self.ENB = 20 #//L298 使能B + self.IN1 = 19 #//电机接口1 + self.IN2 = 16 #//电机接口2 + self.IN3 = 21 #//电机接口3 + self.IN4 = 26 #//电机接口4 + GPIO.setup(self.LED0, GPIO.OUT, initial=GPIO.HIGH) ##led0初始化为高电平 + GPIO.setup(self.LED1, GPIO.OUT, initial=GPIO.HIGH) ##led1初始化为高电平 + GPIO.setup(self.LED2, GPIO.OUT, initial=GPIO.HIGH) ##led2初始化为高电平 + GPIO.setup(self.ENA, GPIO.OUT, initial=GPIO.LOW) ##ENA初始化为低电平 + GPIO.setup(self.ENB, GPIO.OUT, initial=GPIO.LOW) ##ENB初始化为低电平 + GPIO.setup(self.IN1, GPIO.OUT, initial=GPIO.LOW) ##IN1初始化为低电平 + GPIO.setup(self.IN2, GPIO.OUT, initial=GPIO.LOW) ##IN2初始化为低电平 + GPIO.setup(self.IN3, GPIO.OUT, initial=GPIO.LOW) ##IN3初始化为低电平 + GPIO.setup(self.IN4, GPIO.OUT, initial=GPIO.LOW) ##IN4初始化为低电平 + + def led(self): + GPIO.output(self.LED0,False) + GPIO.output(self.LED1,False) + GPIO.output(self.LED2,False)###LED0,LED1,LED2 = 亮 亮 亮 + time.sleep(0.5) + GPIO.output(self.LED0,True) + GPIO.output(self.LED1,False) + GPIO.output(self.LED2,False)###LED0,LED1,LED2 = 灭 亮 亮 + time.sleep(0.5) + GPIO.output(self.LED0,False) + GPIO.output(self.LED1,True) + GPIO.output(self.LED2,False)###LED0,LED1,LED2 = 亮 灭 亮 + time.sleep(0.5) + GPIO.output(self.LED0,False) + GPIO.output(self.LED1,False) + GPIO.output(self.LED2,True)###LED0,LED1,LED2 = 亮 亮 灭 + time.sleep(0.5) + GPIO.output(self.LED0,False) + GPIO.output(self.LED1,False) + GPIO.output(self.LED2,False)###LED0,LED1,LED2 = 亮 亮 亮 + time.sleep(0.5) + GPIO.output(self.LED0,True) + GPIO.output(self.LED1,True) + GPIO.output(self.LED2,True)###LED0,LED1,LED2 = 灭 灭 灭 + time.sleep(0.5) + print("run: led") + + def led_light(self): + GPIO.output(self.LED0,False) + GPIO.output(self.LED1,False) + GPIO.output(self.LED2,False)###LED0,LED1,LED2 = 亮 亮 亮 + print("run: led_light") + + def led_dark(self): + GPIO.output(self.LED0,True) + GPIO.output(self.LED1,True) + GPIO.output(self.LED2,True)###LED0,LED1,LED2 = 灭 灭 灭 + print("run: led_dark") + + def stop(self): # 停止运行 + GPIO.output(self.ENA,False) + GPIO.output(self.ENB,False) + GPIO.output(self.IN1,False) + GPIO.output(self.IN2,False) + GPIO.output(self.IN3,False) + GPIO.output(self.IN4,False) + print("run: stop move") + + def Q(self): # 停止的快捷键 + self.stop() + def q(self): + self.stop() + + def forward(self): # 前进 + base.send_command({"T":1,"L":0.2,"R":0.2}) + time.sleep(2) + base.send_command({"T":1,"L":0,"R":0}) + print("run: move !!!!forward") + + def W(self): # 前进的快捷键 + self.forward() + def w(self): + self.forward() + + def back(self): # 后退 + GPIO.output(self.ENA,True) + GPIO.output(self.ENB,True) + GPIO.output(self.IN1,True) + GPIO.output(self.IN2,False) + GPIO.output(self.IN3,True) + GPIO.output(self.IN4,False) + print("run: move back") + + def S(self): # 后退的快捷键 + self.back() + def s(self): + self.back() + + def left(self): # 左转 + GPIO.output(self.ENA,True) + GPIO.output(self.ENB,True) + GPIO.output(self.IN1,False) + GPIO.output(self.IN2,True) + GPIO.output(self.IN3,True) + GPIO.output(self.IN4,False) + print("run: move left") + + def A(self): # 左转的快捷键 + self.left() + def a(self): + self.left() + + def right(self): # 右转 + GPIO.output(self.ENA,True) + GPIO.output(self.ENB,True) + GPIO.output(self.IN1,True) + GPIO.output(self.IN2,False) + GPIO.output(self.IN3,False) + GPIO.output(self.IN4,True) + print("run: move right") + + def D(self): # 右转的快捷键 + self.right() + def d(self): + self.right() \ No newline at end of file diff --git a/carsrun/config.yaml b/carsrun/config.yaml new file mode 100644 index 0000000..b5949fe --- /dev/null +++ b/carsrun/config.yaml @@ -0,0 +1,107 @@ +args_config: + arm_default_e: 60 + arm_default_r: 0 + arm_default_z: 24 + max_rate: 1.0 + max_speed: 1.3 + mid_rate: 0.66 + min_rate: 0.3 + slow_speed: 0.2 +audio_config: + audio_output: true + default_volume: 1.0 + min_time_bewteen_play: 1 + speed_rate: 180 +base_config: + add_osd: false + extra_sensor: false + main_type: 2 + module_type: 0 + robot_name: UGV Rover + sbc_version: 0.93 + use_lidar: false +cmd_config: + cmd_arm_ctrl_ui: 144 + cmd_gimbal_base_ctrl: 141 + cmd_gimbal_ctrl: 133 + cmd_gimbal_steady: 137 + cmd_movition_ctrl: 1 + cmd_pwm_ctrl: 11 + cmd_servo_torque: 210 + cmd_set_servo_id: 501 + cmd_set_servo_mid: 502 +code: + base_ct: 10410 + base_of: 10407 + base_on: 10408 + cv_auto: 10307 + cv_clor: 10305 + cv_face: 10303 + cv_moti: 10302 + cv_none: 10301 + cv_objs: 10304 + head_ct: 10409 + led_aut: 10405 + led_off: 10404 + led_ton: 10406 + max_res: 10101 + mc_lock: 10501 + mc_unlo: 10502 + mid_res: 10102 + min_res: 10103 + mp_face: 10308 + mp_hand: 10306 + mp_pose: 10309 + pic_cap: 10201 + re_capt: 10402 + re_none: 10401 + re_reco: 10403 + release: 10902 + s_panid: 10901 + s_tilid: 10904 + set_mid: 10903 + vid_end: 10203 + vid_sta: 10202 + zoom_x1: 10104 + zoom_x2: 10105 + zoom_x4: 10106 +cv: + aimed_error: 8 + color_lower: + - 101 + - 50 + - 38 + color_upper: + - 110 + - 255 + - 255 + default_color: blue + min_radius: 12 + sampling_rad: 25 + track_acc_rate: 0.4 + track_color_iterate: 0.023 + track_faces_iterate: 0.045 + track_spd_rate: 60 +fb: + base_light: 115 + base_voltage: 112 + cpu_load: 106 + cpu_temp: 107 + cv_movtion_mode: 114 + detect_react: 103 + detect_type: 101 + led_mode: 102 + pan_angle: 109 + picture_size: 104 + ram_usage: 108 + tilt_angle: 110 + video_fps: 113 + video_size: 105 + wifi_rssi: 111 +sbc_config: + disabled_http_log: true + feedback_interval: 0.001 +video: + default_quality: 20 + default_res_h: 480 + default_res_w: 640 diff --git a/carsrun/qrcode.png b/carsrun/qrcode.png new file mode 100644 index 0000000000000000000000000000000000000000..e9f5f4f19320fcdbeb0ce08eb05e59c3644f1159 GIT binary patch literal 48065 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 GqyGc4%_DpO literal 0 HcmV?d00001 diff --git a/carsrun/static/Decoder.js b/carsrun/static/Decoder.js new file mode 100644 index 0000000..6f09f55 --- /dev/null +++ b/carsrun/static/Decoder.js @@ -0,0 +1,891 @@ +// universal module definition +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define([], factory); + } else if (typeof exports === 'object') { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(); + } else { + // Browser globals (root is window) + root.Decoder = factory(); + } +}(this, function () { + + var global; + + function initglobal(){ + global = this; + if (!global){ + if (typeof window != "undefined"){ + global = window; + }else if (typeof self != "undefined"){ + global = self; + }; + }; + }; + initglobal(); + + + function error(message) { + console.error(message); + console.trace(); + }; + + + function assert(condition, message) { + if (!condition) { + error(message); + }; + }; + + + + + var getModule = function(par_broadwayOnHeadersDecoded, par_broadwayOnPictureDecoded){ + + + /*var ModuleX = { + 'print': function(text) { console.log('stdout: ' + text); }, + 'printErr': function(text) { console.log('stderr: ' + text); } + };*/ + + + /* + + The reason why this is all packed into one file is that this file can also function as worker. + you can integrate the file into your build system and provide the original file to be loaded into a worker. + + */ + + //var Module = (function(){ + + +var Module=typeof Module!=="undefined"?Module:{};var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}Module["arguments"]=[];Module["thisProgram"]="./this.program";Module["quit"]=(function(status,toThrow){throw toThrow});Module["preRun"]=[];Module["postRun"]=[];var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var ENVIRONMENT_IS_NODE=false;var ENVIRONMENT_IS_SHELL=false;if(Module["ENVIRONMENT"]){if(Module["ENVIRONMENT"]==="WEB"){ENVIRONMENT_IS_WEB=true}else if(Module["ENVIRONMENT"]==="WORKER"){ENVIRONMENT_IS_WORKER=true}else if(Module["ENVIRONMENT"]==="NODE"){ENVIRONMENT_IS_NODE=true}else if(Module["ENVIRONMENT"]==="SHELL"){ENVIRONMENT_IS_SHELL=true}else{throw new Error("Module['ENVIRONMENT'] value is not valid. must be one of: WEB|WORKER|NODE|SHELL.")}}else{ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_WORKER=typeof importScripts==="function";ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof null==="function"&&!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_WORKER;ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER}if(ENVIRONMENT_IS_NODE){var nodeFS;var nodePath;Module["read"]=function shell_read(filename,binary){var ret;if(!nodeFS)nodeFS=(null)("fs");if(!nodePath)nodePath=(null)("path");filename=nodePath["normalize"](filename);ret=nodeFS["readFileSync"](filename);return binary?ret:ret.toString()};Module["readBinary"]=function readBinary(filename){var ret=Module["read"](filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};if(process["argv"].length>1){Module["thisProgram"]=process["argv"][1].replace(/\\/g,"/")}Module["arguments"]=process["argv"].slice(2);if(typeof module!=="undefined"){module["exports"]=Module}process["on"]("uncaughtException",(function(ex){if(!(ex instanceof ExitStatus)){throw ex}}));process["on"]("unhandledRejection",(function(reason,p){process["exit"](1)}));Module["inspect"]=(function(){return"[Emscripten Module object]"})}else if(ENVIRONMENT_IS_SHELL){if(typeof read!="undefined"){Module["read"]=function shell_read(f){return read(f)}}Module["readBinary"]=function readBinary(f){var data;if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){Module["arguments"]=scriptArgs}else if(typeof arguments!="undefined"){Module["arguments"]=arguments}if(typeof quit==="function"){Module["quit"]=(function(status,toThrow){quit(status)})}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){Module["read"]=function shell_read(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){Module["readBinary"]=function readBinary(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}Module["readAsync"]=function readAsync(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function xhr_onload(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)};Module["setWindowTitle"]=(function(title){document.title=title})}else{throw new Error("not compiled for this environment")}Module["print"]=typeof console!=="undefined"?console.log.bind(console):typeof print!=="undefined"?print:null;Module["printErr"]=typeof printErr!=="undefined"?printErr:typeof console!=="undefined"&&console.warn.bind(console)||Module["print"];Module.print=Module["print"];Module.printErr=Module["printErr"];for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=undefined;var STACK_ALIGN=16;function staticAlloc(size){assert(!staticSealed);var ret=STATICTOP;STATICTOP=STATICTOP+size+15&-16;return ret}function alignMemory(size,factor){if(!factor)factor=STACK_ALIGN;var ret=size=Math.ceil(size/factor)*factor;return ret}var asm2wasmImports={"f64-rem":(function(x,y){return x%y}),"debugger":(function(){debugger})};var functionPointers=new Array(0);var GLOBAL_BASE=1024;var ABORT=0;var EXITSTATUS=0;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}function Pointer_stringify(ptr,length){if(length===0||!ptr)return"";var hasUtf=0;var t;var i=0;while(1){t=HEAPU8[ptr+i>>0];hasUtf|=t;if(t==0&&!length)break;i++;if(length&&i==length)break}if(!length)length=i;var ret="";if(hasUtf<128){var MAX_CHUNK=1024;var curr;while(length>0){curr=String.fromCharCode.apply(String,HEAPU8.subarray(ptr,ptr+Math.min(length,MAX_CHUNK)));ret=ret?ret+curr:curr;ptr+=MAX_CHUNK;length-=MAX_CHUNK}return ret}return UTF8ToString(ptr)}var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf8"):undefined;function UTF8ArrayToString(u8Array,idx){var endPtr=idx;while(u8Array[endPtr])++endPtr;if(endPtr-idx>16&&u8Array.subarray&&UTF8Decoder){return UTF8Decoder.decode(u8Array.subarray(idx,endPtr))}else{var u0,u1,u2,u3,u4,u5;var str="";while(1){u0=u8Array[idx++];if(!u0)return str;if(!(u0&128)){str+=String.fromCharCode(u0);continue}u1=u8Array[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}u2=u8Array[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u3=u8Array[idx++]&63;if((u0&248)==240){u0=(u0&7)<<18|u1<<12|u2<<6|u3}else{u4=u8Array[idx++]&63;if((u0&252)==248){u0=(u0&3)<<24|u1<<18|u2<<12|u3<<6|u4}else{u5=u8Array[idx++]&63;u0=(u0&1)<<30|u1<<24|u2<<18|u3<<12|u4<<6|u5}}}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}}}function UTF8ToString(ptr){return UTF8ArrayToString(HEAPU8,ptr)}var UTF16Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf-16le"):undefined;var WASM_PAGE_SIZE=65536;var ASMJS_PAGE_SIZE=16777216;function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}return x}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBuffer(buf){Module["buffer"]=buffer=buf}function updateGlobalBufferViews(){Module["HEAP8"]=HEAP8=new Int8Array(buffer);Module["HEAP16"]=HEAP16=new Int16Array(buffer);Module["HEAP32"]=HEAP32=new Int32Array(buffer);Module["HEAPU8"]=HEAPU8=new Uint8Array(buffer);Module["HEAPU16"]=HEAPU16=new Uint16Array(buffer);Module["HEAPU32"]=HEAPU32=new Uint32Array(buffer);Module["HEAPF32"]=HEAPF32=new Float32Array(buffer);Module["HEAPF64"]=HEAPF64=new Float64Array(buffer)}var STATIC_BASE,STATICTOP,staticSealed;var STACK_BASE,STACKTOP,STACK_MAX;var DYNAMIC_BASE,DYNAMICTOP_PTR;STATIC_BASE=STATICTOP=STACK_BASE=STACKTOP=STACK_MAX=DYNAMIC_BASE=DYNAMICTOP_PTR=0;staticSealed=false;function abortOnCannotGrowMemory(){abort("Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value "+TOTAL_MEMORY+", (2) compile with -s ALLOW_MEMORY_GROWTH=1 which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with -s ABORTING_MALLOC=0 ")}function enlargeMemory(){abortOnCannotGrowMemory()}var TOTAL_STACK=Module["TOTAL_STACK"]||5242880;var TOTAL_MEMORY=Module["TOTAL_MEMORY"]||52428800;if(TOTAL_MEMORY0){var callback=callbacks.shift();if(typeof callback=="function"){callback();continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){Module["dynCall_v"](func)}else{Module["dynCall_vi"](func,callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATEXIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;var runtimeExited=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function ensureInitRuntime(){if(runtimeInitialized)return;runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function exitRuntime(){callRuntimeCallbacks(__ATEXIT__);runtimeExited=true}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var Math_abs=Math.abs;var Math_cos=Math.cos;var Math_sin=Math.sin;var Math_tan=Math.tan;var Math_acos=Math.acos;var Math_asin=Math.asin;var Math_atan=Math.atan;var Math_atan2=Math.atan2;var Math_exp=Math.exp;var Math_log=Math.log;var Math_sqrt=Math.sqrt;var Math_ceil=Math.ceil;var Math_floor=Math.floor;var Math_pow=Math.pow;var Math_imul=Math.imul;var Math_fround=Math.fround;var Math_round=Math.round;var Math_min=Math.min;var Math_max=Math.max;var Math_clz32=Math.clz32;var Math_trunc=Math.trunc;var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return String.prototype.startsWith?filename.startsWith(dataURIPrefix):filename.indexOf(dataURIPrefix)===0}function integrateWasmJS(){var wasmTextFile="avc.wast";var wasmBinaryFile="avc.wasm";var asmjsCodeFile="avc.temp.asm.js";if(typeof Module["locateFile"]==="function"){if(!isDataURI(wasmTextFile)){wasmTextFile=Module["locateFile"](wasmTextFile)}if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=Module["locateFile"](wasmBinaryFile)}if(!isDataURI(asmjsCodeFile)){asmjsCodeFile=Module["locateFile"](asmjsCodeFile)}}var wasmPageSize=64*1024;var info={"global":null,"env":null,"asm2wasm":asm2wasmImports,"parent":Module};var exports=null;function mergeMemory(newBuffer){var oldBuffer=Module["buffer"];if(newBuffer.byteLength>2];return ret}),getStr:(function(){var ret=Pointer_stringify(SYSCALLS.get());return ret}),get64:(function(){var low=SYSCALLS.get(),high=SYSCALLS.get();if(low>=0)assert(high===0);else assert(high===-1);return low}),getZero:(function(){assert(SYSCALLS.get()===0)})};function ___syscall140(which,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(),offset_high=SYSCALLS.get(),offset_low=SYSCALLS.get(),result=SYSCALLS.get(),whence=SYSCALLS.get();var offset=offset_low;FS.llseek(stream,offset,whence);HEAP32[result>>2]=stream.position;if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___syscall146(which,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.get(),iov=SYSCALLS.get(),iovcnt=SYSCALLS.get();var ret=0;if(!___syscall146.buffers){___syscall146.buffers=[null,[],[]];___syscall146.printChar=(function(stream,curr){var buffer=___syscall146.buffers[stream];assert(buffer);if(curr===0||curr===10){(stream===1?Module["print"]:Module["printErr"])(UTF8ArrayToString(buffer,0));buffer.length=0}else{buffer.push(curr)}})}for(var i=0;i>2];var len=HEAP32[iov+(i*8+4)>>2];for(var j=0;j>2]=value;return value}DYNAMICTOP_PTR=staticAlloc(4);STACK_BASE=STACKTOP=alignMemory(STATICTOP);STACK_MAX=STACK_BASE+TOTAL_STACK;DYNAMIC_BASE=alignMemory(STACK_MAX);HEAP32[DYNAMICTOP_PTR>>2]=DYNAMIC_BASE;staticSealed=true;Module["wasmTableSize"]=10;Module["wasmMaxTableSize"]=10;Module.asmGlobalArg={};Module.asmLibraryArg={"abort":abort,"enlargeMemory":enlargeMemory,"getTotalMemory":getTotalMemory,"abortOnCannotGrowMemory":abortOnCannotGrowMemory,"___setErrNo":___setErrNo,"___syscall140":___syscall140,"___syscall146":___syscall146,"___syscall54":___syscall54,"___syscall6":___syscall6,"_broadwayOnHeadersDecoded":_broadwayOnHeadersDecoded,"_broadwayOnPictureDecoded":_broadwayOnPictureDecoded,"_emscripten_memcpy_big":_emscripten_memcpy_big,"DYNAMICTOP_PTR":DYNAMICTOP_PTR,"STACKTOP":STACKTOP};var asm=Module["asm"](Module.asmGlobalArg,Module.asmLibraryArg,buffer);Module["asm"]=asm;var _broadwayCreateStream=Module["_broadwayCreateStream"]=(function(){return Module["asm"]["_broadwayCreateStream"].apply(null,arguments)});var _broadwayExit=Module["_broadwayExit"]=(function(){return Module["asm"]["_broadwayExit"].apply(null,arguments)});var _broadwayGetMajorVersion=Module["_broadwayGetMajorVersion"]=(function(){return Module["asm"]["_broadwayGetMajorVersion"].apply(null,arguments)});var _broadwayGetMinorVersion=Module["_broadwayGetMinorVersion"]=(function(){return Module["asm"]["_broadwayGetMinorVersion"].apply(null,arguments)});var _broadwayInit=Module["_broadwayInit"]=(function(){return Module["asm"]["_broadwayInit"].apply(null,arguments)});var _broadwayPlayStream=Module["_broadwayPlayStream"]=(function(){return Module["asm"]["_broadwayPlayStream"].apply(null,arguments)});Module["asm"]=asm;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}ExitStatus.prototype=new Error;ExitStatus.prototype.constructor=ExitStatus;var initialStackTop;dependenciesFulfilled=function runCaller(){if(!Module["calledRun"])run();if(!Module["calledRun"])dependenciesFulfilled=runCaller};function run(args){args=args||Module["arguments"];if(runDependencies>0){return}preRun();if(runDependencies>0)return;if(Module["calledRun"])return;function doRun(){if(Module["calledRun"])return;Module["calledRun"]=true;if(ABORT)return;ensureInitRuntime();preMain();if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout((function(){setTimeout((function(){Module["setStatus"]("")}),1);doRun()}),1)}else{doRun()}}Module["run"]=run;function exit(status,implicit){if(implicit&&Module["noExitRuntime"]&&status===0){return}if(Module["noExitRuntime"]){}else{ABORT=true;EXITSTATUS=status;STACKTOP=initialStackTop;exitRuntime();if(Module["onExit"])Module["onExit"](status)}if(ENVIRONMENT_IS_NODE){process["exit"](status)}Module["quit"](status,new ExitStatus(status))}Module["exit"]=exit;function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}if(what!==undefined){Module.print(what);Module.printErr(what);what=JSON.stringify(what)}else{what=""}ABORT=true;EXITSTATUS=1;throw"abort("+what+"). Build with -s ASSERTIONS=1 for more info."}Module["abort"]=abort;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}Module["noExitRuntime"]=true;run() + + + + // return Module; + //})(); + + var resultModule; + if (typeof global !== "undefined"){ + if (global.Module){ + resultModule = global.Module; + }; + }; + if (typeof Module != "undefined"){ + resultModule = Module; + }; + + resultModule._broadwayOnHeadersDecoded = par_broadwayOnHeadersDecoded; + resultModule._broadwayOnPictureDecoded = par_broadwayOnPictureDecoded; + + var moduleIsReady = false; + var cbFun; + var moduleReady = function(){ + moduleIsReady = true; + if (cbFun){ + cbFun(resultModule); + } + }; + + resultModule.onRuntimeInitialized = function(){ + moduleReady(resultModule); + }; + return function(callback){ + if (moduleIsReady){ + callback(resultModule); + }else{ + cbFun = callback; + }; + }; + }; + + return (function(){ + "use strict"; + + + var nowValue = function(){ + return (new Date()).getTime(); + }; + + if (typeof performance != "undefined"){ + if (performance.now){ + nowValue = function(){ + return performance.now(); + }; + }; + }; + + + var Decoder = function(parOptions){ + this.options = parOptions || {}; + + this.now = nowValue; + + var asmInstance; + + var fakeWindow = { + }; + + var toU8Array; + var toU32Array; + + var onPicFun = function ($buffer, width, height) { + var buffer = this.pictureBuffers[$buffer]; + if (!buffer) { + buffer = this.pictureBuffers[$buffer] = toU8Array($buffer, (width * height * 3) / 2); + }; + + var infos; + var doInfo = false; + if (this.infoAr.length){ + doInfo = true; + infos = this.infoAr; + }; + this.infoAr = []; + + if (this.options.rgb){ + if (!asmInstance){ + asmInstance = getAsm(width, height); + }; + asmInstance.inp.set(buffer); + asmInstance.doit(); + + var copyU8 = new Uint8Array(asmInstance.outSize); + copyU8.set( asmInstance.out ); + + if (doInfo){ + infos[0].finishDecoding = nowValue(); + }; + + this.onPictureDecoded(copyU8, width, height, infos); + return; + + }; + + if (doInfo){ + infos[0].finishDecoding = nowValue(); + }; + this.onPictureDecoded(buffer, width, height, infos); + }.bind(this); + + var ignore = false; + + if (this.options.sliceMode){ + onPicFun = function ($buffer, width, height, $sliceInfo) { + if (ignore){ + return; + }; + var buffer = this.pictureBuffers[$buffer]; + if (!buffer) { + buffer = this.pictureBuffers[$buffer] = toU8Array($buffer, (width * height * 3) / 2); + }; + var sliceInfo = this.pictureBuffers[$sliceInfo]; + if (!sliceInfo) { + sliceInfo = this.pictureBuffers[$sliceInfo] = toU32Array($sliceInfo, 18); + }; + + var infos; + var doInfo = false; + if (this.infoAr.length){ + doInfo = true; + infos = this.infoAr; + }; + this.infoAr = []; + + /*if (this.options.rgb){ + + no rgb in slice mode + + };*/ + + infos[0].finishDecoding = nowValue(); + var sliceInfoAr = []; + for (var i = 0; i < 20; ++i){ + sliceInfoAr.push(sliceInfo[i]); + }; + infos[0].sliceInfoAr = sliceInfoAr; + + this.onPictureDecoded(buffer, width, height, infos); + }.bind(this); + }; + + var ModuleCallback = getModule.apply(fakeWindow, [function () { + }, onPicFun]); + + + var MAX_STREAM_BUFFER_LENGTH = 1024 * 1024; + + var instance = this; + this.onPictureDecoded = function (buffer, width, height, infos) { + + }; + + this.onDecoderReady = function(){}; + + var bufferedCalls = []; + this.decode = function decode(typedAr, parInfo, copyDoneFun) { + bufferedCalls.push([typedAr, parInfo, copyDoneFun]); + }; + + ModuleCallback(function(Module){ + var HEAP8 = Module.HEAP8; + var HEAPU8 = Module.HEAPU8; + var HEAP16 = Module.HEAP16; + var HEAP32 = Module.HEAP32; + // from old constructor + Module._broadwayInit(); + + /** + * Creates a typed array from a HEAP8 pointer. + */ + toU8Array = function(ptr, length) { + return HEAPU8.subarray(ptr, ptr + length); + }; + toU32Array = function(ptr, length) { + //var tmp = HEAPU8.subarray(ptr, ptr + (length * 4)); + return new Uint32Array(HEAPU8.buffer, ptr, length); + }; + instance.streamBuffer = toU8Array(Module._broadwayCreateStream(MAX_STREAM_BUFFER_LENGTH), MAX_STREAM_BUFFER_LENGTH); + instance.pictureBuffers = {}; + // collect extra infos that are provided with the nal units + instance.infoAr = []; + + /** + * Decodes a stream buffer. This may be one single (unframed) NAL unit without the + * start code, or a sequence of NAL units with framing start code prefixes. This + * function overwrites stream buffer allocated by the codec with the supplied buffer. + */ + + var sliceNum = 0; + if (instance.options.sliceMode){ + sliceNum = instance.options.sliceNum; + + instance.decode = function decode(typedAr, parInfo, copyDoneFun) { + instance.infoAr.push(parInfo); + parInfo.startDecoding = nowValue(); + var nals = parInfo.nals; + var i; + if (!nals){ + nals = []; + parInfo.nals = nals; + var l = typedAr.length; + var foundSomething = false; + var lastFound = 0; + var lastStart = 0; + for (i = 0; i < l; ++i){ + if (typedAr[i] === 1){ + if ( + typedAr[i - 1] === 0 && + typedAr[i - 2] === 0 + ){ + var startPos = i - 2; + if (typedAr[i - 3] === 0){ + startPos = i - 3; + }; + // its a nal; + if (foundSomething){ + nals.push({ + offset: lastFound, + end: startPos, + type: typedAr[lastStart] & 31 + }); + }; + lastFound = startPos; + lastStart = startPos + 3; + if (typedAr[i - 3] === 0){ + lastStart = startPos + 4; + }; + foundSomething = true; + }; + }; + }; + if (foundSomething){ + nals.push({ + offset: lastFound, + end: i, + type: typedAr[lastStart] & 31 + }); + }; + }; + + var currentSlice = 0; + var playAr; + var offset = 0; + for (i = 0; i < nals.length; ++i){ + if (nals[i].type === 1 || nals[i].type === 5){ + if (currentSlice === sliceNum){ + playAr = typedAr.subarray(nals[i].offset, nals[i].end); + instance.streamBuffer[offset] = 0; + offset += 1; + instance.streamBuffer.set(playAr, offset); + offset += playAr.length; + }; + currentSlice += 1; + }else{ + playAr = typedAr.subarray(nals[i].offset, nals[i].end); + instance.streamBuffer[offset] = 0; + offset += 1; + instance.streamBuffer.set(playAr, offset); + offset += playAr.length; + Module._broadwayPlayStream(offset); + offset = 0; + }; + }; + copyDoneFun(); + Module._broadwayPlayStream(offset); + }; + + }else{ + instance.decode = function decode(typedAr, parInfo) { + // console.info("Decoding: " + buffer.length); + // collect infos + if (parInfo){ + instance.infoAr.push(parInfo); + parInfo.startDecoding = nowValue(); + }; + + instance.streamBuffer.set(typedAr); + Module._broadwayPlayStream(typedAr.length); + }; + }; + + if (bufferedCalls.length){ + var bi = 0; + for (bi = 0; bi < bufferedCalls.length; ++bi){ + instance.decode(bufferedCalls[bi][0], bufferedCalls[bi][1], bufferedCalls[bi][2]); + }; + bufferedCalls = []; + }; + + instance.onDecoderReady(instance); + + }); + + + }; + + + Decoder.prototype = { + + }; + + + + + /* + + asm.js implementation of a yuv to rgb convertor + provided by @soliton4 + + based on + http://www.wordsaretoys.com/2013/10/18/making-yuv-conversion-a-little-faster/ + + */ + + + // factory to create asm.js yuv -> rgb convertor for a given resolution + var asmInstances = {}; + var getAsm = function(parWidth, parHeight){ + var idStr = "" + parWidth + "x" + parHeight; + if (asmInstances[idStr]){ + return asmInstances[idStr]; + }; + + var lumaSize = parWidth * parHeight; + var chromaSize = (lumaSize|0) >> 2; + + var inpSize = lumaSize + chromaSize + chromaSize; + var outSize = parWidth * parHeight * 4; + var cacheSize = Math.pow(2, 24) * 4; + var size = inpSize + outSize + cacheSize; + + var chunkSize = Math.pow(2, 24); + var heapSize = chunkSize; + while (heapSize < size){ + heapSize += chunkSize; + }; + var heap = new ArrayBuffer(heapSize); + + var res = asmFactory(global, {}, heap); + res.init(parWidth, parHeight); + asmInstances[idStr] = res; + + res.heap = heap; + res.out = new Uint8Array(heap, 0, outSize); + res.inp = new Uint8Array(heap, outSize, inpSize); + res.outSize = outSize; + + return res; + }; + + + function asmFactory(stdlib, foreign, heap) { + "use asm"; + + var imul = stdlib.Math.imul; + var min = stdlib.Math.min; + var max = stdlib.Math.max; + var pow = stdlib.Math.pow; + var out = new stdlib.Uint8Array(heap); + var out32 = new stdlib.Uint32Array(heap); + var inp = new stdlib.Uint8Array(heap); + var mem = new stdlib.Uint8Array(heap); + var mem32 = new stdlib.Uint32Array(heap); + + // for double algo + /*var vt = 1.370705; + var gt = 0.698001; + var gt2 = 0.337633; + var bt = 1.732446;*/ + + var width = 0; + var height = 0; + var lumaSize = 0; + var chromaSize = 0; + var inpSize = 0; + var outSize = 0; + + var inpStart = 0; + var outStart = 0; + + var widthFour = 0; + + var cacheStart = 0; + + + function init(parWidth, parHeight){ + parWidth = parWidth|0; + parHeight = parHeight|0; + + var i = 0; + var s = 0; + + width = parWidth; + widthFour = imul(parWidth, 4)|0; + height = parHeight; + lumaSize = imul(width|0, height|0)|0; + chromaSize = (lumaSize|0) >> 2; + outSize = imul(imul(width, height)|0, 4)|0; + inpSize = ((lumaSize + chromaSize)|0 + chromaSize)|0; + + outStart = 0; + inpStart = (outStart + outSize)|0; + cacheStart = (inpStart + inpSize)|0; + + // initializing memory (to be on the safe side) + s = ~~(+pow(+2, +24)); + s = imul(s, 4)|0; + + for (i = 0|0; ((i|0) < (s|0))|0; i = (i + 4)|0){ + mem32[((cacheStart + i)|0) >> 2] = 0; + }; + }; + + function doit(){ + var ystart = 0; + var ustart = 0; + var vstart = 0; + + var y = 0; + var yn = 0; + var u = 0; + var v = 0; + + var o = 0; + + var line = 0; + var col = 0; + + var usave = 0; + var vsave = 0; + + var ostart = 0; + var cacheAdr = 0; + + ostart = outStart|0; + + ystart = inpStart|0; + ustart = (ystart + lumaSize|0)|0; + vstart = (ustart + chromaSize)|0; + + for (line = 0; (line|0) < (height|0); line = (line + 2)|0){ + usave = ustart; + vsave = vstart; + for (col = 0; (col|0) < (width|0); col = (col + 2)|0){ + y = inp[ystart >> 0]|0; + yn = inp[((ystart + width)|0) >> 0]|0; + + u = inp[ustart >> 0]|0; + v = inp[vstart >> 0]|0; + + cacheAdr = (((((y << 16)|0) + ((u << 8)|0))|0) + v)|0; + o = mem32[((cacheStart + cacheAdr)|0) >> 2]|0; + if (o){}else{ + o = yuv2rgbcalc(y,u,v)|0; + mem32[((cacheStart + cacheAdr)|0) >> 2] = o|0; + }; + mem32[ostart >> 2] = o; + + cacheAdr = (((((yn << 16)|0) + ((u << 8)|0))|0) + v)|0; + o = mem32[((cacheStart + cacheAdr)|0) >> 2]|0; + if (o){}else{ + o = yuv2rgbcalc(yn,u,v)|0; + mem32[((cacheStart + cacheAdr)|0) >> 2] = o|0; + }; + mem32[((ostart + widthFour)|0) >> 2] = o; + + //yuv2rgb5(y, u, v, ostart); + //yuv2rgb5(yn, u, v, (ostart + widthFour)|0); + ostart = (ostart + 4)|0; + + // next step only for y. u and v stay the same + ystart = (ystart + 1)|0; + y = inp[ystart >> 0]|0; + yn = inp[((ystart + width)|0) >> 0]|0; + + //yuv2rgb5(y, u, v, ostart); + cacheAdr = (((((y << 16)|0) + ((u << 8)|0))|0) + v)|0; + o = mem32[((cacheStart + cacheAdr)|0) >> 2]|0; + if (o){}else{ + o = yuv2rgbcalc(y,u,v)|0; + mem32[((cacheStart + cacheAdr)|0) >> 2] = o|0; + }; + mem32[ostart >> 2] = o; + + //yuv2rgb5(yn, u, v, (ostart + widthFour)|0); + cacheAdr = (((((yn << 16)|0) + ((u << 8)|0))|0) + v)|0; + o = mem32[((cacheStart + cacheAdr)|0) >> 2]|0; + if (o){}else{ + o = yuv2rgbcalc(yn,u,v)|0; + mem32[((cacheStart + cacheAdr)|0) >> 2] = o|0; + }; + mem32[((ostart + widthFour)|0) >> 2] = o; + ostart = (ostart + 4)|0; + + //all positions inc 1 + + ystart = (ystart + 1)|0; + ustart = (ustart + 1)|0; + vstart = (vstart + 1)|0; + }; + ostart = (ostart + widthFour)|0; + ystart = (ystart + width)|0; + + }; + + }; + + function yuv2rgbcalc(y, u, v){ + y = y|0; + u = u|0; + v = v|0; + + var r = 0; + var g = 0; + var b = 0; + + var o = 0; + + var a0 = 0; + var a1 = 0; + var a2 = 0; + var a3 = 0; + var a4 = 0; + + a0 = imul(1192, (y - 16)|0)|0; + a1 = imul(1634, (v - 128)|0)|0; + a2 = imul(832, (v - 128)|0)|0; + a3 = imul(400, (u - 128)|0)|0; + a4 = imul(2066, (u - 128)|0)|0; + + r = (((a0 + a1)|0) >> 10)|0; + g = (((((a0 - a2)|0) - a3)|0) >> 10)|0; + b = (((a0 + a4)|0) >> 10)|0; + + if ((((r & 255)|0) != (r|0))|0){ + r = min(255, max(0, r|0)|0)|0; + }; + if ((((g & 255)|0) != (g|0))|0){ + g = min(255, max(0, g|0)|0)|0; + }; + if ((((b & 255)|0) != (b|0))|0){ + b = min(255, max(0, b|0)|0)|0; + }; + + o = 255; + o = (o << 8)|0; + o = (o + b)|0; + o = (o << 8)|0; + o = (o + g)|0; + o = (o << 8)|0; + o = (o + r)|0; + + return o|0; + + }; + + + + return { + init: init, + doit: doit + }; + }; + + + /* + potential worker initialization + + */ + + + if (typeof self != "undefined"){ + var isWorker = false; + var decoder; + var reuseMemory = false; + var sliceMode = false; + var sliceNum = 0; + var sliceCnt = 0; + var lastSliceNum = 0; + var sliceInfoAr; + var lastBuf; + var awaiting = 0; + var pile = []; + var startDecoding; + var finishDecoding; + var timeDecoding; + + var memAr = []; + var getMem = function(length){ + if (memAr.length){ + var u = memAr.shift(); + while (u && u.byteLength !== length){ + u = memAr.shift(); + }; + if (u){ + return u; + }; + }; + return new ArrayBuffer(length); + }; + + var copySlice = function(source, target, infoAr, width, height){ + + var length = width * height; + var length4 = length / 4 + var plane2 = length; + var plane3 = length + length4; + + var copy16 = function(parBegin, parEnd){ + var i = 0; + for (i = 0; i < 16; ++i){ + var begin = parBegin + (width * i); + var end = parEnd + (width * i) + target.set(source.subarray(begin, end), begin); + }; + }; + var copy8 = function(parBegin, parEnd){ + var i = 0; + for (i = 0; i < 8; ++i){ + var begin = parBegin + ((width / 2) * i); + var end = parEnd + ((width / 2) * i) + target.set(source.subarray(begin, end), begin); + }; + }; + var copyChunk = function(begin, end){ + target.set(source.subarray(begin, end), begin); + }; + + var begin = infoAr[0]; + var end = infoAr[1]; + if (end > 0){ + copy16(begin, end); + copy8(infoAr[2], infoAr[3]); + copy8(infoAr[4], infoAr[5]); + }; + begin = infoAr[6]; + end = infoAr[7]; + if (end > 0){ + copy16(begin, end); + copy8(infoAr[8], infoAr[9]); + copy8(infoAr[10], infoAr[11]); + }; + + begin = infoAr[12]; + end = infoAr[15]; + if (end > 0){ + copyChunk(begin, end); + copyChunk(infoAr[13], infoAr[16]); + copyChunk(infoAr[14], infoAr[17]); + }; + + }; + + var sliceMsgFun = function(){}; + + var setSliceCnt = function(parSliceCnt){ + sliceCnt = parSliceCnt; + lastSliceNum = sliceCnt - 1; + }; + + + self.addEventListener('message', function(e) { + + if (isWorker){ + if (reuseMemory){ + if (e.data.reuse){ + memAr.push(e.data.reuse); + }; + }; + if (e.data.buf){ + if (sliceMode && awaiting !== 0){ + pile.push(e.data); + }else{ + decoder.decode( + new Uint8Array(e.data.buf, e.data.offset || 0, e.data.length), + e.data.info, + function(){ + if (sliceMode && sliceNum !== lastSliceNum){ + postMessage(e.data, [e.data.buf]); + }; + } + ); + }; + return; + }; + + if (e.data.slice){ + // update ref pic + var copyStart = nowValue(); + copySlice(new Uint8Array(e.data.slice), lastBuf, e.data.infos[0].sliceInfoAr, e.data.width, e.data.height); + // is it the one? then we need to update it + if (e.data.theOne){ + copySlice(lastBuf, new Uint8Array(e.data.slice), sliceInfoAr, e.data.width, e.data.height); + if (timeDecoding > e.data.infos[0].timeDecoding){ + e.data.infos[0].timeDecoding = timeDecoding; + }; + e.data.infos[0].timeCopy += (nowValue() - copyStart); + }; + // move on + postMessage(e.data, [e.data.slice]); + + // next frame in the pipe? + awaiting -= 1; + if (awaiting === 0 && pile.length){ + var data = pile.shift(); + decoder.decode( + new Uint8Array(data.buf, data.offset || 0, data.length), + data.info, + function(){ + if (sliceMode && sliceNum !== lastSliceNum){ + postMessage(data, [data.buf]); + }; + } + ); + }; + return; + }; + + if (e.data.setSliceCnt){ + setSliceCnt(e.data.sliceCnt); + return; + }; + + }else{ + if (e.data && e.data.type === "Broadway.js - Worker init"){ + isWorker = true; + decoder = new Decoder(e.data.options); + + if (e.data.options.sliceMode){ + reuseMemory = true; + sliceMode = true; + sliceNum = e.data.options.sliceNum; + setSliceCnt(e.data.options.sliceCnt); + + decoder.onPictureDecoded = function (buffer, width, height, infos) { + + // buffer needs to be copied because we give up ownership + var copyU8 = new Uint8Array(getMem(buffer.length)); + copySlice(buffer, copyU8, infos[0].sliceInfoAr, width, height); + + startDecoding = infos[0].startDecoding; + finishDecoding = infos[0].finishDecoding; + timeDecoding = finishDecoding - startDecoding; + infos[0].timeDecoding = timeDecoding; + infos[0].timeCopy = 0; + + postMessage({ + slice: copyU8.buffer, + sliceNum: sliceNum, + width: width, + height: height, + infos: infos + }, [copyU8.buffer]); // 2nd parameter is used to indicate transfer of ownership + + awaiting = sliceCnt - 1; + + lastBuf = buffer; + sliceInfoAr = infos[0].sliceInfoAr; + + }; + + }else if (e.data.options.reuseMemory){ + reuseMemory = true; + decoder.onPictureDecoded = function (buffer, width, height, infos) { + + // buffer needs to be copied because we give up ownership + var copyU8 = new Uint8Array(getMem(buffer.length)); + copyU8.set( buffer, 0, buffer.length ); + + postMessage({ + buf: copyU8.buffer, + length: buffer.length, + width: width, + height: height, + infos: infos + }, [copyU8.buffer]); // 2nd parameter is used to indicate transfer of ownership + + }; + + }else{ + decoder.onPictureDecoded = function (buffer, width, height, infos) { + if (buffer) { + buffer = new Uint8Array(buffer); + }; + + // buffer needs to be copied because we give up ownership + var copyU8 = new Uint8Array(buffer.length); + copyU8.set( buffer, 0, buffer.length ); + + postMessage({ + buf: copyU8.buffer, + length: buffer.length, + width: width, + height: height, + infos: infos + }, [copyU8.buffer]); // 2nd parameter is used to indicate transfer of ownership + + }; + }; + postMessage({ consoleLog: "broadway worker initialized" }); + }; + }; + + + }, false); + }; + + Decoder.nowValue = nowValue; + + return Decoder; + + })(); + + +})); + diff --git a/carsrun/static/Player.js b/carsrun/static/Player.js new file mode 100644 index 0000000..ff2e649 --- /dev/null +++ b/carsrun/static/Player.js @@ -0,0 +1,335 @@ +/* + + +usage: + +p = new Player({ + useWorker: , + workerFile: // give path to Decoder.js + webgl: true | false | "auto" // defaults to "auto" +}); + +// canvas property represents the canvas node +// put it somewhere in the dom +p.canvas; + +p.webgl; // contains the used rendering mode. if you pass auto to webgl you can see what auto detection resulted in + +p.decode(); + + +*/ + + + +// universal module definition +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(["./Decoder", "./YUVCanvas"], factory); + } else if (typeof exports === 'object') { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(require("./Decoder"), require("./YUVCanvas")); + } else { + // Browser globals (root is window) + root.Player = factory(root.Decoder, root.YUVCanvas); + } +}(this, function (Decoder, WebGLCanvas) { + "use strict"; + + + var nowValue = Decoder.nowValue; + + + var Player = function(parOptions){ + var self = this; + this._config = parOptions || {}; + + this.render = true; + if (this._config.render === false){ + this.render = false; + }; + + this.nowValue = nowValue; + + this._config.workerFile = this._config.workerFile || "Decoder.js"; + if (this._config.preserveDrawingBuffer){ + this._config.contextOptions = this._config.contextOptions || {}; + this._config.contextOptions.preserveDrawingBuffer = true; + }; + + var webgl = "auto"; + if (this._config.webgl === true){ + webgl = true; + }else if (this._config.webgl === false){ + webgl = false; + }; + + if (webgl == "auto"){ + webgl = true; + try{ + if (!window.WebGLRenderingContext) { + // the browser doesn't even know what WebGL is + webgl = false; + } else { + var canvas = document.createElement('canvas'); + var ctx = canvas.getContext("webgl"); + if (!ctx) { + // browser supports WebGL but initialization failed. + webgl = false; + }; + }; + }catch(e){ + webgl = false; + }; + }; + + this.webgl = webgl; + + // choose functions + if (this.webgl){ + this.createCanvasObj = this.createCanvasWebGL; + this.renderFrame = this.renderFrameWebGL; + }else{ + this.createCanvasObj = this.createCanvasRGB; + this.renderFrame = this.renderFrameRGB; + }; + + + var lastWidth; + var lastHeight; + var onPictureDecoded = function(buffer, width, height, infos) { + self.onPictureDecoded(buffer, width, height, infos); + + var startTime = nowValue(); + + if (!buffer || !self.render) { + return; + }; + + self.renderFrame({ + canvasObj: self.canvasObj, + data: buffer, + width: width, + height: height + }); + + if (self.onRenderFrameComplete){ + self.onRenderFrameComplete({ + data: buffer, + width: width, + height: height, + infos: infos, + canvasObj: self.canvasObj + }); + }; + + }; + + // provide size + + if (!this._config.size){ + this._config.size = {}; + }; + this._config.size.width = this._config.size.width || 200; + this._config.size.height = this._config.size.height || 200; + + if (this._config.useWorker){ + var worker = new Worker(this._config.workerFile); + this.worker = worker; + worker.addEventListener('message', function(e) { + var data = e.data; + if (data.consoleLog){ + console.log(data.consoleLog); + return; + }; + + onPictureDecoded.call(self, new Uint8Array(data.buf, 0, data.length), data.width, data.height, data.infos); + + }, false); + + worker.postMessage({type: "Broadway.js - Worker init", options: { + rgb: !webgl, + memsize: this.memsize, + reuseMemory: this._config.reuseMemory ? true : false + }}); + + if (this._config.transferMemory){ + this.decode = function(parData, parInfo){ + // no copy + // instead we are transfering the ownership of the buffer + // dangerous!!! + + worker.postMessage({buf: parData.buffer, offset: parData.byteOffset, length: parData.length, info: parInfo}, [parData.buffer]); // Send data to our worker. + }; + + }else{ + this.decode = function(parData, parInfo){ + // Copy the sample so that we only do a structured clone of the + // region of interest + var copyU8 = new Uint8Array(parData.length); + copyU8.set( parData, 0, parData.length ); + worker.postMessage({buf: copyU8.buffer, offset: 0, length: parData.length, info: parInfo}, [copyU8.buffer]); // Send data to our worker. + }; + + }; + + if (this._config.reuseMemory){ + this.recycleMemory = function(parArray){ + //this.beforeRecycle(); + worker.postMessage({reuse: parArray.buffer}, [parArray.buffer]); // Send data to our worker. + //this.afterRecycle(); + }; + } + + }else{ + + this.decoder = new Decoder({ + rgb: !webgl + }); + this.decoder.onPictureDecoded = onPictureDecoded; + + this.decode = function(parData, parInfo){ + self.decoder.decode(parData, parInfo); + }; + + }; + + + + if (this.render){ + this.canvasObj = this.createCanvasObj({ + contextOptions: this._config.contextOptions + }); + this.canvas = this.canvasObj.canvas; + }; + + this.domNode = this.canvas; + + lastWidth = this._config.size.width; + lastHeight = this._config.size.height; + + }; + + Player.prototype = { + + onPictureDecoded: function(buffer, width, height, infos){}, + + // call when memory of decoded frames is not used anymore + recycleMemory: function(buf){ + }, + /*beforeRecycle: function(){}, + afterRecycle: function(){},*/ + + // for both functions options is: + // + // width + // height + // enableScreenshot + // + // returns a object that has a property canvas which is a html5 canvas + createCanvasWebGL: function(options){ + var canvasObj = this._createBasicCanvasObj(options); + canvasObj.contextOptions = options.contextOptions; + return canvasObj; + }, + + createCanvasRGB: function(options){ + var canvasObj = this._createBasicCanvasObj(options); + return canvasObj; + }, + + // part that is the same for webGL and RGB + _createBasicCanvasObj: function(options){ + options = options || {}; + + var obj = {}; + var width = options.width; + if (!width){ + width = this._config.size.width; + }; + var height = options.height; + if (!height){ + height = this._config.size.height; + }; + obj.canvas = document.createElement('canvas'); + obj.canvas.width = width; + obj.canvas.height = height; + obj.canvas.style.backgroundColor = "#0D0E1B"; + + + return obj; + }, + + // options: + // + // canvas + // data + renderFrameWebGL: function(options){ + + var canvasObj = options.canvasObj; + + var width = options.width || canvasObj.canvas.width; + var height = options.height || canvasObj.canvas.height; + + if (canvasObj.canvas.width !== width || canvasObj.canvas.height !== height || !canvasObj.webGLCanvas){ + canvasObj.canvas.width = width; + canvasObj.canvas.height = height; + canvasObj.webGLCanvas = new WebGLCanvas({ + canvas: canvasObj.canvas, + contextOptions: canvasObj.contextOptions, + width: width, + height: height + }); + }; + + var ylen = width * height; + var uvlen = (width / 2) * (height / 2); + + canvasObj.webGLCanvas.drawNextOutputPicture({ + yData: options.data.subarray(0, ylen), + uData: options.data.subarray(ylen, ylen + uvlen), + vData: options.data.subarray(ylen + uvlen, ylen + uvlen + uvlen) + }); + + var self = this; + self.recycleMemory(options.data); + + }, + renderFrameRGB: function(options){ + var canvasObj = options.canvasObj; + + var width = options.width || canvasObj.canvas.width; + var height = options.height || canvasObj.canvas.height; + + if (canvasObj.canvas.width !== width || canvasObj.canvas.height !== height){ + canvasObj.canvas.width = width; + canvasObj.canvas.height = height; + }; + + var ctx = canvasObj.ctx; + var imgData = canvasObj.imgData; + + if (!ctx){ + canvasObj.ctx = canvasObj.canvas.getContext('2d'); + ctx = canvasObj.ctx; + + canvasObj.imgData = ctx.createImageData(width, height); + imgData = canvasObj.imgData; + }; + + imgData.data.set(options.data); + ctx.putImageData(imgData, 0, 0); + var self = this; + self.recycleMemory(options.data); + + } + + }; + + return Player; + +})); + diff --git a/carsrun/static/YUVCanvas.js b/carsrun/static/YUVCanvas.js new file mode 100644 index 0000000..1bca8a3 --- /dev/null +++ b/carsrun/static/YUVCanvas.js @@ -0,0 +1,551 @@ +// +// Copyright (c) 2015 Paperspace Co. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// + + +// universal module definition +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define([], factory); + } else if (typeof exports === 'object') { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(); + } else { + // Browser globals (root is window) + root.YUVCanvas = factory(); + } +}(this, function () { + + +/** + * This class can be used to render output pictures from an H264bsdDecoder to a canvas element. + * If available the content is rendered using WebGL. + */ + function YUVCanvas(parOptions) { + + parOptions = parOptions || {}; + + this.canvasElement = parOptions.canvas || document.createElement("canvas"); + this.contextOptions = parOptions.contextOptions; + + this.type = parOptions.type || "yuv420"; + + this.customYUV444 = parOptions.customYUV444; + + this.conversionType = parOptions.conversionType || "rec601"; + + this.width = parOptions.width || 640; + this.height = parOptions.height || 320; + + this.animationTime = parOptions.animationTime || 0; + + this.canvasElement.width = this.width; + this.canvasElement.height = this.height; + + this.initContextGL(); + + if(this.contextGL) { + this.initProgram(); + this.initBuffers(); + this.initTextures(); + }; + + +/** + * Draw the next output picture using WebGL + */ + if (this.type === "yuv420"){ + this.drawNextOuptutPictureGL = function(par) { + var gl = this.contextGL; + var texturePosBuffer = this.texturePosBuffer; + var uTexturePosBuffer = this.uTexturePosBuffer; + var vTexturePosBuffer = this.vTexturePosBuffer; + + var yTextureRef = this.yTextureRef; + var uTextureRef = this.uTextureRef; + var vTextureRef = this.vTextureRef; + + var yData = par.yData; + var uData = par.uData; + var vData = par.vData; + + var width = this.width; + var height = this.height; + + var yDataPerRow = par.yDataPerRow || width; + var yRowCnt = par.yRowCnt || height; + + var uDataPerRow = par.uDataPerRow || (width / 2); + var uRowCnt = par.uRowCnt || (height / 2); + + var vDataPerRow = par.vDataPerRow || uDataPerRow; + var vRowCnt = par.vRowCnt || uRowCnt; + + gl.viewport(0, 0, width, height); + + var tTop = 0; + var tLeft = 0; + var tBottom = height / yRowCnt; + var tRight = width / yDataPerRow; + var texturePosValues = new Float32Array([tRight, tTop, tLeft, tTop, tRight, tBottom, tLeft, tBottom]); + + gl.bindBuffer(gl.ARRAY_BUFFER, texturePosBuffer); + gl.bufferData(gl.ARRAY_BUFFER, texturePosValues, gl.DYNAMIC_DRAW); + + if (this.customYUV444){ + tBottom = height / uRowCnt; + tRight = width / uDataPerRow; + }else{ + tBottom = (height / 2) / uRowCnt; + tRight = (width / 2) / uDataPerRow; + }; + var uTexturePosValues = new Float32Array([tRight, tTop, tLeft, tTop, tRight, tBottom, tLeft, tBottom]); + + gl.bindBuffer(gl.ARRAY_BUFFER, uTexturePosBuffer); + gl.bufferData(gl.ARRAY_BUFFER, uTexturePosValues, gl.DYNAMIC_DRAW); + + + if (this.customYUV444){ + tBottom = height / vRowCnt; + tRight = width / vDataPerRow; + }else{ + tBottom = (height / 2) / vRowCnt; + tRight = (width / 2) / vDataPerRow; + }; + var vTexturePosValues = new Float32Array([tRight, tTop, tLeft, tTop, tRight, tBottom, tLeft, tBottom]); + + gl.bindBuffer(gl.ARRAY_BUFFER, vTexturePosBuffer); + gl.bufferData(gl.ARRAY_BUFFER, vTexturePosValues, gl.DYNAMIC_DRAW); + + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, yTextureRef); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, yDataPerRow, yRowCnt, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, yData); + + gl.activeTexture(gl.TEXTURE1); + gl.bindTexture(gl.TEXTURE_2D, uTextureRef); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, uDataPerRow, uRowCnt, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, uData); + + gl.activeTexture(gl.TEXTURE2); + gl.bindTexture(gl.TEXTURE_2D, vTextureRef); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, vDataPerRow, vRowCnt, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, vData); + + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); + }; + + }else if (this.type === "yuv422"){ + this.drawNextOuptutPictureGL = function(par) { + var gl = this.contextGL; + var texturePosBuffer = this.texturePosBuffer; + + var textureRef = this.textureRef; + + var data = par.data; + + var width = this.width; + var height = this.height; + + var dataPerRow = par.dataPerRow || (width * 2); + var rowCnt = par.rowCnt || height; + + gl.viewport(0, 0, width, height); + + var tTop = 0; + var tLeft = 0; + var tBottom = height / rowCnt; + var tRight = width / (dataPerRow / 2); + var texturePosValues = new Float32Array([tRight, tTop, tLeft, tTop, tRight, tBottom, tLeft, tBottom]); + + gl.bindBuffer(gl.ARRAY_BUFFER, texturePosBuffer); + gl.bufferData(gl.ARRAY_BUFFER, texturePosValues, gl.DYNAMIC_DRAW); + + gl.uniform2f(gl.getUniformLocation(this.shaderProgram, 'resolution'), dataPerRow, height); + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, textureRef); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, dataPerRow, rowCnt, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, data); + + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); + }; + }; + + }; + + /** + * Returns true if the canvas supports WebGL + */ + YUVCanvas.prototype.isWebGL = function() { + return this.contextGL; + }; + + /** + * Create the GL context from the canvas element + */ + YUVCanvas.prototype.initContextGL = function() { + var canvas = this.canvasElement; + var gl = null; + + var validContextNames = ["webgl", "experimental-webgl", "moz-webgl", "webkit-3d"]; + var nameIndex = 0; + + while(!gl && nameIndex < validContextNames.length) { + var contextName = validContextNames[nameIndex]; + + try { + if (this.contextOptions){ + gl = canvas.getContext(contextName, this.contextOptions); + }else{ + gl = canvas.getContext(contextName); + }; + } catch (e) { + gl = null; + } + + if(!gl || typeof gl.getParameter !== "function") { + gl = null; + } + + ++nameIndex; + }; + + this.contextGL = gl; + }; + +/** + * Initialize GL shader program + */ +YUVCanvas.prototype.initProgram = function() { + var gl = this.contextGL; + + // vertex shader is the same for all types + var vertexShaderScript; + var fragmentShaderScript; + + if (this.type === "yuv420"){ + + vertexShaderScript = [ + 'attribute vec4 vertexPos;', + 'attribute vec4 texturePos;', + 'attribute vec4 uTexturePos;', + 'attribute vec4 vTexturePos;', + 'varying vec2 textureCoord;', + 'varying vec2 uTextureCoord;', + 'varying vec2 vTextureCoord;', + + 'void main()', + '{', + ' gl_Position = vertexPos;', + ' textureCoord = texturePos.xy;', + ' uTextureCoord = uTexturePos.xy;', + ' vTextureCoord = vTexturePos.xy;', + '}' + ].join('\n'); + + fragmentShaderScript = [ + 'precision highp float;', + 'varying highp vec2 textureCoord;', + 'varying highp vec2 uTextureCoord;', + 'varying highp vec2 vTextureCoord;', + 'uniform sampler2D ySampler;', + 'uniform sampler2D uSampler;', + 'uniform sampler2D vSampler;', + 'uniform mat4 YUV2RGB;', + + 'void main(void) {', + ' highp float y = texture2D(ySampler, textureCoord).r;', + ' highp float u = texture2D(uSampler, uTextureCoord).r;', + ' highp float v = texture2D(vSampler, vTextureCoord).r;', + ' gl_FragColor = vec4(y, u, v, 1) * YUV2RGB;', + '}' + ].join('\n'); + + }else if (this.type === "yuv422"){ + vertexShaderScript = [ + 'attribute vec4 vertexPos;', + 'attribute vec4 texturePos;', + 'varying vec2 textureCoord;', + + 'void main()', + '{', + ' gl_Position = vertexPos;', + ' textureCoord = texturePos.xy;', + '}' + ].join('\n'); + + fragmentShaderScript = [ + 'precision highp float;', + 'varying highp vec2 textureCoord;', + 'uniform sampler2D sampler;', + 'uniform highp vec2 resolution;', + 'uniform mat4 YUV2RGB;', + + 'void main(void) {', + + ' highp float texPixX = 1.0 / resolution.x;', + ' highp float logPixX = 2.0 / resolution.x;', // half the resolution of the texture + ' highp float logHalfPixX = 4.0 / resolution.x;', // half of the logical resolution so every 4th pixel + ' highp float steps = floor(textureCoord.x / logPixX);', + ' highp float uvSteps = floor(textureCoord.x / logHalfPixX);', + ' highp float y = texture2D(sampler, vec2((logPixX * steps) + texPixX, textureCoord.y)).r;', + ' highp float u = texture2D(sampler, vec2((logHalfPixX * uvSteps), textureCoord.y)).r;', + ' highp float v = texture2D(sampler, vec2((logHalfPixX * uvSteps) + texPixX + texPixX, textureCoord.y)).r;', + + //' highp float y = texture2D(sampler, textureCoord).r;', + //' gl_FragColor = vec4(y, u, v, 1) * YUV2RGB;', + ' gl_FragColor = vec4(y, u, v, 1.0) * YUV2RGB;', + '}' + ].join('\n'); + }; + + var YUV2RGB = []; + + if (this.conversionType == "rec709") { + // ITU-T Rec. 709 + YUV2RGB = [ + 1.16438, 0.00000, 1.79274, -0.97295, + 1.16438, -0.21325, -0.53291, 0.30148, + 1.16438, 2.11240, 0.00000, -1.13340, + 0, 0, 0, 1, + ]; + } else { + // assume ITU-T Rec. 601 + YUV2RGB = [ + 1.16438, 0.00000, 1.59603, -0.87079, + 1.16438, -0.39176, -0.81297, 0.52959, + 1.16438, 2.01723, 0.00000, -1.08139, + 0, 0, 0, 1 + ]; + }; + + var vertexShader = gl.createShader(gl.VERTEX_SHADER); + gl.shaderSource(vertexShader, vertexShaderScript); + gl.compileShader(vertexShader); + if(!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) { + console.log('Vertex shader failed to compile: ' + gl.getShaderInfoLog(vertexShader)); + } + + var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); + gl.shaderSource(fragmentShader, fragmentShaderScript); + gl.compileShader(fragmentShader); + if(!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) { + console.log('Fragment shader failed to compile: ' + gl.getShaderInfoLog(fragmentShader)); + } + + var program = gl.createProgram(); + gl.attachShader(program, vertexShader); + gl.attachShader(program, fragmentShader); + gl.linkProgram(program); + if(!gl.getProgramParameter(program, gl.LINK_STATUS)) { + console.log('Program failed to compile: ' + gl.getProgramInfoLog(program)); + } + + gl.useProgram(program); + + var YUV2RGBRef = gl.getUniformLocation(program, 'YUV2RGB'); + gl.uniformMatrix4fv(YUV2RGBRef, false, YUV2RGB); + + this.shaderProgram = program; +}; + +/** + * Initialize vertex buffers and attach to shader program + */ +YUVCanvas.prototype.initBuffers = function() { + var gl = this.contextGL; + var program = this.shaderProgram; + + var vertexPosBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1, 1, -1, 1, 1, -1, -1, -1]), gl.STATIC_DRAW); + + var vertexPosRef = gl.getAttribLocation(program, 'vertexPos'); + gl.enableVertexAttribArray(vertexPosRef); + gl.vertexAttribPointer(vertexPosRef, 2, gl.FLOAT, false, 0, 0); + + if (this.animationTime){ + + var animationTime = this.animationTime; + var timePassed = 0; + var stepTime = 15; + + var aniFun = function(){ + + timePassed += stepTime; + var mul = ( 1 * timePassed ) / animationTime; + + if (timePassed >= animationTime){ + mul = 1; + }else{ + setTimeout(aniFun, stepTime); + }; + + var neg = -1 * mul; + var pos = 1 * mul; + + var vertexPosBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([pos, pos, neg, pos, pos, neg, neg, neg]), gl.STATIC_DRAW); + + var vertexPosRef = gl.getAttribLocation(program, 'vertexPos'); + gl.enableVertexAttribArray(vertexPosRef); + gl.vertexAttribPointer(vertexPosRef, 2, gl.FLOAT, false, 0, 0); + + try{ + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); + }catch(e){}; + + }; + aniFun(); + + }; + + + + var texturePosBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, texturePosBuffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1, 0, 0, 0, 1, 1, 0, 1]), gl.STATIC_DRAW); + + var texturePosRef = gl.getAttribLocation(program, 'texturePos'); + gl.enableVertexAttribArray(texturePosRef); + gl.vertexAttribPointer(texturePosRef, 2, gl.FLOAT, false, 0, 0); + + this.texturePosBuffer = texturePosBuffer; + + if (this.type === "yuv420"){ + var uTexturePosBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, uTexturePosBuffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1, 0, 0, 0, 1, 1, 0, 1]), gl.STATIC_DRAW); + + var uTexturePosRef = gl.getAttribLocation(program, 'uTexturePos'); + gl.enableVertexAttribArray(uTexturePosRef); + gl.vertexAttribPointer(uTexturePosRef, 2, gl.FLOAT, false, 0, 0); + + this.uTexturePosBuffer = uTexturePosBuffer; + + + var vTexturePosBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vTexturePosBuffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1, 0, 0, 0, 1, 1, 0, 1]), gl.STATIC_DRAW); + + var vTexturePosRef = gl.getAttribLocation(program, 'vTexturePos'); + gl.enableVertexAttribArray(vTexturePosRef); + gl.vertexAttribPointer(vTexturePosRef, 2, gl.FLOAT, false, 0, 0); + + this.vTexturePosBuffer = vTexturePosBuffer; + }; + +}; + +/** + * Initialize GL textures and attach to shader program + */ +YUVCanvas.prototype.initTextures = function() { + var gl = this.contextGL; + var program = this.shaderProgram; + + if (this.type === "yuv420"){ + + var yTextureRef = this.initTexture(); + var ySamplerRef = gl.getUniformLocation(program, 'ySampler'); + gl.uniform1i(ySamplerRef, 0); + this.yTextureRef = yTextureRef; + + var uTextureRef = this.initTexture(); + var uSamplerRef = gl.getUniformLocation(program, 'uSampler'); + gl.uniform1i(uSamplerRef, 1); + this.uTextureRef = uTextureRef; + + var vTextureRef = this.initTexture(); + var vSamplerRef = gl.getUniformLocation(program, 'vSampler'); + gl.uniform1i(vSamplerRef, 2); + this.vTextureRef = vTextureRef; + + }else if (this.type === "yuv422"){ + // only one texture for 422 + var textureRef = this.initTexture(); + var samplerRef = gl.getUniformLocation(program, 'sampler'); + gl.uniform1i(samplerRef, 0); + this.textureRef = textureRef; + + }; +}; + +/** + * Create and configure a single texture + */ +YUVCanvas.prototype.initTexture = function() { + var gl = this.contextGL; + + var textureRef = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, textureRef); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.bindTexture(gl.TEXTURE_2D, null); + + return textureRef; +}; + +/** + * Draw picture data to the canvas. + * If this object is using WebGL, the data must be an I420 formatted ArrayBuffer, + * Otherwise, data must be an RGBA formatted ArrayBuffer. + */ +YUVCanvas.prototype.drawNextOutputPicture = function(width, height, croppingParams, data) { + var gl = this.contextGL; + + if(gl) { + this.drawNextOuptutPictureGL(width, height, croppingParams, data); + } else { + this.drawNextOuptutPictureRGBA(width, height, croppingParams, data); + } +}; + + + +/** + * Draw next output picture using ARGB data on a 2d canvas. + */ +YUVCanvas.prototype.drawNextOuptutPictureRGBA = function(width, height, croppingParams, data) { + var canvas = this.canvasElement; + + var croppingParams = null; + + var argbData = data; + + var ctx = canvas.getContext('2d'); + var imageData = ctx.getImageData(0, 0, width, height); + imageData.data.set(argbData); + + if(croppingParams === null) { + ctx.putImageData(imageData, 0, 0); + } else { + ctx.putImageData(imageData, -croppingParams.left, -croppingParams.top, 0, 0, croppingParams.width, croppingParams.height); + } +}; + + return YUVCanvas; + +})); diff --git a/carsrun/static/avc.wasm b/carsrun/static/avc.wasm new file mode 100644 index 0000000000000000000000000000000000000000..378ac32d9a676a169af5c87ad0ccba9649e02366 GIT binary patch literal 132979 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["thisProgram"]=process["argv"][1].replace(/\\/g,"/")}Module["arguments"]=process["argv"].slice(2);if(typeof module!=="undefined"){module["exports"]=Module}process["on"]("uncaughtException",(function(ex){if(!(ex instanceof ExitStatus)){throw ex}}));process["on"]("unhandledRejection",(function(reason,p){process["exit"](1)}));Module["inspect"]=(function(){return"[Emscripten Module object]"})}else if(ENVIRONMENT_IS_SHELL){if(typeof read!="undefined"){Module["read"]=function shell_read(f){return read(f)}}Module["readBinary"]=function readBinary(f){var data;if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){Module["arguments"]=scriptArgs}else if(typeof arguments!="undefined"){Module["arguments"]=arguments}if(typeof quit==="function"){Module["quit"]=(function(status,toThrow){quit(status)})}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){Module["read"]=function shell_read(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){Module["readBinary"]=function readBinary(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}Module["readAsync"]=function readAsync(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function xhr_onload(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)};Module["setWindowTitle"]=(function(title){document.title=title})}else{throw new Error("not compiled for this environment")}Module["print"]=typeof console!=="undefined"?console.log.bind(console):typeof print!=="undefined"?print:null;Module["printErr"]=typeof printErr!=="undefined"?printErr:typeof console!=="undefined"&&console.warn.bind(console)||Module["print"];Module.print=Module["print"];Module.printErr=Module["printErr"];for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=undefined;var STACK_ALIGN=16;function staticAlloc(size){assert(!staticSealed);var ret=STATICTOP;STATICTOP=STATICTOP+size+15&-16;return ret}function alignMemory(size,factor){if(!factor)factor=STACK_ALIGN;var ret=size=Math.ceil(size/factor)*factor;return ret}var asm2wasmImports={"f64-rem":(function(x,y){return x%y}),"debugger":(function(){debugger})};var functionPointers=new Array(0);var GLOBAL_BASE=1024;var ABORT=0;var EXITSTATUS=0;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}function Pointer_stringify(ptr,length){if(length===0||!ptr)return"";var hasUtf=0;var t;var i=0;while(1){t=HEAPU8[ptr+i>>0];hasUtf|=t;if(t==0&&!length)break;i++;if(length&&i==length)break}if(!length)length=i;var ret="";if(hasUtf<128){var MAX_CHUNK=1024;var curr;while(length>0){curr=String.fromCharCode.apply(String,HEAPU8.subarray(ptr,ptr+Math.min(length,MAX_CHUNK)));ret=ret?ret+curr:curr;ptr+=MAX_CHUNK;length-=MAX_CHUNK}return ret}return UTF8ToString(ptr)}var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf8"):undefined;function UTF8ArrayToString(u8Array,idx){var endPtr=idx;while(u8Array[endPtr])++endPtr;if(endPtr-idx>16&&u8Array.subarray&&UTF8Decoder){return UTF8Decoder.decode(u8Array.subarray(idx,endPtr))}else{var u0,u1,u2,u3,u4,u5;var str="";while(1){u0=u8Array[idx++];if(!u0)return str;if(!(u0&128)){str+=String.fromCharCode(u0);continue}u1=u8Array[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}u2=u8Array[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u3=u8Array[idx++]&63;if((u0&248)==240){u0=(u0&7)<<18|u1<<12|u2<<6|u3}else{u4=u8Array[idx++]&63;if((u0&252)==248){u0=(u0&3)<<24|u1<<18|u2<<12|u3<<6|u4}else{u5=u8Array[idx++]&63;u0=(u0&1)<<30|u1<<24|u2<<18|u3<<12|u4<<6|u5}}}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}}}function UTF8ToString(ptr){return UTF8ArrayToString(HEAPU8,ptr)}var UTF16Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf-16le"):undefined;var WASM_PAGE_SIZE=65536;var ASMJS_PAGE_SIZE=16777216;function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}return x}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBuffer(buf){Module["buffer"]=buffer=buf}function updateGlobalBufferViews(){Module["HEAP8"]=HEAP8=new Int8Array(buffer);Module["HEAP16"]=HEAP16=new Int16Array(buffer);Module["HEAP32"]=HEAP32=new Int32Array(buffer);Module["HEAPU8"]=HEAPU8=new Uint8Array(buffer);Module["HEAPU16"]=HEAPU16=new Uint16Array(buffer);Module["HEAPU32"]=HEAPU32=new Uint32Array(buffer);Module["HEAPF32"]=HEAPF32=new Float32Array(buffer);Module["HEAPF64"]=HEAPF64=new Float64Array(buffer)}var STATIC_BASE,STATICTOP,staticSealed;var STACK_BASE,STACKTOP,STACK_MAX;var DYNAMIC_BASE,DYNAMICTOP_PTR;STATIC_BASE=STATICTOP=STACK_BASE=STACKTOP=STACK_MAX=DYNAMIC_BASE=DYNAMICTOP_PTR=0;staticSealed=false;function abortOnCannotGrowMemory(){abort("Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value "+TOTAL_MEMORY+", (2) compile with -s ALLOW_MEMORY_GROWTH=1 which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with -s ABORTING_MALLOC=0 ")}function enlargeMemory(){abortOnCannotGrowMemory()}var TOTAL_STACK=Module["TOTAL_STACK"]||5242880;var TOTAL_MEMORY=Module["TOTAL_MEMORY"]||52428800;if(TOTAL_MEMORY0){var callback=callbacks.shift();if(typeof callback=="function"){callback();continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){Module["dynCall_v"](func)}else{Module["dynCall_vi"](func,callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATEXIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;var runtimeExited=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function ensureInitRuntime(){if(runtimeInitialized)return;runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function exitRuntime(){callRuntimeCallbacks(__ATEXIT__);runtimeExited=true}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var Math_abs=Math.abs;var Math_cos=Math.cos;var Math_sin=Math.sin;var Math_tan=Math.tan;var Math_acos=Math.acos;var Math_asin=Math.asin;var Math_atan=Math.atan;var Math_atan2=Math.atan2;var Math_exp=Math.exp;var Math_log=Math.log;var Math_sqrt=Math.sqrt;var Math_ceil=Math.ceil;var Math_floor=Math.floor;var Math_pow=Math.pow;var Math_imul=Math.imul;var Math_fround=Math.fround;var Math_round=Math.round;var Math_min=Math.min;var Math_max=Math.max;var Math_clz32=Math.clz32;var Math_trunc=Math.trunc;var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return String.prototype.startsWith?filename.startsWith(dataURIPrefix):filename.indexOf(dataURIPrefix)===0}function integrateWasmJS(){var wasmTextFile="avc.wast";var wasmBinaryFile="avc.wasm";var asmjsCodeFile="avc.temp.asm.js";if(typeof Module["locateFile"]==="function"){if(!isDataURI(wasmTextFile)){wasmTextFile=Module["locateFile"](wasmTextFile)}if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=Module["locateFile"](wasmBinaryFile)}if(!isDataURI(asmjsCodeFile)){asmjsCodeFile=Module["locateFile"](asmjsCodeFile)}}var wasmPageSize=64*1024;var info={"global":null,"env":null,"asm2wasm":asm2wasmImports,"parent":Module};var exports=null;function mergeMemory(newBuffer){var oldBuffer=Module["buffer"];if(newBuffer.byteLength>2];return ret}),getStr:(function(){var ret=Pointer_stringify(SYSCALLS.get());return ret}),get64:(function(){var low=SYSCALLS.get(),high=SYSCALLS.get();if(low>=0)assert(high===0);else assert(high===-1);return low}),getZero:(function(){assert(SYSCALLS.get()===0)})};function ___syscall140(which,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(),offset_high=SYSCALLS.get(),offset_low=SYSCALLS.get(),result=SYSCALLS.get(),whence=SYSCALLS.get();var offset=offset_low;FS.llseek(stream,offset,whence);HEAP32[result>>2]=stream.position;if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___syscall146(which,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.get(),iov=SYSCALLS.get(),iovcnt=SYSCALLS.get();var ret=0;if(!___syscall146.buffers){___syscall146.buffers=[null,[],[]];___syscall146.printChar=(function(stream,curr){var buffer=___syscall146.buffers[stream];assert(buffer);if(curr===0||curr===10){(stream===1?Module["print"]:Module["printErr"])(UTF8ArrayToString(buffer,0));buffer.length=0}else{buffer.push(curr)}})}for(var i=0;i>2];var len=HEAP32[iov+(i*8+4)>>2];for(var j=0;j>2]=value;return value}DYNAMICTOP_PTR=staticAlloc(4);STACK_BASE=STACKTOP=alignMemory(STATICTOP);STACK_MAX=STACK_BASE+TOTAL_STACK;DYNAMIC_BASE=alignMemory(STACK_MAX);HEAP32[DYNAMICTOP_PTR>>2]=DYNAMIC_BASE;staticSealed=true;Module["wasmTableSize"]=10;Module["wasmMaxTableSize"]=10;Module.asmGlobalArg={};Module.asmLibraryArg={"abort":abort,"enlargeMemory":enlargeMemory,"getTotalMemory":getTotalMemory,"abortOnCannotGrowMemory":abortOnCannotGrowMemory,"___setErrNo":___setErrNo,"___syscall140":___syscall140,"___syscall146":___syscall146,"___syscall54":___syscall54,"___syscall6":___syscall6,"_broadwayOnHeadersDecoded":_broadwayOnHeadersDecoded,"_broadwayOnPictureDecoded":_broadwayOnPictureDecoded,"_emscripten_memcpy_big":_emscripten_memcpy_big,"DYNAMICTOP_PTR":DYNAMICTOP_PTR,"STACKTOP":STACKTOP};var asm=Module["asm"](Module.asmGlobalArg,Module.asmLibraryArg,buffer);Module["asm"]=asm;var _broadwayCreateStream=Module["_broadwayCreateStream"]=(function(){return Module["asm"]["_broadwayCreateStream"].apply(null,arguments)});var _broadwayExit=Module["_broadwayExit"]=(function(){return Module["asm"]["_broadwayExit"].apply(null,arguments)});var _broadwayGetMajorVersion=Module["_broadwayGetMajorVersion"]=(function(){return Module["asm"]["_broadwayGetMajorVersion"].apply(null,arguments)});var _broadwayGetMinorVersion=Module["_broadwayGetMinorVersion"]=(function(){return Module["asm"]["_broadwayGetMinorVersion"].apply(null,arguments)});var _broadwayInit=Module["_broadwayInit"]=(function(){return Module["asm"]["_broadwayInit"].apply(null,arguments)});var _broadwayPlayStream=Module["_broadwayPlayStream"]=(function(){return Module["asm"]["_broadwayPlayStream"].apply(null,arguments)});Module["asm"]=asm;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}ExitStatus.prototype=new Error;ExitStatus.prototype.constructor=ExitStatus;var initialStackTop;dependenciesFulfilled=function runCaller(){if(!Module["calledRun"])run();if(!Module["calledRun"])dependenciesFulfilled=runCaller};function run(args){args=args||Module["arguments"];if(runDependencies>0){return}preRun();if(runDependencies>0)return;if(Module["calledRun"])return;function doRun(){if(Module["calledRun"])return;Module["calledRun"]=true;if(ABORT)return;ensureInitRuntime();preMain();if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout((function(){setTimeout((function(){Module["setStatus"]("")}),1);doRun()}),1)}else{doRun()}}Module["run"]=run;function exit(status,implicit){if(implicit&&Module["noExitRuntime"]&&status===0){return}if(Module["noExitRuntime"]){}else{ABORT=true;EXITSTATUS=status;STACKTOP=initialStackTop;exitRuntime();if(Module["onExit"])Module["onExit"](status)}if(ENVIRONMENT_IS_NODE){process["exit"](status)}Module["quit"](status,new ExitStatus(status))}Module["exit"]=exit;function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}if(what!==undefined){Module.print(what);Module.printErr(what);what=JSON.stringify(what)}else{what=""}ABORT=true;EXITSTATUS=1;throw"abort("+what+"). Build with -s ASSERTIONS=1 for more info."}Module["abort"]=abort;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}Module["noExitRuntime"]=true;run() + + + + // return Module; + //})(); + + var resultModule; + if (typeof global !== "undefined"){ + if (global.Module){ + resultModule = global.Module; + }; + }; + if (typeof Module != "undefined"){ + resultModule = Module; + }; + + resultModule._broadwayOnHeadersDecoded = par_broadwayOnHeadersDecoded; + resultModule._broadwayOnPictureDecoded = par_broadwayOnPictureDecoded; + + var moduleIsReady = false; + var cbFun; + var moduleReady = function(){ + moduleIsReady = true; + if (cbFun){ + cbFun(resultModule); + } + }; + + resultModule.onRuntimeInitialized = function(){ + moduleReady(resultModule); + }; + return function(callback){ + if (moduleIsReady){ + callback(resultModule); + }else{ + cbFun = callback; + }; + }; + }; + + return (function(){ + "use strict"; + + + var nowValue = function(){ + return (new Date()).getTime(); + }; + + if (typeof performance != "undefined"){ + if (performance.now){ + nowValue = function(){ + return performance.now(); + }; + }; + }; + + + var Decoder = function(parOptions){ + this.options = parOptions || {}; + + this.now = nowValue; + + var asmInstance; + + var fakeWindow = { + }; + + var toU8Array; + var toU32Array; + + var onPicFun = function ($buffer, width, height) { + var buffer = this.pictureBuffers[$buffer]; + if (!buffer) { + buffer = this.pictureBuffers[$buffer] = toU8Array($buffer, (width * height * 3) / 2); + }; + + var infos; + var doInfo = false; + if (this.infoAr.length){ + doInfo = true; + infos = this.infoAr; + }; + this.infoAr = []; + + if (this.options.rgb){ + if (!asmInstance){ + asmInstance = getAsm(width, height); + }; + asmInstance.inp.set(buffer); + asmInstance.doit(); + + var copyU8 = new Uint8Array(asmInstance.outSize); + copyU8.set( asmInstance.out ); + + if (doInfo){ + infos[0].finishDecoding = nowValue(); + }; + + this.onPictureDecoded(copyU8, width, height, infos); + return; + + }; + + if (doInfo){ + infos[0].finishDecoding = nowValue(); + }; + this.onPictureDecoded(buffer, width, height, infos); + }.bind(this); + + var ignore = false; + + if (this.options.sliceMode){ + onPicFun = function ($buffer, width, height, $sliceInfo) { + if (ignore){ + return; + }; + var buffer = this.pictureBuffers[$buffer]; + if (!buffer) { + buffer = this.pictureBuffers[$buffer] = toU8Array($buffer, (width * height * 3) / 2); + }; + var sliceInfo = this.pictureBuffers[$sliceInfo]; + if (!sliceInfo) { + sliceInfo = this.pictureBuffers[$sliceInfo] = toU32Array($sliceInfo, 18); + }; + + var infos; + var doInfo = false; + if (this.infoAr.length){ + doInfo = true; + infos = this.infoAr; + }; + this.infoAr = []; + + /*if (this.options.rgb){ + + no rgb in slice mode + + };*/ + + infos[0].finishDecoding = nowValue(); + var sliceInfoAr = []; + for (var i = 0; i < 20; ++i){ + sliceInfoAr.push(sliceInfo[i]); + }; + infos[0].sliceInfoAr = sliceInfoAr; + + this.onPictureDecoded(buffer, width, height, infos); + }.bind(this); + }; + + var ModuleCallback = getModule.apply(fakeWindow, [function () { + }, onPicFun]); + + + var MAX_STREAM_BUFFER_LENGTH = 1024 * 1024; + + var instance = this; + this.onPictureDecoded = function (buffer, width, height, infos) { + + }; + + this.onDecoderReady = function(){}; + + var bufferedCalls = []; + this.decode = function decode(typedAr, parInfo, copyDoneFun) { + bufferedCalls.push([typedAr, parInfo, copyDoneFun]); + }; + + ModuleCallback(function(Module){ + var HEAP8 = Module.HEAP8; + var HEAPU8 = Module.HEAPU8; + var HEAP16 = Module.HEAP16; + var HEAP32 = Module.HEAP32; + // from old constructor + Module._broadwayInit(); + + /** + * Creates a typed array from a HEAP8 pointer. + */ + toU8Array = function(ptr, length) { + return HEAPU8.subarray(ptr, ptr + length); + }; + toU32Array = function(ptr, length) { + //var tmp = HEAPU8.subarray(ptr, ptr + (length * 4)); + return new Uint32Array(HEAPU8.buffer, ptr, length); + }; + instance.streamBuffer = toU8Array(Module._broadwayCreateStream(MAX_STREAM_BUFFER_LENGTH), MAX_STREAM_BUFFER_LENGTH); + instance.pictureBuffers = {}; + // collect extra infos that are provided with the nal units + instance.infoAr = []; + + /** + * Decodes a stream buffer. This may be one single (unframed) NAL unit without the + * start code, or a sequence of NAL units with framing start code prefixes. This + * function overwrites stream buffer allocated by the codec with the supplied buffer. + */ + + var sliceNum = 0; + if (instance.options.sliceMode){ + sliceNum = instance.options.sliceNum; + + instance.decode = function decode(typedAr, parInfo, copyDoneFun) { + instance.infoAr.push(parInfo); + parInfo.startDecoding = nowValue(); + var nals = parInfo.nals; + var i; + if (!nals){ + nals = []; + parInfo.nals = nals; + var l = typedAr.length; + var foundSomething = false; + var lastFound = 0; + var lastStart = 0; + for (i = 0; i < l; ++i){ + if (typedAr[i] === 1){ + if ( + typedAr[i - 1] === 0 && + typedAr[i - 2] === 0 + ){ + var startPos = i - 2; + if (typedAr[i - 3] === 0){ + startPos = i - 3; + }; + // its a nal; + if (foundSomething){ + nals.push({ + offset: lastFound, + end: startPos, + type: typedAr[lastStart] & 31 + }); + }; + lastFound = startPos; + lastStart = startPos + 3; + if (typedAr[i - 3] === 0){ + lastStart = startPos + 4; + }; + foundSomething = true; + }; + }; + }; + if (foundSomething){ + nals.push({ + offset: lastFound, + end: i, + type: typedAr[lastStart] & 31 + }); + }; + }; + + var currentSlice = 0; + var playAr; + var offset = 0; + for (i = 0; i < nals.length; ++i){ + if (nals[i].type === 1 || nals[i].type === 5){ + if (currentSlice === sliceNum){ + playAr = typedAr.subarray(nals[i].offset, nals[i].end); + instance.streamBuffer[offset] = 0; + offset += 1; + instance.streamBuffer.set(playAr, offset); + offset += playAr.length; + }; + currentSlice += 1; + }else{ + playAr = typedAr.subarray(nals[i].offset, nals[i].end); + instance.streamBuffer[offset] = 0; + offset += 1; + instance.streamBuffer.set(playAr, offset); + offset += playAr.length; + Module._broadwayPlayStream(offset); + offset = 0; + }; + }; + copyDoneFun(); + Module._broadwayPlayStream(offset); + }; + + }else{ + instance.decode = function decode(typedAr, parInfo) { + // console.info("Decoding: " + buffer.length); + // collect infos + if (parInfo){ + instance.infoAr.push(parInfo); + parInfo.startDecoding = nowValue(); + }; + + instance.streamBuffer.set(typedAr); + Module._broadwayPlayStream(typedAr.length); + }; + }; + + if (bufferedCalls.length){ + var bi = 0; + for (bi = 0; bi < bufferedCalls.length; ++bi){ + instance.decode(bufferedCalls[bi][0], bufferedCalls[bi][1], bufferedCalls[bi][2]); + }; + bufferedCalls = []; + }; + + instance.onDecoderReady(instance); + + }); + + + }; + + + Decoder.prototype = { + + }; + + + + + /* + + asm.js implementation of a yuv to rgb convertor + provided by @soliton4 + + based on + http://www.wordsaretoys.com/2013/10/18/making-yuv-conversion-a-little-faster/ + + */ + + + // factory to create asm.js yuv -> rgb convertor for a given resolution + var asmInstances = {}; + var getAsm = function(parWidth, parHeight){ + var idStr = "" + parWidth + "x" + parHeight; + if (asmInstances[idStr]){ + return asmInstances[idStr]; + }; + + var lumaSize = parWidth * parHeight; + var chromaSize = (lumaSize|0) >> 2; + + var inpSize = lumaSize + chromaSize + chromaSize; + var outSize = parWidth * parHeight * 4; + var cacheSize = Math.pow(2, 24) * 4; + var size = inpSize + outSize + cacheSize; + + var chunkSize = Math.pow(2, 24); + var heapSize = chunkSize; + while (heapSize < size){ + heapSize += chunkSize; + }; + var heap = new ArrayBuffer(heapSize); + + var res = asmFactory(global, {}, heap); + res.init(parWidth, parHeight); + asmInstances[idStr] = res; + + res.heap = heap; + res.out = new Uint8Array(heap, 0, outSize); + res.inp = new Uint8Array(heap, outSize, inpSize); + res.outSize = outSize; + + return res; + }; + + + function asmFactory(stdlib, foreign, heap) { + "use asm"; + + var imul = stdlib.Math.imul; + var min = stdlib.Math.min; + var max = stdlib.Math.max; + var pow = stdlib.Math.pow; + var out = new stdlib.Uint8Array(heap); + var out32 = new stdlib.Uint32Array(heap); + var inp = new stdlib.Uint8Array(heap); + var mem = new stdlib.Uint8Array(heap); + var mem32 = new stdlib.Uint32Array(heap); + + // for double algo + /*var vt = 1.370705; + var gt = 0.698001; + var gt2 = 0.337633; + var bt = 1.732446;*/ + + var width = 0; + var height = 0; + var lumaSize = 0; + var chromaSize = 0; + var inpSize = 0; + var outSize = 0; + + var inpStart = 0; + var outStart = 0; + + var widthFour = 0; + + var cacheStart = 0; + + + function init(parWidth, parHeight){ + parWidth = parWidth|0; + parHeight = parHeight|0; + + var i = 0; + var s = 0; + + width = parWidth; + widthFour = imul(parWidth, 4)|0; + height = parHeight; + lumaSize = imul(width|0, height|0)|0; + chromaSize = (lumaSize|0) >> 2; + outSize = imul(imul(width, height)|0, 4)|0; + inpSize = ((lumaSize + chromaSize)|0 + chromaSize)|0; + + outStart = 0; + inpStart = (outStart + outSize)|0; + cacheStart = (inpStart + inpSize)|0; + + // initializing memory (to be on the safe side) + s = ~~(+pow(+2, +24)); + s = imul(s, 4)|0; + + for (i = 0|0; ((i|0) < (s|0))|0; i = (i + 4)|0){ + mem32[((cacheStart + i)|0) >> 2] = 0; + }; + }; + + function doit(){ + var ystart = 0; + var ustart = 0; + var vstart = 0; + + var y = 0; + var yn = 0; + var u = 0; + var v = 0; + + var o = 0; + + var line = 0; + var col = 0; + + var usave = 0; + var vsave = 0; + + var ostart = 0; + var cacheAdr = 0; + + ostart = outStart|0; + + ystart = inpStart|0; + ustart = (ystart + lumaSize|0)|0; + vstart = (ustart + chromaSize)|0; + + for (line = 0; (line|0) < (height|0); line = (line + 2)|0){ + usave = ustart; + vsave = vstart; + for (col = 0; (col|0) < (width|0); col = (col + 2)|0){ + y = inp[ystart >> 0]|0; + yn = inp[((ystart + width)|0) >> 0]|0; + + u = inp[ustart >> 0]|0; + v = inp[vstart >> 0]|0; + + cacheAdr = (((((y << 16)|0) + ((u << 8)|0))|0) + v)|0; + o = mem32[((cacheStart + cacheAdr)|0) >> 2]|0; + if (o){}else{ + o = yuv2rgbcalc(y,u,v)|0; + mem32[((cacheStart + cacheAdr)|0) >> 2] = o|0; + }; + mem32[ostart >> 2] = o; + + cacheAdr = (((((yn << 16)|0) + ((u << 8)|0))|0) + v)|0; + o = mem32[((cacheStart + cacheAdr)|0) >> 2]|0; + if (o){}else{ + o = yuv2rgbcalc(yn,u,v)|0; + mem32[((cacheStart + cacheAdr)|0) >> 2] = o|0; + }; + mem32[((ostart + widthFour)|0) >> 2] = o; + + //yuv2rgb5(y, u, v, ostart); + //yuv2rgb5(yn, u, v, (ostart + widthFour)|0); + ostart = (ostart + 4)|0; + + // next step only for y. u and v stay the same + ystart = (ystart + 1)|0; + y = inp[ystart >> 0]|0; + yn = inp[((ystart + width)|0) >> 0]|0; + + //yuv2rgb5(y, u, v, ostart); + cacheAdr = (((((y << 16)|0) + ((u << 8)|0))|0) + v)|0; + o = mem32[((cacheStart + cacheAdr)|0) >> 2]|0; + if (o){}else{ + o = yuv2rgbcalc(y,u,v)|0; + mem32[((cacheStart + cacheAdr)|0) >> 2] = o|0; + }; + mem32[ostart >> 2] = o; + + //yuv2rgb5(yn, u, v, (ostart + widthFour)|0); + cacheAdr = (((((yn << 16)|0) + ((u << 8)|0))|0) + v)|0; + o = mem32[((cacheStart + cacheAdr)|0) >> 2]|0; + if (o){}else{ + o = yuv2rgbcalc(yn,u,v)|0; + mem32[((cacheStart + cacheAdr)|0) >> 2] = o|0; + }; + mem32[((ostart + widthFour)|0) >> 2] = o; + ostart = (ostart + 4)|0; + + //all positions inc 1 + + ystart = (ystart + 1)|0; + ustart = (ustart + 1)|0; + vstart = (vstart + 1)|0; + }; + ostart = (ostart + widthFour)|0; + ystart = (ystart + width)|0; + + }; + + }; + + function yuv2rgbcalc(y, u, v){ + y = y|0; + u = u|0; + v = v|0; + + var r = 0; + var g = 0; + var b = 0; + + var o = 0; + + var a0 = 0; + var a1 = 0; + var a2 = 0; + var a3 = 0; + var a4 = 0; + + a0 = imul(1192, (y - 16)|0)|0; + a1 = imul(1634, (v - 128)|0)|0; + a2 = imul(832, (v - 128)|0)|0; + a3 = imul(400, (u - 128)|0)|0; + a4 = imul(2066, (u - 128)|0)|0; + + r = (((a0 + a1)|0) >> 10)|0; + g = (((((a0 - a2)|0) - a3)|0) >> 10)|0; + b = (((a0 + a4)|0) >> 10)|0; + + if ((((r & 255)|0) != (r|0))|0){ + r = min(255, max(0, r|0)|0)|0; + }; + if ((((g & 255)|0) != (g|0))|0){ + g = min(255, max(0, g|0)|0)|0; + }; + if ((((b & 255)|0) != (b|0))|0){ + b = min(255, max(0, b|0)|0)|0; + }; + + o = 255; + o = (o << 8)|0; + o = (o + b)|0; + o = (o << 8)|0; + o = (o + g)|0; + o = (o << 8)|0; + o = (o + r)|0; + + return o|0; + + }; + + + + return { + init: init, + doit: doit + }; + }; + + + /* + potential worker initialization + + */ + + + if (typeof self != "undefined"){ + var isWorker = false; + var decoder; + var reuseMemory = false; + var sliceMode = false; + var sliceNum = 0; + var sliceCnt = 0; + var lastSliceNum = 0; + var sliceInfoAr; + var lastBuf; + var awaiting = 0; + var pile = []; + var startDecoding; + var finishDecoding; + var timeDecoding; + + var memAr = []; + var getMem = function(length){ + if (memAr.length){ + var u = memAr.shift(); + while (u && u.byteLength !== length){ + u = memAr.shift(); + }; + if (u){ + return u; + }; + }; + return new ArrayBuffer(length); + }; + + var copySlice = function(source, target, infoAr, width, height){ + + var length = width * height; + var length4 = length / 4 + var plane2 = length; + var plane3 = length + length4; + + var copy16 = function(parBegin, parEnd){ + var i = 0; + for (i = 0; i < 16; ++i){ + var begin = parBegin + (width * i); + var end = parEnd + (width * i) + target.set(source.subarray(begin, end), begin); + }; + }; + var copy8 = function(parBegin, parEnd){ + var i = 0; + for (i = 0; i < 8; ++i){ + var begin = parBegin + ((width / 2) * i); + var end = parEnd + ((width / 2) * i) + target.set(source.subarray(begin, end), begin); + }; + }; + var copyChunk = function(begin, end){ + target.set(source.subarray(begin, end), begin); + }; + + var begin = infoAr[0]; + var end = infoAr[1]; + if (end > 0){ + copy16(begin, end); + copy8(infoAr[2], infoAr[3]); + copy8(infoAr[4], infoAr[5]); + }; + begin = infoAr[6]; + end = infoAr[7]; + if (end > 0){ + copy16(begin, end); + copy8(infoAr[8], infoAr[9]); + copy8(infoAr[10], infoAr[11]); + }; + + begin = infoAr[12]; + end = infoAr[15]; + if (end > 0){ + copyChunk(begin, end); + copyChunk(infoAr[13], infoAr[16]); + copyChunk(infoAr[14], infoAr[17]); + }; + + }; + + var sliceMsgFun = function(){}; + + var setSliceCnt = function(parSliceCnt){ + sliceCnt = parSliceCnt; + lastSliceNum = sliceCnt - 1; + }; + + + self.addEventListener('message', function(e) { + + if (isWorker){ + if (reuseMemory){ + if (e.data.reuse){ + memAr.push(e.data.reuse); + }; + }; + if (e.data.buf){ + if (sliceMode && awaiting !== 0){ + pile.push(e.data); + }else{ + decoder.decode( + new Uint8Array(e.data.buf, e.data.offset || 0, e.data.length), + e.data.info, + function(){ + if (sliceMode && sliceNum !== lastSliceNum){ + postMessage(e.data, [e.data.buf]); + }; + } + ); + }; + return; + }; + + if (e.data.slice){ + // update ref pic + var copyStart = nowValue(); + copySlice(new Uint8Array(e.data.slice), lastBuf, e.data.infos[0].sliceInfoAr, e.data.width, e.data.height); + // is it the one? then we need to update it + if (e.data.theOne){ + copySlice(lastBuf, new Uint8Array(e.data.slice), sliceInfoAr, e.data.width, e.data.height); + if (timeDecoding > e.data.infos[0].timeDecoding){ + e.data.infos[0].timeDecoding = timeDecoding; + }; + e.data.infos[0].timeCopy += (nowValue() - copyStart); + }; + // move on + postMessage(e.data, [e.data.slice]); + + // next frame in the pipe? + awaiting -= 1; + if (awaiting === 0 && pile.length){ + var data = pile.shift(); + decoder.decode( + new Uint8Array(data.buf, data.offset || 0, data.length), + data.info, + function(){ + if (sliceMode && sliceNum !== lastSliceNum){ + postMessage(data, [data.buf]); + }; + } + ); + }; + return; + }; + + if (e.data.setSliceCnt){ + setSliceCnt(e.data.sliceCnt); + return; + }; + + }else{ + if (e.data && e.data.type === "Broadway.js - Worker init"){ + isWorker = true; + decoder = new Decoder(e.data.options); + + if (e.data.options.sliceMode){ + reuseMemory = true; + sliceMode = true; + sliceNum = e.data.options.sliceNum; + setSliceCnt(e.data.options.sliceCnt); + + decoder.onPictureDecoded = function (buffer, width, height, infos) { + + // buffer needs to be copied because we give up ownership + var copyU8 = new Uint8Array(getMem(buffer.length)); + copySlice(buffer, copyU8, infos[0].sliceInfoAr, width, height); + + startDecoding = infos[0].startDecoding; + finishDecoding = infos[0].finishDecoding; + timeDecoding = finishDecoding - startDecoding; + infos[0].timeDecoding = timeDecoding; + infos[0].timeCopy = 0; + + postMessage({ + slice: copyU8.buffer, + sliceNum: sliceNum, + width: width, + height: height, + infos: infos + }, [copyU8.buffer]); // 2nd parameter is used to indicate transfer of ownership + + awaiting = sliceCnt - 1; + + lastBuf = buffer; + sliceInfoAr = infos[0].sliceInfoAr; + + }; + + }else if (e.data.options.reuseMemory){ + reuseMemory = true; + decoder.onPictureDecoded = function (buffer, width, height, infos) { + + // buffer needs to be copied because we give up ownership + var copyU8 = new Uint8Array(getMem(buffer.length)); + copyU8.set( buffer, 0, buffer.length ); + + postMessage({ + buf: copyU8.buffer, + length: buffer.length, + width: width, + height: height, + infos: infos + }, [copyU8.buffer]); // 2nd parameter is used to indicate transfer of ownership + + }; + + }else{ + decoder.onPictureDecoded = function (buffer, width, height, infos) { + if (buffer) { + buffer = new Uint8Array(buffer); + }; + + // buffer needs to be copied because we give up ownership + var copyU8 = new Uint8Array(buffer.length); + copyU8.set( buffer, 0, buffer.length ); + + postMessage({ + buf: copyU8.buffer, + length: buffer.length, + width: width, + height: height, + infos: infos + }, [copyU8.buffer]); // 2nd parameter is used to indicate transfer of ownership + + }; + }; + postMessage({ consoleLog: "broadway worker initialized" }); + }; + }; + + + }, false); + }; + + Decoder.nowValue = nowValue; + + return Decoder; + + })(); + + +})); + diff --git a/carsrun/templates/Player.js b/carsrun/templates/Player.js new file mode 100644 index 0000000..ff2e649 --- /dev/null +++ b/carsrun/templates/Player.js @@ -0,0 +1,335 @@ +/* + + +usage: + +p = new Player({ + useWorker: , + workerFile: // give path to Decoder.js + webgl: true | false | "auto" // defaults to "auto" +}); + +// canvas property represents the canvas node +// put it somewhere in the dom +p.canvas; + +p.webgl; // contains the used rendering mode. if you pass auto to webgl you can see what auto detection resulted in + +p.decode(); + + +*/ + + + +// universal module definition +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(["./Decoder", "./YUVCanvas"], factory); + } else if (typeof exports === 'object') { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(require("./Decoder"), require("./YUVCanvas")); + } else { + // Browser globals (root is window) + root.Player = factory(root.Decoder, root.YUVCanvas); + } +}(this, function (Decoder, WebGLCanvas) { + "use strict"; + + + var nowValue = Decoder.nowValue; + + + var Player = function(parOptions){ + var self = this; + this._config = parOptions || {}; + + this.render = true; + if (this._config.render === false){ + this.render = false; + }; + + this.nowValue = nowValue; + + this._config.workerFile = this._config.workerFile || "Decoder.js"; + if (this._config.preserveDrawingBuffer){ + this._config.contextOptions = this._config.contextOptions || {}; + this._config.contextOptions.preserveDrawingBuffer = true; + }; + + var webgl = "auto"; + if (this._config.webgl === true){ + webgl = true; + }else if (this._config.webgl === false){ + webgl = false; + }; + + if (webgl == "auto"){ + webgl = true; + try{ + if (!window.WebGLRenderingContext) { + // the browser doesn't even know what WebGL is + webgl = false; + } else { + var canvas = document.createElement('canvas'); + var ctx = canvas.getContext("webgl"); + if (!ctx) { + // browser supports WebGL but initialization failed. + webgl = false; + }; + }; + }catch(e){ + webgl = false; + }; + }; + + this.webgl = webgl; + + // choose functions + if (this.webgl){ + this.createCanvasObj = this.createCanvasWebGL; + this.renderFrame = this.renderFrameWebGL; + }else{ + this.createCanvasObj = this.createCanvasRGB; + this.renderFrame = this.renderFrameRGB; + }; + + + var lastWidth; + var lastHeight; + var onPictureDecoded = function(buffer, width, height, infos) { + self.onPictureDecoded(buffer, width, height, infos); + + var startTime = nowValue(); + + if (!buffer || !self.render) { + return; + }; + + self.renderFrame({ + canvasObj: self.canvasObj, + data: buffer, + width: width, + height: height + }); + + if (self.onRenderFrameComplete){ + self.onRenderFrameComplete({ + data: buffer, + width: width, + height: height, + infos: infos, + canvasObj: self.canvasObj + }); + }; + + }; + + // provide size + + if (!this._config.size){ + this._config.size = {}; + }; + this._config.size.width = this._config.size.width || 200; + this._config.size.height = this._config.size.height || 200; + + if (this._config.useWorker){ + var worker = new Worker(this._config.workerFile); + this.worker = worker; + worker.addEventListener('message', function(e) { + var data = e.data; + if (data.consoleLog){ + console.log(data.consoleLog); + return; + }; + + onPictureDecoded.call(self, new Uint8Array(data.buf, 0, data.length), data.width, data.height, data.infos); + + }, false); + + worker.postMessage({type: "Broadway.js - Worker init", options: { + rgb: !webgl, + memsize: this.memsize, + reuseMemory: this._config.reuseMemory ? true : false + }}); + + if (this._config.transferMemory){ + this.decode = function(parData, parInfo){ + // no copy + // instead we are transfering the ownership of the buffer + // dangerous!!! + + worker.postMessage({buf: parData.buffer, offset: parData.byteOffset, length: parData.length, info: parInfo}, [parData.buffer]); // Send data to our worker. + }; + + }else{ + this.decode = function(parData, parInfo){ + // Copy the sample so that we only do a structured clone of the + // region of interest + var copyU8 = new Uint8Array(parData.length); + copyU8.set( parData, 0, parData.length ); + worker.postMessage({buf: copyU8.buffer, offset: 0, length: parData.length, info: parInfo}, [copyU8.buffer]); // Send data to our worker. + }; + + }; + + if (this._config.reuseMemory){ + this.recycleMemory = function(parArray){ + //this.beforeRecycle(); + worker.postMessage({reuse: parArray.buffer}, [parArray.buffer]); // Send data to our worker. + //this.afterRecycle(); + }; + } + + }else{ + + this.decoder = new Decoder({ + rgb: !webgl + }); + this.decoder.onPictureDecoded = onPictureDecoded; + + this.decode = function(parData, parInfo){ + self.decoder.decode(parData, parInfo); + }; + + }; + + + + if (this.render){ + this.canvasObj = this.createCanvasObj({ + contextOptions: this._config.contextOptions + }); + this.canvas = this.canvasObj.canvas; + }; + + this.domNode = this.canvas; + + lastWidth = this._config.size.width; + lastHeight = this._config.size.height; + + }; + + Player.prototype = { + + onPictureDecoded: function(buffer, width, height, infos){}, + + // call when memory of decoded frames is not used anymore + recycleMemory: function(buf){ + }, + /*beforeRecycle: function(){}, + afterRecycle: function(){},*/ + + // for both functions options is: + // + // width + // height + // enableScreenshot + // + // returns a object that has a property canvas which is a html5 canvas + createCanvasWebGL: function(options){ + var canvasObj = this._createBasicCanvasObj(options); + canvasObj.contextOptions = options.contextOptions; + return canvasObj; + }, + + createCanvasRGB: function(options){ + var canvasObj = this._createBasicCanvasObj(options); + return canvasObj; + }, + + // part that is the same for webGL and RGB + _createBasicCanvasObj: function(options){ + options = options || {}; + + var obj = {}; + var width = options.width; + if (!width){ + width = this._config.size.width; + }; + var height = options.height; + if (!height){ + height = this._config.size.height; + }; + obj.canvas = document.createElement('canvas'); + obj.canvas.width = width; + obj.canvas.height = height; + obj.canvas.style.backgroundColor = "#0D0E1B"; + + + return obj; + }, + + // options: + // + // canvas + // data + renderFrameWebGL: function(options){ + + var canvasObj = options.canvasObj; + + var width = options.width || canvasObj.canvas.width; + var height = options.height || canvasObj.canvas.height; + + if (canvasObj.canvas.width !== width || canvasObj.canvas.height !== height || !canvasObj.webGLCanvas){ + canvasObj.canvas.width = width; + canvasObj.canvas.height = height; + canvasObj.webGLCanvas = new WebGLCanvas({ + canvas: canvasObj.canvas, + contextOptions: canvasObj.contextOptions, + width: width, + height: height + }); + }; + + var ylen = width * height; + var uvlen = (width / 2) * (height / 2); + + canvasObj.webGLCanvas.drawNextOutputPicture({ + yData: options.data.subarray(0, ylen), + uData: options.data.subarray(ylen, ylen + uvlen), + vData: options.data.subarray(ylen + uvlen, ylen + uvlen + uvlen) + }); + + var self = this; + self.recycleMemory(options.data); + + }, + renderFrameRGB: function(options){ + var canvasObj = options.canvasObj; + + var width = options.width || canvasObj.canvas.width; + var height = options.height || canvasObj.canvas.height; + + if (canvasObj.canvas.width !== width || canvasObj.canvas.height !== height){ + canvasObj.canvas.width = width; + canvasObj.canvas.height = height; + }; + + var ctx = canvasObj.ctx; + var imgData = canvasObj.imgData; + + if (!ctx){ + canvasObj.ctx = canvasObj.canvas.getContext('2d'); + ctx = canvasObj.ctx; + + canvasObj.imgData = ctx.createImageData(width, height); + imgData = canvasObj.imgData; + }; + + imgData.data.set(options.data); + ctx.putImageData(imgData, 0, 0); + var self = this; + self.recycleMemory(options.data); + + } + + }; + + return Player; + +})); + diff --git a/carsrun/templates/YUVCanvas.js b/carsrun/templates/YUVCanvas.js new file mode 100644 index 0000000..1bca8a3 --- /dev/null +++ b/carsrun/templates/YUVCanvas.js @@ -0,0 +1,551 @@ +// +// Copyright (c) 2015 Paperspace Co. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// + + +// universal module definition +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define([], factory); + } else if (typeof exports === 'object') { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(); + } else { + // Browser globals (root is window) + root.YUVCanvas = factory(); + } +}(this, function () { + + +/** + * This class can be used to render output pictures from an H264bsdDecoder to a canvas element. + * If available the content is rendered using WebGL. + */ + function YUVCanvas(parOptions) { + + parOptions = parOptions || {}; + + this.canvasElement = parOptions.canvas || document.createElement("canvas"); + this.contextOptions = parOptions.contextOptions; + + this.type = parOptions.type || "yuv420"; + + this.customYUV444 = parOptions.customYUV444; + + this.conversionType = parOptions.conversionType || "rec601"; + + this.width = parOptions.width || 640; + this.height = parOptions.height || 320; + + this.animationTime = parOptions.animationTime || 0; + + this.canvasElement.width = this.width; + this.canvasElement.height = this.height; + + this.initContextGL(); + + if(this.contextGL) { + this.initProgram(); + this.initBuffers(); + this.initTextures(); + }; + + +/** + * Draw the next output picture using WebGL + */ + if (this.type === "yuv420"){ + this.drawNextOuptutPictureGL = function(par) { + var gl = this.contextGL; + var texturePosBuffer = this.texturePosBuffer; + var uTexturePosBuffer = this.uTexturePosBuffer; + var vTexturePosBuffer = this.vTexturePosBuffer; + + var yTextureRef = this.yTextureRef; + var uTextureRef = this.uTextureRef; + var vTextureRef = this.vTextureRef; + + var yData = par.yData; + var uData = par.uData; + var vData = par.vData; + + var width = this.width; + var height = this.height; + + var yDataPerRow = par.yDataPerRow || width; + var yRowCnt = par.yRowCnt || height; + + var uDataPerRow = par.uDataPerRow || (width / 2); + var uRowCnt = par.uRowCnt || (height / 2); + + var vDataPerRow = par.vDataPerRow || uDataPerRow; + var vRowCnt = par.vRowCnt || uRowCnt; + + gl.viewport(0, 0, width, height); + + var tTop = 0; + var tLeft = 0; + var tBottom = height / yRowCnt; + var tRight = width / yDataPerRow; + var texturePosValues = new Float32Array([tRight, tTop, tLeft, tTop, tRight, tBottom, tLeft, tBottom]); + + gl.bindBuffer(gl.ARRAY_BUFFER, texturePosBuffer); + gl.bufferData(gl.ARRAY_BUFFER, texturePosValues, gl.DYNAMIC_DRAW); + + if (this.customYUV444){ + tBottom = height / uRowCnt; + tRight = width / uDataPerRow; + }else{ + tBottom = (height / 2) / uRowCnt; + tRight = (width / 2) / uDataPerRow; + }; + var uTexturePosValues = new Float32Array([tRight, tTop, tLeft, tTop, tRight, tBottom, tLeft, tBottom]); + + gl.bindBuffer(gl.ARRAY_BUFFER, uTexturePosBuffer); + gl.bufferData(gl.ARRAY_BUFFER, uTexturePosValues, gl.DYNAMIC_DRAW); + + + if (this.customYUV444){ + tBottom = height / vRowCnt; + tRight = width / vDataPerRow; + }else{ + tBottom = (height / 2) / vRowCnt; + tRight = (width / 2) / vDataPerRow; + }; + var vTexturePosValues = new Float32Array([tRight, tTop, tLeft, tTop, tRight, tBottom, tLeft, tBottom]); + + gl.bindBuffer(gl.ARRAY_BUFFER, vTexturePosBuffer); + gl.bufferData(gl.ARRAY_BUFFER, vTexturePosValues, gl.DYNAMIC_DRAW); + + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, yTextureRef); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, yDataPerRow, yRowCnt, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, yData); + + gl.activeTexture(gl.TEXTURE1); + gl.bindTexture(gl.TEXTURE_2D, uTextureRef); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, uDataPerRow, uRowCnt, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, uData); + + gl.activeTexture(gl.TEXTURE2); + gl.bindTexture(gl.TEXTURE_2D, vTextureRef); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, vDataPerRow, vRowCnt, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, vData); + + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); + }; + + }else if (this.type === "yuv422"){ + this.drawNextOuptutPictureGL = function(par) { + var gl = this.contextGL; + var texturePosBuffer = this.texturePosBuffer; + + var textureRef = this.textureRef; + + var data = par.data; + + var width = this.width; + var height = this.height; + + var dataPerRow = par.dataPerRow || (width * 2); + var rowCnt = par.rowCnt || height; + + gl.viewport(0, 0, width, height); + + var tTop = 0; + var tLeft = 0; + var tBottom = height / rowCnt; + var tRight = width / (dataPerRow / 2); + var texturePosValues = new Float32Array([tRight, tTop, tLeft, tTop, tRight, tBottom, tLeft, tBottom]); + + gl.bindBuffer(gl.ARRAY_BUFFER, texturePosBuffer); + gl.bufferData(gl.ARRAY_BUFFER, texturePosValues, gl.DYNAMIC_DRAW); + + gl.uniform2f(gl.getUniformLocation(this.shaderProgram, 'resolution'), dataPerRow, height); + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, textureRef); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, dataPerRow, rowCnt, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, data); + + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); + }; + }; + + }; + + /** + * Returns true if the canvas supports WebGL + */ + YUVCanvas.prototype.isWebGL = function() { + return this.contextGL; + }; + + /** + * Create the GL context from the canvas element + */ + YUVCanvas.prototype.initContextGL = function() { + var canvas = this.canvasElement; + var gl = null; + + var validContextNames = ["webgl", "experimental-webgl", "moz-webgl", "webkit-3d"]; + var nameIndex = 0; + + while(!gl && nameIndex < validContextNames.length) { + var contextName = validContextNames[nameIndex]; + + try { + if (this.contextOptions){ + gl = canvas.getContext(contextName, this.contextOptions); + }else{ + gl = canvas.getContext(contextName); + }; + } catch (e) { + gl = null; + } + + if(!gl || typeof gl.getParameter !== "function") { + gl = null; + } + + ++nameIndex; + }; + + this.contextGL = gl; + }; + +/** + * Initialize GL shader program + */ +YUVCanvas.prototype.initProgram = function() { + var gl = this.contextGL; + + // vertex shader is the same for all types + var vertexShaderScript; + var fragmentShaderScript; + + if (this.type === "yuv420"){ + + vertexShaderScript = [ + 'attribute vec4 vertexPos;', + 'attribute vec4 texturePos;', + 'attribute vec4 uTexturePos;', + 'attribute vec4 vTexturePos;', + 'varying vec2 textureCoord;', + 'varying vec2 uTextureCoord;', + 'varying vec2 vTextureCoord;', + + 'void main()', + '{', + ' gl_Position = vertexPos;', + ' textureCoord = texturePos.xy;', + ' uTextureCoord = uTexturePos.xy;', + ' vTextureCoord = vTexturePos.xy;', + '}' + ].join('\n'); + + fragmentShaderScript = [ + 'precision highp float;', + 'varying highp vec2 textureCoord;', + 'varying highp vec2 uTextureCoord;', + 'varying highp vec2 vTextureCoord;', + 'uniform sampler2D ySampler;', + 'uniform sampler2D uSampler;', + 'uniform sampler2D vSampler;', + 'uniform mat4 YUV2RGB;', + + 'void main(void) {', + ' highp float y = texture2D(ySampler, textureCoord).r;', + ' highp float u = texture2D(uSampler, uTextureCoord).r;', + ' highp float v = texture2D(vSampler, vTextureCoord).r;', + ' gl_FragColor = vec4(y, u, v, 1) * YUV2RGB;', + '}' + ].join('\n'); + + }else if (this.type === "yuv422"){ + vertexShaderScript = [ + 'attribute vec4 vertexPos;', + 'attribute vec4 texturePos;', + 'varying vec2 textureCoord;', + + 'void main()', + '{', + ' gl_Position = vertexPos;', + ' textureCoord = texturePos.xy;', + '}' + ].join('\n'); + + fragmentShaderScript = [ + 'precision highp float;', + 'varying highp vec2 textureCoord;', + 'uniform sampler2D sampler;', + 'uniform highp vec2 resolution;', + 'uniform mat4 YUV2RGB;', + + 'void main(void) {', + + ' highp float texPixX = 1.0 / resolution.x;', + ' highp float logPixX = 2.0 / resolution.x;', // half the resolution of the texture + ' highp float logHalfPixX = 4.0 / resolution.x;', // half of the logical resolution so every 4th pixel + ' highp float steps = floor(textureCoord.x / logPixX);', + ' highp float uvSteps = floor(textureCoord.x / logHalfPixX);', + ' highp float y = texture2D(sampler, vec2((logPixX * steps) + texPixX, textureCoord.y)).r;', + ' highp float u = texture2D(sampler, vec2((logHalfPixX * uvSteps), textureCoord.y)).r;', + ' highp float v = texture2D(sampler, vec2((logHalfPixX * uvSteps) + texPixX + texPixX, textureCoord.y)).r;', + + //' highp float y = texture2D(sampler, textureCoord).r;', + //' gl_FragColor = vec4(y, u, v, 1) * YUV2RGB;', + ' gl_FragColor = vec4(y, u, v, 1.0) * YUV2RGB;', + '}' + ].join('\n'); + }; + + var YUV2RGB = []; + + if (this.conversionType == "rec709") { + // ITU-T Rec. 709 + YUV2RGB = [ + 1.16438, 0.00000, 1.79274, -0.97295, + 1.16438, -0.21325, -0.53291, 0.30148, + 1.16438, 2.11240, 0.00000, -1.13340, + 0, 0, 0, 1, + ]; + } else { + // assume ITU-T Rec. 601 + YUV2RGB = [ + 1.16438, 0.00000, 1.59603, -0.87079, + 1.16438, -0.39176, -0.81297, 0.52959, + 1.16438, 2.01723, 0.00000, -1.08139, + 0, 0, 0, 1 + ]; + }; + + var vertexShader = gl.createShader(gl.VERTEX_SHADER); + gl.shaderSource(vertexShader, vertexShaderScript); + gl.compileShader(vertexShader); + if(!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) { + console.log('Vertex shader failed to compile: ' + gl.getShaderInfoLog(vertexShader)); + } + + var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); + gl.shaderSource(fragmentShader, fragmentShaderScript); + gl.compileShader(fragmentShader); + if(!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) { + console.log('Fragment shader failed to compile: ' + gl.getShaderInfoLog(fragmentShader)); + } + + var program = gl.createProgram(); + gl.attachShader(program, vertexShader); + gl.attachShader(program, fragmentShader); + gl.linkProgram(program); + if(!gl.getProgramParameter(program, gl.LINK_STATUS)) { + console.log('Program failed to compile: ' + gl.getProgramInfoLog(program)); + } + + gl.useProgram(program); + + var YUV2RGBRef = gl.getUniformLocation(program, 'YUV2RGB'); + gl.uniformMatrix4fv(YUV2RGBRef, false, YUV2RGB); + + this.shaderProgram = program; +}; + +/** + * Initialize vertex buffers and attach to shader program + */ +YUVCanvas.prototype.initBuffers = function() { + var gl = this.contextGL; + var program = this.shaderProgram; + + var vertexPosBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1, 1, -1, 1, 1, -1, -1, -1]), gl.STATIC_DRAW); + + var vertexPosRef = gl.getAttribLocation(program, 'vertexPos'); + gl.enableVertexAttribArray(vertexPosRef); + gl.vertexAttribPointer(vertexPosRef, 2, gl.FLOAT, false, 0, 0); + + if (this.animationTime){ + + var animationTime = this.animationTime; + var timePassed = 0; + var stepTime = 15; + + var aniFun = function(){ + + timePassed += stepTime; + var mul = ( 1 * timePassed ) / animationTime; + + if (timePassed >= animationTime){ + mul = 1; + }else{ + setTimeout(aniFun, stepTime); + }; + + var neg = -1 * mul; + var pos = 1 * mul; + + var vertexPosBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([pos, pos, neg, pos, pos, neg, neg, neg]), gl.STATIC_DRAW); + + var vertexPosRef = gl.getAttribLocation(program, 'vertexPos'); + gl.enableVertexAttribArray(vertexPosRef); + gl.vertexAttribPointer(vertexPosRef, 2, gl.FLOAT, false, 0, 0); + + try{ + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); + }catch(e){}; + + }; + aniFun(); + + }; + + + + var texturePosBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, texturePosBuffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1, 0, 0, 0, 1, 1, 0, 1]), gl.STATIC_DRAW); + + var texturePosRef = gl.getAttribLocation(program, 'texturePos'); + gl.enableVertexAttribArray(texturePosRef); + gl.vertexAttribPointer(texturePosRef, 2, gl.FLOAT, false, 0, 0); + + this.texturePosBuffer = texturePosBuffer; + + if (this.type === "yuv420"){ + var uTexturePosBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, uTexturePosBuffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1, 0, 0, 0, 1, 1, 0, 1]), gl.STATIC_DRAW); + + var uTexturePosRef = gl.getAttribLocation(program, 'uTexturePos'); + gl.enableVertexAttribArray(uTexturePosRef); + gl.vertexAttribPointer(uTexturePosRef, 2, gl.FLOAT, false, 0, 0); + + this.uTexturePosBuffer = uTexturePosBuffer; + + + var vTexturePosBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vTexturePosBuffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1, 0, 0, 0, 1, 1, 0, 1]), gl.STATIC_DRAW); + + var vTexturePosRef = gl.getAttribLocation(program, 'vTexturePos'); + gl.enableVertexAttribArray(vTexturePosRef); + gl.vertexAttribPointer(vTexturePosRef, 2, gl.FLOAT, false, 0, 0); + + this.vTexturePosBuffer = vTexturePosBuffer; + }; + +}; + +/** + * Initialize GL textures and attach to shader program + */ +YUVCanvas.prototype.initTextures = function() { + var gl = this.contextGL; + var program = this.shaderProgram; + + if (this.type === "yuv420"){ + + var yTextureRef = this.initTexture(); + var ySamplerRef = gl.getUniformLocation(program, 'ySampler'); + gl.uniform1i(ySamplerRef, 0); + this.yTextureRef = yTextureRef; + + var uTextureRef = this.initTexture(); + var uSamplerRef = gl.getUniformLocation(program, 'uSampler'); + gl.uniform1i(uSamplerRef, 1); + this.uTextureRef = uTextureRef; + + var vTextureRef = this.initTexture(); + var vSamplerRef = gl.getUniformLocation(program, 'vSampler'); + gl.uniform1i(vSamplerRef, 2); + this.vTextureRef = vTextureRef; + + }else if (this.type === "yuv422"){ + // only one texture for 422 + var textureRef = this.initTexture(); + var samplerRef = gl.getUniformLocation(program, 'sampler'); + gl.uniform1i(samplerRef, 0); + this.textureRef = textureRef; + + }; +}; + +/** + * Create and configure a single texture + */ +YUVCanvas.prototype.initTexture = function() { + var gl = this.contextGL; + + var textureRef = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, textureRef); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.bindTexture(gl.TEXTURE_2D, null); + + return textureRef; +}; + +/** + * Draw picture data to the canvas. + * If this object is using WebGL, the data must be an I420 formatted ArrayBuffer, + * Otherwise, data must be an RGBA formatted ArrayBuffer. + */ +YUVCanvas.prototype.drawNextOutputPicture = function(width, height, croppingParams, data) { + var gl = this.contextGL; + + if(gl) { + this.drawNextOuptutPictureGL(width, height, croppingParams, data); + } else { + this.drawNextOuptutPictureRGBA(width, height, croppingParams, data); + } +}; + + + +/** + * Draw next output picture using ARGB data on a 2d canvas. + */ +YUVCanvas.prototype.drawNextOuptutPictureRGBA = function(width, height, croppingParams, data) { + var canvas = this.canvasElement; + + var croppingParams = null; + + var argbData = data; + + var ctx = canvas.getContext('2d'); + var imageData = ctx.getImageData(0, 0, width, height); + imageData.data.set(argbData); + + if(croppingParams === null) { + ctx.putImageData(imageData, 0, 0); + } else { + ctx.putImageData(imageData, -croppingParams.left, -croppingParams.top, 0, 0, croppingParams.width, croppingParams.height); + } +}; + + return YUVCanvas; + +})); diff --git a/carsrun/templates/avc.wasm b/carsrun/templates/avc.wasm new file mode 100644 index 0000000000000000000000000000000000000000..378ac32d9a676a169af5c87ad0ccba9649e02366 GIT binary patch literal 132979 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 + + + + + + + Live Video Based on Flask + + + + + +

小车控制界面

+ + 小车控制 +
+ +
+ +
+
+ +
+ +
+ + + +
+
+ +
+
+ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/carsrun/templates/index.html b/carsrun/templates/index.html new file mode 100644 index 0000000..385df2a --- /dev/null +++ b/carsrun/templates/index.html @@ -0,0 +1,187 @@ + + + + + 小车拍摄画面 + + + + + + +

MJRoBot Lab Live Streaming

+
+ + +
+
+

小车控制界面

+ + 小车控制 +
+ +
+ +
+
+ + + +
+
+ +
+
+ +
+
+ + + + + + + + + + + + + + + diff --git a/carsrun/templates/index2.html b/carsrun/templates/index2.html new file mode 100644 index 0000000..1694c9f --- /dev/null +++ b/carsrun/templates/index2.html @@ -0,0 +1,19 @@ + + + + + 小车拍摄画面 + + + +

MJRoBot Lab Live Streaming

+

+
+

小车控制界面

+ + 小车控制 + + diff --git a/carsrun/templates/index3.html b/carsrun/templates/index3.html new file mode 100644 index 0000000..bcb2b84 --- /dev/null +++ b/carsrun/templates/index3.html @@ -0,0 +1,34 @@ + + + + +PiCamera H264 Streaming + + +

PiCamera H264 Streaming

+
+ + + + + + diff --git a/carsrun/templates/style.css b/carsrun/templates/style.css new file mode 100644 index 0000000..6030dec --- /dev/null +++ b/carsrun/templates/style.css @@ -0,0 +1,9 @@ + + +body{ + background: white; + color: black; + padding:1%; + text-align: center; +} +