diff --git a/demo/capture-upload.sh b/demo/capture-upload.sh index 482c0e2..9ae841d 100755 --- a/demo/capture-upload.sh +++ b/demo/capture-upload.sh @@ -179,113 +179,131 @@ discover_and_report_controls() { log "Discovering available camera controls..." - # Helper function to escape strings for JSON - json_escape() { - local str="$1" - # Escape backslashes first, then quotes - str="${str//\\/\\\\}" - str="${str//\"/\\\"}" - # Remove any control characters - str=$(echo "$str" | tr -d '\n\r\t') - echo "$str" - } + # Get v4l2-ctl output + local v4l2_output + v4l2_output=$(v4l2-ctl -d "$VIDEO_DEVICE" -L 2>/dev/null) || true - # Parse v4l2-ctl -L output into JSON - # Example lines: - # brightness 0x00980900 (int) : min=-64 max=64 step=1 default=0 value=0 - # exposure_auto 0x009a0901 (menu) : min=0 max=3 default=3 value=3 - # 1: Manual Mode - # 3: Aperture Priority Mode - - local controls_json="[" - local current_values_json="{" - local first_control=true - local first_value=true - local current_ctrl="" - local ctrl_json="" - local menu_options="" - local in_menu=false - - while IFS= read -r line || [[ -n "$line" ]]; do - # Check if this is a control line (starts with control name) - if [[ "$line" =~ ^[[:space:]]*([a-z_][a-z0-9_]*)[[:space:]]+0x[0-9a-f]+[[:space:]]+\(([a-z]+)\)[[:space:]]*:[[:space:]]*(.*) ]]; then - # Save previous control if exists - if [[ -n "$current_ctrl" ]]; then - if [[ "$in_menu" == "true" && -n "$menu_options" ]]; then - ctrl_json="${ctrl_json%, }, \"options\": {${menu_options%,}}}" - else - ctrl_json="${ctrl_json%,}}" - fi - if [[ "$first_control" == "true" ]]; then - first_control=false - else - controls_json+="," - fi - controls_json+="$ctrl_json" - fi - - current_ctrl="${BASH_REMATCH[1]}" - local ctrl_type="${BASH_REMATCH[2]}" - local params="${BASH_REMATCH[3]}" - - ctrl_json="{\"name\": \"$current_ctrl\", \"type\": \"$ctrl_type\"," - menu_options="" - in_menu=false - - # Parse parameters - if [[ "$params" =~ min=(-?[0-9]+) ]]; then - ctrl_json+=" \"min\": ${BASH_REMATCH[1]}," - fi - if [[ "$params" =~ max=(-?[0-9]+) ]]; then - ctrl_json+=" \"max\": ${BASH_REMATCH[1]}," - fi - if [[ "$params" =~ step=([0-9]+) ]]; then - ctrl_json+=" \"step\": ${BASH_REMATCH[1]}," - fi - if [[ "$params" =~ default=(-?[0-9]+) ]]; then - ctrl_json+=" \"default\": ${BASH_REMATCH[1]}," - fi - if [[ "$params" =~ value=(-?[0-9]+) ]]; then - local current_value="${BASH_REMATCH[1]}" - if [[ "$first_value" == "true" ]]; then - first_value=false - else - current_values_json+="," - fi - current_values_json+="\"$current_ctrl\": $current_value" - fi - - if [[ "$ctrl_type" == "menu" ]]; then - in_menu=true - fi - # Check if this is a menu option line (indented with number: description) - elif [[ "$in_menu" == "true" && "$line" =~ ^[[:space:]]+([0-9]+):[[:space:]]+(.+)$ ]]; then - local opt_val="${BASH_REMATCH[1]}" - local opt_name - opt_name=$(json_escape "${BASH_REMATCH[2]}") - menu_options+="\"$opt_val\": \"$opt_name\"," - fi - done < <(v4l2-ctl -d "$VIDEO_DEVICE" -L 2>/dev/null) - - # Save last control - if [[ -n "$current_ctrl" ]]; then - if [[ "$in_menu" == "true" && -n "$menu_options" ]]; then - ctrl_json="${ctrl_json%, }, \"options\": {${menu_options%,}}}" - else - ctrl_json="${ctrl_json%,}}" - fi - if [[ "$first_control" != "true" ]]; then - controls_json+="," - fi - controls_json+="$ctrl_json" + if [[ -z "$v4l2_output" ]]; then + log "No v4l2 controls found" + return fi - controls_json+="]" - current_values_json+="}" + local payload + + if command -v jq &>/dev/null; then + # Use jq for proper JSON generation + local controls_array="[]" + local values_obj="{}" + local current_ctrl="" + local current_type="" + local current_min="" + local current_max="" + local current_step="" + local current_default="" + local current_value="" + local menu_opts="{}" + + while IFS= read -r line; do + # Control line pattern: name 0xhex (type) : params + if [[ "$line" =~ ^[[:space:]]*([a-z_][a-z0-9_]*)[[:space:]]+0x[0-9a-f]+[[:space:]]+\(([a-z]+)\)[[:space:]]*:[[:space:]]*(.*) ]]; then + # Save previous control + if [[ -n "$current_ctrl" ]]; then + local ctrl_obj + ctrl_obj=$(jq -n \ + --arg name "$current_ctrl" \ + --arg type "$current_type" \ + --argjson min "${current_min:-null}" \ + --argjson max "${current_max:-null}" \ + --argjson step "${current_step:-null}" \ + --argjson default "${current_default:-null}" \ + --argjson opts "$menu_opts" \ + '{name: $name, type: $type} + + (if $min != null then {min: $min} else {} end) + + (if $max != null then {max: $max} else {} end) + + (if $step != null then {step: $step} else {} end) + + (if $default != null then {default: $default} else {} end) + + (if ($opts | length) > 0 then {options: $opts} else {} end)') + controls_array=$(echo "$controls_array" | jq --argjson ctrl "$ctrl_obj" '. + [$ctrl]') + + if [[ -n "$current_value" ]]; then + values_obj=$(echo "$values_obj" | jq --arg k "$current_ctrl" --argjson v "$current_value" '. + {($k): $v}') + fi + fi + + current_ctrl="${BASH_REMATCH[1]}" + current_type="${BASH_REMATCH[2]}" + local params="${BASH_REMATCH[3]}" + menu_opts="{}" + current_min="" current_max="" current_step="" current_default="" current_value="" + + [[ "$params" =~ min=(-?[0-9]+) ]] && current_min="${BASH_REMATCH[1]}" + [[ "$params" =~ max=(-?[0-9]+) ]] && current_max="${BASH_REMATCH[1]}" + [[ "$params" =~ step=([0-9]+) ]] && current_step="${BASH_REMATCH[1]}" + [[ "$params" =~ default=(-?[0-9]+) ]] && current_default="${BASH_REMATCH[1]}" + [[ "$params" =~ value=(-?[0-9]+) ]] && current_value="${BASH_REMATCH[1]}" + + # Menu option line + elif [[ "$current_type" == "menu" && "$line" =~ ^[[:space:]]+([0-9]+):[[:space:]]+(.+)$ ]]; then + local opt_key="${BASH_REMATCH[1]}" + local opt_val="${BASH_REMATCH[2]}" + menu_opts=$(echo "$menu_opts" | jq --arg k "$opt_key" --arg v "$opt_val" '. + {($k): $v}') + fi + done <<< "$v4l2_output" + + # Save last control + if [[ -n "$current_ctrl" ]]; then + local ctrl_obj + ctrl_obj=$(jq -n \ + --arg name "$current_ctrl" \ + --arg type "$current_type" \ + --argjson min "${current_min:-null}" \ + --argjson max "${current_max:-null}" \ + --argjson step "${current_step:-null}" \ + --argjson default "${current_default:-null}" \ + --argjson opts "$menu_opts" \ + '{name: $name, type: $type} + + (if $min != null then {min: $min} else {} end) + + (if $max != null then {max: $max} else {} end) + + (if $step != null then {step: $step} else {} end) + + (if $default != null then {default: $default} else {} end) + + (if ($opts | length) > 0 then {options: $opts} else {} end)') + controls_array=$(echo "$controls_array" | jq --argjson ctrl "$ctrl_obj" '. + [$ctrl]') + + if [[ -n "$current_value" ]]; then + values_obj=$(echo "$values_obj" | jq --arg k "$current_ctrl" --argjson v "$current_value" '. + {($k): $v}') + fi + fi + + payload=$(jq -n --argjson controls "$controls_array" --argjson values "$values_obj" \ + '{controls: $controls, currentValues: $values}') + else + # Fallback: simple approach without jq (no menu options, basic controls only) + log "jq not available, using simple control discovery" + local controls="[" + local values="{" + local first_ctrl=true + local first_val=true + + while IFS= read -r line; do + if [[ "$line" =~ ^[[:space:]]*([a-z_][a-z0-9_]*)[[:space:]]+0x[0-9a-f]+[[:space:]]+\(([a-z]+)\)[[:space:]]*:.*value=(-?[0-9]+) ]]; then + local name="${BASH_REMATCH[1]}" + local type="${BASH_REMATCH[2]}" + local value="${BASH_REMATCH[3]}" + + [[ "$first_ctrl" == "true" ]] && first_ctrl=false || controls+="," + controls+="{\"name\":\"$name\",\"type\":\"$type\"}" + + [[ "$first_val" == "true" ]] && first_val=false || values+="," + values+="\"$name\":$value" + fi + done <<< "$v4l2_output" + + controls+="]" + values+="}" + payload="{\"controls\":$controls,\"currentValues\":$values}" + fi # POST to server - local payload="{\"controls\": $controls_json, \"currentValues\": $current_values_json}" - local response response=$(curl -s --max-time 10 \ -X POST \ @@ -296,7 +314,11 @@ discover_and_report_controls() { if echo "$response" | grep -q '"success"'; then local count - count=$(echo "$response" | grep -o '"controlsRegistered":[0-9]*' | cut -d: -f2) + if command -v jq &>/dev/null; then + count=$(echo "$response" | jq -r '.controlsRegistered // "?"') + else + count=$(echo "$response" | grep -o '"controlsRegistered":[0-9]*' | cut -d: -f2) + fi log "Reported $count available controls to server" else log "Failed to report available controls: $response"