Wednesday 18 July 2007

UPDATE: Fix status timeout: status=0xd0 { Busy }

I got a nice Acer laptop for not much from PC World, but it has a sucky TSSTcorpCD/DVDW TS-L632D, ATAPI  CD/DVD-ROM drive. The manufacturers have released a firmware update, but not for my OEM. Bah!

Gareth and Gareth have explained how to do a firmware update which (as it works for me) is a better solution:

See https://bugs.launchpad.net/ubuntu/+source/linux-source-2.6.20/+bug/75295

My own solution below is buggy and prone to calling the atapi reset TWICE (hanging the machine). I have a fix (not with me as I edit this now) which prevents the atapi reset being called twice in one minute, but I think the better solution is the firmware update.

As a consequence if the moon shakes a bit too much or one of the electrons gets a bit too hot, the CDROM drive stops responding. For windows users it disappears from My Computer altogether and they have to reboot.

Linux users are both better and worse off. Worse because the system hangs 4 seconds out of 5 and the syslog fills up with messages like "/dev/hdc: status timeout: status=0xd0 { Busy }"

If you can bear to slowly get a command window, a bit of "sudo hdparm -w /dev/hdc" will fix it, but thats too painful.

So, this script will fix it. You need to modify /etc/syslog.conf to add this line "kern.* /var/log/kern.fifo" to log kernel messages to a named pipe from which the script will read.

Install the script in /usr/local/sbin/fixhdc, and run it from /etc/rc.local with: "setsid /usr/local/sbin/fixhdc &"

#! /bin/bash
# /usr/local/sbin/fixhdc
# by Sam Liddicott
# Monitor kernel messages to see if the cdrom is jiggered
# If so do an atapi reset
# First add this line to /etc/syslog.conf
# kern.* /var/log/kern.fifo
# Then run this script from /etc/rc.local with:
# setsid /usr/local/sbin/fixhdc &

KERNFIFO=/var/log/kern.fifo
CDROM=hdc

CDROM_NODE="/dev/$CDROM"

daemon() {
  while read logline
  do
    case "$logline" in
      *"$CDROM: status timeout: status=0xd0 { Busy }"*)
        hdparm -w "$CDROM_NODE"
        logger -t ‘kern.crit’ — "$0 reset $CDROM"
      ;;
      *) echo "Ignore $logline";;
    esac
  done
}

while :
do
  if test -p "$KERNFIFO" || mkfifo "$KERNFIFO"
  then daemon < "$KERNFIFO" &
       # restart syslog now we have the pipe open
       /etc/init.d/sysklogd reload
       # it probably won’t finish till syslog does
       wait
       logger — "Restarting $0"
  else echo "Can’t make fifo $KERNFIFO"
       exit 1
  fi
done

Thursday 12 July 2007

Windows-only IP camera on linux

I got a nice panning VGA camera for £69.00, but it’s one of those cameras from hell that only supports internet explorer on windows and crashes windows a lot.

As far as I can tell generically it is an SQ IP Cam or a GP-280 IP Camera.

I want to use it on linux, so out comes ethereal to do some packet sniffing and here is what I get:

I can get a video stream (special header and concatenated JPEG’s) with this URL:

http://192.168.1.69/cgi-bin/Stream?Video?Acc=USERNAME?Pwd=PASSWORD?webcamPWD=RookCookies00000

Possibly the RootCookies bit is important, perhaps related to a login that occurred earlier.

I can move the camera wth:
http://192.168.1.69/motor_control_right?webcamPWD=RootCookie00000

http://192.168.1.69/motor_control_left?webcamPWD=RootCookie00000

http://192.168.1.69/motor_control_up?webcamPWD=RootCookie00000

http://192.168.1.69/motor_control_down?webcamPWD=RootCookie00000

For horizontal and vertical centre

http://192.168.1.69/motor_control_auto_h?webcamPWD=RootCookie00000

http://192.168.1.69/motor_control_auto_v?webcamPWD=RootCookie00000

Full centre

http://192.168.1.69/motor_control_center?webcamPWD=RootCookie00000

To stop

http://192.168.1.69/motor_control_stop?webcamPWD=RootCookie00000

The special header on the video stream is a 40 byte header. I’m expecting certain values so they were easy to find:

Stream Header
Offset Format Data
0×16-0×17 16 bit integer LSB size of jpeg in bytes, or in other words, the number of bytes to read after this header before the start of the next header
0×1a-0×1b 16 bit integer LSB jpeg width in pixels
0×1c-0×1d 16 bit integer LSB jpeg height in pixels
0×28- Start of JPEG header

So stripping off the first 40 byte with
dd if=stream of=1.jpeg bs=1 skip=40

leaves me a jpeg (with loads more jpegs on the end, ech prefixed by the same 28 byte header).

So I can control the camera and rip video from it, now to make it like zoneminder.

UPDATE:

I can watch a video stream with VLC using:

wget -O /dev/stdout http://192.168.1.69/cgi-bin/Stream?Video?Acc=USERNAME?Pwd=PASSWORD?webcamPWD=RookCookies00000 |dd  skip=40 bs=1 | vlc -

I modified axmjpeg so it can split images from my camera too.

Update: 20 Sept 2007
This patch has been reworked applied to the standard axmjpeg project. (a while ago!)

This script can be used to control the camera from zoneminder:

#! /bin/bash
preset=home
presets="nothing motor_control_auto_h motor_control_auto_v motor_control_stop motor_control_center"

while test -n "$1"
do
  case "$1" in
    --address=*) address="${1##--address=}";;
    --address)   address="$2"; shift;;
    --command=*) command="${1##--command=}";;
    --command)   command="$2"; shift;;
    --preset=*)  preset="${1##--command=}";;
    --preset)    preset="$2"; shift;;
  esac
  shift;
done

url_base="http://$address/"
url_tail="?webcamPWD=RootCookie00000"

case "$command" in
  # home goto
  preset_home) url_command="motor_control_center";;
  preset_goto) set $presets; shift "$preset"; url_command="$1";;
  # right left down up
  move_rel_*) url_command="motor_control_${command##move_rel_}" ;;
esac

url="$url_base$url_command$url_tail"
wget -O /dev/null "$url" 2>/dev/null


UPDATE snapshot

I found a URL that can be used for a single snapshot (based on this http://ultradigital.freshdesk.com/support/solutions/articles/77478-comment-acc-der-au-flux-vid-o-d-une-cam-ra-ip-9060-i-9060-o-ou-9060a-mp):

http://192.168.1.69/snapshot.jpg?account=admin&password=password

Also related source code here: http://read.pudn.com/downloads65/sourcecode/internet/webserver/235705/Net_apps/httpd/api/HttpFileSystem.c__.htm

UPDATE streaming

I can play the stream like this:

ffplay -f mjpeg -probesize 40 -i 'http://192.168.1.69/cgi-bin/Stream?Video?Acc=admin?Pwd=password?webcamPWD=RookCookies00000&f=file.mjpg'

tip from http://www.lavrsen.dk/foswiki/bin/view/Motion/MplayerMjpegStreamViewing

but it seems more reliable to use axmjpeg to remove the extra data and output just a concatenation of jpegs:

axmjpeg -N -o /dev/stdout  'http://192.168.1.69/cgi-bin/Stream?Video?Acc=admin?Pwd=password?webcamPWD=RookCookies00000' | ffplay -f mjpeg /dev/stdin