fix many
This commit is contained in:
parent
8427211b78
commit
d5ebfb4b8a
@ -19,7 +19,11 @@ DOC = BASE / 'doc'
|
|||||||
CONFIG = BASE / 'config'
|
CONFIG = BASE / 'config'
|
||||||
DS.mkdir(exist_ok=True, parents=True)
|
DS.mkdir(exist_ok=True, parents=True)
|
||||||
DOC.mkdir(exist_ok=True, parents=True)
|
DOC.mkdir(exist_ok=True, parents=True)
|
||||||
CONFIG.mkdir(exist_ok=True, parents=True)
|
DEVICE_CONFIG = CONFIG / 'device'
|
||||||
|
IMAGING_CONFIG = CONFIG / 'imaging'
|
||||||
|
DEVICE_CONFIG.mkdir(exist_ok=True, parents=True)
|
||||||
|
IMAGING_CONFIG.mkdir(exist_ok=True, parents=True)
|
||||||
|
# CONFIG.mkdir(exist_ok=True, parents=True)
|
||||||
|
|
||||||
CONFIG_FOLDER = BASE / 'config'
|
CONFIG_FOLDER = BASE / 'config'
|
||||||
LAST_CONFIG = BASE / 'config' / 'last_imaging_config.json'
|
LAST_CONFIG = BASE / 'config' / 'last_imaging_config.json'
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
import struct
|
import struct
|
||||||
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import cupy as cp
|
import cupy as cp
|
||||||
@ -7,10 +8,10 @@ import cv2
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import zmq
|
import zmq
|
||||||
|
|
||||||
from config import PLAYBACK_SOCKET, VIDEO_WIDTH, VIDEO_HEIGHT, LIVE_SOCKET
|
from config import PLAYBACK_SOCKET, VIDEO_WIDTH, VIDEO_HEIGHT, LIVE_SOCKET, IMAGING_CONFIG
|
||||||
from nodes.Node import Node
|
from nodes.Node import Node
|
||||||
from utils.Msg import BMMsg, ImageArgMsg, KillMsg, SetSeqMetaMsg, SetPlayMode, SetDeviceConfigMsg, SetRecordMsg, \
|
from utils.Msg import BMMsg, ImageArgMsg, KillMsg, SetSeqMetaMsg, SetPlayMode, SetDeviceConfigMsg, SetRecordMsg, \
|
||||||
RecordFrameMsg
|
RecordFrameMsg, ImagingConfigNameListMsg
|
||||||
from utils.RfFile import RfFrame, RfSequenceMeta
|
from utils.RfFile import RfFrame, RfSequenceMeta
|
||||||
from utils.RfMat import RfMat
|
from utils.RfMat import RfMat
|
||||||
from utils.RfMeta import RfFrameMeta
|
from utils.RfMeta import RfFrameMeta
|
||||||
@ -60,6 +61,8 @@ class Beamformer(Node):
|
|||||||
device_socket.setsockopt(zmq.CONFLATE, 1)
|
device_socket.setsockopt(zmq.CONFLATE, 1)
|
||||||
self.c.poller.register(device_socket, zmq.POLLIN)
|
self.c.poller.register(device_socket, zmq.POLLIN)
|
||||||
mat = None
|
mat = None
|
||||||
|
time.sleep(1)
|
||||||
|
self.send(ImagingConfigNameListMsg([path.stem for path in IMAGING_CONFIG.glob('*.json')]))
|
||||||
while True:
|
while True:
|
||||||
socks = dict(self.c.poller.poll())
|
socks = dict(self.c.poller.poll())
|
||||||
if device_socket in socks and socks[device_socket] == zmq.POLLIN:
|
if device_socket in socks and socks[device_socket] == zmq.POLLIN:
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import time
|
|||||||
|
|
||||||
import zmq
|
import zmq
|
||||||
|
|
||||||
from config import LIVE_REP_SOCKET, CONFIG
|
from config import LIVE_REP_SOCKET, CONFIG, DEVICE_CONFIG
|
||||||
from nodes.Node import Node
|
from nodes.Node import Node
|
||||||
from utils.Msg import ImageArgMsg, KillMsg, SetDeviceConnectedMsg, SetDeviceEnabledMsg, DeviceEnabledMsg, \
|
from utils.Msg import ImageArgMsg, KillMsg, SetDeviceConnectedMsg, SetDeviceEnabledMsg, DeviceEnabledMsg, \
|
||||||
DeviceConnectedMsg, SetDeviceConfigMsg, DeviceOnlineMsg, DeviceConfigListMsg
|
DeviceConnectedMsg, SetDeviceConfigMsg, DeviceOnlineMsg, DeviceConfigListMsg
|
||||||
@ -132,7 +132,7 @@ class Device(Node):
|
|||||||
if self.online():
|
if self.online():
|
||||||
self.connected()
|
self.connected()
|
||||||
self.enabled()
|
self.enabled()
|
||||||
for f in CONFIG.glob('*.txt'):
|
for f in DEVICE_CONFIG.glob('*.txt'):
|
||||||
arr.append((f.stem, f.read_text()))
|
arr.append((f.stem, f.read_text()))
|
||||||
self.send(DeviceConfigListMsg(arr))
|
self.send(DeviceConfigListMsg(arr))
|
||||||
# if arr.__len__() > 0:
|
# if arr.__len__() > 0:
|
||||||
|
|||||||
58
src/nodes/ImageFFMPEG.py
Normal file
58
src/nodes/ImageFFMPEG.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import logging
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import zmq
|
||||||
|
|
||||||
|
from BusClient import BusClient
|
||||||
|
from config import VIDEO_HEIGHT, VIDEO_WIDTH
|
||||||
|
from nodes.Node import Node
|
||||||
|
from utils.Msg import BMMsg, SetWindowVisibleMsg, Msg
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ImageFFMPEG(Node):
|
||||||
|
topics = [BMMsg, SetWindowVisibleMsg]
|
||||||
|
|
||||||
|
def __init__(self, level=logging.INFO):
|
||||||
|
super().__init__(level=level)
|
||||||
|
self.buffer = np.zeros((VIDEO_HEIGHT, VIDEO_WIDTH, 3), dtype=np.uint8) + 128
|
||||||
|
self.buffer = self.buffer.tobytes()
|
||||||
|
|
||||||
|
def loop(self):
|
||||||
|
self.c = BusClient(BMMsg, pub=False, conflare=True, poller=True)
|
||||||
|
p = subprocess.Popen(['ffmpeg',
|
||||||
|
'-f', 'rawvideo',
|
||||||
|
'-pixel_format', 'rgb24',
|
||||||
|
'-video_size', '1080x1920',
|
||||||
|
'-framerate', '60',
|
||||||
|
'-hwaccel', 'nvdec',
|
||||||
|
'-i', '-',
|
||||||
|
'-vcodec', 'h264_nvenc',
|
||||||
|
'-preset', 'faster',
|
||||||
|
'-pix_fmt', 'yuv420p',
|
||||||
|
# '-vcodec', 'libx264',
|
||||||
|
'-b:v', '40M',
|
||||||
|
'-f', 'flv',
|
||||||
|
'rtmp://localhost/live/livestream'
|
||||||
|
# '-f', 'mpegts',
|
||||||
|
# 'srt://localhost:10080?streamid=#!::r=live/livestream,m=publish'
|
||||||
|
],
|
||||||
|
stdin=subprocess.PIPE,
|
||||||
|
)
|
||||||
|
while True:
|
||||||
|
# socks = dict(self.c.poller.poll(1 / 30))
|
||||||
|
events = self.c.poller.poll(1)
|
||||||
|
if events:
|
||||||
|
msg: BMMsg = Msg.decode_msg(events[0][0].recv())
|
||||||
|
b = np.frombuffer(msg.data, dtype=np.uint8)
|
||||||
|
b = np.reshape(b, (VIDEO_HEIGHT, VIDEO_WIDTH, 4))[:, :, :3]
|
||||||
|
self.buffer = b.tobytes()
|
||||||
|
p.stdin.write(self.buffer)
|
||||||
|
time.sleep(1 / 60)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
ImageFFMPEG()()
|
||||||
@ -5,19 +5,18 @@ import traceback
|
|||||||
from enum import Enum, auto
|
from enum import Enum, auto
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from PyQt6 import QtCore
|
from PyQt6 import QtCore, QtWidgets
|
||||||
from PyQt6 import QtGui
|
|
||||||
from PyQt6.QtCore import QByteArray
|
from PyQt6.QtCore import QByteArray
|
||||||
from PyQt6.QtWidgets import QMainWindow, QApplication, QFrame, QMessageBox, QFileDialog
|
from PyQt6.QtWidgets import QMainWindow, QApplication, QFrame, QMessageBox, QFileDialog, QLineEdit
|
||||||
|
|
||||||
from Main import Ui_MainWindow
|
from Main import Ui_MainWindow
|
||||||
from ZMQReceiver import ZMQReceiver
|
from ZMQReceiver import ZMQReceiver
|
||||||
from config import DS, SOFTWARE_CONFIG
|
from config import DS, SOFTWARE_CONFIG, IMAGING_CONFIG
|
||||||
from nodes.Node import Node
|
from nodes.Node import Node
|
||||||
from utils.Msg import KillMsg, Msg, ImageArgMsg, SeqIdMinMax, MoveAxisMsg, SeqListMsg, SetBaseMsg, \
|
from utils.Msg import KillMsg, Msg, ImageArgMsg, SeqIdMinMax, MoveAxisMsg, SeqListMsg, SetBaseMsg, \
|
||||||
SetSeqMetaMsg, SetPlayMode, DeviceConnectedMsg, DeviceEnabledMsg, DeviceOnlineMsg, SetDeviceEnabledMsg, \
|
SetSeqMetaMsg, SetPlayMode, DeviceConnectedMsg, DeviceEnabledMsg, DeviceOnlineMsg, SetDeviceEnabledMsg, \
|
||||||
SetDeviceConnectedMsg, DeviceConfigListMsg, SetDeviceConfigMsg, SetRecordMsg, RobotRtsiMsg, RecordFrameMsg, \
|
SetDeviceConnectedMsg, DeviceConfigListMsg, SetDeviceConfigMsg, SetRecordMsg, RobotRtsiMsg, RecordFrameMsg, \
|
||||||
SeqIdList, SetWindowVisibleMsg, SetSidMsg
|
SeqIdList, SetWindowVisibleMsg, SetSidMsg, ImagingConfigNameListMsg
|
||||||
from utils.RfMeta import RfSequenceMeta
|
from utils.RfMeta import RfSequenceMeta
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -95,6 +94,22 @@ class Adv(QMainWindow, Ui_MainWindow):
|
|||||||
# self.cb_bscan.checkStateChanged.connect(lambda e: print(e == 2, flush=True))
|
# self.cb_bscan.checkStateChanged.connect(lambda e: print(e == 2, flush=True))
|
||||||
# self.cb_bscan.checkStateChanged.connect(lambda e: print(e.name, flush=True))
|
# self.cb_bscan.checkStateChanged.connect(lambda e: print(e.name, flush=True))
|
||||||
|
|
||||||
|
self.b_new_imaging_config.clicked.connect(self.on_new_imaging_config)
|
||||||
|
self.c_imaging_config.currentIndexChanged.connect(self.on_imaging_config)
|
||||||
|
|
||||||
|
def on_imaging_config(self, i):
|
||||||
|
name = self.c_imaging_config.itemText(i)
|
||||||
|
self.p.send(ImageArgMsg.from_path(IMAGING_CONFIG / f'{name}.json'))
|
||||||
|
|
||||||
|
def on_new_imaging_config(self):
|
||||||
|
text, okPressed = QtWidgets.QInputDialog.getText(None,
|
||||||
|
"Set New Imaging Config Name",
|
||||||
|
"Config Name:",
|
||||||
|
QLineEdit.EchoMode.Normal,
|
||||||
|
"")
|
||||||
|
if okPressed and text != '':
|
||||||
|
(IMAGING_CONFIG / f'{text}.json').write_text(self.arg.json())
|
||||||
|
|
||||||
def on_select_base(self):
|
def on_select_base(self):
|
||||||
base = QFileDialog.getExistingDirectory(self, 'Select Base Folder', DS.__str__())
|
base = QFileDialog.getExistingDirectory(self, 'Select Base Folder', DS.__str__())
|
||||||
self.l_base.setText(Path(base).__str__())
|
self.l_base.setText(Path(base).__str__())
|
||||||
@ -256,6 +271,8 @@ class Adv(QMainWindow, Ui_MainWindow):
|
|||||||
if msg.name == '':
|
if msg.name == '':
|
||||||
self.close()
|
self.close()
|
||||||
elif isinstance(msg, ImageArgMsg):
|
elif isinstance(msg, ImageArgMsg):
|
||||||
|
self.arg = msg
|
||||||
|
self.arg.sender = 'ui'
|
||||||
self.s_t_start.setValue(msg.t_start)
|
self.s_t_start.setValue(msg.t_start)
|
||||||
self.s_t_end.setValue(msg.t_end)
|
self.s_t_end.setValue(msg.t_end)
|
||||||
elif isinstance(msg, MoveAxisMsg):
|
elif isinstance(msg, MoveAxisMsg):
|
||||||
@ -324,6 +341,11 @@ class Adv(QMainWindow, Ui_MainWindow):
|
|||||||
elif isinstance(msg, SetWindowVisibleMsg):
|
elif isinstance(msg, SetWindowVisibleMsg):
|
||||||
if msg.name == 'bscan' and msg.sender != 'ui':
|
if msg.name == 'bscan' and msg.sender != 'ui':
|
||||||
self.cb_bscan.setChecked(msg.value)
|
self.cb_bscan.setChecked(msg.value)
|
||||||
|
elif isinstance(msg, ImagingConfigNameListMsg):
|
||||||
|
print(msg, flush=True)
|
||||||
|
self.c_imaging_config.clear()
|
||||||
|
for name in msg.value:
|
||||||
|
self.c_imaging_config.addItem(name)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
traceback.print_exception(e)
|
traceback.print_exception(e)
|
||||||
@ -377,6 +399,7 @@ class Adv(QMainWindow, Ui_MainWindow):
|
|||||||
|
|
||||||
class MainUI(Node):
|
class MainUI(Node):
|
||||||
topics = [ImageArgMsg, SeqIdMinMax, MoveAxisMsg,
|
topics = [ImageArgMsg, SeqIdMinMax, MoveAxisMsg,
|
||||||
|
ImagingConfigNameListMsg,
|
||||||
SeqListMsg, SetSeqMetaMsg, SeqIdList, SetWindowVisibleMsg, SetSidMsg,
|
SeqListMsg, SetSeqMetaMsg, SeqIdList, SetWindowVisibleMsg, SetSidMsg,
|
||||||
DeviceConnectedMsg, DeviceEnabledMsg, DeviceOnlineMsg, DeviceConfigListMsg,
|
DeviceConnectedMsg, DeviceEnabledMsg, DeviceOnlineMsg, DeviceConfigListMsg,
|
||||||
RobotRtsiMsg,
|
RobotRtsiMsg,
|
||||||
|
|||||||
@ -12,10 +12,11 @@ class Node:
|
|||||||
bp = BusClient.bp
|
bp = BusClient.bp
|
||||||
topics = []
|
topics = []
|
||||||
|
|
||||||
def __init__(self, enable_init=True, level=logging.INFO):
|
def __init__(self, enable_init=True, level=logging.INFO, conflare=False):
|
||||||
self.enable_init = enable_init
|
self.enable_init = enable_init
|
||||||
self.isalive = True
|
self.isalive = True
|
||||||
self.level = level
|
self.level = level
|
||||||
|
self.conflare = conflare
|
||||||
|
|
||||||
def recv(self):
|
def recv(self):
|
||||||
return self.c.recv()
|
return self.c.recv()
|
||||||
@ -33,6 +34,6 @@ class Node:
|
|||||||
logging.basicConfig(level=self.level, format=FORMAT)
|
logging.basicConfig(level=self.level, format=FORMAT)
|
||||||
self.context = zmq.Context()
|
self.context = zmq.Context()
|
||||||
if self.enable_init:
|
if self.enable_init:
|
||||||
self.c = BusClient(*([KillMsg] + self.topics), poller=True)
|
self.c = BusClient(*([KillMsg] + self.topics), poller=True, conflare=self.conflare)
|
||||||
self.loop()
|
self.loop()
|
||||||
print(self.__class__.__name__, 'exiting')
|
print(self.__class__.__name__, 'exiting')
|
||||||
|
|||||||
@ -12,7 +12,7 @@ from PyQt6 import QtCore, QtGui, QtWidgets
|
|||||||
class Ui_MainWindow(object):
|
class Ui_MainWindow(object):
|
||||||
def setupUi(self, MainWindow):
|
def setupUi(self, MainWindow):
|
||||||
MainWindow.setObjectName("MainWindow")
|
MainWindow.setObjectName("MainWindow")
|
||||||
MainWindow.resize(936, 732)
|
MainWindow.resize(1158, 805)
|
||||||
self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
|
self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
|
||||||
self.centralwidget.setObjectName("centralwidget")
|
self.centralwidget.setObjectName("centralwidget")
|
||||||
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
|
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
|
||||||
@ -182,31 +182,24 @@ class Ui_MainWindow(object):
|
|||||||
self.gridLayout.addLayout(self.gridLayout_4, 2, 1, 1, 1)
|
self.gridLayout.addLayout(self.gridLayout_4, 2, 1, 1, 1)
|
||||||
self.gridLayout_5 = QtWidgets.QGridLayout()
|
self.gridLayout_5 = QtWidgets.QGridLayout()
|
||||||
self.gridLayout_5.setObjectName("gridLayout_5")
|
self.gridLayout_5.setObjectName("gridLayout_5")
|
||||||
self.label_5 = QtWidgets.QLabel(parent=self.centralwidget)
|
self.spinBox_3 = QtWidgets.QSpinBox(parent=self.centralwidget)
|
||||||
self.label_5.setObjectName("label_5")
|
self.spinBox_3.setObjectName("spinBox_3")
|
||||||
self.gridLayout_5.addWidget(self.label_5, 2, 0, 1, 1)
|
self.gridLayout_5.addWidget(self.spinBox_3, 6, 2, 1, 1)
|
||||||
self.label_7 = QtWidgets.QLabel(parent=self.centralwidget)
|
self.label_7 = QtWidgets.QLabel(parent=self.centralwidget)
|
||||||
self.label_7.setObjectName("label_7")
|
self.label_7.setObjectName("label_7")
|
||||||
self.gridLayout_5.addWidget(self.label_7, 4, 0, 1, 1)
|
self.gridLayout_5.addWidget(self.label_7, 6, 0, 1, 1)
|
||||||
self.s_t_start = QtWidgets.QSlider(parent=self.centralwidget)
|
self.spinBox_12 = QtWidgets.QSpinBox(parent=self.centralwidget)
|
||||||
self.s_t_start.setOrientation(QtCore.Qt.Orientation.Horizontal)
|
self.spinBox_12.setObjectName("spinBox_12")
|
||||||
self.s_t_start.setObjectName("s_t_start")
|
self.gridLayout_5.addWidget(self.spinBox_12, 3, 2, 1, 1)
|
||||||
self.gridLayout_5.addWidget(self.s_t_start, 1, 1, 1, 1)
|
self.spinBox_2 = QtWidgets.QSpinBox(parent=self.centralwidget)
|
||||||
|
self.spinBox_2.setObjectName("spinBox_2")
|
||||||
|
self.gridLayout_5.addWidget(self.spinBox_2, 5, 2, 1, 1)
|
||||||
self.horizontalSlider_2 = QtWidgets.QSlider(parent=self.centralwidget)
|
self.horizontalSlider_2 = QtWidgets.QSlider(parent=self.centralwidget)
|
||||||
self.horizontalSlider_2.setOrientation(QtCore.Qt.Orientation.Horizontal)
|
self.horizontalSlider_2.setOrientation(QtCore.Qt.Orientation.Horizontal)
|
||||||
self.horizontalSlider_2.setObjectName("horizontalSlider_2")
|
self.horizontalSlider_2.setObjectName("horizontalSlider_2")
|
||||||
self.gridLayout_5.addWidget(self.horizontalSlider_2, 3, 1, 1, 1)
|
self.gridLayout_5.addWidget(self.horizontalSlider_2, 5, 1, 1, 1)
|
||||||
self.spinBox_12 = QtWidgets.QSpinBox(parent=self.centralwidget)
|
|
||||||
self.spinBox_12.setObjectName("spinBox_12")
|
|
||||||
self.gridLayout_5.addWidget(self.spinBox_12, 1, 2, 1, 1)
|
|
||||||
spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding)
|
spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding)
|
||||||
self.gridLayout_5.addItem(spacerItem1, 5, 1, 1, 1)
|
self.gridLayout_5.addItem(spacerItem1, 7, 1, 1, 1)
|
||||||
self.spinBox_2 = QtWidgets.QSpinBox(parent=self.centralwidget)
|
|
||||||
self.spinBox_2.setObjectName("spinBox_2")
|
|
||||||
self.gridLayout_5.addWidget(self.spinBox_2, 3, 2, 1, 1)
|
|
||||||
self.spinBox_3 = QtWidgets.QSpinBox(parent=self.centralwidget)
|
|
||||||
self.spinBox_3.setObjectName("spinBox_3")
|
|
||||||
self.gridLayout_5.addWidget(self.spinBox_3, 4, 2, 1, 1)
|
|
||||||
self.label_2 = QtWidgets.QLabel(parent=self.centralwidget)
|
self.label_2 = QtWidgets.QLabel(parent=self.centralwidget)
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
font.setPointSize(20)
|
font.setPointSize(20)
|
||||||
@ -215,25 +208,45 @@ class Ui_MainWindow(object):
|
|||||||
self.label_2.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
|
self.label_2.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
|
||||||
self.label_2.setObjectName("label_2")
|
self.label_2.setObjectName("label_2")
|
||||||
self.gridLayout_5.addWidget(self.label_2, 0, 0, 1, 3)
|
self.gridLayout_5.addWidget(self.label_2, 0, 0, 1, 3)
|
||||||
self.spinBox = QtWidgets.QSpinBox(parent=self.centralwidget)
|
|
||||||
self.spinBox.setObjectName("spinBox")
|
|
||||||
self.gridLayout_5.addWidget(self.spinBox, 2, 2, 1, 1)
|
|
||||||
self.s_t_end = QtWidgets.QSlider(parent=self.centralwidget)
|
self.s_t_end = QtWidgets.QSlider(parent=self.centralwidget)
|
||||||
self.s_t_end.setMinimum(1)
|
self.s_t_end.setMinimum(1)
|
||||||
self.s_t_end.setMaximum(1500)
|
self.s_t_end.setMaximum(1500)
|
||||||
self.s_t_end.setOrientation(QtCore.Qt.Orientation.Horizontal)
|
self.s_t_end.setOrientation(QtCore.Qt.Orientation.Horizontal)
|
||||||
self.s_t_end.setObjectName("s_t_end")
|
self.s_t_end.setObjectName("s_t_end")
|
||||||
self.gridLayout_5.addWidget(self.s_t_end, 2, 1, 1, 1)
|
self.gridLayout_5.addWidget(self.s_t_end, 4, 1, 1, 1)
|
||||||
|
self.label_6 = QtWidgets.QLabel(parent=self.centralwidget)
|
||||||
|
self.label_6.setObjectName("label_6")
|
||||||
|
self.gridLayout_5.addWidget(self.label_6, 5, 0, 1, 1)
|
||||||
|
self.spinBox = QtWidgets.QSpinBox(parent=self.centralwidget)
|
||||||
|
self.spinBox.setObjectName("spinBox")
|
||||||
|
self.gridLayout_5.addWidget(self.spinBox, 4, 2, 1, 1)
|
||||||
self.label_15 = QtWidgets.QLabel(parent=self.centralwidget)
|
self.label_15 = QtWidgets.QLabel(parent=self.centralwidget)
|
||||||
self.label_15.setObjectName("label_15")
|
self.label_15.setObjectName("label_15")
|
||||||
self.gridLayout_5.addWidget(self.label_15, 1, 0, 1, 1)
|
self.gridLayout_5.addWidget(self.label_15, 3, 0, 1, 1)
|
||||||
|
self.s_t_start = QtWidgets.QSlider(parent=self.centralwidget)
|
||||||
|
self.s_t_start.setOrientation(QtCore.Qt.Orientation.Horizontal)
|
||||||
|
self.s_t_start.setObjectName("s_t_start")
|
||||||
|
self.gridLayout_5.addWidget(self.s_t_start, 3, 1, 1, 1)
|
||||||
|
self.label_5 = QtWidgets.QLabel(parent=self.centralwidget)
|
||||||
|
self.label_5.setObjectName("label_5")
|
||||||
|
self.gridLayout_5.addWidget(self.label_5, 4, 0, 1, 1)
|
||||||
self.horizontalSlider_4 = QtWidgets.QSlider(parent=self.centralwidget)
|
self.horizontalSlider_4 = QtWidgets.QSlider(parent=self.centralwidget)
|
||||||
self.horizontalSlider_4.setOrientation(QtCore.Qt.Orientation.Horizontal)
|
self.horizontalSlider_4.setOrientation(QtCore.Qt.Orientation.Horizontal)
|
||||||
self.horizontalSlider_4.setObjectName("horizontalSlider_4")
|
self.horizontalSlider_4.setObjectName("horizontalSlider_4")
|
||||||
self.gridLayout_5.addWidget(self.horizontalSlider_4, 4, 1, 1, 1)
|
self.gridLayout_5.addWidget(self.horizontalSlider_4, 6, 1, 1, 1)
|
||||||
self.label_6 = QtWidgets.QLabel(parent=self.centralwidget)
|
self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
|
||||||
self.label_6.setObjectName("label_6")
|
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
|
||||||
self.gridLayout_5.addWidget(self.label_6, 3, 0, 1, 1)
|
self.label_27 = QtWidgets.QLabel(parent=self.centralwidget)
|
||||||
|
self.label_27.setObjectName("label_27")
|
||||||
|
self.horizontalLayout_3.addWidget(self.label_27)
|
||||||
|
self.c_imaging_config = QtWidgets.QComboBox(parent=self.centralwidget)
|
||||||
|
self.c_imaging_config.setObjectName("c_imaging_config")
|
||||||
|
self.horizontalLayout_3.addWidget(self.c_imaging_config)
|
||||||
|
self.b_new_imaging_config = QtWidgets.QPushButton(parent=self.centralwidget)
|
||||||
|
self.b_new_imaging_config.setObjectName("b_new_imaging_config")
|
||||||
|
self.horizontalLayout_3.addWidget(self.b_new_imaging_config)
|
||||||
|
self.horizontalLayout_3.setStretch(1, 1)
|
||||||
|
self.gridLayout_5.addLayout(self.horizontalLayout_3, 2, 0, 1, 3)
|
||||||
self.gridLayout.addLayout(self.gridLayout_5, 2, 0, 1, 1)
|
self.gridLayout.addLayout(self.gridLayout_5, 2, 0, 1, 1)
|
||||||
self.gridLayout_7 = QtWidgets.QGridLayout()
|
self.gridLayout_7 = QtWidgets.QGridLayout()
|
||||||
self.gridLayout_7.setObjectName("gridLayout_7")
|
self.gridLayout_7.setObjectName("gridLayout_7")
|
||||||
@ -358,7 +371,7 @@ class Ui_MainWindow(object):
|
|||||||
self.gridLayout.addLayout(self.horizontalLayout_6, 4, 0, 1, 2)
|
self.gridLayout.addLayout(self.horizontalLayout_6, 4, 0, 1, 2)
|
||||||
MainWindow.setCentralWidget(self.centralwidget)
|
MainWindow.setCentralWidget(self.centralwidget)
|
||||||
self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
|
self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
|
||||||
self.menubar.setGeometry(QtCore.QRect(0, 0, 936, 30))
|
self.menubar.setGeometry(QtCore.QRect(0, 0, 1158, 30))
|
||||||
self.menubar.setObjectName("menubar")
|
self.menubar.setObjectName("menubar")
|
||||||
MainWindow.setMenuBar(self.menubar)
|
MainWindow.setMenuBar(self.menubar)
|
||||||
self.statusbar = QtWidgets.QStatusBar(parent=MainWindow)
|
self.statusbar = QtWidgets.QStatusBar(parent=MainWindow)
|
||||||
@ -393,11 +406,13 @@ class Ui_MainWindow(object):
|
|||||||
self.label_12.setText(_translate("MainWindow", "Roll"))
|
self.label_12.setText(_translate("MainWindow", "Roll"))
|
||||||
self.label_11.setText(_translate("MainWindow", "E"))
|
self.label_11.setText(_translate("MainWindow", "E"))
|
||||||
self.label_8.setText(_translate("MainWindow", "X"))
|
self.label_8.setText(_translate("MainWindow", "X"))
|
||||||
self.label_5.setText(_translate("MainWindow", "t_end"))
|
|
||||||
self.label_7.setText(_translate("MainWindow", "TextLabel"))
|
self.label_7.setText(_translate("MainWindow", "TextLabel"))
|
||||||
self.label_2.setText(_translate("MainWindow", "Imaging"))
|
self.label_2.setText(_translate("MainWindow", "Imaging"))
|
||||||
self.label_15.setText(_translate("MainWindow", "t_start"))
|
|
||||||
self.label_6.setText(_translate("MainWindow", "TextLabel"))
|
self.label_6.setText(_translate("MainWindow", "TextLabel"))
|
||||||
|
self.label_15.setText(_translate("MainWindow", "t_start"))
|
||||||
|
self.label_5.setText(_translate("MainWindow", "t_end"))
|
||||||
|
self.label_27.setText(_translate("MainWindow", "Profile"))
|
||||||
|
self.b_new_imaging_config.setText(_translate("MainWindow", "New"))
|
||||||
self.b_play_playback.setText(_translate("MainWindow", "Playback"))
|
self.b_play_playback.setText(_translate("MainWindow", "Playback"))
|
||||||
self.label_16.setText(_translate("MainWindow", "SeqName"))
|
self.label_16.setText(_translate("MainWindow", "SeqName"))
|
||||||
self.label_4.setText(_translate("MainWindow", "Frame ID"))
|
self.label_4.setText(_translate("MainWindow", "Frame ID"))
|
||||||
|
|||||||
@ -6,8 +6,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>936</width>
|
<width>1158</width>
|
||||||
<height>732</height>
|
<height>805</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
@ -337,38 +337,30 @@
|
|||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
<item row="2" column="0">
|
||||||
<layout class="QGridLayout" name="gridLayout_5">
|
<layout class="QGridLayout" name="gridLayout_5">
|
||||||
<item row="2" column="0">
|
<item row="6" column="2">
|
||||||
<widget class="QLabel" name="label_5">
|
<widget class="QSpinBox" name="spinBox_3"/>
|
||||||
<property name="text">
|
|
||||||
<string>t_end</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="0">
|
<item row="6" column="0">
|
||||||
<widget class="QLabel" name="label_7">
|
<widget class="QLabel" name="label_7">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>TextLabel</string>
|
<string>TextLabel</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
<item row="3" column="2">
|
||||||
<widget class="QSlider" name="s_t_start">
|
<widget class="QSpinBox" name="spinBox_12"/>
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Orientation::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1">
|
<item row="5" column="2">
|
||||||
|
<widget class="QSpinBox" name="spinBox_2"/>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="1">
|
||||||
<widget class="QSlider" name="horizontalSlider_2">
|
<widget class="QSlider" name="horizontalSlider_2">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Orientation::Horizontal</enum>
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="2">
|
<item row="7" column="1">
|
||||||
<widget class="QSpinBox" name="spinBox_12"/>
|
|
||||||
</item>
|
|
||||||
<item row="5" column="1">
|
|
||||||
<spacer name="verticalSpacer_2">
|
<spacer name="verticalSpacer_2">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Orientation::Vertical</enum>
|
<enum>Qt::Orientation::Vertical</enum>
|
||||||
@ -381,12 +373,6 @@
|
|||||||
</property>
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="2">
|
|
||||||
<widget class="QSpinBox" name="spinBox_2"/>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="2">
|
|
||||||
<widget class="QSpinBox" name="spinBox_3"/>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="0" colspan="3">
|
<item row="0" column="0" colspan="3">
|
||||||
<widget class="QLabel" name="label_2">
|
<widget class="QLabel" name="label_2">
|
||||||
<property name="font">
|
<property name="font">
|
||||||
@ -403,10 +389,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="2">
|
<item row="4" column="1">
|
||||||
<widget class="QSpinBox" name="spinBox"/>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="1">
|
|
||||||
<widget class="QSlider" name="s_t_end">
|
<widget class="QSlider" name="s_t_end">
|
||||||
<property name="minimum">
|
<property name="minimum">
|
||||||
<number>1</number>
|
<number>1</number>
|
||||||
@ -419,27 +402,65 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="5" column="0">
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="text">
|
||||||
|
<string>TextLabel</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="2">
|
||||||
|
<widget class="QSpinBox" name="spinBox"/>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
<widget class="QLabel" name="label_15">
|
<widget class="QLabel" name="label_15">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>t_start</string>
|
<string>t_start</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="1">
|
<item row="3" column="1">
|
||||||
|
<widget class="QSlider" name="s_t_start">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="text">
|
||||||
|
<string>t_end</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="1">
|
||||||
<widget class="QSlider" name="horizontalSlider_4">
|
<widget class="QSlider" name="horizontalSlider_4">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Orientation::Horizontal</enum>
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
<item row="2" column="0" colspan="3">
|
||||||
<widget class="QLabel" name="label_6">
|
<layout class="QHBoxLayout" name="horizontalLayout_3" stretch="0,1,0">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_27">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>TextLabel</string>
|
<string>Profile</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="c_imaging_config"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="b_new_imaging_config">
|
||||||
|
<property name="text">
|
||||||
|
<string>New</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
@ -701,7 +722,7 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>936</width>
|
<width>1158</width>
|
||||||
<height>30</height>
|
<height>30</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import dataclasses
|
|||||||
import json
|
import json
|
||||||
import struct
|
import struct
|
||||||
from enum import auto, Enum
|
from enum import auto, Enum
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
class BG(Enum):
|
class BG(Enum):
|
||||||
@ -31,6 +32,7 @@ class BG(Enum):
|
|||||||
SeqIdList = auto()
|
SeqIdList = auto()
|
||||||
SetWindowVisibleMsg = auto()
|
SetWindowVisibleMsg = auto()
|
||||||
SetSidMsg = auto()
|
SetSidMsg = auto()
|
||||||
|
ImagingConfigNameListMsg = auto()
|
||||||
|
|
||||||
|
|
||||||
class Msg:
|
class Msg:
|
||||||
@ -142,6 +144,10 @@ class SeqListMsg(Msg):
|
|||||||
value: list[str]
|
value: list[str]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class ImagingConfigNameListMsg(Msg):
|
||||||
|
value: list[str]
|
||||||
|
|
||||||
|
|
||||||
class SetPlayMode(StrMsg):
|
class SetPlayMode(StrMsg):
|
||||||
pass
|
pass
|
||||||
@ -177,6 +183,21 @@ class ImageArgMsg(Msg):
|
|||||||
t_start: int
|
t_start: int
|
||||||
t_end: int
|
t_end: int
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_path(p: Path):
|
||||||
|
j = json.loads(p.read_text())
|
||||||
|
return ImageArgMsg(
|
||||||
|
'load',
|
||||||
|
t_start=j['t_start'],
|
||||||
|
t_end=j['t_end'],
|
||||||
|
)
|
||||||
|
|
||||||
|
def json(self):
|
||||||
|
return json.dumps(dict(
|
||||||
|
t_start=self.t_start,
|
||||||
|
t_end=self.t_end,
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
class DeviceConfigListMsg(Msg):
|
class DeviceConfigListMsg(Msg):
|
||||||
@ -188,10 +209,12 @@ class RecordFrameMsg(Msg):
|
|||||||
size: int
|
size: int
|
||||||
current_sid: int
|
current_sid: int
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
class SetSidMsg(Msg):
|
class SetSidMsg(Msg):
|
||||||
value: int
|
value: int
|
||||||
|
|
||||||
|
|
||||||
class BMMsg(Msg):
|
class BMMsg(Msg):
|
||||||
def __init__(self, t: int, data: bytes):
|
def __init__(self, t: int, data: bytes):
|
||||||
self.data = data
|
self.data = data
|
||||||
|
|||||||
182
test/avencode.py
Normal file
182
test/avencode.py
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
import fractions
|
||||||
|
import logging
|
||||||
|
from typing import cast
|
||||||
|
|
||||||
|
import av
|
||||||
|
import numpy as np
|
||||||
|
from av import VideoCodecContext, VideoFrame
|
||||||
|
|
||||||
|
# for e in av.codecs_available:
|
||||||
|
# print(e)
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
logging.getLogger('libav').setLevel(logging.DEBUG)
|
||||||
|
MAX_FRAME_RATE = 60
|
||||||
|
|
||||||
|
|
||||||
|
def f0():
|
||||||
|
# input_ = av.open('asd','w')
|
||||||
|
# in_stream = input_.streams.video[0]
|
||||||
|
# libx264
|
||||||
|
codec: VideoCodecContext = av.CodecContext.create('libx264', "w")
|
||||||
|
|
||||||
|
codec.width = 640
|
||||||
|
codec.height = 480
|
||||||
|
codec.pix_fmt = 'yuv420p' # 常用格式, 大部分解码器都支持
|
||||||
|
|
||||||
|
# codec.width = 100
|
||||||
|
# codec.height = 100
|
||||||
|
# codec.bit_rate = 100000
|
||||||
|
# codec.pix_fmt = "yuv420p"
|
||||||
|
# codec.framerate = fractions.Fraction(MAX_FRAME_RATE, 1)
|
||||||
|
# codec.time_base = fractions.Fraction(1, MAX_FRAME_RATE)
|
||||||
|
# codec.options = {
|
||||||
|
# "profile": "baseline",
|
||||||
|
# "level": "31",
|
||||||
|
# "tune": "zerolatency",
|
||||||
|
# }
|
||||||
|
codec.open()
|
||||||
|
pts = 0
|
||||||
|
while True:
|
||||||
|
f = VideoFrame.from_ndarray(np.zeros((640, 480, 3), dtype=np.uint8))
|
||||||
|
pts += 1
|
||||||
|
f.pts = pts
|
||||||
|
r = codec.encode(f)
|
||||||
|
if r.__len__() > 0:
|
||||||
|
print(r[0].__buffer__(0).__len__())
|
||||||
|
|
||||||
|
|
||||||
|
def f1():
|
||||||
|
import av
|
||||||
|
|
||||||
|
# 创建一个输出容器
|
||||||
|
output = av.open('output.mp4', 'w')
|
||||||
|
|
||||||
|
# 添加一个视频流
|
||||||
|
stream = output.add_stream('libx264', rate=24)
|
||||||
|
stream.width = 640
|
||||||
|
stream.height = 480
|
||||||
|
stream.pix_fmt = 'yuv420p'
|
||||||
|
|
||||||
|
# 编码循环
|
||||||
|
for i in range(100):
|
||||||
|
frame = av.VideoFrame(width=640, height=480, format='rgb24')
|
||||||
|
# ... 对帧进行处理 ...
|
||||||
|
for packet in stream.encode(frame):
|
||||||
|
output.mux(packet)
|
||||||
|
|
||||||
|
# 完成编码
|
||||||
|
for packet in stream.encode():
|
||||||
|
output.mux(packet)
|
||||||
|
|
||||||
|
# 关闭输出容器
|
||||||
|
output.close()
|
||||||
|
|
||||||
|
|
||||||
|
def f2():
|
||||||
|
import av
|
||||||
|
from av import VideoFrame
|
||||||
|
from fractions import Fraction
|
||||||
|
|
||||||
|
# 1. 创建输出容器
|
||||||
|
output_path = 'output.mp4'
|
||||||
|
output = av.open(output_path, 'w')
|
||||||
|
|
||||||
|
# 2. 创建视频流(使用 libx264 编码器)
|
||||||
|
stream = output.add_stream('libx264', rate=24) # 帧率
|
||||||
|
|
||||||
|
# 3. 设置编解码器上下文参数
|
||||||
|
stream.width = 640
|
||||||
|
stream.height = 480
|
||||||
|
stream.pix_fmt = 'yuv420p' # 常用格式, 大部分解码器都支持
|
||||||
|
stream.codec_context.time_base = Fraction(1, 24)
|
||||||
|
|
||||||
|
# 4. 手动打开编解码器上下文 (可选, add_stream 已经完成了这一步)
|
||||||
|
stream.codec_context.open()
|
||||||
|
|
||||||
|
# 5. 编码循环
|
||||||
|
for i in range(100): # 生成 100 帧
|
||||||
|
frame = av.VideoFrame(width=640, height=480, format='rgb24')
|
||||||
|
|
||||||
|
# 生成测试数据
|
||||||
|
# 在实际应用中,您需要从图像源获取数据
|
||||||
|
import numpy as np
|
||||||
|
import colorsys
|
||||||
|
cx = int(640 / 100 * i)
|
||||||
|
cy = int(480 / 100 * i)
|
||||||
|
rgb = (np.array(colorsys.hsv_to_rgb(i / 100.0, 1.0, 1.0)) * 255).astype(np.uint8)
|
||||||
|
|
||||||
|
data = np.zeros((frame.height, frame.width, 3), dtype=np.uint8)
|
||||||
|
data[cy - 10:cy + 10, cx - 10:cx + 10, :] = rgb
|
||||||
|
frame.planes[0].update(data)
|
||||||
|
|
||||||
|
for packet in stream.encode(frame):
|
||||||
|
output.mux(packet)
|
||||||
|
|
||||||
|
# 6. 刷新编码器
|
||||||
|
for packet in stream.encode():
|
||||||
|
output.mux(packet)
|
||||||
|
|
||||||
|
# 7. 关闭容器
|
||||||
|
output.close()
|
||||||
|
|
||||||
|
|
||||||
|
def f3():
|
||||||
|
import av
|
||||||
|
from av import VideoFrame
|
||||||
|
from fractions import Fraction
|
||||||
|
|
||||||
|
# 1. 创建输出容器
|
||||||
|
output_path = 'output.mp4'
|
||||||
|
output = av.open(output_path, 'w')
|
||||||
|
|
||||||
|
# 2. 获取编码器codec
|
||||||
|
codec = av.Codec('libx264', "w")
|
||||||
|
|
||||||
|
# 3. 手动创建并配置编解码器上下文
|
||||||
|
ctx = av.CodecContext.create(codec, mode="w")
|
||||||
|
ctx.width = 640
|
||||||
|
ctx.height = 480
|
||||||
|
ctx.pix_fmt = 'yuv420p' # 常用格式, 大部分解码器都支持
|
||||||
|
ctx.time_base = Fraction(1, 24)
|
||||||
|
|
||||||
|
# 4. 添加视频流 (使用创建好的编解码器上下文).
|
||||||
|
stream = output.add_stream('libx264', rate=24) # 使用编码器名称
|
||||||
|
stream.codec_context = ctx # 将之前创建好的 ctx 赋值给 stream
|
||||||
|
stream.width = ctx.width # 宽度
|
||||||
|
stream.height = ctx.height
|
||||||
|
stream.pix_fmt = ctx.pix_fmt
|
||||||
|
|
||||||
|
# 5. 手动打开编解码器上下文 (在添加到 stream 后会自动打开)
|
||||||
|
ctx.open()
|
||||||
|
|
||||||
|
# 6. 编码循环
|
||||||
|
for i in range(100): # 生成 100 帧
|
||||||
|
frame = av.VideoFrame(width=640, height=480, format='rgb24')
|
||||||
|
|
||||||
|
# 生成测试数据
|
||||||
|
# 在实际应用中,您需要从图像源获取数据
|
||||||
|
import numpy as np
|
||||||
|
import colorsys
|
||||||
|
cx = int(640 / 100 * i)
|
||||||
|
cy = int(480 / 100 * i)
|
||||||
|
rgb = (np.array(colorsys.hsv_to_rgb(i / 100.0, 1.0, 1.0)) * 255).astype(np.uint8)
|
||||||
|
|
||||||
|
data = np.zeros((frame.height, frame.width, 3), dtype=np.uint8)
|
||||||
|
data[cy - 10:cy + 10, cx - 10:cx + 10, :] = rgb
|
||||||
|
frame.planes[0].update(data)
|
||||||
|
|
||||||
|
for packet in stream.encode(frame):
|
||||||
|
output.mux(packet)
|
||||||
|
|
||||||
|
# 7. 刷新编码器
|
||||||
|
for packet in stream.encode(None):
|
||||||
|
output.mux(packet)
|
||||||
|
|
||||||
|
# 8. 关闭容器
|
||||||
|
output.close()
|
||||||
|
|
||||||
|
print(f"视频已保存到 {output_path}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
f0()
|
||||||
@ -19,7 +19,7 @@ if __name__ == '__main__':
|
|||||||
pps = []
|
pps = []
|
||||||
ps = [
|
ps = [
|
||||||
Broker(),
|
Broker(),
|
||||||
WebRTC(),
|
# WebRTC(),
|
||||||
kde_pyqt6_mainui,
|
kde_pyqt6_mainui,
|
||||||
Device(level=logging.DEBUG),
|
Device(level=logging.DEBUG),
|
||||||
ImageCV(level=logging.DEBUG),
|
ImageCV(level=logging.DEBUG),
|
||||||
|
|||||||
69
test/rtc.py
Normal file
69
test/rtc.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
import aiohttp_cors
|
||||||
|
from aiohttp import web
|
||||||
|
from aiortc import MediaStreamTrack, RTCPeerConnection, RTCSessionDescription, RTCConfiguration, RTCRtpCodecCapability
|
||||||
|
from aiortc.contrib.media import MediaBlackhole, MediaPlayer, MediaRecorder, MediaRelay
|
||||||
|
|
||||||
|
ROOT = os.path.dirname(__file__)
|
||||||
|
|
||||||
|
web.WebSocketResponse()
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
pcs = set()
|
||||||
|
|
||||||
|
|
||||||
|
async def offer(request):
|
||||||
|
params = await request.json()
|
||||||
|
offer = RTCSessionDescription(sdp=params["sdp"], type=params["type"])
|
||||||
|
pc = RTCPeerConnection(RTCConfiguration([]))
|
||||||
|
pcs.add(pc)
|
||||||
|
player = MediaPlayer(os.path.join(ROOT, "demo-instruct.wav"))
|
||||||
|
rc = pc.addTransceiver(player.video, 'sendonly')
|
||||||
|
rc.setCodecPreferences([RTCRtpCodecCapability(mimeType='video/H264',
|
||||||
|
clockRate=90000,
|
||||||
|
channels=None,
|
||||||
|
parameters={
|
||||||
|
'level-asymmetry-allowed': '1',
|
||||||
|
'packetization-mode': '1',
|
||||||
|
'profile-level-id': '42e01f'
|
||||||
|
})])
|
||||||
|
await pc.setRemoteDescription(offer)
|
||||||
|
answer = await pc.createAnswer()
|
||||||
|
await pc.setLocalDescription(answer)
|
||||||
|
return web.Response(
|
||||||
|
content_type="application/json",
|
||||||
|
text=json.dumps(
|
||||||
|
{"sdp": pc.localDescription.sdp, "type": pc.localDescription.type}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def on_shutdown(app):
|
||||||
|
# close peer connections
|
||||||
|
coros = [pc.close() for pc in pcs]
|
||||||
|
await asyncio.gather(*coros)
|
||||||
|
pcs.clear()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app = web.Application()
|
||||||
|
app.on_shutdown.append(on_shutdown)
|
||||||
|
app.router.add_post("/offer", offer)
|
||||||
|
|
||||||
|
cors = aiohttp_cors.setup(app, defaults={
|
||||||
|
"*": aiohttp_cors.ResourceOptions(
|
||||||
|
allow_credentials=True,
|
||||||
|
expose_headers="*",
|
||||||
|
allow_headers="*"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
for route in list(app.router.routes()):
|
||||||
|
cors.add(route)
|
||||||
|
web.run_app(
|
||||||
|
app, access_log=None, host='0.0.0.0', port=8081
|
||||||
|
)
|
||||||
Loading…
Reference in New Issue
Block a user