rewrite to use scapy.all.sniff callback parameter, better, still sucks

This commit is contained in:
devdesk 2024-02-15 20:56:18 +02:00
parent 679a87bf45
commit d661bf27ae

143
decode.py
View File

@ -25,38 +25,13 @@ args = parser.parse_args()
# TODO - probably a better way to do this # TODO - probably a better way to do this
def live_capture(): def live_capture_cb(cb):
while True: def outer(pkt):
# 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) data = bytes(pkt)
yield data l = len(data)
if l == 6972:
cb(data)
if args.live: scapy.all.sniff(iface="enp1s0f0", filter='udp', prn=outer)
print('live stream, import scapy')
import scapy.all
print('open stream')
stream = live_capture()
start = datetime.now()
timestamp = start.strftime('%Y%m%d_%H%M%S')
basename = f'live_{timestamp}'
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):
@ -77,7 +52,11 @@ def rightsize(it):
def removestart(it): def removestart(it):
"Remove the UDP header from the packets" "Remove the UDP header from the packets"
for x in it: for x in it:
yield x[0x2A:] yield removestart_inner(x)
def removestart_inner(x):
return x[0x2A:]
# Function to parse packet data # Function to parse packet data
@ -97,28 +76,51 @@ def parsed(it):
yield parse(x) yield parse(x)
class FrameCollector:
def __init__(self):
self.current = []
def handle(self, obj):
ret = None
if obj['part'] == 0:
if len(self.current) > 0:
ret = b"".join(self.current)
self.current = []
#otherdata = []
self.current.append(obj["data"])
return ret
#otherdata.append(obj)
def last(self):
if len(self.current) > 0:
return b"".join(current)
return None
# Function to group data into frames # Function to group data into frames
def frames(it): def frames(it):
current = [] handler = FrameCollector()
#otherdata = [] #otherdata = []
for obj in it: for obj in it:
if obj['part'] == 0: ret = handler.handle(obj)
if len(current) > 0: if ret:
yield b"".join(current) yield ret
current = [] last = handler.last()
#otherdata = [] if last:
current.append(obj["data"]) yield last
#otherdata.append(obj)
if len(current) > 0:
yield b"".join(current)
WIDTH = 384 WIDTH = 384
HEIGHT = 288 HEIGHT = 288
def bad_frame(frame, width=WIDTH, height=HEIGHT):
return len(frame) != width * height * 2 # 16 bpp
def skip_bad_frames(it, width=WIDTH, height=HEIGHT): 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 bad_frame(frame): # 16 bpp
# Will be fixed when we stopped doing restarts # Will be fixed when we stopped doing restarts
#print(f'{len(frame)} != {width} * {height} * 2') #print(f'{len(frame)} != {width} * {height} * 2')
continue continue
@ -130,16 +132,24 @@ def iterimages(it, width=WIDTH, height=HEIGHT, pixelformat=">H"):
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 def process_video():
frames = skip_bad_frames(frames(parsed(removestart(rightsize(blocks))))) # 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)
# Create the directory for frames if not exists # Get frames and convert them to images
frame_dir = f"frames/{basename}" frames = skip_bad_frames(frames(parsed(removestart(rightsize(blocks)))))
if not os.path.exists(frame_dir):
# Create the directory for frames if not exists
frame_dir = f"frames/{basename}"
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
if not args.live:
images = iterimages(it=frames) images = iterimages(it=frames)
for i, img in enumerate(images): for i, img in enumerate(images):
img.save(f'frames/{basename}/{basename}_{i:04}.png') img.save(f'frames/{basename}/{basename}_{i:04}.png')
@ -162,7 +172,8 @@ if not args.live:
subprocess.run(command) subprocess.run(command)
print("to play: ffplay thermal.mp4") print("to play: ffplay thermal.mp4")
else:
if args.live:
# TODO: to video via ffmpeg; right now just a single png # TODO: to video via ffmpeg; right now just a single png
# of the last frame # of the last frame
def todo_live_ffmpeg(): def todo_live_ffmpeg():
@ -175,7 +186,33 @@ else:
for frame in frames: for frame in frames:
fd.write(frame) fd.write(frame)
for frame in frames: print('live stream, import scapy')
print('.') import scapy.all
print('open stream')
class PacketHandler:
def __init__(self, cb):
self.frame_collector = FrameCollector()
self.cb = cb
def handle(self, pkt):
pkt = removestart_inner(pkt)
parsed = parse(pkt)
frame_maybe = self.frame_collector.handle(parsed)
if not frame_maybe or bad_frame(frame_maybe):
return
self.cb(frame_maybe)
progress = tqdm()
def on_frame(frame):
progress.update(1)
Image.fromarray(np.frombuffer(frame, dtype='>H').reshape(WIDTH, HEIGHT)).save(f'live.new.png') Image.fromarray(np.frombuffer(frame, dtype='>H').reshape(WIDTH, HEIGHT)).save(f'live.new.png')
os.rename('live.new.png', 'live.png') os.rename('live.new.png', 'live.png')
handler = PacketHandler(on_frame)
live_capture_cb(handler.handle)
else:
process_video()