#!/bin/bash
# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright (C) 2018, Google Inc.
#
# Author: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
#
# Capture raw frames from cameras based on the Intel IPU3
#
# The scripts makes use of the following tools, which are expected to be
# executable from the system-wide path or from the local directory:
#
# - media-ctl (from v4l-utils git://linuxtv.org/v4l-utils.git)
# - raw2pnm (from nvt https://github.com/intel/nvt.git)
# - yavta (from git://git.ideasonboard.org/yavta.git)

# Locate the sensor entity
find_sensor() {
	local bus
	local sensor_name=$1

	bus=$(grep "$sensor_name" /sys/class/video4linux/v4l-subdev*/name | cut -d ' ' -f 2)
	if [[ -z $bus ]]; then
		echo "Sensor '$sensor_name' not found." >&2
		exit 1
	fi

	echo "$sensor_name $bus"
}

# Locate the media device
find_media_device() {
	local mdev

	for mdev in /dev/media* ; do
		media-ctl -d $mdev -p | grep -q "^driver[ \t]*ipu3-cio2$" && break
		mdev=
	done

	if [[ -z $mdev ]] ; then
		echo "IPU3 media device not found." >&2
		exit 1
	fi

	echo $mdev
}

# Locate the CSI2 and CIO2 and get the sensor format
parse_pipeline() {
	local cio2_queue
	local resolution
	local sensor=$1

	read cio2_queue bus_format sensor_size <<< $($mediactl -p | awk "
	/^- entity [0-9]*:/ {
		sensor=0;
	}

	/^- entity [0-9]*: $sensor/ {
		sensor=1;
	}

	/^[ \t]*(stream:0)?\[fmt:/ {
		if (sensor) {
			gsub(\".*fmt:\", \"\");
			gsub(\"[] ].*\", \"\");
			sub(\"/\", \" \");
			sub(\"@[0-9]+/[0-9]+\", \"\");
			format=\$0;
		}
	}

	/^[ \t]*->/ {
		if (sensor)
			cio2=substr(\$3, 0, 1);
	}

	END {
		print cio2 \" \" format;
	}
	")

	ipu3_csi2="ipu3-csi2 $cio2_queue"
	ipu3_capture="ipu3-cio2 $cio2_queue"

	sensor_width=$(echo $sensor_size | cut -d 'x' -f 1)
	sensor_height=$(echo $sensor_size | cut -d 'x' -f 2)

	echo "Using device $mdev with IPU3 CIO2 queue $cio2_queue"
}

# Configure the pipeline
configure_pipeline() {
	local format="fmt:$bus_format/$sensor_size"

	echo "Configuring pipeline for $sensor in $format"

	$mediactl -r

	$mediactl -l "\"$sensor\":0 -> \"$ipu3_csi2\":0[1]"
	$mediactl -l "\"$ipu3_csi2\":1 -> \"$ipu3_capture\":0[1]"

	$mediactl -V "\"$sensor\":0 [$format]"
	$mediactl -V "\"$ipu3_csi2\":1 [$format]"
}

# Capture frames
capture_frames() {
	local file_op
	local frame_count=$1
	local ipu3_format=IPU3_${bus_format/_1X10/}
	local save_file=$2

	if [[ $save_file -eq 1 ]]; then
		file_op="--file=/tmp/frame-#.bin"
	fi

	yavta -c$frame_count -n5 -I -f $ipu3_format -s $sensor_size $file_op \
		$($mediactl -e "$ipu3_capture")
}

# Convert captured files to ppm
convert_files() {
	local frame_count=$1
	local format=${bus_format/_1X10/}
	local padded_width=$(expr \( $sensor_width + 49 \) / 50 \* 50)

	echo "Converting ${sensor_width}x${sensor_height} (${padded_width}x${sensor_height})"

	for i in `seq -f '%06.0f' 0 $(($frame_count - 1))`; do
		ipu3-unpack /tmp/frame-$i.bin /tmp/frame-$i.raw
		raw2pnm -x$padded_width -y$sensor_height -f$format /tmp/frame-$i.raw /tmp/frame-$i.ppm
	done
}

# Print usage message
usage() {
	echo "Usage: $1 [options] sensor-name"
	echo "Supported options:"
	echo "-c,--count n      Number of frame to capture"
	echo "--no-save         Do not save captured frames to disk"
}

# Parse command line arguments
frame_count=10
save_file=1

while (( "$#" )) ; do
	case $1 in
	-c|--count)
		frame_count=$2
		shift 2
		;;
	--no-save)
		save_file=0
		shift
		;;
	-*)
		echo "Unsupported option $1" >&2
		usage $0
		exit 1
		;;
	*)
		break
		;;
	esac
done

if [[ $# -ne 1 ]] ; then
	usage $0
	exit 1
fi

sensor_name=$1

sensor=$(find_sensor $sensor_name) || exit
mdev=$(find_media_device) || exit
mediactl="media-ctl -d $mdev"

parse_pipeline $sensor
configure_pipeline
capture_frames $frame_count $save_file
[[ $save_file -eq 1 ]] && convert_files $frame_count
