almost live, but horrible hack for stream capture, and not live ffmpeg, just live save of images
This commit is contained in:
parent
7d75ad7596
commit
17c7d0e555
23
README.md
23
README.md
|
@ -3,6 +3,29 @@
|
||||||
https://telavivmakers.org/tamiwiki/projects/thermalcam
|
https://telavivmakers.org/tamiwiki/projects/thermalcam
|
||||||
|
|
||||||
|
|
||||||
|
### Starting the stream
|
||||||
|
|
||||||
|
#### Enable jumbo frames
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo ip link set eth0 mtu 9000
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Send start packet
|
||||||
|
You need to send a special packet.
|
||||||
|
|
||||||
|
Sending it via sudo because of raw sockets:
|
||||||
|
```bash
|
||||||
|
sudo ./venv/bin/python ./replay.py
|
||||||
|
```
|
||||||
|
|
||||||
|
To send it you need the capability to open sockets in raw mode, but that does not work well with scripts (see [1]
|
||||||
|
|
||||||
|
[1] setcap for executables, not helpful for python scripts:
|
||||||
|
```
|
||||||
|
setcap cap_net_raw,cap_net_admin=eip ./replay.py
|
||||||
|
```
|
||||||
|
|
||||||
### Rust lib usage
|
### Rust lib usage
|
||||||
|
|
||||||
# if you don't already have a virtualenv. Linux specific, adjust to your OS.
|
# if you don't already have a virtualenv. Linux specific, adjust to your OS.
|
||||||
|
|
138
decode.py
Executable file → Normal file
138
decode.py
Executable file → Normal file
|
@ -1,9 +1,12 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import argparse
|
import argparse
|
||||||
|
from pathlib import Path
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
from io import BytesIO
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
|
from datetime import datetime
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import pcapng
|
import pcapng
|
||||||
from struct import unpack
|
from struct import unpack
|
||||||
|
@ -12,6 +15,7 @@ from PIL import Image
|
||||||
|
|
||||||
# Create the parser
|
# Create the parser
|
||||||
parser = argparse.ArgumentParser(description="Process a pcap file.")
|
parser = argparse.ArgumentParser(description="Process a pcap file.")
|
||||||
|
parser.add_argument("--live", action="store_true", help="Process images live")
|
||||||
|
|
||||||
# Add an argument for the pcap file, with a default value
|
# Add an argument for the pcap file, with a default value
|
||||||
parser.add_argument('input_file', nargs='?', default='in.pcap', help='The pcap file to process')
|
parser.add_argument('input_file', nargs='?', default='in.pcap', help='The pcap file to process')
|
||||||
|
@ -19,31 +23,57 @@ parser.add_argument('input_file', nargs='?', default='in.pcap', help='The pcap f
|
||||||
# Parse the arguments
|
# Parse the arguments
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
# Now use args.input_file as the file to process
|
|
||||||
input_file = args.input_file
|
# TODO - probably a better way to do this
|
||||||
basename = os.path.splitext(os.path.basename(input_file))[0]
|
def live_capture():
|
||||||
|
while True:
|
||||||
|
# TODO: This is wrong:
|
||||||
|
# during scapy.all.sniff construction we lose about 27 packets.
|
||||||
|
# we work around it by capturing 2* packets(frame), so we just lose
|
||||||
|
# about half the frames.
|
||||||
|
cap = scapy.all.sniff(iface="enp1s0f0", count=32 * 2)
|
||||||
|
for pkt in cap:
|
||||||
|
l = len(bytes(pkt))
|
||||||
|
if l != 6972:
|
||||||
|
continue
|
||||||
|
data = bytes(pkt)
|
||||||
|
with open('udp.bytes', 'ab+') as fd:
|
||||||
|
fd.write(bytes(pkt.payload))
|
||||||
|
yield data
|
||||||
|
|
||||||
|
|
||||||
# Read packets from a pcap file
|
if args.live:
|
||||||
scanner = pcapng.scanner.FileScanner(open(input_file, "rb"))
|
print('live stream, import scapy')
|
||||||
blocks = tqdm(scanner)
|
import scapy.all
|
||||||
|
print('open stream')
|
||||||
# Helper function to safely get an attribute from an object
|
stream = live_capture()
|
||||||
def tryget(obj, att):
|
start = datetime.now()
|
||||||
if hasattr(obj, att):
|
timestamp = start.strftime('%Y%m%d_%H%M%S')
|
||||||
return getattr(obj, att)
|
basename = f'live_{timestamp}'
|
||||||
return None
|
blocks = tqdm(stream)
|
||||||
|
else:
|
||||||
|
# Now use args.input_file as the file to process
|
||||||
|
input_file = args.input_file
|
||||||
|
basename = os.path.splitext(os.path.basename(input_file))[0]
|
||||||
|
stream = open(input_file, 'rb')
|
||||||
|
# Read packets from a pcap file
|
||||||
|
scanner = pcapng.scanner.FileScanner(stream)
|
||||||
|
blocks = tqdm(scanner)
|
||||||
|
|
||||||
|
|
||||||
def rightsize(it):
|
def rightsize(it):
|
||||||
for i, obj in enumerate(it):
|
for i, obj in enumerate(it):
|
||||||
if not hasattr(obj, 'packet_len'):
|
if isinstance(obj, bytes):
|
||||||
|
l = len(obj)
|
||||||
|
data = obj
|
||||||
|
else:
|
||||||
|
if not hasattr(obj, 'packet_len'):
|
||||||
|
continue
|
||||||
|
l = obj.packet_len
|
||||||
|
data = obj.packet_data
|
||||||
|
if l != 6972:
|
||||||
continue
|
continue
|
||||||
len = obj.packet_len
|
yield data
|
||||||
if len != 6972:
|
|
||||||
continue
|
|
||||||
yield obj.packet_data
|
|
||||||
|
|
||||||
|
|
||||||
def removestart(it):
|
def removestart(it):
|
||||||
|
@ -52,7 +82,6 @@ def removestart(it):
|
||||||
yield x[0x2A:]
|
yield x[0x2A:]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Function to parse packet data
|
# Function to parse packet data
|
||||||
def parse(data):
|
def parse(data):
|
||||||
hdr = 4 + 2 * 7 # Header length
|
hdr = 4 + 2 * 7 # Header length
|
||||||
|
@ -73,26 +102,38 @@ def parsed(it):
|
||||||
# Function to group data into frames
|
# Function to group data into frames
|
||||||
def frames(it):
|
def frames(it):
|
||||||
current = []
|
current = []
|
||||||
|
#otherdata = []
|
||||||
for obj in it:
|
for obj in it:
|
||||||
if obj['part'] == 0:
|
if obj['part'] == 0:
|
||||||
if len(current) > 0:
|
if len(current) > 0:
|
||||||
yield b"".join(current)
|
yield b"".join(current)
|
||||||
current = []
|
current = []
|
||||||
|
#otherdata = []
|
||||||
current.append(obj["data"])
|
current.append(obj["data"])
|
||||||
|
#otherdata.append(obj)
|
||||||
if len(current) > 0:
|
if len(current) > 0:
|
||||||
yield b"".join(current)
|
yield b"".join(current)
|
||||||
|
|
||||||
|
WIDTH = 384
|
||||||
|
HEIGHT = 288
|
||||||
|
|
||||||
def iterimages(it, width, height, pixelformat=">H"):
|
|
||||||
|
def skip_bad_frames(it, width=WIDTH, height=HEIGHT):
|
||||||
for frame in it:
|
for frame in it:
|
||||||
if len(frame) != width * height * 2: # 16 bpp
|
if len(frame) != width * height * 2: # 16 bpp
|
||||||
|
# Will be fixed when we stopped doing restarts
|
||||||
|
#print(f'{len(frame)} != {width} * {height} * 2')
|
||||||
continue
|
continue
|
||||||
|
yield frame
|
||||||
|
|
||||||
|
|
||||||
|
def iterimages(it, width=WIDTH, height=HEIGHT, pixelformat=">H"):
|
||||||
|
for frame in it:
|
||||||
yield Image.fromarray(np.frombuffer(frame, dtype=pixelformat).reshape(width, height))
|
yield Image.fromarray(np.frombuffer(frame, dtype=pixelformat).reshape(width, height))
|
||||||
|
|
||||||
|
|
||||||
# Get frames and convert them to images
|
# Get frames and convert them to images
|
||||||
frames = frames(parsed(removestart(rightsize(blocks))))
|
frames = skip_bad_frames(frames(parsed(removestart(rightsize(blocks)))))
|
||||||
images = iterimages(it=frames, width=384, height=288)
|
|
||||||
|
|
||||||
# Create the directory for frames if not exists
|
# Create the directory for frames if not exists
|
||||||
frame_dir = f"frames/{basename}"
|
frame_dir = f"frames/{basename}"
|
||||||
|
@ -100,26 +141,37 @@ if not os.path.exists(frame_dir):
|
||||||
os.makedirs(frame_dir)
|
os.makedirs(frame_dir)
|
||||||
|
|
||||||
# Save each image as a PNG file
|
# Save each image as a PNG file
|
||||||
for i, img in enumerate(images):
|
if not args.live:
|
||||||
img.save(f'frames/{basename}/{basename}_{i:04}.png')
|
images = iterimages(it=frames)
|
||||||
|
for i, img in enumerate(images):
|
||||||
|
img.save(f'frames/{basename}/{basename}_{i:04}.png')
|
||||||
|
ffmpeg_input = f"frames/{basename}/{basename}_%04d.png"
|
||||||
|
command = [
|
||||||
|
"ffmpeg",
|
||||||
|
"-y", # Overwrite output file without asking
|
||||||
|
"-hide_banner", # Hide banner
|
||||||
|
"-loglevel", "info", # Log level
|
||||||
|
"-f", "image2", # Input format
|
||||||
|
"-framerate", "25", # Framerate
|
||||||
|
"-i", ffmpeg_input, # Input file pattern
|
||||||
|
"-vf", "transpose=1", # Video filter for transposing
|
||||||
|
"-s", "384x288", # Size of one frame
|
||||||
|
"-vcodec", "libopenh264", # Video codec
|
||||||
|
"-pix_fmt", "yuv420p", # Pixel format: YUV 4:2:0
|
||||||
|
"thermal.mp4", # Output file in MP4 container
|
||||||
|
]
|
||||||
|
|
||||||
# Produce a video from the saved images
|
subprocess.run(command)
|
||||||
ffmpeg_input = f"frames/{basename}/{basename}_%04d.png"
|
print("to play: ffplay thermal.mp4")
|
||||||
command = [
|
|
||||||
"ffmpeg",
|
|
||||||
"-y", # Overwrite output file without asking
|
|
||||||
"-hide_banner", # Hide banner
|
|
||||||
"-loglevel", "info", # Log level
|
|
||||||
"-f", "image2", # Input format
|
|
||||||
"-framerate", "25", # Framerate
|
|
||||||
"-i", ffmpeg_input, # Input file pattern
|
|
||||||
"-vf", "transpose=1", # Video filter for transposing
|
|
||||||
"-s", "384x288", # Size of one frame
|
|
||||||
"-vcodec", "libx264", # Video codec
|
|
||||||
"-pix_fmt", "yuv420p", # Pixel format: YUV 4:2:0
|
|
||||||
"thermal.mp4", # Output file in MP4 container
|
|
||||||
]
|
|
||||||
|
|
||||||
subprocess.run(command)
|
else:
|
||||||
|
output = 'to_ffmpeg'
|
||||||
print("to play: ffplay thermal.mp4")
|
# live: write to named pipe
|
||||||
|
if not Path(output).exists():
|
||||||
|
print(f'making fifo at {output}')
|
||||||
|
os.mkfifo(output)
|
||||||
|
fd = open(output, 'wb')
|
||||||
|
for i, frame in enumerate(frames):
|
||||||
|
print(f'frame {i:3}')
|
||||||
|
Image.fromarray(np.frombuffer(frame, dtype='>H').reshape(WIDTH, HEIGHT)).save(f'test_{i:04}.png')
|
||||||
|
fd.write(frame)
|
||||||
|
|
9
listen.py
Normal file
9
listen.py
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
from socket import socket, AF_INET, SOCK_DGRAM
|
||||||
|
|
||||||
|
s = socket(AF_INET, SOCK_DGRAM)
|
||||||
|
s.bind(('', 8090))
|
||||||
|
|
||||||
|
while True:
|
||||||
|
d = s.recvfrom(1024)
|
||||||
|
print(d)
|
||||||
|
|
1
replay.py
Normal file → Executable file
1
replay.py
Normal file → Executable file
|
@ -1,3 +1,4 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
#replay the "trigger" packet.
|
#replay the "trigger" packet.
|
||||||
#this packets will start the source broadcasting its packets.
|
#this packets will start the source broadcasting its packets.
|
||||||
|
|
||||||
|
|
30
test_rust.py
30
test_rust.py
|
@ -2,11 +2,31 @@
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from thermaldecoder import decode
|
from thermaldecoder import decode
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
import subprocess
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
|
||||||
|
# Create a directory to store the frames if it doesn't exist
|
||||||
root = Path('frames')
|
root = Path('frames')
|
||||||
root.mkdir(exist_ok=True)
|
root.mkdir(exist_ok=True)
|
||||||
frames = list(decode('in.pcap'))
|
|
||||||
f = np.array(frames[0])
|
# Decode the frames from the pcap file
|
||||||
f.shape = (384, 288)
|
frames = list(decode('indesk.pcapng'))
|
||||||
plt.imshow(f)
|
|
||||||
plt.show()
|
# Iterate over the frames
|
||||||
|
for i, frame in enumerate(frames):
|
||||||
|
try:
|
||||||
|
# Convert the frame to an image file
|
||||||
|
img_path = root / f"frame_{i}.png"
|
||||||
|
f = np.array(frame)
|
||||||
|
f.shape = (384, 288)
|
||||||
|
plt.imshow(f)
|
||||||
|
plt.axis('off')
|
||||||
|
plt.savefig(img_path, bbox_inches='tight', pad_inches=0)
|
||||||
|
plt.close()
|
||||||
|
|
||||||
|
# Use ffmpeg to display the image
|
||||||
|
subprocess.run(['ffmpeg', '-i', str(img_path), '-vf', 'scale=800:600', '-framerate', '25', '-f', 'image2pipe', '-'], check=True)
|
||||||
|
|
||||||
|
except ValueError as e:
|
||||||
|
print(f"Error processing frame {i}: {e}")
|
||||||
|
|
BIN
thermal.mp4
BIN
thermal.mp4
Binary file not shown.
Loading…
Reference in New Issue
Block a user