EyeToy

From RGDWiki


A PlayStation 2 EyeToy produced by Logitech.

The EyeToy is a digital camera, similar to a webcam, for the PlayStation 2. The device also supports the PlayStation 3 in some titles, in place of a PlayStation Eye camera. The technology uses computer vision and gesture recognition to process images taken by the camera. This allows players to interact with games using motion, colour detection and sound using its built-in microphone. It was released in October 2003.

The camera was originally manufactured by Logitech (known as "Logicool" in Japan), but newer EyeToys were manufactured by Namtai. The camera is mainly used for playing EyeToy games developed by Sony and other companies. It is not intended for use as a normal PC camera, although some people have developed unofficial drivers for it. As of 2008, the EyeToy has sold over 10 million units worldwide.

Use in games

The EyeToy is the primary device in 25 PS2 games, in which it is required to play the game. All of these games, aside from the two Korean-exclusive games, were released in PAL territories - but only 8 were released in North America.

Game title Year released Developer Publisher
EyeToy: Play July 2003 (Europe)
November 2003 (North America)
SCE London Studio Sony Computer Entertainment
EyeToy: Groove November 2003 (Europe)
April 2004 (North America)
SCE London Studio Sony Computer Entertainment
U-Move Super Sports July 2004 (Japan)
October 2004 (Europe)
Konami Konami
EyeToy: Monkey Mania August 2004 (Japan)
March 2005 (Europe)
SCE Japan Studio Sony Computer Entertainment
Nicktoons Movin' October 2004 (North America)
November 2004 (Europe)
Mass Media THQ
Sega Superstars October 2004 (North America)
November 2004 (Europe)
Sonic Team Sega
EyeToy: AntiGrav November 2004 (North America)
March 2005 (Europe)
Harmonix Sony Computer Entertainment
EyeToy: Play 2 November 2004 (Europe)
August 2005 (North America)
SCE London Studio Sony Computer Entertainment
Disney Move November 2004 (Europe) Artificial Mind & Movement Buena Vista Games
EyeToy: EduKids January 2005 (Korea) SCE Korea
Arisu Media
Sony Computer Entertainment Korea
EyeToy: Chat February 2005 (Europe) SCE London Studio Sony Computer Entertainment Europe
EyeToy: Tales March 2005 (Korea) SCE Korea Sony Computer Entertainment Korea
YetiSports Arctic Adventures July 2005 (Europe) Pirate Games JoWooD Productions
EyeToy: Kinetic September 2005 (Europe)
November 2005 (North America)
SCE London Studio]] Sony Computer Entertainment
SpyToy October 2005 (Europe)
November 2005 (North America)
SCE London Studio Sony Computer Entertainment
EyeToy: Play 3 November 2005 (Europe) SCE London Studio Sony Computer Entertainment Europe
Rythmic Star! April 2006 (Europe) Namco Ignition Entertainment/Namco
Clumsy Shumsy October 2006 (Europe) Phoenix Games Phoenix Games
EyeToy: Kinetic Combat November 2006 (Europe) SCE London Studio Sony Computer Entertainment Europe
EyeToy Play: Sports November 2006 (Europe) SCE London Studio Sony Computer Entertainment Europe
Bob the Builder August 2007 (Europe) Atomic Planet Entertainment Mastertronic Group
Thomas & Friends: A Day at the Races August 2007 (Europe) Broadsword Interactive Mastertronic Group
EyeToy Play: Astro Zoo November 2007 (Europe) SCE London Studio Sony Computer Entertainment Europe
EyeToy Play: Hero 2008 (Europe) SCE London Studio Sony Computer Entertainment Europe
EyeToy Play: PomPom Party 2008 (Europe) SCE London Studio Sony Computer Entertainment Europe

The following games support the EyeToy, typically with an "EyeToy Enhanced" label on the box. The ^ symbol denotes a PlayStation 3 title; the rest are for PlayStation 2. It is often used for mini-games, pictures taken for an in-game avatar or other similar use cases.

  • AFL Premiership 2005
  • AFL Premiership 2006
  • AND 1 Streetball
  • Buzz! The Music Quiz
  • Buzz! The Big Quiz
  • Buzz! The Mega Quiz
  • CMT Presents: Karaoke Revolution Country
  • Dance Dance Revolution Extreme
  • DDR Festival Dance Dance Revolution
  • Dancing Stage Fusion
  • Dance Dance Revolution Extreme 2
  • Dancing Stage Max
  • Dance Dance Revolution Strike
  • Dance Dance Revolution SuperNova (also known as Dancing Stage SuperNova)
  • Dance Dance Revolution SuperNova 2 (also known as Dancing Stage SuperNova 2)
  • Dance Factory
  • DT Racer
  • Formula One 05
  • Flow: Urban Dance Uprising
  • Gaelic Games: Football
  • Get On Da Mic
  • Go! Puzzle^
  • Gretzky NHL 2005
  • Harry Potter and the Prisoner of Azkaban
  • Jackie Chan Adventures
  • Karaoke Revolution Party
  • Karaoke Revolution Presents: American Idol
  • Lemmings
  • LittleBigPlanet^
  • LMA Manager 2005
  • MLB 2005
  • MLB '06: The Show
  • MLB '07: The Show
  • MLB '08: The Show
  • MLB '09: The Show
  • NBA 07
  • The Polar Express
  • Racing Battle: C1 Grand Prix
  • SingStar series
  • The Sims 2
  • Stuart Little 3: Big Photo Adventure
  • This Is Football 2005
  • Tony Hawk's Underground 2
  • Tony Hawk's American Wasteland
  • The Urbz: Sims in the City
  • Who Wants To Be A Millionaire? Party Edition
  • World Tour Soccer 2006
  • YetiSports Arctic Adventures


Design

The EyeToy is designed to record 320x240 video at 25 frames per second. Despite the fact that no official drivers were offered for Windows, macOS or Linux, drivers have been created to use the EyeToy with any modern operating system and Linux sports built-in drivers for the device. There are 3 models of the EyeToy:

  • SLEH-00030
  • SLEH-00031
  • SCEH-0004

Of these models, the first two were the original models produced by Logitech, while SCEH-0004 is the later model produced by Namtai. The later model is slightly smaller and is silver in colour, whereas the original model(s) are black.

Issues

The development team of LittleBigPlanet encountered an issue with the EyeToy just two weeks before the game went gold. A Japanese quality assurance tester started to reliably crash the game by leaving it running overnight. The issue baffled the development team, who were now under time pressure to fix a game-breaking bug. After an investigation, they found the game was always crashing for the tester at 4 am. Puzzled, the development team looked at what happened at 4 am every night, and their answer came. The cleaners arrived, and the white noise generated by the vacuum caused the in-game audio chat compression to leak a few bytes of memory - and over the course of an hour, this caused the game to crash.

It is unknown if this issue affects other EyeToy titles or whether this bug was specific to the LittleBigPlanet code.

Development

The EyeToy uses the PS2's Image Processing Unit (IPU) to decode the incoming feed to a video in hardware on-the-fly and the result is sent to the PS2's GPU, the Graphics Synthesizer (GS). Here is a snippet of decoding code for the EyeToy, going through the process of decoding the video to RGBA32:

//------------------------------------------------------------------------------
// Syntax:
// void decodeToRGBA32( void* bitstreamIn,  Input bitstream 
//                      void* mbOut,        Buffer to recieve the macroblocks
//                      int width,          Width of texture in pixels
//                      int height)         Height of texture in pixels
//
// Description:
// Begins decoding RGBA32 texture on the IPU.  
//
// Note: This IPU decoding should complete within about 6-7% of a frame.  This 
// function only begins the decoding process.  If you wait a full frame, it 
// can be guaranteed that decoding will be complete.  Otherwise, do the 
// following to determine completion:
//
//  // Wait for completion of DMA transfer from IPU
//  decodeSync()
//
// Return value:
// DECODE_SUCCESS    Successful
// DECODE_ERROR      Something was wrong with the bitstream
//
//------------------------------------------------------------------------------
int decodeToRGBA32( void* bitstreamIn, void* mbOut, int width, int height ) {
	u_int flags;
	int mbx = width/16;
	int mby = height/16; 
	sceEyeToyIpuHeader *header = bitstreamIn;

	// bitstream must have valid id    
	if ( ((u_int*)bitstreamIn)[0]!=IPU_ID )
		return (DECODE_ERROR);

	// Make sure that the bitstream size is correct
	if ( header->width != width || header->height != height ) {
		scePrintf("Error, size mismatch.  Expected: %d %d actual: %d %d\n", 
					width, height, header->width, header->height);
		return (DECODE_ERROR);
	}

	// Setup the IPU for decoding
	if ( DecodeIpuSetup(bitstreamIn, &flags) == DECODE_ERROR )
		return (DECODE_ERROR);

	// Execute IDEC command.  Decodes bitstream to RGB32 format
	sceIpuIDEC(SCE_IPU_IDEC_RGB32, SCE_IPU_IDEC_NODITHER, 
				SCE_IPU_IDEC_NOOFFSET, DTD(flags), 1, 0 );

	// Recieve data from IPU     
	sceDmaRecvN(sceDmaGetChan(3), mbOut, (sizeof(sceIpuRGB32)/16)*mbx*mby);

	return (DECODE_SUCCESS);
}

After the decoding on the IPU, the output is sent to the GS. The GS typically sets up a double-buffered 640x224 render target (halved vertical resolution because the output will be 480i, therefore only displaying half the vertical resolution at a time; 448 instead of 480 to account for television overscan) like so:

static DispEnv gDispBuffer[2] __attribute__ ((aligned(64)));
static DrawBuf gDrawBuffer[2] __attribute__ ((aligned(64)));

static u_int gScreenWidth   = (640);
static u_int gScreenHeight  = (224);
static u_int gVideoMode     = (NTSC_FRAME224);
static u_int gDispPSM       = (SCE_GS_PSMCT32);

The macroblock stream from the EyeToy is treated as a texture by the graphics interface (GIF) through to VRAM and then the screen - the following code is a typical example of this process.

//------------------------------------------------------------------------------
// Syntax:
// static void GsPktAddTextureUpoad( sceGifPacket *gifpkt, 
//								GS_CAM_WINDOW *camWindow, 
//								const u_long *giftag  ) 
//
// Description:
// Adds a texture upload to the gif packet.  If the texture is already in 
// row order, it will be uploaded as is.  If the texture is in macroblock order,
// the texture will be rearranged to row order during the BITBLT into VRAM.
//
// Return value:
// 
//------------------------------------------------------------------------------
static void GsPktAddTextureUpoad( sceGifPacket *gifpkt, GS_CAM_WINDOW *camWindow, const u_long *giftag ) {
	u_int dbw, pixelBytes, i=0, j=0;
	void *textureData = camWindow->texData;
	u_long* tag;

	sceGifPkCnt( gifpkt, 0, 0, 0);

	if ( camWindow->texPSM == SCE_GS_PSMCT32 ) {
		pixelBytes = 4;
	}
	else if ( camWindow->texPSM == SCE_GS_PSMCT24 ) {
		pixelBytes = 3;
	}
	else {
		pixelBytes = 2;
	}

	dbw = (camWindow->texMetrics[0]/64) + (camWindow->texMetrics[0]%64 > 0);

	if ( camWindow->texFormatting & TEXFORMAT_REARRANGE ) {
		// Do macroblock rearrange during the BITBLT up to VRAM
		sceGifPkOpenGifTag( gifpkt, *(u_long128*) giftag );
		sceGifPkAddGsAD(gifpkt, 
			SCE_GS_BITBLTBUF,
			SCE_GS_SET_BITBLTBUF(0, 0, 0, VRAM_TEXADDR, (BOUND(camWindow->texMetrics[0],  64))/64, camWindow->texPSM));
		sceGifPkAddGsAD(gifpkt, SCE_GS_TRXREG, SCE_GS_SET_TRXREG(16, 16));
		sceGifPkCloseGifTag(gifpkt);

		for ( i=0; i<(camWindow->texMetrics[1] >> 4); i++ ) {
			for ( j=0; j<(camWindow->texMetrics[0] >> 4); j++ ) {
				sceGifPkCnt( gifpkt, 0, 0, 0 );
				sceGifPkOpenGifTag( gifpkt, *(u_long128*) giftag );
				sceGifPkAddGsAD( gifpkt, 
					SCE_GS_TRXPOS,
					SCE_GS_SET_TRXPOS(0, 0, j<<4, i<<4, 0));
				sceGifPkAddGsAD( gifpkt, 
					SCE_GS_TRXDIR, 
					SCE_GS_SET_TRXDIR(0));
				sceGifPkCloseGifTag(gifpkt );

				tag = (u_long*) sceGifPkReserve( gifpkt, 4);
				tag[0] = SCE_GIF_SET_TAG((pixelBytes<<4), 0, 0, 0, 2, 0);
				tag[1] = 0;

				sceGifPkRef( gifpkt, 
					(void *) ((u_int) textureData&0x0fffffff), 
					(pixelBytes<<4), 0, 0, 0);
				textureData = (u_char*) textureData + (pixelBytes<<8);         
			}
		}
	}
	else {
		// Assume that the macroblocks are already rearranged
		sceGifPkOpenGifTag( gifpkt, *(u_long128*) giftag );
		sceGifPkAddGsAD( gifpkt, 
			SCE_GS_BITBLTBUF,
			SCE_GS_SET_BITBLTBUF(0, 0, 0, VRAM_TEXADDR, (BOUND(camWindow->texMetrics[0], 64))/64, camWindow->texPSM));
		sceGifPkAddGsAD( gifpkt, 
			SCE_GS_TRXREG, 
			SCE_GS_SET_TRXREG(camWindow->texMetrics[0], camWindow->texMetrics[1]));
		sceGifPkAddGsAD( gifpkt,
			SCE_GS_TRXPOS, 
			SCE_GS_SET_TRXPOS(0, 0, 0, 0, 0));
		sceGifPkAddGsAD( gifpkt, 
			SCE_GS_TRXDIR, 
			SCE_GS_SET_TRXDIR(0));
		sceGifPkCloseGifTag( gifpkt );

		tag = (u_long*) sceGifPkReserve( gifpkt, 4);
		tag[0] = SCE_GIF_SET_TAG( camWindow->texMetrics[0]*camWindow->texMetrics[1]*pixelBytes/16, 0, 0, 0, 2, 0);
		tag[1] = 0;

		sceGifPkRef(gifpkt, 
			(void *) ((u_int) textureData&0x0fffffff), 
			camWindow->texMetrics[0]*camWindow->texMetrics[1]*pixelBytes/16, 
			0, 
			0, 
			0);         
	}

	sceGifPkEnd( gifpkt, 0, 0, 0);
}