Current version of my USB Webcam driver. WORK IN PROGRESS!

Uses the USB Kit (userland API, needs libusb) to publish a 
media node representing the webcam.
It currently only works with my Sonix webcam (3Euro cheapo 
cam), but is modular enough to easily expand it, some code 
is already there to detect Quickcams.
For now you should be able to build it under Zeta with the 
makefile provided.
Making a Jamfile might get tricky as several source files are 
created by the makefile itself to include addons and censors 
in the build.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@18670 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
François Revol
2006-08-28 16:01:41 +00:00
parent ad99025a4b
commit 2c9bd70310
37 changed files with 4581 additions and 0 deletions
@@ -0,0 +1,179 @@
#include <support/Autolock.h>
#include <media/MediaFormats.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "AddOn.h"
#include "Producer.h"
#include "CamRoster.h"
#include "CamDebug.h"
#include "CamDevice.h"
WebCamMediaAddOn::WebCamMediaAddOn(image_id imid)
: BMediaAddOn(imid),
fInitStatus(B_NO_INIT),
fRoster(NULL)
{
PRINT((CH "()" CT));
/* Customize these parameters to match those of your node */
fMediaFormat.type = B_MEDIA_RAW_VIDEO;
fMediaFormat.u.raw_video = media_raw_video_format::wildcard;
fMediaFormat.u.raw_video.interlace = 1;
fMediaFormat.u.raw_video.display.format = B_RGB32;
FillDefaultFlavorInfo(&fDefaultFlavorInfo);
fRoster = new CamRoster(this);
fRoster->Start();
// if( fRoster->CountCameras() < 1 )
/* fInitStatus = B_ERROR;
else
*/
fInitStatus = B_OK;
}
WebCamMediaAddOn::~WebCamMediaAddOn()
{
delete fRoster;
}
status_t
WebCamMediaAddOn::InitCheck(const char **out_failure_text)
{
if (fInitStatus < B_OK) {
*out_failure_text = "No cameras attached";
return fInitStatus;
}
return B_OK;
}
int32
WebCamMediaAddOn::CountFlavors()
{
PRINT((CH "()" CT));
int32 count;
if (!fRoster)
return B_NO_INIT;
if (fInitStatus < B_OK)
return fInitStatus;
/* This addon only supports a single flavor, as defined in the
* constructor */
count = fRoster->CountCameras();
return count;//(count > 0)?count:1;//1;
}
/*
* The pointer to the flavor received only needs to be valid between
* successive calls to BMediaAddOn::GetFlavorAt().
*/
status_t
WebCamMediaAddOn::GetFlavorAt(int32 n, const flavor_info **out_info)
{
PRINT((CH "(%d, ) roster %p is %lx" CT, n, fRoster, fInitStatus));
int32 count;
CamDevice* cam;
if (!fRoster)
return B_NO_INIT;
if (fInitStatus < B_OK)
return fInitStatus;
count = fRoster->CountCameras();
PRINT((CH ": %d cameras" CT, count));
if (n >= count)//(n != 0)
return B_BAD_INDEX;
fRoster->Lock();
cam = fRoster->CameraAt(n);
*out_info = &fDefaultFlavorInfo;
if (cam && cam->FlavorInfo())
*out_info = cam->FlavorInfo();
fRoster->Unlock();
PRINT((CH ": returning flavor for %d" CT, n));
return B_OK;
}
BMediaNode *
WebCamMediaAddOn::InstantiateNodeFor(
const flavor_info *info, BMessage* /*_config*/, status_t* /*_out_error*/)
{
PRINT((CH "()" CT));
VideoProducer *node;
CamDevice *cam=NULL;
if (fInitStatus < B_OK)
return NULL;
fRoster->Lock();
for (int i = 0; i < fRoster->CountCameras(); i++) {
CamDevice *c;
c = fRoster->CameraAt(i);
PRINT((CH ": cam[%d]: %d, %s" CT, i, c->FlavorInfo()->internal_id, c->BrandName()));
if (c && (c->FlavorInfo()->internal_id == info->internal_id)) {
cam = c;
break;
}
}
fRoster->Unlock();
if (!cam)
return NULL;
#if 0
fRoster->Lock();
cam = fRoster->CameraAt(n);
*out_info = &fDefaultFlavorInfo;
if (cam && cam->FlavorInfo())
*out_info = cam->FlavorInfo();
fRoster->Unlock();
#endif
/* At most one instance of the node should be instantiated at any given
* time. The locking for this restriction may be found in the VideoProducer
* class. */
node = new VideoProducer(this, cam, cam->FlavorInfo()->name, fDefaultFlavorInfo.internal_id);
if (node && (node->InitCheck() < B_OK)) {
delete node;
node = NULL;
}
return node;
}
status_t
WebCamMediaAddOn::CameraAdded(CamDevice* device)
{
PRINT((CH "()" CT));
NotifyFlavorChange();
}
status_t
WebCamMediaAddOn::CameraRemoved(CamDevice* device)
{
PRINT((CH "()" CT));
NotifyFlavorChange();
}
void
WebCamMediaAddOn::FillDefaultFlavorInfo(flavor_info* info)
{
info->name = "USB Web Camera";
info->info = "USB Web Camera";
info->kinds = B_BUFFER_PRODUCER | B_CONTROLLABLE | B_PHYSICAL_INPUT;
info->flavor_flags = 0;//B_FLAVOR_IS_GLOBAL;
info->internal_id = atomic_add((vint32 *)&fInternalIDCounter, 1);
info->possible_count = 1;//0;
info->in_format_count = 0;
info->in_format_flags = 0;
info->in_formats = NULL;
info->out_format_count = 1;
info->out_format_flags = 0;
info->out_formats = &fMediaFormat;
}
BMediaAddOn *
make_media_addon(image_id imid)
{
return new WebCamMediaAddOn(imid);
}
@@ -0,0 +1,52 @@
#ifndef _VIDEO_ADDON_H
#define _VIDEO_ADDON_H
#include <media/MediaAddOn.h>
#define TOUCH(x) ((void)(x))
extern "C" _EXPORT BMediaAddOn *make_media_addon(image_id you);
class CamRoster;
class CamDevice;
class WebCamMediaAddOn : public BMediaAddOn
{
public:
WebCamMediaAddOn(image_id imid);
virtual ~WebCamMediaAddOn();
virtual status_t InitCheck(const char **out_failure_text);
virtual int32 CountFlavors();
virtual status_t GetFlavorAt(int32 n, const flavor_info ** out_info);
virtual BMediaNode *InstantiateNodeFor(
const flavor_info * info,
BMessage * config,
status_t * out_error);
virtual status_t GetConfigurationFor(BMediaNode *node, BMessage *message)
{ TOUCH(node); TOUCH(message); return B_OK; }
virtual status_t SaveConfigInfo(BMediaNode *node, BMessage *message)
{ TOUCH(node); TOUCH(message); return B_OK; }
virtual bool WantsAutoStart() { return false; }
virtual status_t AutoStart(int in_count, BMediaNode **out_node,
int32 *out_internal_id, bool *out_has_more)
{ TOUCH(in_count); TOUCH(out_node);
TOUCH(out_internal_id); TOUCH(out_has_more);
return B_ERROR; }
// those are for use by CamDevices
status_t CameraAdded(CamDevice* device);
status_t CameraRemoved(CamDevice* device);
void FillDefaultFlavorInfo(flavor_info* info);
private:
uint32 fInternalIDCounter;
status_t fInitStatus;
flavor_info fDefaultFlavorInfo;
media_format fMediaFormat;
CamRoster* fRoster;
};
#endif
@@ -0,0 +1,87 @@
#include "CamBufferedFilterInterface.h"
#include "CamDevice.h"
#include "CamDebug.h"
CamBufferedFilterInterface::CamBufferedFilterInterface(CamDevice *device, bool allowWrite)
: CamFilterInterface(device),
fAllowWrite(allowWrite)
{
}
CamBufferedFilterInterface::~CamBufferedFilterInterface()
{
}
ssize_t
CamBufferedFilterInterface::Read(void *buffer, size_t size)
{
return fInternalBuffer.Read(buffer, size);
}
ssize_t
CamBufferedFilterInterface::ReadAt(off_t pos, void *buffer, size_t size)
{
return fInternalBuffer.ReadAt(pos, buffer, size);
}
ssize_t
CamBufferedFilterInterface::Write(const void *buffer, size_t size)
{
if (!fAllowWrite)
return B_READ_ONLY_DEVICE;
return fInternalBuffer.Write(buffer, size);
}
ssize_t
CamBufferedFilterInterface::WriteAt(off_t pos, const void *buffer, size_t size)
{
if (!fAllowWrite)
return B_READ_ONLY_DEVICE;
return fInternalBuffer.WriteAt(pos, buffer, size);
}
off_t
CamBufferedFilterInterface::Seek(off_t position, uint32 seek_mode)
{
return fInternalBuffer.Seek(position, seek_mode);
}
off_t
CamBufferedFilterInterface::Position() const
{
return fInternalBuffer.Position();
}
status_t
CamBufferedFilterInterface::SetSize(off_t size)
{
if (!fAllowWrite)
return B_READ_ONLY_DEVICE;
return fInternalBuffer.SetSize(size);
}
size_t
CamBufferedFilterInterface::FrameSize()
{
return fInternalBuffer.BufferLength(); // XXX: really ??
return 0;
}
status_t
CamBufferedFilterInterface::DropFrame()
{
fInternalBuffer.SetSize(0LL);
if (fNextOfKin)
return fNextOfKin->DropFrame();
return B_OK;
}
status_t
CamBufferedFilterInterface::SetVideoFrame(BRect frame)
{
fVideoFrame = frame;
fInternalBuffer.SetSize(FrameSize()); // XXX: really ??
if (fNextOfKin)
return fNextOfKin->SetVideoFrame(frame);
return B_OK;
}
@@ -0,0 +1,40 @@
#ifndef _CAM_BUFFERED_FILTER_INTERFACE_H
#define _CAM_BUFFERED_FILTER_INTERFACE_H
#include <kernel/OS.h>
#include <support/DataIO.h>
#include <interface/Rect.h>
#include "CamFilterInterface.h"
class CamBufferedFilterInterface : public CamFilterInterface
{
public:
CamBufferedFilterInterface(CamDevice *device, bool allowWrite);
virtual ~CamBufferedFilterInterface();
// BPositionIO interface
virtual ssize_t Read(void *buffer, size_t size);
virtual ssize_t ReadAt(off_t pos, void *buffer, size_t size);
virtual ssize_t Write(const void *buffer, size_t size);
virtual ssize_t WriteAt(off_t pos, const void *buffer, size_t size);
virtual off_t Seek(off_t position, uint32 seek_mode);
virtual off_t Position() const;
virtual status_t SetSize(off_t size);
// size of the buffer required for reading a whole frame
virtual size_t FrameSize();
// frame handling
virtual status_t DropFrame();
// video settings propagation
virtual status_t SetVideoFrame(BRect frame);
protected:
bool fAllowWrite;
BMallocIO fInternalBuffer;
};
#endif /* _CAM_BUFFERED_FILTER_INTERFACE_H */
@@ -0,0 +1,120 @@
/*
* buffer based deframer
* buffers all packet until it finds a complete frame.
* simpler than StreamingDeframer, but doesn't work any better
* and hogs the cpu intermitently :^)
*/
#define CD_COL "31"
#include "CamBufferingDeframer.h"
#include "CamDevice.h"
#include "CamDebug.h"
#include <Autolock.h>
#define MAX_TAG_LEN CAMDEFRAMER_MAX_TAG_LEN
#define MAXFRAMEBUF CAMDEFRAMER_MAX_QUEUED_FRAMES
#define IB fInputBuffs[fInputBuffIndex]
CamBufferingDeframer::CamBufferingDeframer(CamDevice *device)
: CamDeframer(device),
fInputBuffIndex(0)
{
}
CamBufferingDeframer::~CamBufferingDeframer()
{
}
ssize_t
CamBufferingDeframer::Write(const void *buffer, size_t size)
{
uint8 *b;
int l;
int i, s, e;
int which;
fMinFrameSize = fDevice->MinRawFrameSize();
fMaxFrameSize = fDevice->MaxRawFrameSize();
IB.Write(buffer, size);
b = (uint8 *)IB.Buffer();
l = IB.BufferLength();
PRINT((CH "(%p, %d), IB: %d" CT, buffer, size, IB.BufferLength()));
if (l < fMinFrameSize + fSkipSOFTags + fSkipEOFTags)
return size; // not enough data anyway
if (!fCurrentFrame) {
BAutolock l(fLocker);
if (fFrames.CountItems() < MAXFRAMEBUF)
fCurrentFrame = AllocFrame();
else {
PRINT((CH "DROPPED %d bytes! (too many queued frames)" CT, size));
return size; // drop XXX
}
}
for (s = 0; (l - s > fMinFrameSize) && ((i = FindSOF(b + s, l - fMinFrameSize - s, &which)) > -1); s++) {
s += i;
if (s + fSkipSOFTags + fMinFrameSize + fSkipEOFTags > l)
break;
if (!fDevice->ValidateStartOfFrameTag(b + s, fSkipSOFTags))
continue;
PRINT((CH ": SOF[%d] at offset %d" CT, which, s));
PRINT((CH ": SOF: ... %02x %02x %02x %02x %02x %02x" CT, b[s+6], b[s+7], b[s+8], b[s+9], b[s+10], b[s+11]));
for (e = s + fSkipSOFTags + fMinFrameSize;
((e <= s + fSkipSOFTags + fMaxFrameSize) &&
(e < l) && ((i = 0*FindEOF(b + e, l - e, &which)) > -1));
e++) {
e += i;
//PRINT((CH ": EOF[%d] at offset %d" CT, which, s));
if (!fDevice->ValidateEndOfFrameTag(b + e, fSkipEOFTags, e - s - fSkipSOFTags))
continue;
PRINT((CH ": SOF= ... %02x %02x %02x %02x %02x %02x" CT, b[s+6], b[s+7], b[s+8], b[s+9], b[s+10], b[s+11]));
// we have one!
s += fSkipSOFTags;
// fill it
fCurrentFrame->Write(b + s, e - s);
// queue it
BAutolock f(fLocker);
PRINT((CH ": Detaching a frame (%d bytes, %d to %d / %d)" CT, (size_t)fCurrentFrame->Position(), s, e, l));
fCurrentFrame->Seek(0LL, SEEK_SET);
fFrames.AddItem(fCurrentFrame);
release_sem(fFrameSem);
// next Write() will allocate a new one
fCurrentFrame = NULL;
// discard the frame and everything before it.
DiscardFromInput(e + fSkipEOFTags);
return size;
}
}
return size;
}
size_t
CamBufferingDeframer::DiscardFromInput(size_t size)
{
int next = (fInputBuffIndex+1)%2;
PRINT((CH ": %d bytes of %d from buffs[%d] (%d left)" CT, size, IB.BufferLength(), fInputBuffIndex, IB.BufferLength() - size));
fInputBuffs[next].Seek(0LL, SEEK_SET);
fInputBuffs[next].SetSize(0);
uint8 *buff = (uint8 *)IB.Buffer();
if (IB.BufferLength() > size) {
buff += size;
fInputBuffs[next].Write(buff, IB.BufferLength() - size);
}
IB.Seek(0LL, SEEK_SET);
IB.SetSize(0);
fInputBuffIndex = next;
}
@@ -0,0 +1,25 @@
#ifndef _CAM_BUFFERING_DEFRAMER_H
#define _CAM_BUFFERING_DEFRAMER_H
#include "CamDeframer.h"
class CamBufferingDeframer : public CamDeframer
{
public:
CamBufferingDeframer(CamDevice *device);
virtual ~CamBufferingDeframer();
// BPositionIO interface
// write from usb transfers
virtual ssize_t Write(const void *buffer, size_t size);
size_t DiscardFromInput(size_t size);
private:
BMallocIO fInputBuffs[2];
int fInputBuffIndex;
};
#endif /* _CAM_BUFFERING_DEFRAMER_H */
@@ -0,0 +1,74 @@
#include "CamColorSpaceTransform.h"
#include "CamDebug.h"
/* I should replace this by a generic colorspace TranslatorAddOn... */
#undef B_WEBCAM_DECLARE_CSTRANSFORM
#define B_WEBCAM_DECLARE_CSTRANSFORM(trclass,trname) \
extern "C" CamColorSpaceTransform *Instantiate##trclass();
#include "CamInternalColorSpaceTransforms.h"
#undef B_WEBCAM_DECLARE_CSTRANSFORM
typedef CamColorSpaceTransform *(*TransformInstFunc)();
struct { const char *name; TransformInstFunc instfunc; } kTransformTable[] = {
#define B_WEBCAM_DECLARE_CSTRANSFORM(trclass,trname) \
{ #trname, &Instantiate##trclass },
#include "CamInternalColorSpaceTransforms.h"
{ NULL, NULL },
};
#undef B_WEBCAM_DECLARE_CSTRANSFORM
// -----------------------------------------------------------------------------
CamColorSpaceTransform::CamColorSpaceTransform()
: fInitStatus(B_NO_INIT),
fVideoFrame()
{
}
// -----------------------------------------------------------------------------
CamColorSpaceTransform::~CamColorSpaceTransform()
{
}
// -----------------------------------------------------------------------------
status_t
CamColorSpaceTransform::InitCheck()
{
return fInitStatus;
}
// -----------------------------------------------------------------------------
const char *
CamColorSpaceTransform::Name()
{
return "<unknown>";
}
// -----------------------------------------------------------------------------
color_space
CamColorSpaceTransform::OutputSpace()
{
return B_RGB32;
}
// -----------------------------------------------------------------------------
status_t
CamColorSpaceTransform::SetVideoFrame(BRect rect)
{
return ENOSYS;
}
// -----------------------------------------------------------------------------
CamColorSpaceTransform *
CamColorSpaceTransform::Create(const char *name)
{
int i;
for (i = 0; kTransformTable[i].name; i++) {
if (!strcmp(kTransformTable[i].name, name))
return kTransformTable[i].instfunc();
}
return NULL;
}
@@ -0,0 +1,37 @@
#ifndef _CAM_COLOR_SPACE_TRANSFORM_H
#define _CAM_COLOR_SPACE_TRANSFORM_H
#include "CamDevice.h"
#include <Rect.h>
// This class represents the camera's (cmos or whatever) sensor chip
class CamColorSpaceTransform
{
public:
CamColorSpaceTransform();
virtual ~CamColorSpaceTransform();
virtual status_t InitCheck();
virtual const char* Name();
virtual color_space OutputSpace();
virtual status_t SetVideoFrame(BRect rect);
virtual BRect VideoFrame() const { return fVideoFrame; };
static CamColorSpaceTransform *Create(const char *name);
protected:
status_t fInitStatus;
BRect fVideoFrame;
private:
};
// internal modules
#define B_WEBCAM_DECLARE_CSTRANSFORM(trclass,trname) \
extern "C" CamColorSpaceTransform *Instantiate##trclass(); \
CamColorSpaceTransform *Instantiate##trclass() \
{ return new trclass(); };
#endif /* _CAM_COLOR_SPACE_TRANSFORM_H */
@@ -0,0 +1,14 @@
#ifndef _CAM_DEBUG_H
#define _CAM_DEBUG_H
#include <Debug.h>
/* allow overriding ANSI color */
#ifndef CD_COL
#define CD_COL "34"
#endif
#define CH "\033[" CD_COL "mWebcam::%s::%s"
#define CT "\033[0m\n", __FILE__, __FUNCTION__
#endif
@@ -0,0 +1,194 @@
#define CD_COL "31"
#include "CamDeframer.h"
#include "CamDevice.h"
#include "CamDebug.h"
#include <Autolock.h>
#define MAX_TAG_LEN CAMDEFRAMER_MAX_TAG_LEN
#define MAXFRAMEBUF CAMDEFRAMER_MAX_QUEUED_FRAMES
CamDeframer::CamDeframer(CamDevice *device)
: CamFilterInterface(device),
fDevice(device),
fState(ST_SYNC),
fFrameSem(B_ERROR),
fLocker("CamDeframer Framelist lock", true),
fNumSOFTags(0),
fNumEOFTags(0)
{
fMinFrameSize = fDevice->MinRawFrameSize();
fMaxFrameSize = fDevice->MaxRawFrameSize();
fFrameSem = create_sem(0, "CamDeframer sem");
fCurrentFrame = AllocFrame();
}
CamDeframer::~CamDeframer()
{
delete_sem(fFrameSem);
BAutolock l(fLocker);
delete fCurrentFrame;
}
ssize_t
CamDeframer::Read(void *buffer, size_t size)
{
BAutolock l(fLocker);
CamFrame *f = (CamFrame *)fFrames.ItemAt(0);
if (!f)
return EIO;
return f->Read(buffer, size);
}
ssize_t
CamDeframer::ReadAt(off_t pos, void *buffer, size_t size)
{
BAutolock l(fLocker);
CamFrame *f = (CamFrame *)fFrames.ItemAt(0);
if (!f)
return EIO;
return f->ReadAt(pos, buffer, size);
}
off_t
CamDeframer::Seek(off_t position, uint32 seek_mode)
{
BAutolock l(fLocker);
CamFrame *f = (CamFrame *)fFrames.ItemAt(0);
if (!f)
return EIO;
return f->Seek(position, seek_mode);
}
off_t
CamDeframer::Position() const
{
BAutolock l((BLocker &)fLocker); // need to get rid of const here
CamFrame *f = (CamFrame *)fFrames.ItemAt(0);
if (!f)
return EIO;
return f->Position();
}
status_t
CamDeframer::SetSize(off_t size)
{
(void)size;
return EINVAL;
}
ssize_t
CamDeframer::Write(const void *buffer, size_t size)
{
(void)buffer;
(void)size;
return EINVAL;
}
ssize_t
CamDeframer::WriteAt(off_t pos, const void *buffer, size_t size)
{
(void)pos;
(void)buffer;
(void)size;
return EINVAL;
}
status_t
CamDeframer::WaitFrame(bigtime_t timeout)
{
return acquire_sem_etc(fFrameSem, 1, B_RELATIVE_TIMEOUT, timeout);
}
status_t
CamDeframer::GetFrame(CamFrame **frame, bigtime_t *stamp)
{
status_t err = EINTR;
PRINT((CH "()" CT));
BAutolock l(fLocker);
CamFrame *f = (CamFrame *)fFrames.RemoveItem((int32)0);
if (!f)
return ENOENT;
*frame = f;
*stamp = 0LL;
return B_OK;
}
status_t
CamDeframer::DropFrame()
{
status_t err = EINTR;
PRINT((CH "()" CT));
BAutolock l(fLocker);
CamFrame *f = (CamFrame *)fFrames.RemoveItem((int32)0);
if (!f)
return ENOENT;
delete f;
return B_OK;
}
status_t
CamDeframer::RegisterSOFTags(const uint8 **tags, int count, size_t len, size_t skip)
{
if (fSOFTags)
return EALREADY;
if (len > MAX_TAG_LEN)
return EINVAL;
if (count > 16)
return EINVAL;
fSOFTags = tags;
fNumSOFTags = count;
fLenSOFTags = len;
fSkipSOFTags = skip;
return B_OK;
}
status_t
CamDeframer::RegisterEOFTags(const uint8 **tags, int count, size_t len, size_t skip)
{
if (fEOFTags)
return EALREADY;
if (len > MAX_TAG_LEN)
return EINVAL;
if (count > 16)
return EINVAL;
fEOFTags = tags;
fNumEOFTags = count;
fLenEOFTags = len;
fSkipEOFTags = skip;
return B_OK;
}
int
CamDeframer::FindTags(const uint8 *buf, size_t buflen, const uint8 **tags, int tagcount, size_t taglen, size_t skiplen, int *which)
{
int i, t;
for (i = 0; i < buflen - skiplen + 1; i++) {
for (t = 0; t < tagcount; t++) {
if (!memcmp(buf+i, tags[t], taglen)) {
if (which)
*which = t;
return i;
}
}
}
return -1;
}
int
CamDeframer::FindSOF(const uint8 *buf, size_t buflen, int *which)
{
return FindTags(buf, buflen, fSOFTags, fNumSOFTags, fLenSOFTags, fSkipSOFTags, which);
}
int
CamDeframer::FindEOF(const uint8 *buf, size_t buflen, int *which)
{
return FindTags(buf, buflen, fEOFTags, fNumEOFTags, fLenEOFTags, fSkipEOFTags, which);
}
CamFrame *
CamDeframer::AllocFrame()
{
return new CamFrame();
}
@@ -0,0 +1,78 @@
#ifndef _CAM_DEFRAMER_H
#define _CAM_DEFRAMER_H
#include <OS.h>
#include <DataIO.h>
#include <Locker.h>
#include <List.h>
#include "CamFilterInterface.h"
class CamDevice;
#define CAMDEFRAMER_MAX_TAG_LEN 16
#define CAMDEFRAMER_MAX_QUEUED_FRAMES 50
enum {
ST_SYNC, /* waiting for start of frame */
ST_FRAME
};
/* should have a real Frame class someday */
#define CamFrame BMallocIO
class CamDeframer : public CamFilterInterface
{
public:
CamDeframer(CamDevice *device);
virtual ~CamDeframer();
// BPositionIO interface
// read from translators/cs transforms
virtual ssize_t Read(void *buffer, size_t size);
virtual ssize_t ReadAt(off_t pos, void *buffer, size_t size);
virtual off_t Seek(off_t position, uint32 seek_mode);
virtual off_t Position() const;
virtual status_t SetSize(off_t size);
// write from usb transfers
virtual ssize_t Write(const void *buffer, size_t size);
virtual ssize_t WriteAt(off_t pos, const void *buffer, size_t size);
virtual status_t WaitFrame(bigtime_t timeout);
virtual status_t GetFrame(CamFrame **frame, bigtime_t *stamp); // caller deletes
virtual status_t DropFrame();
status_t RegisterSOFTags(const uint8 **tags, int count, size_t len, size_t skip);
status_t RegisterEOFTags(const uint8 **tags, int count, size_t len, size_t skip);
protected:
int FindTags(const uint8 *buf, size_t buflen, const uint8 **tags, int tagcount, size_t taglen, size_t skiplen, int *which=NULL);
int FindSOF(const uint8 *buf, size_t buflen, int *which=NULL);
int FindEOF(const uint8 *buf, size_t buflen, int *which=NULL);
CamFrame *AllocFrame();
CamDevice *fDevice;
size_t fMinFrameSize;
size_t fMaxFrameSize;
int fState;
sem_id fFrameSem;
BList fFrames;
BLocker fLocker;
CamFrame *fCurrentFrame; /* the one we write to*/
/* tags */
const uint8 **fSOFTags;
const uint8 **fEOFTags;
int fNumSOFTags;
int fNumEOFTags;
size_t fLenSOFTags;
size_t fLenEOFTags;
size_t fSkipSOFTags;
size_t fSkipEOFTags;
};
#endif /* _CAM_DEFRAMER_H */
@@ -0,0 +1,9 @@
#include "CamDefs.h"
supported_usb_camera gkSupportedCameras[] =
{
{ 0x046d, 0xd001 }, // Logitech quick cam pro
{ 0, 0 }
};
@@ -0,0 +1,14 @@
#ifndef _CAM_DEFS_H
#define _CAM_DEFS_H
#include <be_prim.h>
struct supported_usb_camera
{
uint16 vendor_id;
uint16 product_id;
};
extern supported_usb_camera gkSupportedCameras[];
#endif
@@ -0,0 +1,463 @@
#include "CamDevice.h"
#include "CamSensor.h"
#include "CamDeframer.h"
#include "CamDebug.h"
#include "AddOn.h"
#include <OS.h>
#include <Autolock.h>
#include <usb/USBDevice.h>
//#define DEBUG_WRITE_DUMP
//#define DEBUG_DISCARD_DATA
//#define DEBUG_READ_DUMP
//#define DEBUG_DISCARD_INPUT
#undef B_WEBCAM_DECLARE_SENSOR
#define B_WEBCAM_DECLARE_SENSOR(sensorclass,sensorname) \
extern "C" CamSensor *Instantiate##sensorclass(CamDevice *cam);
#include "CamInternalSensors.h"
#undef B_WEBCAM_DECLARE_SENSOR
typedef CamSensor *(*SensorInstFunc)(CamDevice *cam);
struct { const char *name; SensorInstFunc instfunc; } kSensorTable[] = {
#define B_WEBCAM_DECLARE_SENSOR(sensorclass,sensorname) \
{ #sensorname, &Instantiate##sensorclass },
#include "CamInternalSensors.h"
{ NULL, NULL },
};
#undef B_WEBCAM_DECLARE_SENSOR
// -----------------------------------------------------------------------------
CamDevice::CamDevice(CamDeviceAddon &_addon, BUSBDevice* _device)
: fInitStatus(B_NO_INIT),
fCamDeviceAddon(_addon),
fDevice(_device),
fSupportedDeviceIndex(-1),
fTransferEnabled(false),
fLocker("WebcamDeviceLock"),
fSensor(NULL)
{
// fill in the generic flavor
memset(&fFlavorInfo, 0, sizeof(fFlavorInfo));
_addon.WebCamAddOn()->FillDefaultFlavorInfo(&fFlavorInfo);
// if we use id matching, cache the index to the list
if (fCamDeviceAddon.SupportedDevices())
{
fSupportedDeviceIndex = fCamDeviceAddon.Sniff(_device);
fFlavorInfoNameStr = "";
fFlavorInfoNameStr << fCamDeviceAddon.SupportedDevices()[fSupportedDeviceIndex].vendor << " USB Webcam";
fFlavorInfoInfoStr = "";
fFlavorInfoInfoStr << fCamDeviceAddon.SupportedDevices()[fSupportedDeviceIndex].vendor;
fFlavorInfoInfoStr << " (" << fCamDeviceAddon.SupportedDevices()[fSupportedDeviceIndex].product << ") USB Webcam";
fFlavorInfo.name = (char *)fFlavorInfoNameStr.String();
fFlavorInfo.info = (char *)fFlavorInfoInfoStr.String();
}
#ifdef DEBUG_WRITE_DUMP
fDumpFD = open("/boot/home/webcam.out", O_CREAT|O_RDWR, 0644);
#endif
#ifdef DEBUG_READ_DUMP
fDumpFD = open("/boot/home/webcam.out", O_RDONLY, 0644);
#endif
fBufferLen = 1*B_PAGE_SIZE;
fBuffer = (uint8 *)malloc(fBufferLen);
}
// -----------------------------------------------------------------------------
CamDevice::~CamDevice()
{
close(fDumpFD);
free(fBuffer);
if (fDeframer)
delete fDeframer;
}
// -----------------------------------------------------------------------------
status_t
CamDevice::InitCheck()
{
return fInitStatus;
}
// -----------------------------------------------------------------------------
bool
CamDevice::Matches(BUSBDevice* _device)
{
return (_device) == (fDevice);
}
// -----------------------------------------------------------------------------
BUSBDevice*
CamDevice::GetDevice()
{
return fDevice;
}
// -----------------------------------------------------------------------------
void
CamDevice::Unplugged()
{
fDevice = NULL;
fBulkIn = NULL;
}
// -----------------------------------------------------------------------------
bool
CamDevice::IsPlugged()
{
return (fDevice != NULL);
}
// -----------------------------------------------------------------------------
const char *
CamDevice::BrandName()
{
if (fCamDeviceAddon.SupportedDevices() && (fSupportedDeviceIndex > -1))
return fCamDeviceAddon.SupportedDevices()[fSupportedDeviceIndex].vendor;
return "<unknown>";
}
// -----------------------------------------------------------------------------
const char *
CamDevice::ModelName()
{
if (fCamDeviceAddon.SupportedDevices() && (fSupportedDeviceIndex > -1))
return fCamDeviceAddon.SupportedDevices()[fSupportedDeviceIndex].product;
return "<unknown>";
}
// -----------------------------------------------------------------------------
bool
CamDevice::SupportsBulk()
{
return false;
}
// -----------------------------------------------------------------------------
bool
CamDevice::SupportsIsochronous()
{
return false;
}
// -----------------------------------------------------------------------------
status_t
CamDevice::StartTransfer()
{
status_t err = B_OK;
PRINT((CH "()" CT));
if (fTransferEnabled)
return EALREADY;
fPumpThread = spawn_thread(_DataPumpThread, "USB Webcam Data Pump", 50, this);
if (fPumpThread < B_OK)
return fPumpThread;
if (fSensor)
err = fSensor->StartTransfer();
if (err < B_OK)
return err;
fTransferEnabled = true;
resume_thread(fPumpThread);
PRINT((CH ": transfer enabled" CT));
return B_OK;
}
// -----------------------------------------------------------------------------
status_t
CamDevice::StopTransfer()
{
status_t err = B_OK;
PRINT((CH "()" CT));
if (!fTransferEnabled)
return EALREADY;
if (fSensor)
err = fSensor->StopTransfer();
if (err < B_OK)
return err;
fTransferEnabled = false;
wait_for_thread(fPumpThread, &err);
return B_OK;
}
// -----------------------------------------------------------------------------
status_t
CamDevice::SetVideoFrame(BRect frame)
{
fVideoFrame = frame;
return B_OK;
}
// -----------------------------------------------------------------------------
status_t
CamDevice::SetScale(float scale)
{
return B_OK;
}
// -----------------------------------------------------------------------------
status_t
CamDevice::SetVideoParams(float brightness, float contrast, float hue, float red, float green, float blue)
{
return B_OK;
}
// -----------------------------------------------------------------------------
size_t
CamDevice::MinRawFrameSize()
{
return 0;
}
// -----------------------------------------------------------------------------
size_t
CamDevice::MaxRawFrameSize()
{
return 0;
}
// -----------------------------------------------------------------------------
bool
CamDevice::ValidateStartOfFrameTag(const uint8 *tag, size_t taglen)
{
return true;
}
// -----------------------------------------------------------------------------
bool
CamDevice::ValidateEndOfFrameTag(const uint8 *tag, size_t taglen, size_t datalen)
{
return true;
}
// -----------------------------------------------------------------------------
status_t
CamDevice::GetFrameBitmap(BBitmap **bm)
{
return EINVAL;
}
// -----------------------------------------------------------------------------
status_t
CamDevice::FillFrameBuffer(BBuffer *buffer)
{
return EINVAL;
}
// -----------------------------------------------------------------------------
bool
CamDevice::Lock()
{
return fLocker.Lock();
}
// -----------------------------------------------------------------------------
void
CamDevice::Unlock()
{
fLocker.Unlock();
}
// -----------------------------------------------------------------------------
ssize_t
CamDevice::WriteReg(uint16 address, uint8 *data, size_t count)
{
return ENOSYS;
}
// -----------------------------------------------------------------------------
ssize_t
CamDevice::WriteReg8(uint16 address, uint8 data)
{
return WriteReg(address, &data, sizeof(uint8));
}
// -----------------------------------------------------------------------------
ssize_t
CamDevice::WriteReg16(uint16 address, uint16 data)
{
// XXX: ENDIAN???
return WriteReg(address, (uint8 *)&data, sizeof(uint16));
}
// -----------------------------------------------------------------------------
ssize_t
CamDevice::ReadReg(uint16 address, uint8 *data, size_t count, bool cached)
{
return ENOSYS;
}
// -----------------------------------------------------------------------------
/*
status_t
CamDevice::GetStatusIIC()
{
return ENOSYS;
}
*/
// -----------------------------------------------------------------------------
/*status_t
CamDevice::WaitReadyIIC()
{
return ENOSYS;
}
*/
// -----------------------------------------------------------------------------
ssize_t
CamDevice::WriteIIC(uint8 address, uint8 *data, size_t count)
{
return ENOSYS;
}
// -----------------------------------------------------------------------------
ssize_t
CamDevice::WriteIIC8(uint8 address, uint8 data)
{
return WriteIIC(address, &data, 1);
}
// -----------------------------------------------------------------------------
ssize_t
CamDevice::ReadIIC(uint8 address, uint8 *data)
{
return ENOSYS;
}
// -----------------------------------------------------------------------------
CamSensor *
CamDevice::CreateSensor(const char *name)
{
int i;
for (i = 0; kSensorTable[i].name; i++) {
if (!strcmp(kSensorTable[i].name, name))
return kSensorTable[i].instfunc(this);
}
return NULL;
}
// -----------------------------------------------------------------------------
void
CamDevice::SetDataInput(BDataIO *input)
{
fDataInput = input;
}
// -----------------------------------------------------------------------------
status_t
CamDevice::DataPumpThread()
{
if (SupportsBulk()) {
PRINT((CH ": using Bulk" CT));
while (fTransferEnabled) {
ssize_t len = -1;
BAutolock lock(fLocker);
if (!lock.IsLocked())
break;
if (!fBulkIn)
break;
#ifndef DEBUG_DISCARD_INPUT
len = fBulkIn->BulkTransfer(fBuffer, fBufferLen);
#endif
//PRINT((CH ": got %d bytes" CT, len));
#ifdef DEBUG_WRITE_DUMP
write(fDumpFD, fBuffer, len);
#endif
#ifdef DEBUG_READ_DUMP
if ((len = read(fDumpFD, fBuffer, fBufferLen)) < fBufferLen)
lseek(fDumpFD, 0LL, SEEK_SET);
#endif
if (len <= 0) {
PRINT((CH ": BulkIn: %s" CT, strerror(len)));
break;
}
#ifndef DEBUG_DISCARD_DATA
if (fDataInput) {
fDataInput->Write(fBuffer, len);
// else drop
}
#endif
//snooze(2000);
}
}
if (SupportsIsochronous()) {
;//XXX: TODO
}
return B_OK;
}
// -----------------------------------------------------------------------------
int32
CamDevice::_DataPumpThread(void *_this)
{
CamDevice *dev = (CamDevice *)_this;
return dev->DataPumpThread();
}
// -----------------------------------------------------------------------------
void
CamDevice::DumpRegs()
{
}
// -----------------------------------------------------------------------------
CamDeviceAddon::CamDeviceAddon(WebCamMediaAddOn* webcam)
: fWebCamAddOn(webcam),
fSupportedDevices(NULL)
{
}
// -----------------------------------------------------------------------------
CamDeviceAddon::~CamDeviceAddon()
{
}
// -----------------------------------------------------------------------------
const char *
CamDeviceAddon::BrandName()
{
return "<unknown>";
}
// -----------------------------------------------------------------------------
status_t
CamDeviceAddon::Sniff(BUSBDevice *device)
{
PRINT((CH ": Sniffing for %s" CT, BrandName()));
if (!fSupportedDevices)
return ENODEV;
if (!device)
return EINVAL;
for (uint32 i = 0; fSupportedDevices[i].vendor; i++)
{
/* PRINT((CH "{%u,%u,%u,0x%x,0x%x} <> {%u,%u,%u,0x%x,0x%x}" CT,
device.Class(), device.Subclass(), device.Protocol(), device.VendorID(), device.ProductID(),
fSupportedDevices[i].desc.dev_class, fSupportedDevices[i].desc.dev_subclass, fSupportedDevices[i].desc.dev_protocol, fSupportedDevices[i].desc.vendor, fSupportedDevices[i].desc.product));*/
/* if (device.Class() != fSupportedDevices[i].desc.dev_class)
continue;
if (device.Subclass() != fSupportedDevices[i].desc.dev_subclass)
continue;
if (device.Protocol() != fSupportedDevices[i].desc.dev_protocol)
continue;*/
if (device->VendorID() != fSupportedDevices[i].desc.vendor)
continue;
if (device->ProductID() != fSupportedDevices[i].desc.product)
continue;
return i;
}
return ENODEV;
}
// -----------------------------------------------------------------------------
CamDevice *
CamDeviceAddon::Instantiate(CamRoster &roster, BUSBDevice *from)
{
return NULL;
}
// -----------------------------------------------------------------------------
void
CamDeviceAddon::SetSupportedDevices(const usb_named_support_descriptor *devs)
{
fSupportedDevices = devs;
}
@@ -0,0 +1,155 @@
#ifndef _CAM_DEVICE_H
#define _CAM_DEVICE_H
#include <OS.h>
#include <image.h>
#include <USB.h>
#include <usb/USBEndpoint.h>
#include <Locker.h>
#include <media/MediaAddOn.h>
#include <support/String.h>
namespace Z {
namespace USB {
class BUSBDevice;
}
}
using Z::USB::BUSBDevice;
typedef struct {
usb_support_descriptor desc;
const char *vendor;
const char *product;
} usb_named_support_descriptor;
class CamRoster;
class CamDeviceAddon;
class CamSensor;
class CamDeframer;
class WebCamMediaAddOn;
class BBitmap;
class BBuffer;
// This class represents each webcam
class CamDevice
{
public:
CamDevice(CamDeviceAddon &_addon, BUSBDevice* _device);
virtual ~CamDevice();
virtual status_t InitCheck();
bool Matches(BUSBDevice* _device);
BUSBDevice* GetDevice();
virtual void Unplugged(); // called before the BUSBDevice deletion
virtual bool IsPlugged(); // asserts on-line hardware
virtual const char* BrandName();
virtual const char* ModelName();
const flavor_info* FlavorInfo() const { return &fFlavorInfo; };
virtual bool SupportsBulk();
virtual bool SupportsIsochronous();
virtual status_t StartTransfer();
virtual status_t StopTransfer();
virtual bool TransferEnabled() const { return fTransferEnabled; };
virtual status_t SetVideoFrame(BRect rect);
virtual BRect VideoFrame() const { return fVideoFrame; };
virtual status_t SetScale(float scale);
virtual status_t SetVideoParams(float brightness, float contrast, float hue, float red, float green, float blue);
// for use by deframer
virtual size_t MinRawFrameSize();
virtual size_t MaxRawFrameSize();
virtual bool ValidateStartOfFrameTag(const uint8 *tag, size_t taglen);
virtual bool ValidateEndOfFrameTag(const uint8 *tag, size_t taglen, size_t datalen);
// several ways to get raw frames
virtual status_t GetFrameBitmap(BBitmap **bm);
virtual status_t FillFrameBuffer(BBuffer *buffer);
// locking
bool Lock();
void Unlock();
BLocker* Locker() { return &fLocker; };
// sensor chip handling
CamSensor* Sensor() const { return fSensor; };
// generic register-like access
virtual ssize_t WriteReg(uint16 address, uint8 *data, size_t count=1);
virtual ssize_t WriteReg8(uint16 address, uint8 data);
virtual ssize_t WriteReg16(uint16 address, uint16 data);
virtual ssize_t ReadReg(uint16 address, uint8 *data, size_t count=1, bool cached=false);
// I2C-like access
//virtual status_t GetStatusIIC();
//virtual status_t WaitReadyIIC();
virtual ssize_t WriteIIC(uint8 address, uint8 *data, size_t count);
virtual ssize_t WriteIIC8(uint8 address, uint8 data);
virtual ssize_t ReadIIC(uint8 address, uint8 *data);
void SetDataInput(BDataIO *input);
virtual status_t DataPumpThread();
static int32 _DataPumpThread(void *_this);
virtual void DumpRegs();
protected:
CamSensor *CreateSensor(const char *name);
status_t fInitStatus;
flavor_info fFlavorInfo;
media_format fMediaFormat;
BString fFlavorInfoNameStr;
BString fFlavorInfoInfoStr;
CamSensor* fSensor;
CamDeframer* fDeframer;
BDataIO* fDataInput; // where data from usb goes, likely fDeframer
const BUSBEndpoint* fBulkIn;
private:
friend class CamDeviceAddon;
CamDeviceAddon& fCamDeviceAddon;
BUSBDevice* fDevice;
int fSupportedDeviceIndex;
bool fTransferEnabled;
thread_id fPumpThread;
BLocker fLocker;
uint8 *fBuffer;
size_t fBufferLen;
BRect fVideoFrame;
int fDumpFD;
};
// the addon itself, that instanciate
class CamDeviceAddon
{
public:
CamDeviceAddon(WebCamMediaAddOn* webcam);
virtual ~CamDeviceAddon();
virtual const char* BrandName();
virtual status_t Sniff(BUSBDevice *device);
virtual CamDevice* Instantiate(CamRoster &roster, BUSBDevice *from);
void SetSupportedDevices(const usb_named_support_descriptor *devs);
const usb_named_support_descriptor* SupportedDevices() const { return fSupportedDevices; };
WebCamMediaAddOn* WebCamAddOn() const { return fWebCamAddOn; };
private:
WebCamMediaAddOn* fWebCamAddOn;
const usb_named_support_descriptor* fSupportedDevices; // last is {{0,0,0,0,0}, NULL, NULL}
};
// internal modules
#define B_WEBCAM_MKINTFUNC(modname) \
get_webcam_addon_##modname
// external addons -- UNIMPLEMENTED
extern "C" status_t get_webcam_addon(WebCamMediaAddOn* webcam, CamDeviceAddon **addon);
#define B_WEBCAM_ADDON_INSTANTIATION_FUNC_NAME "get_webcam_addon"
#endif _CAM_DEVICE_H
@@ -0,0 +1,126 @@
#include "CamFilterInterface.h"
#include "CamDevice.h"
#include "CamDebug.h"
CamFilterInterface::CamFilterInterface(CamDevice *device)
: BPositionIO(),
fDevice(device),
fNextOfKin(NULL)
{
fVideoFrame = BRect(0,0,-1,-1);
}
CamFilterInterface::~CamFilterInterface()
{
}
status_t
CamFilterInterface::ChainFilter(CamFilterInterface *to)
{
if (fNextOfKin)
return EALREADY;
fNextOfKin = to;
return B_OK;
}
status_t
CamFilterInterface::DetachFilter(CamFilterInterface *from)
{
if (from && (fNextOfKin != from))
return EINVAL;
fNextOfKin = NULL;
return B_OK;
}
CamFilterInterface*
CamFilterInterface::ChainFilter()
{
return fNextOfKin;
}
ssize_t
CamFilterInterface::Read(void *buffer, size_t size)
{
(void)buffer;
(void)size;
return EINVAL;
}
ssize_t
CamFilterInterface::ReadAt(off_t pos, void *buffer, size_t size)
{
(void)pos;
(void)buffer;
(void)size;
return EINVAL;
}
ssize_t
CamFilterInterface::Write(const void *buffer, size_t size)
{
(void)buffer;
(void)size;
return EINVAL;
}
ssize_t
CamFilterInterface::WriteAt(off_t pos, const void *buffer, size_t size)
{
(void)pos;
(void)buffer;
(void)size;
return EINVAL;
}
off_t
CamFilterInterface::Seek(off_t position, uint32 seek_mode)
{
(void)position;
(void)seek_mode;
return EINVAL;
}
off_t
CamFilterInterface::Position() const
{
return 0LL;
}
status_t
CamFilterInterface::SetSize(off_t size)
{
(void)size;
return EINVAL;
}
size_t
CamFilterInterface::FrameSize()
{
return 0;
}
status_t
CamFilterInterface::WaitFrame(bigtime_t timeout)
{
if (fNextOfKin)
return fNextOfKin->WaitFrame(timeout);
return EINVAL;
}
status_t
CamFilterInterface::DropFrame()
{
if (fNextOfKin)
return fNextOfKin->DropFrame();
return B_OK;
}
status_t
CamFilterInterface::SetVideoFrame(BRect frame)
{
if (fNextOfKin)
return fNextOfKin->SetVideoFrame(frame);
fVideoFrame = frame;
return B_OK;
}
@@ -0,0 +1,52 @@
#ifndef _CAM_FILTER_INTERFACE_H
#define _CAM_FILTER_INTERFACE_H
#include <kernel/OS.h>
#include <support/DataIO.h>
#include <interface/Rect.h>
class CamDevice;
class CamFilterInterface : public BPositionIO
{
public:
CamFilterInterface(CamDevice *device);
virtual ~CamFilterInterface();
// filter chain handling, should be accessed with device locked
// stack up this filter on top of 'to'
status_t ChainFilter(CamFilterInterface *to);
// break the chain
status_t DetachFilter(CamFilterInterface *from);
// accessor (next is actually processing data before self)
CamFilterInterface *ChainFilter();
// BPositionIO interface
virtual ssize_t Read(void *buffer, size_t size);
virtual ssize_t ReadAt(off_t pos, void *buffer, size_t size);
virtual ssize_t Write(const void *buffer, size_t size);
virtual ssize_t WriteAt(off_t pos, const void *buffer, size_t size);
virtual off_t Seek(off_t position, uint32 seek_mode);
virtual off_t Position() const;
virtual status_t SetSize(off_t size);
// size of the buffer required for reading a whole frame
virtual size_t FrameSize();
// frame handling
virtual status_t WaitFrame(bigtime_t timeout);
virtual status_t DropFrame();
// video settings propagation
virtual status_t SetVideoFrame(BRect frame);
protected:
CamDevice *fDevice;
CamFilterInterface *fNextOfKin;
BRect fVideoFrame;
};
#endif /* _CAM_FILTER_INTERFACE_H */
@@ -0,0 +1,2 @@
B_WEBCAM_MKINTFUNC(quickcam)
B_WEBCAM_MKINTFUNC(sonix)
@@ -0,0 +1 @@
B_WEBCAM_DECLARE_CSTRANSFORM(BayerTransform, bayer)
@@ -0,0 +1,3 @@
B_WEBCAM_DECLARE_SENSOR(HDCS1000Sensor, hdcs1000)
B_WEBCAM_DECLARE_SENSOR(HV7131E1Sensor, hv7131e1)
B_WEBCAM_DECLARE_SENSOR(TAS5110C1BSensor, tas5110c1b)
@@ -0,0 +1,168 @@
#include "CamRoster.h"
#include "AddOn.h"
#include "CamDevice.h"
#include "CamDebug.h"
#include "CamDefs.h"
#include <usb/USBDevice.h>
#include <OS.h>
#undef B_WEBCAM_MKINTFUNC
#define B_WEBCAM_MKINTFUNC(modname) \
extern "C" status_t get_webcam_addon_##modname(WebCamMediaAddOn* webcam, CamDeviceAddon **addon);
#include "CamInternalAddons.h"
#undef B_WEBCAM_MKINTFUNC
// ---------------------------------------------------------------------------
CamRoster::CamRoster(WebCamMediaAddOn* _addon)
: BUSBRoster(),
fLocker("WebcamRosterLock"),
fAddon(_addon)
{
PRINT((CH "()" CT));
LoadInternalAddons();
LoadExternalAddons();
}
// ---------------------------------------------------------------------------
CamRoster::~CamRoster()
{
}
// ---------------------------------------------------------------------------
status_t
CamRoster::DeviceAdded(BUSBDevice* _device)
{
PRINT((CH "()" CT));
status_t err;
for( int16 i = fCamerasAddons.CountItems()-1; i >= 0; --i )
{
PRINT((CH ": checking %s for support..." CT, fCamerasAddons[i]->BrandName()));
err = fCamerasAddons[i]->Sniff(_device);
if (err >= B_OK)
{
CamDevice *cam = fCamerasAddons[i]->Instantiate(*this, _device);
PRINT((CH ": found camera %s:%s!" CT, cam->BrandName(), cam->ModelName()));
err = cam->InitCheck();
if (err >= B_OK)
{
fCameras.AddItem(cam);
fAddon->CameraAdded(cam);
return B_OK;
}
PRINT((CH " error 0x%08lx" CT, err));
}
}
return B_ERROR;
}
// ---------------------------------------------------------------------------
void
CamRoster::DeviceRemoved(BUSBDevice* _device)
{
PRINT((CH "()" CT));
for(uint32 i = 0; i < fCameras.CountItems(); ++i)
{
CamDevice* cam = fCameras[i];
if( cam->Matches(_device) )
{
PRINT((CH ": camera %s:%s removed" CT, cam->BrandName(), cam->ModelName()));
fCameras.RemoveItemsAt(i, 1);
fAddon->CameraRemoved(cam);
// XXX: B_DONT_DO_THAT!
//delete cam;
cam->Unplugged();
return;
}
}
}
// ---------------------------------------------------------------------------
uint32
CamRoster::CountCameras()
{
int32 count;
PRINT((CH "(): %d cameras" CT, fCameras.CountItems()));
fLocker.Lock();
count = fCameras.CountItems();
fLocker.Unlock();
return count;
}
// ---------------------------------------------------------------------------
bool
CamRoster::Lock()
{
PRINT((CH "()" CT));
return fLocker.Lock();
}
// ---------------------------------------------------------------------------
void
CamRoster::Unlock()
{
PRINT((CH "()" CT));
fLocker.Unlock();
return;
}
// ---------------------------------------------------------------------------
CamDevice*
CamRoster::CameraAt(int32 index)
{
PRINT((CH "()" CT));
return fCameras.ItemAt(index);
}
// ---------------------------------------------------------------------------
status_t
CamRoster::LoadInternalAddons()
{
PRINT((CH "()" CT));
CamDeviceAddon *addon;
status_t err;
#undef B_WEBCAM_MKINTFUNC
#define B_WEBCAM_MKINTFUNC(modname) \
err = get_webcam_addon_##modname(fAddon, &addon); \
if (err >= B_OK) { \
fCamerasAddons.AddItem(addon); \
PRINT((CH ": registered %s addon" CT, addon->BrandName())); \
}
#include "CamInternalAddons.h"
#undef B_WEBCAM_MKINTFUNC
return B_OK;
}
// ---------------------------------------------------------------------------
status_t
CamRoster::LoadExternalAddons()
{
PRINT((CH "()" CT));
// FIXME
return B_ERROR;
int32 index;
int32 sclass;
status_t err;
CamDeviceAddon *addon;
status_t (*get_webcam_addon_func)(WebCamMediaAddOn* webcam, CamDeviceAddon **addon);
for (index = 0; get_nth_image_symbol(fAddon->ImageID(),
index, NULL, NULL,
&sclass,
(void **)&get_webcam_addon_func) == B_OK; index++) {
PRINT((CH ": got sym" CT));
// if (sclass != B_SYMBOL_TYPE_TEXT)
// continue;
err = (*get_webcam_addon_func)(fAddon, &addon);
PRINT((CH ": Loaded addon '%s' with error 0x%08lx" CT, (err>0)?NULL:addon->BrandName(), err));
}
return B_OK;
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
@@ -0,0 +1,49 @@
#ifndef _CAM_ROSTER_H
#define _CAM_ROSTER_H
#include <image.h>
#include <support/Vector.h>
#include <usb/USBRoster.h>
#include <Locker.h>
class WebCamMediaAddOn;
class CamDevice;
class CamDeviceAddon;
namespace Z {
namespace USB {
class BUSBDevice;
}
}
using Z::USB::BUSBDevice;
class CamRoster : public BUSBRoster
{
public:
CamRoster(WebCamMediaAddOn* _addon);
virtual ~CamRoster();
virtual status_t DeviceAdded(BUSBDevice* _device);
virtual void DeviceRemoved(BUSBDevice* _device);
uint32 CountCameras();
bool Lock();
void Unlock();
// those must be called with Lock()
CamDevice* CameraAt(int32 index);
private:
status_t LoadInternalAddons();
status_t LoadExternalAddons();
BLocker fLocker;
WebCamMediaAddOn* fAddon;
B::Support::
BVector<CamDeviceAddon*> fCamerasAddons;
B::Support::
BVector<CamDevice*> fCameras;
};
#endif
@@ -0,0 +1,76 @@
#include "CamSensor.h"
#include "CamDebug.h"
// -----------------------------------------------------------------------------
CamSensor::CamSensor(CamDevice *_camera)
: fInitStatus(B_NO_INIT),
fTransferEnabled(false),
fVideoFrame(),
fCamDevice(_camera)
{
}
// -----------------------------------------------------------------------------
CamSensor::~CamSensor()
{
}
// -----------------------------------------------------------------------------
status_t
CamSensor::InitCheck()
{
return fInitStatus;
}
// -----------------------------------------------------------------------------
status_t
CamSensor::Setup()
{
return fInitStatus;
}
// -----------------------------------------------------------------------------
const char *
CamSensor::Name()
{
return "<unknown>";
}
// -----------------------------------------------------------------------------
status_t
CamSensor::StartTransfer()
{
fTransferEnabled = true;
return B_OK;
}
// -----------------------------------------------------------------------------
status_t
CamSensor::StopTransfer()
{
fTransferEnabled = false;
return B_OK;
}
// -----------------------------------------------------------------------------
status_t
CamSensor::SetVideoFrame(BRect rect)
{
return ENOSYS;
}
// -----------------------------------------------------------------------------
status_t
CamSensor::SetVideoParams(float brightness, float contrast, float hue, float red, float green, float blue)
{
return ENOSYS;
}
// -----------------------------------------------------------------------------
CamDevice *
CamSensor::Device()
{
return fCamDevice;
}
@@ -0,0 +1,64 @@
#ifndef _CAM_SENSOR_H
#define _CAM_SENSOR_H
#include "CamDevice.h"
#include <Rect.h>
// This class represents the camera's (cmos or whatever) sensor chip
class CamSensor
{
public:
CamSensor(CamDevice *_camera);
virtual ~CamSensor();
virtual status_t InitCheck();
virtual status_t Setup();
virtual const char* Name();
virtual status_t StartTransfer();
virtual status_t StopTransfer();
virtual bool TransferEnabled() const { return fTransferEnabled; };
virtual bool Use400kHz() const { return false; };
virtual bool UseRealIIC() const { return true; };
virtual uint8 IICReadAddress() const { return 0; };
virtual uint8 IICWriteAddress() const { return 0; };;
virtual int MaxWidth() const { return -1; };
virtual int MaxHeight() const { return -1; };
virtual status_t SetVideoFrame(BRect rect);
virtual BRect VideoFrame() const { return fVideoFrame; };
virtual status_t SetVideoParams(float brightness, float contrast, float hue, float red, float green, float blue);
CamDevice *Device();
#if 0
// generic register-like access
virtual status_t WriteReg(uint16 address, uint8 *data, size_t count=1);
virtual status_t WriteReg8(uint16 address, uint8 data);
virtual status_t WriteReg16(uint16 address, uint16 data);
virtual status_t ReadReg(uint16 address, uint8 *data, size_t count=1, bool cached=false);
// I2C-like access
virtual status_t WriteIIC(uint8 address, uint8 *data, size_t count=1);
virtual status_t ReadIIC(uint8 address, uint8 *data);
#endif
protected:
status_t fInitStatus;
bool fTransferEnabled;
BRect fVideoFrame;
private:
CamDevice *fCamDevice;
};
// internal modules
#define B_WEBCAM_DECLARE_SENSOR(sensorclass,sensorname) \
extern "C" CamSensor *Instantiate##sensorclass(CamDevice *cam); \
CamSensor *Instantiate##sensorclass(CamDevice *cam) \
{ return new sensorclass(cam); };
#endif /* _CAM_SENSOR_H */
@@ -0,0 +1,191 @@
/*
* stream based deframer
* has a state machine and handles each packet separately.
* much more complex than the buffering one, and I thought it didn't work,
* but since I fixed the rest it seems to be working even better without
* taking the cpu over like the other one.
*/
#define CD_COL "31"
#include "CamStreamingDeframer.h"
#include "CamDevice.h"
#include "CamDebug.h"
#include <Autolock.h>
#define MAX_TAG_LEN CAMDEFRAMER_MAX_TAG_LEN
#define MAXFRAMEBUF CAMDEFRAMER_MAX_QUEUED_FRAMES
CamStreamingDeframer::CamStreamingDeframer(CamDevice *device)
: CamDeframer(device)
{
}
CamStreamingDeframer::~CamStreamingDeframer()
{
}
ssize_t
CamStreamingDeframer::Write(const void *buffer, size_t size)
{
int i = -1;
int j;
int end = size;
int which;
const uint8 *buf = (const uint8 *)buffer;
int bufsize = size;
bool detach = false;
bool discard = false;
PRINT((CH "(%p, %d); state=%s framesz=%u queued=%u" CT, buffer, size, (fState==ST_SYNC)?"sync":"frame", (size_t)fCurrentFrame->Position(), (size_t)fInputBuff.Position()));
if (!fCurrentFrame) {
BAutolock l(fLocker);
if (fFrames.CountItems() < MAXFRAMEBUF)
fCurrentFrame = AllocFrame();
else {
PRINT((CH "DROPPED %d bytes! (too many queued frames)" CT, size));
return size; // drop XXX
}
}
// update in case resolution changed
fMinFrameSize = fDevice->MinRawFrameSize();
fMaxFrameSize = fDevice->MaxRawFrameSize();
if (fInputBuff.Position()) {
// residual data ? append to it
fInputBuff.Write(buffer, size);
// and use it as input buf
buf = (uint8 *)fInputBuff.Buffer();
bufsize = fInputBuff.BufferLength();
end = bufsize;
}
// whole buffer belongs to a frame, simple
if ((fState == ST_FRAME) && (fCurrentFrame->Position() + bufsize < fMinFrameSize)) {
// no residual data, and
fCurrentFrame->Write(buf, bufsize);
fInputBuff.Seek(0LL, SEEK_SET);
fInputBuff.SetSize(0);
return size;
}
// waiting for a frame...
if (fState == ST_SYNC) {
i = 0;
while ((j = FindSOF(buf+i, bufsize-i, &which)) > -1) {
i += j;
if (fDevice->ValidateStartOfFrameTag(buf+i, fSkipSOFTags))
break;
i++;
}
// got one
if (j >= 0) {
PRINT((CH ": SOF[%d] at offset %d" CT, which, i));
//PRINT((CH ": SOF: ... %02x %02x %02x %02x %02x %02x" CT, buf[i+6], buf[i+7], buf[i+8], buf[i+9], buf[i+10], buf[i+11]));
int start = i + fSkipSOFTags;
buf += start;
bufsize -= start;
end = bufsize;
fState = ST_FRAME;
}
}
// check for end of frame
if (fState == ST_FRAME) {
#if 0
int j, k;
i = -1;
k = 0;
while ((j = FindEOF(buf + k, bufsize - k, &which)) > -1) {
k += j;
//PRINT((CH "| EOF[%d] at offset %d; pos %Ld" CT, which, k, fCurrentFrame->Position()));
if (fCurrentFrame->Position()+k >= fMinFrameSize) {
i = k;
break;
}
k++;
if (k >= bufsize)
break;
}
#endif
#if 1
i = 0;
if (fCurrentFrame->Position() < fMinFrameSize) {
if (fCurrentFrame->Position() + bufsize >= fMinFrameSize)
i = (fMinFrameSize - (size_t)fCurrentFrame->Position());
else
i = bufsize;
}
PRINT((CH ": checking for EOF; bufsize=%d i=%d" CT, bufsize, i));
if (i + fSkipEOFTags > bufsize) { // not enough room to check for EOF, leave it for next time
end = i;
i = -1; // don't detach yet
} else {
PRINT((CH ": EOF? %02x [%02x %02x %02x %02x] %02x" CT, buf[i-1], buf[i], buf[i+1], buf[i+2], buf[i+3], buf[i+4]));
while ((j = FindEOF(buf + i, bufsize - i, &which)) > -1) {
i += j;
PRINT((CH "| EOF[%d] at offset %d; pos %Ld" CT, which, i, fCurrentFrame->Position()));
if (fCurrentFrame->Position()+i >= fMaxFrameSize) {
// too big: discard
//i = -1;
discard = true;
break;
}
if (fDevice->ValidateEndOfFrameTag(buf+i, fSkipEOFTags, fCurrentFrame->Position()+i))
break;
i++;
if (i >= bufsize) {
i = -1;
break;
}
}
if (j < 0)
i = -1;
}
#endif
if (i >= 0) {
PRINT((CH ": EOF[%d] at offset %d" CT, which, i));
end = i;
detach = true;
}
PRINT((CH ": writing %d bytes" CT, end));
if (end <= bufsize)
fCurrentFrame->Write(buf, end);
if (fCurrentFrame->Position() > fMaxFrameSize) {
fCurrentFrame->SetSize(fMaxFrameSize);
detach = true;
}
if (detach) {
BAutolock f(fLocker);
PRINT((CH ": Detaching a frame (%d bytes, end = %d, )" CT, (size_t)fCurrentFrame->Position(), end));
fCurrentFrame->Seek(0LL, SEEK_SET);
if (discard) {
delete fCurrentFrame;
} else {
fFrames.AddItem(fCurrentFrame);
release_sem(fFrameSem);
}
fCurrentFrame = NULL;
if (fFrames.CountItems() < MAXFRAMEBUF) {
fCurrentFrame = AllocFrame();
}
fState = ST_SYNC;
}
}
// put the remainder in input buff, discarding old data
#if 0
fInputBuff.Seek(0LL, SEEK_SET);
if (bufsize - end > 0)
fInputBuff.Write(buf+end, bufsize - end);
#endif
BMallocIO m;
m.Write(buf+end, bufsize - end);
fInputBuff.Seek(0LL, SEEK_SET);
if (bufsize - end > 0)
fInputBuff.Write(m.Buffer(), bufsize - end);
fInputBuff.SetSize(bufsize - end);
return size;
}
@@ -0,0 +1,23 @@
#ifndef _CAM_STREAMING_DEFRAMER_H
#define _CAM_STREAMING_DEFRAMER_H
#include "CamDeframer.h"
class CamStreamingDeframer : public CamDeframer
{
public:
CamStreamingDeframer(CamDevice *device);
virtual ~CamStreamingDeframer();
// BPositionIO interface
// write from usb transfers
virtual ssize_t Write(const void *buffer, size_t size);
private:
BMallocIO fInputBuff;
};
#endif /* _CAM_STREAMING_DEFRAMER_H */
@@ -0,0 +1,784 @@
#include <fcntl.h>
#include <malloc.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <sys/uio.h>
#include <unistd.h>
#include <media/Buffer.h>
#include <media/BufferGroup.h>
#include <media/ParameterWeb.h>
#include <media/TimeSource.h>
#include <support/Autolock.h>
#include <support/Debug.h>
//XXX: change interface
#include <interface/Bitmap.h>
#include "CamDevice.h"
#define TOUCH(x) ((void)(x))
#define PRINTF(a,b) \
do { \
if (a < 2) { \
printf("VideoProducer::"); \
printf b; \
} \
} while (0)
#include "Producer.h"
#define FIELD_RATE 30.f
int32 VideoProducer::fInstances = 0;
VideoProducer::VideoProducer(
BMediaAddOn *addon, CamDevice *dev, const char *name, int32 internal_id)
: BMediaNode(name),
BMediaEventLooper(),
BBufferProducer(B_MEDIA_RAW_VIDEO),
BControllable()
{
// status_t err;
fInitStatus = B_NO_INIT;
/* Only allow one instance of the node to exist at any time */
if (atomic_add(&fInstances, 1) != 0)
return;
fInternalID = internal_id;
fAddOn = addon;
fCamDevice = dev;
fBufferGroup = NULL;
fThread = -1;
fFrameSync = -1;
fProcessingLatency = 0LL;
fRunning = false;
fConnected = false;
fEnabled = false;
fOutput.destination = media_destination::null;
AddNodeKind(B_PHYSICAL_INPUT);
fInitStatus = B_OK;
return;
}
VideoProducer::~VideoProducer()
{
if (fInitStatus == B_OK) {
/* Clean up after ourselves, in case the application didn't make us
* do so. */
if (fConnected)
Disconnect(fOutput.source, fOutput.destination);
if (fRunning)
HandleStop();
}
atomic_add(&fInstances, -1);
}
/* BMediaNode */
port_id
VideoProducer::ControlPort() const
{
return BMediaNode::ControlPort();
}
BMediaAddOn *
VideoProducer::AddOn(int32 *internal_id) const
{
if (internal_id)
*internal_id = fInternalID;
return fAddOn;
}
status_t
VideoProducer::HandleMessage(int32 /*message*/, const void* /*data*/, size_t /*size*/)
{
return B_ERROR;
}
void
VideoProducer::Preroll()
{
/* This hook may be called before the node is started to give the hardware
* a chance to start. */
}
void
VideoProducer::SetTimeSource(BTimeSource* /*time_source*/)
{
/* Tell frame generation thread to recalculate delay value */
release_sem(fFrameSync);
}
status_t
VideoProducer::RequestCompleted(const media_request_info &info)
{
return BMediaNode::RequestCompleted(info);
}
/* BMediaEventLooper */
void
VideoProducer::NodeRegistered()
{
if (fInitStatus != B_OK) {
ReportError(B_NODE_IN_DISTRESS);
return;
}
/* Set up the parameter web */
BParameterWeb *web = new BParameterWeb();
BParameterGroup *main = web->MakeGroup(Name());
BDiscreteParameter *state = main->MakeDiscreteParameter(
P_COLOR, B_MEDIA_RAW_VIDEO, "Color", "Color");
state->AddItem(B_HOST_TO_LENDIAN_INT32(0x00ff0000), "Red");
state->AddItem(B_HOST_TO_LENDIAN_INT32(0x0000ff00), "Green");
state->AddItem(B_HOST_TO_LENDIAN_INT32(0x000000ff), "Blue");
fColor = B_HOST_TO_LENDIAN_INT32(0x00ff0000);
fLastColorChange = system_time();
/* After this call, the BControllable owns the BParameterWeb object and
* will delete it for you */
SetParameterWeb(web);
fOutput.node = Node();
fOutput.source.port = ControlPort();
fOutput.source.id = 0;
fOutput.destination = media_destination::null;
strcpy(fOutput.name, Name());
/* Tailor these for the output of your device */
fOutput.format.type = B_MEDIA_RAW_VIDEO;
fOutput.format.u.raw_video = media_raw_video_format::wildcard;
fOutput.format.u.raw_video.interlace = 1;
fOutput.format.u.raw_video.display.format = B_RGB32;
fOutput.format.u.raw_video.field_rate = 29.97f; // XXX: mmu
/* Start the BMediaEventLooper control loop running */
Run();
}
void
VideoProducer::Start(bigtime_t performance_time)
{
BMediaEventLooper::Start(performance_time);
}
void
VideoProducer::Stop(bigtime_t performance_time, bool immediate)
{
BMediaEventLooper::Stop(performance_time, immediate);
}
void
VideoProducer::Seek(bigtime_t media_time, bigtime_t performance_time)
{
BMediaEventLooper::Seek(media_time, performance_time);
}
void
VideoProducer::TimeWarp(bigtime_t at_real_time, bigtime_t to_performance_time)
{
BMediaEventLooper::TimeWarp(at_real_time, to_performance_time);
}
status_t
VideoProducer::AddTimer(bigtime_t at_performance_time, int32 cookie)
{
return BMediaEventLooper::AddTimer(at_performance_time, cookie);
}
void
VideoProducer::SetRunMode(run_mode mode)
{
BMediaEventLooper::SetRunMode(mode);
}
void
VideoProducer::HandleEvent(const media_timed_event *event,
bigtime_t lateness, bool realTimeEvent)
{
TOUCH(lateness); TOUCH(realTimeEvent);
switch(event->type)
{
case BTimedEventQueue::B_START:
HandleStart(event->event_time);
break;
case BTimedEventQueue::B_STOP:
HandleStop();
break;
case BTimedEventQueue::B_WARP:
HandleTimeWarp(event->bigdata);
break;
case BTimedEventQueue::B_SEEK:
HandleSeek(event->bigdata);
break;
case BTimedEventQueue::B_HANDLE_BUFFER:
case BTimedEventQueue::B_DATA_STATUS:
case BTimedEventQueue::B_PARAMETER:
default:
PRINTF(-1, ("HandleEvent: Unhandled event -- %lx\n", event->type));
break;
}
}
void
VideoProducer::CleanUpEvent(const media_timed_event *event)
{
BMediaEventLooper::CleanUpEvent(event);
}
bigtime_t
VideoProducer::OfflineTime()
{
return BMediaEventLooper::OfflineTime();
}
void
VideoProducer::ControlLoop()
{
BMediaEventLooper::ControlLoop();
}
status_t
VideoProducer::DeleteHook(BMediaNode * node)
{
return BMediaEventLooper::DeleteHook(node);
}
/* BBufferProducer */
status_t
VideoProducer::FormatSuggestionRequested(
media_type type, int32 quality, media_format *format)
{
if (type != B_MEDIA_ENCODED_VIDEO)
return B_MEDIA_BAD_FORMAT;
TOUCH(quality);
*format = fOutput.format;
format->u.raw_video.field_rate = 29.97f;
return B_OK;
}
status_t
VideoProducer::FormatProposal(const media_source &output, media_format *format)
{
status_t err;
if (!format)
return B_BAD_VALUE;
if (output != fOutput.source)
return B_MEDIA_BAD_SOURCE;
err = format_is_compatible(*format, fOutput.format) ?
B_OK : B_MEDIA_BAD_FORMAT;
*format = fOutput.format;
return err;
}
status_t
VideoProducer::FormatChangeRequested(const media_source &source,
const media_destination &destination, media_format *io_format,
int32 *_deprecated_)
{
TOUCH(destination); TOUCH(io_format); TOUCH(_deprecated_);
if (source != fOutput.source)
return B_MEDIA_BAD_SOURCE;
return B_ERROR;
}
status_t
VideoProducer::GetNextOutput(int32 *cookie, media_output *out_output)
{
if (!out_output)
return B_BAD_VALUE;
if ((*cookie) != 0)
return B_BAD_INDEX;
*out_output = fOutput;
(*cookie)++;
return B_OK;
}
status_t
VideoProducer::DisposeOutputCookie(int32 cookie)
{
TOUCH(cookie);
return B_OK;
}
status_t
VideoProducer::SetBufferGroup(const media_source &for_source,
BBufferGroup *group)
{
TOUCH(for_source); TOUCH(group);
return B_ERROR;
}
status_t
VideoProducer::VideoClippingChanged(const media_source &for_source,
int16 num_shorts, int16 *clip_data,
const media_video_display_info &display, int32 *_deprecated_)
{
TOUCH(for_source); TOUCH(num_shorts); TOUCH(clip_data);
TOUCH(display); TOUCH(_deprecated_);
return B_ERROR;
}
status_t
VideoProducer::GetLatency(bigtime_t *out_latency)
{
*out_latency = EventLatency() + SchedulingLatency();
return B_OK;
}
status_t
VideoProducer::PrepareToConnect(const media_source &source,
const media_destination &destination, media_format *format,
media_source *out_source, char *out_name)
{
// status_t err;
PRINTF(1, ("PrepareToConnect() %ldx%ld\n", \
format->u.raw_video.display.line_width, \
format->u.raw_video.display.line_count));
if (fConnected) {
PRINTF(0, ("PrepareToConnect: Already connected\n"));
return EALREADY;
}
if (source != fOutput.source)
return B_MEDIA_BAD_SOURCE;
if (fOutput.destination != media_destination::null)
return B_MEDIA_ALREADY_CONNECTED;
/* The format parameter comes in with the suggested format, and may be
* specialized as desired by the node */
if (!format_is_compatible(*format, fOutput.format)) {
*format = fOutput.format;
return B_MEDIA_BAD_FORMAT;
}
//XXX:FIXME
// if (format->u.raw_video.display.line_width == 0)
format->u.raw_video.display.line_width = 352;//320;
format->u.raw_video.display.line_width = 320;
// if (format->u.raw_video.display.line_count == 0)
format->u.raw_video.display.line_count = 288;//240;
format->u.raw_video.display.line_count = 240;
if (format->u.raw_video.field_rate == 0)
format->u.raw_video.field_rate = 29.97f;
*out_source = fOutput.source;
strcpy(out_name, fOutput.name);
fOutput.destination = destination;
return B_OK;
}
void
VideoProducer::Connect(status_t error, const media_source &source,
const media_destination &destination, const media_format &format,
char *io_name)
{
PRINTF(1, ("Connect() %ldx%ld\n", \
format.u.raw_video.display.line_width, \
format.u.raw_video.display.line_count));
if (fConnected) {
PRINTF(0, ("Connect: Already connected\n"));
return;
}
BAutolock lock(fCamDevice->Locker());
if (!fCamDevice->IsPlugged()) {
PRINTF(0, ("Connect: Device unplugged\n"));
return;
}
if ( (source != fOutput.source) || (error < B_OK) ||
!const_cast<media_format *>(&format)->Matches(&fOutput.format)) {
PRINTF(1, ("Connect: Connect error\n"));
return;
}
fOutput.destination = destination;
strcpy(io_name, fOutput.name);
if (fOutput.format.u.raw_video.field_rate != 0.0f) {
fPerformanceTimeBase = fPerformanceTimeBase +
(bigtime_t)
((fFrame - fFrameBase) *
(1000000 / fOutput.format.u.raw_video.field_rate));
fFrameBase = fFrame;
}
fConnectedFormat = format.u.raw_video;
/* get the latency */
bigtime_t latency = 0;
media_node_id tsID = 0;
FindLatencyFor(fOutput.destination, &latency, &tsID);
#define NODE_LATENCY 1000
SetEventLatency(latency + NODE_LATENCY);
uint32 *buffer, *p, f = 3;
p = buffer = (uint32 *)malloc(4 * fConnectedFormat.display.line_count *
fConnectedFormat.display.line_width);
if (!buffer) {
PRINTF(0, ("Connect: Out of memory\n"));
return;
}
bigtime_t now = system_time();
for (uint32 y=0;y<fConnectedFormat.display.line_count;y++)
for (uint32 x=0;x<fConnectedFormat.display.line_width;x++)
*(p++) = ((((x+y)^0^x)+f) & 0xff) * (0x01010101 & fColor);
fProcessingLatency = system_time() - now;
free(buffer);
/* Create the buffer group */
fBufferGroup = new BBufferGroup(4 * fConnectedFormat.display.line_width *
fConnectedFormat.display.line_count, 8);
if (fBufferGroup->InitCheck() < B_OK) {
delete fBufferGroup;
fBufferGroup = NULL;
return;
}
fConnected = true;
fEnabled = true;
/* Tell frame generation thread to recalculate delay value */
release_sem(fFrameSync);
}
void
VideoProducer::Disconnect(const media_source &source,
const media_destination &destination)
{
PRINTF(1, ("Disconnect()\n"));
if (!fConnected) {
PRINTF(0, ("Disconnect: Not connected\n"));
return;
}
if ((source != fOutput.source) || (destination != fOutput.destination)) {
PRINTF(0, ("Disconnect: Bad source and/or destination\n"));
return;
}
fEnabled = false;
fOutput.destination = media_destination::null;
fLock.Lock();
delete fBufferGroup;
fBufferGroup = NULL;
fLock.Unlock();
fConnected = false;
}
void
VideoProducer::LateNoticeReceived(const media_source &source,
bigtime_t how_much, bigtime_t performance_time)
{
TOUCH(source); TOUCH(how_much); TOUCH(performance_time);
}
void
VideoProducer::EnableOutput(const media_source &source, bool enabled,
int32 *_deprecated_)
{
TOUCH(_deprecated_);
if (source != fOutput.source)
return;
fEnabled = enabled;
}
status_t
VideoProducer::SetPlayRate(int32 numer, int32 denom)
{
TOUCH(numer); TOUCH(denom);
return B_ERROR;
}
void
VideoProducer::AdditionalBufferRequested(const media_source &source,
media_buffer_id prev_buffer, bigtime_t prev_time,
const media_seek_tag *prev_tag)
{
TOUCH(source); TOUCH(prev_buffer); TOUCH(prev_time); TOUCH(prev_tag);
}
void
VideoProducer::LatencyChanged(const media_source &source,
const media_destination &destination, bigtime_t new_latency,
uint32 flags)
{
TOUCH(source); TOUCH(destination); TOUCH(new_latency); TOUCH(flags);
}
/* BControllable */
status_t
VideoProducer::GetParameterValue(
int32 id, bigtime_t *last_change, void *value, size_t *size)
{
if (id != P_COLOR)
return B_BAD_VALUE;
*last_change = fLastColorChange;
*size = sizeof(uint32);
*((uint32 *)value) = fColor;
return B_OK;
}
void
VideoProducer::SetParameterValue(
int32 id, bigtime_t when, const void *value, size_t size)
{
if ((id != P_COLOR) || !value || (size != sizeof(uint32)))
return;
if (*(uint32 *)value == fColor)
return;
fColor = *(uint32 *)value;
fLastColorChange = when;
BroadcastNewParameterValue(
fLastColorChange, P_COLOR, &fColor, sizeof(fColor));
}
status_t
VideoProducer::StartControlPanel(BMessenger *out_messenger)
{
return BControllable::StartControlPanel(out_messenger);
}
/* VideoProducer */
void
VideoProducer::HandleStart(bigtime_t performance_time)
{
/* Start producing frames, even if the output hasn't been connected yet. */
PRINTF(1, ("HandleStart(%Ld)\n", performance_time));
if (fRunning) {
PRINTF(-1, ("HandleStart: Node already started\n"));
return;
}
fFrame = 0;
fFrameBase = 0;
fPerformanceTimeBase = performance_time;
fFrameSync = create_sem(0, "frame synchronization");
if (fFrameSync < B_OK)
goto err1;
fThread = spawn_thread(_frame_generator_, "frame generator",
B_NORMAL_PRIORITY, this);
if (fThread < B_OK)
goto err2;
resume_thread(fThread);
fCamDevice->StartTransfer();
fRunning = true;
return;
err2:
delete_sem(fFrameSync);
err1:
return;
}
void
VideoProducer::HandleStop(void)
{
PRINTF(1, ("HandleStop()\n"));
if (!fRunning) {
PRINTF(-1, ("HandleStop: Node isn't running\n"));
return;
}
delete_sem(fFrameSync);
wait_for_thread(fThread, &fThread);
fCamDevice->StopTransfer();
fRunning = false;
}
void
VideoProducer::HandleTimeWarp(bigtime_t performance_time)
{
fPerformanceTimeBase = performance_time;
fFrameBase = fFrame;
/* Tell frame generation thread to recalculate delay value */
release_sem(fFrameSync);
}
void
VideoProducer::HandleSeek(bigtime_t performance_time)
{
fPerformanceTimeBase = performance_time;
fFrameBase = fFrame;
/* Tell frame generation thread to recalculate delay value */
release_sem(fFrameSync);
}
/* The following functions form the thread that generates frames. You should
* replace this with the code that interfaces to your hardware. */
int32
VideoProducer::FrameGenerator()
{
bigtime_t wait_until = system_time();
while (1) {
status_t err = acquire_sem_etc(fFrameSync, 1, B_ABSOLUTE_TIMEOUT,
wait_until);
/* The only acceptable responses are B_OK and B_TIMED_OUT. Everything
* else means the thread should quit. Deleting the semaphore, as in
* VideoProducer::HandleStop(), will trigger this behavior. */
if ((err != B_OK) && (err != B_TIMED_OUT))
break;
fFrame++;
/* Recalculate the time until the thread should wake up to begin
* processing the next frame. Subtract fProcessingLatency so that
* the frame is sent in time. */
wait_until = TimeSource()->RealTimeFor(fPerformanceTimeBase, 0) +
(bigtime_t)
((fFrame - fFrameBase) *
(1000000 / fConnectedFormat.field_rate)) -
fProcessingLatency;
/* Drop frame if it's at least a frame late */
if (wait_until < system_time())
continue;
/* If the semaphore was acquired successfully, it means something
* changed the timing information (see VideoProducer::Connect()) and
* so the thread should go back to sleep until the newly-calculated
* wait_until time. */
if (err == B_OK)
continue;
/* Send buffers only if the node is running and the output has been
* enabled */
if (!fRunning || !fEnabled)
continue;
BAutolock _(fLock);
/* Fetch a buffer from the buffer group */
BBuffer *buffer = fBufferGroup->RequestBuffer(
4 * fConnectedFormat.display.line_width *
fConnectedFormat.display.line_count, 0LL);
if (!buffer)
continue;
/* Fill out the details about this buffer. */
media_header *h = buffer->Header();
h->type = B_MEDIA_RAW_VIDEO;
h->time_source = TimeSource()->ID();
h->size_used = 4 * fConnectedFormat.display.line_width *
fConnectedFormat.display.line_count;
/* For a buffer originating from a device, you might want to calculate
* this based on the PerformanceTimeFor the time your buffer arrived at
* the hardware (plus any applicable adjustments). */
h->start_time = fPerformanceTimeBase +
(bigtime_t)
((fFrame - fFrameBase) *
(1000000 / fConnectedFormat.field_rate));
h->file_pos = 0;
h->orig_size = 0;
h->data_offset = 0;
h->u.raw_video.field_gamma = 1.0;
h->u.raw_video.field_sequence = fFrame;
h->u.raw_video.field_number = 0;
h->u.raw_video.pulldown_number = 0;
h->u.raw_video.first_active_line = 1;
h->u.raw_video.line_count = fConnectedFormat.display.line_count;
// This is where we fill the video buffer.
uint32 *p = (uint32 *)buffer->Data();
#if 0
/* Fill in a pattern */
for (uint32 y=0;y<fConnectedFormat.display.line_count;y++)
for (uint32 x=0;x<fConnectedFormat.display.line_width;x++)
*(p++) = ((((x+y)^0^x)+fFrame) & 0xff) * (0x01010101 & fColor);
#endif
//#ifdef UseFillFrameBuffer
err = fCamDevice->FillFrameBuffer(buffer);
if (err < B_OK) {
;//XXX handle error
}
//#endif
#ifdef UseGetFrameBitmap
BBitmap *bm;
err = fCamDevice->GetFrameBitmap(&bm);
if (err >= B_OK) {
;//XXX handle error
}
#endif
/* Send the buffer on down to the consumer */
if (SendBuffer(buffer, fOutput.destination) < B_OK) {
PRINTF(-1, ("FrameGenerator: Error sending buffer\n"));
/* If there is a problem sending the buffer, return it to its
* buffer group. */
buffer->Recycle();
}
}
return B_OK;
}
int32
VideoProducer::_frame_generator_(void *data)
{
return ((VideoProducer *)data)->FrameGenerator();
}
@@ -0,0 +1,141 @@
#ifndef _VIDEO_PRODUCER_H
#define _VIDEO_PRODUCER_H
#include <kernel/OS.h>
#include <media/BufferProducer.h>
#include <media/Controllable.h>
#include <media/MediaDefs.h>
#include <media/MediaEventLooper.h>
#include <media/MediaNode.h>
#include <support/Locker.h>
class CamDevice;
class VideoProducer :
public virtual BMediaEventLooper,
public virtual BBufferProducer,
public virtual BControllable
{
public:
VideoProducer(BMediaAddOn *addon, CamDevice *dev,
const char *name, int32 internal_id);
virtual ~VideoProducer();
virtual status_t InitCheck() const { return fInitStatus; }
/* BMediaNode */
public:
virtual port_id ControlPort() const;
virtual BMediaAddOn *AddOn(int32 * internal_id) const;
virtual status_t HandleMessage(int32 message, const void *data,
size_t size);
protected:
virtual void Preroll();
virtual void SetTimeSource(BTimeSource * time_source);
virtual status_t RequestCompleted(const media_request_info & info);
/* BMediaEventLooper */
protected:
virtual void NodeRegistered();
virtual void Start(bigtime_t performance_time);
virtual void Stop(bigtime_t performance_time, bool immediate);
virtual void Seek(bigtime_t media_time, bigtime_t performance_time);
virtual void TimeWarp(bigtime_t at_real_time,
bigtime_t to_performance_time);
virtual status_t AddTimer(bigtime_t at_performance_time, int32 cookie);
virtual void SetRunMode(run_mode mode);
virtual void HandleEvent(const media_timed_event *event,
bigtime_t lateness, bool realTimeEvent = false);
virtual void CleanUpEvent(const media_timed_event *event);
virtual bigtime_t OfflineTime();
virtual void ControlLoop();
virtual status_t DeleteHook(BMediaNode * node);
/* BBufferProducer */
protected:
virtual status_t FormatSuggestionRequested(media_type type, int32 quality,
media_format * format);
virtual status_t FormatProposal(const media_source &output,
media_format *format);
virtual status_t FormatChangeRequested(const media_source &source,
const media_destination &destination,
media_format *io_format, int32 *_deprecated_);
virtual status_t GetNextOutput(int32 * cookie, media_output * out_output);
virtual status_t DisposeOutputCookie(int32 cookie);
virtual status_t SetBufferGroup(const media_source &for_source,
BBufferGroup * group);
virtual status_t VideoClippingChanged(const media_source &for_source,
int16 num_shorts, int16 *clip_data,
const media_video_display_info &display,
int32 * _deprecated_);
virtual status_t GetLatency(bigtime_t * out_latency);
virtual status_t PrepareToConnect(const media_source &what,
const media_destination &where,
media_format *format,
media_source *out_source, char *out_name);
virtual void Connect(status_t error, const media_source &source,
const media_destination &destination,
const media_format & format, char *io_name);
virtual void Disconnect(const media_source & what,
const media_destination & where);
virtual void LateNoticeReceived(const media_source & what,
bigtime_t how_much, bigtime_t performance_time);
virtual void EnableOutput(const media_source & what, bool enabled,
int32 * _deprecated_);
virtual status_t SetPlayRate(int32 numer,int32 denom);
virtual void AdditionalBufferRequested(const media_source & source,
media_buffer_id prev_buffer, bigtime_t prev_time,
const media_seek_tag * prev_tag);
virtual void LatencyChanged(const media_source & source,
const media_destination & destination,
bigtime_t new_latency, uint32 flags);
/* BControllable */
protected:
virtual status_t GetParameterValue(int32 id, bigtime_t *last_change,
void *value, size_t *size);
virtual void SetParameterValue(int32 id, bigtime_t when,
const void *value, size_t size);
virtual status_t StartControlPanel(BMessenger *out_messenger);
/* state */
private:
void HandleStart(bigtime_t performance_time);
void HandleStop();
void HandleTimeWarp(bigtime_t performance_time);
void HandleSeek(bigtime_t performance_time);
static int32 fInstances;
status_t fInitStatus;
int32 fInternalID;
BMediaAddOn *fAddOn;
CamDevice *fCamDevice;
BLocker fLock;
BBufferGroup *fBufferGroup;
thread_id fThread;
sem_id fFrameSync;
static int32 _frame_generator_(void *data);
int32 FrameGenerator();
/* The remaining variables should be declared volatile, but they
* are not here to improve the legibility of the sample code. */
uint32 fFrame;
uint32 fFrameBase;
bigtime_t fPerformanceTimeBase;
bigtime_t fProcessingLatency;
media_output fOutput;
media_raw_video_format fConnectedFormat;
bool fRunning;
bool fConnected;
bool fEnabled;
enum { P_COLOR };
uint32 fColor;
bigtime_t fLastColorChange;
};
#endif
@@ -0,0 +1,58 @@
#include "QuickCamDevice.h"
#include <usb/USBDevice.h>
const usb_named_support_descriptor kSupportedDevices[] = {
{{ 0, 0, 0, 0x046d, 0x0840 }, "Logitech", "QuickCam Express"},
{{ 0, 0, 0, 0x046d, 0x0850 }, "Logitech", "QuickCam Express LEGO"},
{{ 0, 0, 0, 0x046d, 0xd001 }, "Logitech", "QuickCam Express"}, // Alan's
{{ 0, 0, 0, 0, 0}, NULL, NULL }
};
// -----------------------------------------------------------------------------
QuickCamDevice::QuickCamDevice(CamDeviceAddon &_addon, BUSBDevice* _device)
:CamDevice(_addon, _device)
{
fInitStatus = B_OK;
}
// -----------------------------------------------------------------------------
QuickCamDevice::~QuickCamDevice()
{
}
// -----------------------------------------------------------------------------
QuickCamDeviceAddon::QuickCamDeviceAddon(WebCamMediaAddOn* webcam)
: CamDeviceAddon(webcam)
{
SetSupportedDevices(kSupportedDevices);
}
// -----------------------------------------------------------------------------
QuickCamDeviceAddon::~QuickCamDeviceAddon()
{
}
// -----------------------------------------------------------------------------
const char *
QuickCamDeviceAddon::BrandName()
{
return "QuickCam";
}
// -----------------------------------------------------------------------------
QuickCamDevice *
QuickCamDeviceAddon::Instantiate(CamRoster &roster, BUSBDevice *from)
{
return new QuickCamDevice(*this, from);
}
extern "C" status_t
B_WEBCAM_MKINTFUNC(quickcam)
(WebCamMediaAddOn* webcam, CamDeviceAddon **addon)
{
*addon = new QuickCamDeviceAddon(webcam);
return B_OK;
}
@@ -0,0 +1,29 @@
#ifndef _QUICK_CAM_DEVICE_H
#define _QUICK_CAM_DEVICE_H
#include "CamDevice.h"
// This class represents each webcam
class QuickCamDevice : public CamDevice
{
public:
QuickCamDevice(CamDeviceAddon &_addon, BUSBDevice* _device);
~QuickCamDevice();
private:
};
// the addon itself, that instanciate
class QuickCamDeviceAddon : public CamDeviceAddon
{
public:
QuickCamDeviceAddon(WebCamMediaAddOn* webcam);
virtual ~QuickCamDeviceAddon();
virtual const char *BrandName();
virtual QuickCamDevice *Instantiate(CamRoster &roster, BUSBDevice *from);
};
#endif /* _QUICK_CAM_CAM_DEVICE_H */
@@ -0,0 +1,742 @@
#include "SonixCamDevice.h"
#include "CamDebug.h"
#include "CamSensor.h"
#include "CamBufferingDeframer.h"
#include "CamStreamingDeframer.h"
#include <usb/USBDevice.h>
#include <usb/USBConfiguration.h>
#include <usb/USBInterface.h>
#include <interface/Bitmap.h>
#include <media/Buffer.h>
const usb_named_support_descriptor kSupportedDevices[] = {
{{ 0, 0, 0, 0x0c45, 0x6005 }, "Sonix", "Sonix"},
{{ 0, 0, 0, 0x0c45, 0x6009 }, "Trust", "spacec@m 120" },
{{ 0, 0, 0, 0x0c45, 0x600D }, "Trust", "spacec@m 120" },
{{ 0, 0, 0, 0, 0}, NULL, NULL }
};
// 12 bytes actually
static const uint8 sof_mark_1[] = { 0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x00 };
static const uint8 sof_mark_2[] = { 0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x01 };
static const uint8 *sof_marks[] = { sof_mark_1, sof_mark_2 };
static const uint8 eof_mark_1[] = { 0x00, 0x00, 0x00, 0x00 };
static const uint8 eof_mark_2[] = { 0x40, 0x00, 0x00, 0x00 };
static const uint8 eof_mark_3[] = { 0x80, 0x00, 0x00, 0x00 };
static const uint8 eof_mark_4[] = { 0xc0, 0x00, 0x00, 0x00 };
static const uint8 *eof_marks[] = { eof_mark_1, eof_mark_2, eof_mark_3, eof_mark_4 };
void bayer2rgb24(unsigned char *dst, unsigned char *src, long int WIDTH, long int HEIGHT);
void bayer2rgb32le(unsigned char *dst, unsigned char *src, long int WIDTH, long int HEIGHT);
// -----------------------------------------------------------------------------
SonixCamDevice::SonixCamDevice(CamDeviceAddon &_addon, BUSBDevice* _device)
:CamDevice(_addon, _device)
{
uchar data[8]; /* store bytes returned from sonix commands */
status_t err;
fFrameTagState = 0;
memset(fCachedRegs, 0, SN9C102_REG_COUNT);
fChipVersion = 2;
if ((GetDevice()->ProductID() & ~0x3F) == 0x6080) {
fChipVersion = 3; // says V4L2
}
switch (GetDevice()->ProductID()) {
case 0x6001:
case 0x6005:
case 0x60ab:
fSensor = CreateSensor("tas5110c1b");
break;
default:
break;
}
// fDeframer = new CamBufferingDeframer(this);
fDeframer = new CamStreamingDeframer(this);
fDeframer->RegisterSOFTags(sof_marks, 2, sizeof(sof_mark_1), 12);
fDeframer->RegisterEOFTags(eof_marks, 4, sizeof(eof_mark_1), sizeof(eof_mark_1));
SetDataInput(fDeframer);
/* init hw */
const BUSBConfiguration *config = GetDevice()->ConfigurationAt(0);
if (config) {
const BUSBInterface *inter = config->InterfaceAt(0);
int i;
GetDevice()->SetConfiguration(config);
for (i = 0; inter && (i < inter->CountEndpoints()); i++) {
const BUSBEndpoint *e = inter->EndpointAt(i);
if (e && e->IsBulk() && e->IsInput()) {
fBulkIn = e;
PRINT((CH ": Using inter[0].endpoint[%d]; maxpktsz: %d" CT, i, e->MaxPacketSize()));
break;
}
}
}
/* sanity check */
err = ReadReg(SN9C102_ASIC_ID, data);
if (err < 0 || data[0] != 0x10) {
PRINT((CH ": BAD ASIC signature! (%u != %u)" CT, data[0], 0x10));
return;
}
if (Sensor()) {
PRINT((CH ": CamSensor: %s" CT, Sensor()->Name()));
fInitStatus = Sensor()->Setup();
// SetVideoFrame(BRect(0, 0, Sensor()->MaxWidth()-1, Sensor()->MaxHeight()-1));
// SetVideoFrame(BRect(0, 0, 320-1, 240-1));
}
//SetScale(1);
}
// -----------------------------------------------------------------------------
SonixCamDevice::~SonixCamDevice()
{
if (Sensor())
delete fSensor;
fSensor = NULL;
}
// -----------------------------------------------------------------------------
bool
SonixCamDevice::SupportsBulk()
{
return true;
}
// -----------------------------------------------------------------------------
bool
SonixCamDevice::SupportsIsochronous()
{
return true;
}
// -----------------------------------------------------------------------------
status_t
SonixCamDevice::StartTransfer()
{
status_t err;
uint8 r;
SetScale(1);
if (Sensor())
SetVideoFrame(BRect(0, 0, Sensor()->MaxWidth()-1, Sensor()->MaxHeight()-1));
SetVideoFrame(BRect(0, 0, 320-1, 240-1));
DumpRegs();
err = ReadReg(SN9C102_CHIP_CTRL, &r, 1, true);
if (err < 0)
return err;
r |= 0x04;
err = WriteReg8(SN9C102_CHIP_CTRL, r);
if (err < 0)
return err;
return CamDevice::StartTransfer();
}
// -----------------------------------------------------------------------------
status_t
SonixCamDevice::StopTransfer()
{
status_t err;
uint8 r;
DumpRegs();
err = CamDevice::StopTransfer();
// if (err < 0)
// return err;
err = ReadReg(SN9C102_CHIP_CTRL, &r, 1, true);
if (err < 0)
return err;
r &= ~0x04;
err = WriteReg8(SN9C102_CHIP_CTRL, r);
if (err < 0)
return err;
return err;
}
// -----------------------------------------------------------------------------
ssize_t
SonixCamDevice::WriteReg(uint16 address, uint8 *data, size_t count)
{
PRINT((CH "(%u, @%p, %u)" CT, address, data, count));
if (address + count > SN9C102_REG_COUNT) {
PRINT((CH ": Invalid register range [%u;%u]" CT, address, address+count));
return EINVAL;
}
memcpy(&fCachedRegs[address], data, count);
return SendCommand(USB_REQTYPE_DEVICE_OUT, 0x08, address, 0, count, data);
}
// -----------------------------------------------------------------------------
ssize_t
SonixCamDevice::ReadReg(uint16 address, uint8 *data, size_t count, bool cached)
{
PRINT((CH "(%u, @%p, %u, %d)" CT, address, data, count, cached));
if (address + count > SN9C102_REG_COUNT) {
PRINT((CH ": Invalid register range [%u;%u]" CT, address, address+count));
return EINVAL;
}
if (cached) {
memcpy(data, &fCachedRegs[address], count);
return count;
}
return SendCommand(USB_REQTYPE_DEVICE_IN, 0x00, address, 0, count, data);
}
// -----------------------------------------------------------------------------
status_t
SonixCamDevice::GetStatusIIC()
{
status_t err;
uint8 status = 0;
err = ReadReg(SN9C102_I2C_SETUP, &status);
//dprintf(ID "i2c_status: error 0x%08lx, status = %02x\n", err, status);
if (err < 0)
return err;
return (status&0x08)?EIO:0;
}
// -----------------------------------------------------------------------------
status_t
SonixCamDevice::WaitReadyIIC()
{
status_t err;
uint8 status = 0;
int tries = 5;
if (!Sensor())
return B_NO_INIT;
while (tries--) {
err = ReadReg(SN9C102_I2C_SETUP, &status);
//dprintf(ID "i2c_wait_ready: error 0x%08lx, status = %02x\n", err, status);
if (err < 0) return err;
if (status & 0x04) return B_OK;
//XXX:FIXME:spin((1+5+11*dev->sensor->use_400kHz)*8);
snooze((1+5+11*Sensor()->Use400kHz())*8);
}
return EBUSY;
}
// -----------------------------------------------------------------------------
ssize_t
SonixCamDevice::WriteIIC(uint8 address, uint8 *data, size_t count)
{
status_t err;
uint8 status;
uint8 buffer[8];
if (!Sensor())
return B_NO_INIT;
//dprintf(ID "sonix_i2c_write_multi(, %02x, %d, {%02x, %02x, %02x, %02x, %02x})\n", slave, count, d0, d1, d2, d3, d4);
count++; // includes address
if (count > 5)
return EINVAL;
buffer[0] = (count << 4) | Sensor()->Use400kHz()?0x01:0
| Sensor()->UseRealIIC()?0x80:0;
buffer[1] = Sensor()->IICWriteAddress();
buffer[2] = address;
memset(&buffer[3], 0, 5);
memcpy(&buffer[3], data, count);
buffer[7] = 0x14; /* absolutely no idea why V4L2 driver use that value */
err = WriteReg(SN9C102_I2C_SETUP, buffer, 8);
//dprintf(ID "sonix_i2c_write_multi: set_regs error 0x%08lx\n", err);
//PRINT((CH ": WriteReg: %s" CT, strerror(err)));
if (err < 0) return err;
err = WaitReadyIIC();
//dprintf(ID "sonix_i2c_write_multi: sonix_i2c_wait_ready error 0x%08lx\n", err);
//PRINT((CH ": Wait: %s" CT, strerror(err)));
if (err) return err;
err = GetStatusIIC();
//dprintf(ID "sonix_i2c_write_multi: sonix_i2c_status error 0x%08lx\n", err);
//PRINT((CH ": Status: %s" CT, strerror(err)));
if (err) return err;
//dprintf(ID "sonix_i2c_write_multi: succeeded\n");
PRINT((CH ": success" CT));
return B_OK;
}
// -----------------------------------------------------------------------------
ssize_t
SonixCamDevice::ReadIIC(uint8 address, uint8 *data)
{
status_t err, lasterr = B_OK;
uint8 status;
uint8 buffer[8];
if (!Sensor())
return B_NO_INIT;
//dprintf(ID "sonix_i2c_write_multi(, %02x, %d, {%02x, %02x, %02x, %02x, %02x})\n", slave, count, d0, d1, d2, d3, d4);
buffer[0] = (1 << 4) | Sensor()->Use400kHz()?0x01:0
| Sensor()->UseRealIIC()?0x80:0;
buffer[1] = Sensor()->IICWriteAddress();
buffer[2] = address;
buffer[7] = 0x10; /* absolutely no idea why V4L2 driver use that value */
err = WriteReg(SN9C102_I2C_SETUP, buffer, 8);
//dprintf(ID "sonix_i2c_write_multi: set_regs error 0x%08lx\n", err);
if (err) return err;
err = WaitReadyIIC();
//dprintf(ID "sonix_i2c_write_multi: sonix_i2c_wait_ready error 0x%08lx\n", err);
//if (err) return err;
//dprintf(ID "sonix_i2c_write_multi(, %02x, %d, {%02x, %02x, %02x, %02x, %02x})\n", slave, count, d0, d1, d2, d3, d4);
buffer[0] = (1 << 4) | Sensor()->Use400kHz()?0x01:0
| 0x02 | Sensor()->UseRealIIC()?0x80:0; /* read 1 byte */
buffer[1] = Sensor()->IICReadAddress();//IICWriteAddress
buffer[7] = 0x10; /* absolutely no idea why V4L2 driver use that value */
err = WriteReg(SN9C102_I2C_SETUP, buffer, 8);
//dprintf(ID "sonix_i2c_write_multi: set_regs error 0x%08lx\n", err);
if (err) return err;
err = WaitReadyIIC();
//dprintf(ID "sonix_i2c_write_multi: sonix_i2c_wait_ready error 0x%08lx\n", err);
if (err) return err;
err = ReadReg(SN9C102_I2C_DATA0, buffer, 5);
if (err) lasterr = err;
err = GetStatusIIC();
//dprintf(ID "sonix_i2c_write_multi: sonix_i2c_status error 0x%08lx\n", err);
if (err) return err;
//dprintf(ID "sonix_i2c_write_multi: succeeded\n");
if (lasterr) return err;
/* we should get what we want in buffer[4] according to the V4L2 driver...
* probably because the 5 bytes are a bit shift register?
*/
*data = buffer[4];
return 1;
}
// -----------------------------------------------------------------------------
status_t
SonixCamDevice::SetVideoFrame(BRect frame)
{
uint16 x, y, width, height;
x = (uint16)frame.left;
y = (uint16)frame.top;
width = (uint16)(frame.right - frame.left + 1) / 16;
height = (uint16)(frame.bottom - frame.top + 1) / 16;
PRINT((CH "(%u, %u, %u, %u)" CT, x, y, width, height));
WriteReg8(SN9C102_H_START, x);
WriteReg8(SN9C102_V_START, y);
WriteReg8(SN9C102_H_SIZE, width);
WriteReg8(SN9C102_V_SIZE, height);
if (Sensor()) {
Sensor()->SetVideoFrame(frame);
}
return CamDevice::SetVideoFrame(frame);
}
// -----------------------------------------------------------------------------
status_t
SonixCamDevice::SetScale(float scale)
{
status_t err;
uint8 r;
int iscale = (int)scale;
PRINT((CH "(%u)" CT, iscale));
err = ReadReg(SN9C102_SYNC_N_SCALE, &r, 1, true);
if (err < 0)
return err;
r &= ~0x30;
switch (iscale) {
case 1:
case 2:
case 4:
r |= ((iscale-1) << 4);
break;
default:
return EINVAL;
}
err = WriteReg8(SN9C102_SYNC_N_SCALE, r);
return err;
}
// -----------------------------------------------------------------------------
status_t
SonixCamDevice::SetVideoParams(float brightness, float contrast, float hue, float red, float green, float blue)
{
return B_OK;
}
// -----------------------------------------------------------------------------
size_t
SonixCamDevice::MinRawFrameSize()
{
// if (fCompressionEnabled) { ... return ; }
BRect vf(VideoFrame());
int w = vf.IntegerWidth()+1;
int h = vf.IntegerHeight()+1;
// 1 byte/pixel
return (size_t)(w*h);
}
// -----------------------------------------------------------------------------
size_t
SonixCamDevice::MaxRawFrameSize()
{
// if (fCompressionEnabled) { ... return ; }
return MinRawFrameSize()+1024*0; // fixed size frame (uncompressed)
}
// -----------------------------------------------------------------------------
bool
SonixCamDevice::ValidateStartOfFrameTag(const uint8 *tag, size_t taglen)
{
// SOF come with an 00, 40, 80, C0 sequence,
// supposedly corresponding with an equal byte in the end tag
fFrameTagState = tag[7] & 0xC0;
PRINT((CH "(, %d) state %x" CT, taglen, fFrameTagState));
// which seems to be the same as of the EOF tag
return true;
}
// -----------------------------------------------------------------------------
bool
SonixCamDevice::ValidateEndOfFrameTag(const uint8 *tag, size_t taglen, size_t datalen)
{
//PRINT((CH "(, %d) %x == %x" CT, taglen, (tag[0] & 0xC0), fFrameTagState));
// make sure the tag corresponds to the SOF we refer to
if ((tag[0] & 0xC0) != fFrameTagState) {
PRINT((CH ": discarded EOF %x != %x" CT, fFrameTagState, tag[0] & 0xC0));
return false;
}
//PRINT((CH ": validated EOF %x, len %d" CT, fFrameTagState, datalen));
return true;
}
// -----------------------------------------------------------------------------
status_t
SonixCamDevice::GetFrameBitmap(BBitmap **bm)
{
BBitmap *b;
CamFrame *f;
bigtime_t stamp;
status_t err;
PRINT((CH "()" CT));
err = fDeframer->WaitFrame(200000);
if (err < B_OK) { PRINT((CH ": WaitFrame: %s" CT, strerror(err))); }
if (err < B_OK)
return err;
err = fDeframer->GetFrame(&f, &stamp);
if (err < B_OK) { PRINT((CH ": GetFrame: %s" CT, strerror(err))); }
if (err < B_OK)
return err;
PRINT((CH ": VideoFrame = %fx%f,%fx%f" CT, VideoFrame().left, VideoFrame().top, VideoFrame().right, VideoFrame().bottom));
long int w = VideoFrame().right - VideoFrame().left + 1;
long int h = VideoFrame().bottom - VideoFrame().top + 1;
b = new BBitmap(VideoFrame().OffsetToSelf(0,0), 0, B_RGB32, w*4);
PRINT((CH ": Frame: %dx%d" CT, w, h));
bayer2rgb24((unsigned char *)b->Bits(), (unsigned char *)f->Buffer(), w, h);
PRINT((CH ": got 1 frame (len %d)" CT, b->BitsLength()));
*bm = b;
return B_OK;
}
// -----------------------------------------------------------------------------
status_t
SonixCamDevice::FillFrameBuffer(BBuffer *buffer)
{
CamFrame *f;
bigtime_t stamp;
status_t err;
PRINT((CH "()" CT));
memset(buffer->Data(), 0, buffer->SizeAvailable());
err = fDeframer->WaitFrame(2000000);
if (err < B_OK) { PRINT((CH ": WaitFrame: %s" CT, strerror(err))); }
if (err < B_OK)
return err;
err = fDeframer->GetFrame(&f, &stamp);
if (err < B_OK) { PRINT((CH ": GetFrame: %s" CT, strerror(err))); }
if (err < B_OK)
return err;
long int w = VideoFrame().right - VideoFrame().left + 1;
long int h = VideoFrame().bottom - VideoFrame().top + 1;
PRINT((CH ": VideoFrame = %fx%f,%fx%f Frame: %dx%d" CT, VideoFrame().left, VideoFrame().top, VideoFrame().right, VideoFrame().bottom, w, h));
if (buffer->SizeAvailable() >= w*h*4)
bayer2rgb32le((unsigned char *)buffer->Data(), (unsigned char *)f->Buffer(), w, h);
delete f;
PRINT((CH ": available %d, required %d" CT, buffer->SizeAvailable(), w*h*4));
if (buffer->SizeAvailable() < w*h*4)
return E2BIG;
PRINT((CH ": got 1 frame (len %d)" CT, buffer->SizeUsed()));
return B_OK;
}
// -----------------------------------------------------------------------------
void
/* DEBUG: dump the SN regs */
SonixCamDevice::DumpRegs()
{
uint8 regs[SN9C102_REG_COUNT];
status_t err;
//err = sonix_get_regs(dev, SN_ASIC_ID, regs, SN_REG_COUNT);
err = ReadReg(0, regs, SN9C102_REG_COUNT);
if (err < 0)
return;
printf("REG1: %02x %02x %02x %02x %02x %02x %02x %02x\n",
regs[0], regs[1], regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]);
printf(" 2: %02x %02x %02x %02x %02x %02x %02x %02x\n",
regs[8], regs[9], regs[10], regs[11], regs[12], regs[13], regs[14], regs[15]);
printf(" 3: %02x %02x %02x %02x %02x %02x %02x %02x\n",
regs[16], regs[17], regs[18], regs[19], regs[20], regs[21], regs[22], regs[23]);
printf(" 4: %02x %02x %02x %02x %02x %02x %02x %02x\n",
regs[24], regs[25], regs[26], regs[27], regs[28], regs[29], regs[30], regs[31]);
}
// -----------------------------------------------------------------------------
status_t
SonixCamDevice::SendCommand(uint8 dir, uint8 request, uint16 value,
uint16 index, uint16 length, void* data)
{
size_t ret;
if (length > 64)
return EINVAL;
if (!GetDevice())
return ENODEV;
ret = GetDevice()->ControlTransfer(
USB_REQTYPE_VENDOR | USB_REQTYPE_INTERFACE_OUT | dir,
request, value, index, length, data);
return ret;
}
// -----------------------------------------------------------------------------
SonixCamDeviceAddon::SonixCamDeviceAddon(WebCamMediaAddOn* webcam)
: CamDeviceAddon(webcam)
{
SetSupportedDevices(kSupportedDevices);
}
// -----------------------------------------------------------------------------
SonixCamDeviceAddon::~SonixCamDeviceAddon()
{
}
// -----------------------------------------------------------------------------
const char *
SonixCamDeviceAddon::BrandName()
{
return "Sonix";
}
// -----------------------------------------------------------------------------
SonixCamDevice *
SonixCamDeviceAddon::Instantiate(CamRoster &roster, BUSBDevice *from)
{
return new SonixCamDevice(*this, from);
}
extern "C" status_t
B_WEBCAM_MKINTFUNC(sonix)
(WebCamMediaAddOn* webcam, CamDeviceAddon **addon)
{
*addon = new SonixCamDeviceAddon(webcam);
return B_OK;
}
// XXX: REMOVE ME
/*
* BAYER2RGB24 ROUTINE TAKEN FROM:
*
* Sonix SN9C101 based webcam basic I/F routines
* Copyright (C) 2004 Takafumi Mizuno <[email protected]>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
void bayer2rgb24(unsigned char *dst, unsigned char *src, long int WIDTH, long int HEIGHT)
{
long int i;
unsigned char *rawpt, *scanpt;
long int size;
rawpt = src;
scanpt = dst;
size = WIDTH*HEIGHT;
for ( i = 0; i < size; i++ ) {
if ( (i/WIDTH) % 2 == 0 ) {
if ( (i % 2) == 0 ) {
/* B */
if ( (i > WIDTH) && ((i % WIDTH) > 0) ) {
*scanpt++ = (*(rawpt-WIDTH-1)+*(rawpt-WIDTH+1)+
*(rawpt+WIDTH-1)+*(rawpt+WIDTH+1))/4; /* R */
*scanpt++ = (*(rawpt-1)+*(rawpt+1)+
*(rawpt+WIDTH)+*(rawpt-WIDTH))/4; /* G */
*scanpt++ = *rawpt; /* B */
} else {
/* first line or left column */
*scanpt++ = *(rawpt+WIDTH+1); /* R */
*scanpt++ = (*(rawpt+1)+*(rawpt+WIDTH))/2; /* G */
*scanpt++ = *rawpt; /* B */
}
} else {
/* (B)G */
if ( (i > WIDTH) && ((i % WIDTH) < (WIDTH-1)) ) {
*scanpt++ = (*(rawpt+WIDTH)+*(rawpt-WIDTH))/2; /* R */
*scanpt++ = *rawpt; /* G */
*scanpt++ = (*(rawpt-1)+*(rawpt+1))/2; /* B */
} else {
/* first line or right column */
*scanpt++ = *(rawpt+WIDTH); /* R */
*scanpt++ = *rawpt; /* G */
*scanpt++ = *(rawpt-1); /* B */
}
}
} else {
if ( (i % 2) == 0 ) {
/* G(R) */
if ( (i < (WIDTH*(HEIGHT-1))) && ((i % WIDTH) > 0) ) {
*scanpt++ = (*(rawpt-1)+*(rawpt+1))/2; /* R */
*scanpt++ = *rawpt; /* G */
*scanpt++ = (*(rawpt+WIDTH)+*(rawpt-WIDTH))/2; /* B */
} else {
/* bottom line or left column */
*scanpt++ = *(rawpt+1); /* R */
*scanpt++ = *rawpt; /* G */
*scanpt++ = *(rawpt-WIDTH); /* B */
}
} else {
/* R */
if ( i < (WIDTH*(HEIGHT-1)) && ((i % WIDTH) < (WIDTH-1)) ) {
*scanpt++ = *rawpt; /* R */
*scanpt++ = (*(rawpt-1)+*(rawpt+1)+
*(rawpt-WIDTH)+*(rawpt+WIDTH))/4; /* G */
*scanpt++ = (*(rawpt-WIDTH-1)+*(rawpt-WIDTH+1)+
*(rawpt+WIDTH-1)+*(rawpt+WIDTH+1))/4; /* B */
} else {
/* bottom line or right column */
*scanpt++ = *rawpt; /* R */
*scanpt++ = (*(rawpt-1)+*(rawpt-WIDTH))/2; /* G */
*scanpt++ = *(rawpt-WIDTH-1); /* B */
}
}
}
rawpt++;
}
}
/* modified bayer2rgb24 to output rgb-32 little endian (B_RGB32)
* François Revol
*/
void bayer2rgb32le(unsigned char *dst, unsigned char *src, long int WIDTH, long int HEIGHT)
{
long int i;
unsigned char *rawpt, *scanpt;
long int size;
rawpt = src;
scanpt = dst;
size = WIDTH*HEIGHT;
for ( i = 0; i < size; i++ ) {
if ( (i/WIDTH) % 2 == 0 ) {
if ( (i % 2) == 0 ) {
/* B */
if ( (i > WIDTH) && ((i % WIDTH) > 0) ) {
*scanpt++ = *rawpt; /* B */
*scanpt++ = (*(rawpt-1)+*(rawpt+1)+
*(rawpt+WIDTH)+*(rawpt-WIDTH))/4; /* G */
*scanpt++ = (*(rawpt-WIDTH-1)+*(rawpt-WIDTH+1)+
*(rawpt+WIDTH-1)+*(rawpt+WIDTH+1))/4; /* R */
} else {
/* first line or left column */
*scanpt++ = *rawpt; /* B */
*scanpt++ = (*(rawpt+1)+*(rawpt+WIDTH))/2; /* G */
*scanpt++ = *(rawpt+WIDTH+1); /* R */
}
} else {
/* (B)G */
if ( (i > WIDTH) && ((i % WIDTH) < (WIDTH-1)) ) {
*scanpt++ = (*(rawpt-1)+*(rawpt+1))/2; /* B */
*scanpt++ = *rawpt; /* G */
*scanpt++ = (*(rawpt+WIDTH)+*(rawpt-WIDTH))/2; /* R */
} else {
/* first line or right column */
*scanpt++ = *(rawpt-1); /* B */
*scanpt++ = *rawpt; /* G */
*scanpt++ = *(rawpt+WIDTH); /* R */
}
}
} else {
if ( (i % 2) == 0 ) {
/* G(R) */
if ( (i < (WIDTH*(HEIGHT-1))) && ((i % WIDTH) > 0) ) {
*scanpt++ = (*(rawpt+WIDTH)+*(rawpt-WIDTH))/2; /* B */
*scanpt++ = *rawpt; /* G */
*scanpt++ = (*(rawpt-1)+*(rawpt+1))/2; /* R */
} else {
/* bottom line or left column */
*scanpt++ = *(rawpt-WIDTH); /* B */
*scanpt++ = *rawpt; /* G */
*scanpt++ = *(rawpt+1); /* R */
}
} else {
/* R */
if ( i < (WIDTH*(HEIGHT-1)) && ((i % WIDTH) < (WIDTH-1)) ) {
*scanpt++ = (*(rawpt-WIDTH-1)+*(rawpt-WIDTH+1)+
*(rawpt+WIDTH-1)+*(rawpt+WIDTH+1))/4; /* B */
*scanpt++ = (*(rawpt-1)+*(rawpt+1)+
*(rawpt-WIDTH)+*(rawpt+WIDTH))/4; /* G */
*scanpt++ = *rawpt; /* R */
} else {
/* bottom line or right column */
*scanpt++ = *(rawpt-WIDTH-1); /* B */
*scanpt++ = (*(rawpt-1)+*(rawpt-WIDTH))/2; /* G */
*scanpt++ = *rawpt; /* R */
}
}
}
rawpt++;
scanpt++;
}
}
@@ -0,0 +1,101 @@
#ifndef _SONIX_CAM_DEVICE_H
#define _SONIX_CAM_DEVICE_H
#include "CamDevice.h"
#define SN9C102_REG_COUNT 0x20
/* SN9c102 registers */
#define SN9C102_ASIC_ID 0x00
#define SN9C102_CHIP_CTRL 0x01
#define SN9C102_GPIO 0x02
#define SN9C102_I2C_SETUP 0x08
#define SN9C102_I2C_SLAVE_ID 0x09
#define SN9C102_I2C_DATA0 0x0a
#define SN9C102_I2C_DATA1 0x0b
#define SN9C102_I2C_DATA2 0x0c
#define SN9C102_I2C_DATA3 0x0d
#define SN9C102_I2C_DATA4 0x0e
#define SN9C102_CONTROL_STAT 0x0f /*I2C ??*/
#define SN9C102_R_B_GAIN 0x10
#define SN9C102_G_GAIN 0x11 /* Green channel gain control. -> Gain = (1+G_GAIN/8)
Note: It is sync with VSYNC */
#define SN9C102_H_START 0x12 /* Start active pixel number after H­sync of sensor
Note:
The 1st line sequence of image data is BGBGBG
The 2nd line sequence of image data is GRGRGR */
#define SN9C102_V_START 0x13 /* Start active line number after V­sync of sensor */
#define SN9C102_OFFSET 0x14 /* Offset adjustment for sensor image data. */
#define SN9C102_H_SIZE 0x15 /* Horizontal pixel number for sensor. */
#define SN9C102_V_SIZE 0x16 /* Vertical pixel number for sensor. */
#define SN9C102_CLOCK_SEL 0x17
#define SN9C102_SYNC_N_SCALE 0x18
#define SN9C102_PIX_CLK 0x19
#define SN9C102_HO_SIZE 0x1a /* /32 */
#define SN9C102_VO_SIZE 0x1b /* /32 */
#define SN9C102_AE_STRX 0x1c
#define SN9C102_AE_STRY 0x1d
#define SN9C102_AE_ENDX 0x1e
#define SN9C102_AE_ENDY 0x1f
// This class represents each webcam
class SonixCamDevice : public CamDevice
{
public:
SonixCamDevice(CamDeviceAddon &_addon, BUSBDevice* _device);
~SonixCamDevice();
virtual bool SupportsBulk();
virtual bool SupportsIsochronous();
virtual status_t StartTransfer();
virtual status_t StopTransfer();
// generic register-like access
virtual ssize_t WriteReg(uint16 address, uint8 *data, size_t count=1);
virtual ssize_t ReadReg(uint16 address, uint8 *data, size_t count=1, bool cached=false);
// I2C-like access
virtual status_t GetStatusIIC();
virtual status_t WaitReadyIIC();
virtual ssize_t WriteIIC(uint8 address, uint8 *data, size_t count=1);
virtual ssize_t ReadIIC(uint8 address, uint8 *data);
virtual status_t SetVideoFrame(BRect rect);
virtual status_t SetScale(float scale);
virtual status_t SetVideoParams(float brightness, float contrast, float hue, float red, float green, float blue);
// for use by deframer
virtual size_t MinRawFrameSize();
virtual size_t MaxRawFrameSize();
virtual bool ValidateStartOfFrameTag(const uint8 *tag, size_t taglen);
virtual bool ValidateEndOfFrameTag(const uint8 *tag, size_t taglen, size_t datalen);
virtual status_t GetFrameBitmap(BBitmap **bm);
virtual status_t FillFrameBuffer(BBuffer *buffer);
void DumpRegs();
private:
status_t SendCommand(uint8 dir, uint8 request, uint16 value,
uint16 index, uint16 length, void* data);
uint8 fCachedRegs[SN9C102_REG_COUNT];
int fChipVersion;
int fFrameTagState;
};
// the addon itself, that instanciate
class SonixCamDeviceAddon : public CamDeviceAddon
{
public:
SonixCamDeviceAddon(WebCamMediaAddOn* webcam);
virtual ~SonixCamDeviceAddon();
virtual const char *BrandName();
virtual SonixCamDevice *Instantiate(CamRoster &roster, BUSBDevice *from);
};
#endif /* _SONIX_CAM_DEVICE_H */
@@ -0,0 +1,51 @@
/*
* Bayer to RGB32 colorspace transformation
*/
#include "CamColorSpaceTransform.h"
class BayerTransform : public CamColorSpaceTransform
{
public:
BayerTransform();
virtual ~BayerTransform();
virtual const char* Name();
virtual color_space OutputSpace();
// virtual status_t SetVideoFrame(BRect rect);
// virtual BRect VideoFrame() const { return fVideoFrame; };
private:
};
// -----------------------------------------------------------------------------
BayerTransform::BayerTransform()
{
}
// -----------------------------------------------------------------------------
BayerTransform::~BayerTransform()
{
}
// -----------------------------------------------------------------------------
const char *
BayerTransform::Name()
{
return "bayer";
}
// -----------------------------------------------------------------------------
color_space
BayerTransform::OutputSpace()
{
return B_RGB32;
}
// -----------------------------------------------------------------------------
B_WEBCAM_DECLARE_CSTRANSFORM(BayerTransform, bayer)
@@ -0,0 +1,204 @@
## ********************************* ##
## Zeta Generic Makefile v3.0 ##
## Fill in this file to specify the project being created, and the referenced
## makefile-engine will do all of the hard work for you.
## Application Specific Settings ---------------------------------------------
# specify the name of the binary
NAME := webcam.media_addon
# specify the type of binary
# APP: Application
# SHARED: Shared library or add-on
# STATIC: Static library archive
# DRIVER: Kernel Driver
# MODULE: Kernel Module
# DECOR: A window decorator project
TYPE := ADDON
# add support for new Pe and Eddie features
# to fill in generic makefile
#%{
# @src->@
# specify the source files to use
# full paths or paths relative to the makefile can be included
# all files, regardless of directory, will have their object
# files created in the common object directory.
# Note that this means this makefile will not work correctly
# if two source files with the same name (source.c or source.cpp)
# are included from different directories. Also note that spaces
# in folder names do not work well with this makefile.
SRCS := $(wildcard *.cpp) \
$(wildcard addons/*/*.cpp) \
$(wildcard sensors/*/*.cpp) \
$(wildcard cstransforms/*.cpp)
# specify the resource files to use
# full path or a relative path to the resource file can be used.
RSRCS :=
# Specify your RDEF files, if any.
RDEFS :=
# @<-src@
#%}
# end support for Pe and Eddie
# specify additional libraries to link against
# there are two acceptable forms of library specifications
# - if your library follows the naming pattern of:
# libXXX.so or libXXX.a you can simply specify XXX
# library: libbe.so entry: be
#
# - if your library does not follow the standard library
# naming scheme you need to specify the path to the library
# and it's name
# library: my_lib.a entry: my_lib.a or path/my_lib.a
LIBS := be media usb
# specify additional paths to directories following the standard
# libXXX.so or libXXX.a naming scheme. You can specify full paths
# or paths relative to the makefile. The paths included may not
# be recursive, so include all of the paths where libraries can
# be found. Directories where source files are found are
# automatically included.
LIBPATHS := /system/lib
# additional paths to look for system headers
# thes use the form: #include <header>
# source file directories are NOT auto-included here
SYSTEM_INCLUDE_PATHS :=
# additional paths to look for local headers
# thes use the form: #include "header"
# source file directories are automatically included
LOCAL_INCLUDE_PATHS :=
# specify the level of optimization that you desire
# NONE, SOME, FULL
OPTIMIZE :=
# specify any preprocessor symbols to be defined. The symbols will not
# have their values set automatically; you must supply the value (if any)
# to use. For example, setting DEFINES to "DEBUG=1" will cause the
# compiler option "-DDEBUG=1" to be used. Setting DEFINES to "DEBUG"
# would pass "-DDEBUG" on the compiler's command line.
DEFINES :=
# specify special warning levels
# if unspecified default warnings will be used
# NONE = supress all warnings
# ALL = enable all warnings
WARNINGS :=
# specify whether image symbols will be created
# so that stack crawls in the debugger are meaningful
# if TRUE symbols will be created
SYMBOLS :=
# specify debug settings
# if TRUE will allow application to be run from a source-level
# debugger. Note that this will disable all optimzation.
DEBUGGER :=
# specify additional compiler flags for all files
COMPILER_FLAGS :=
# specify additional linker flags
LINKER_FLAGS :=
# specify the version of this particular item
# (for example, -app 3 4 0 d 0 -short 340 -long "340 "`echo -n -e '\302\251'`"1999 GNU GPL")
#E This may also be specified in a resource.
APP_VERSION :=
# (for TYPE == DRIVER only) Specify desired location of driver in the /dev
# hierarchy. Used by the driverinstall rule. E.g., DRIVER_PATH = video/usb will
# instruct the driverinstall rule to place a symlink to your driver's binary in
# ~/add-ons/kernel/drivers/dev/video/usb, so that your driver will appear at
# /dev/video/usb when loaded. Default is "misc".
DRIVER_PATH :=
# Specify if you want the object files to be somewhere besides the default location.
OBJ_DIR :=
# Specify a non default placement for the target
TARGET_DIR :=
# Specify a directory for the 'install' target.
INSTALL_DIR :=
# Specify the name of this makefile.
# If you leave this blank, the makefile will not be considered as part of the
# dependenies for the project, and the project will not be rebuilt when the makefile
# is changed
MAKEFILE :=
# Specify TRUE if you want the install target to create links in the BeMenu
MENU_LINKS :=
# Related to MENU_LINKS, specify the name of the direcotry in the BeMenu
# you wish the link to go in. If the directory does not exist, it will be
# created.
APP_MENU :=
# If, for some reason, you don't want to use the dependencies (flex and yacc seem to choke
# on them), set this to false
DODEPS :=
# Set this variable if you have an svg text file you wish to use as your targets
# icon.
SVG_ICON :=
# If you have some fancy custom build steps to do, specify them here
EXTRA_BUILD_STEPS =
# If you have some other files that should trigger a re-link, such as libs in the same
# project that may get rebuilt, specify the full path to them here.
EXTRA_DEPS :=
###########################################################################################
# The following variables are commented out here because the can be very useful to just
# set at the command line or in the env at time of compiling, allowing you to leave your
# makefile the same, but change the build types easily.
# If you wish to have the program output a profiling session file which can be read by bprof,
# set this to 'true'
#BUILD_PROFILE :=
# If you wish to have a debug build,
# set this to 'true'
#BUILD_DEBUG :=
# If you wish to have a build which can do memory checking when MALLOC_DEBUG=15 is set,
# set this to 'true'
#CHECK_MEM :=
# If you want to see the complete build line for every file, then set this to 'true',
# otherwise it will tell you at the end what the build flags were.
#CHATTY :=
## include the makefile-engine
include $(BUILDHOME)/etc/makefile-engine
CamRoster.cpp: CamInternalAddons.h
CamDevice.cpp: CamInternalSensors.h
CamColorSpaceTransform.cpp: CamInternalColorSpaceTransforms.h
CamInternalAddons.h: $(wildcard addons/*/*CamDevice.cpp)
grep -h B_WEBCAM_MKINTFUNC $(CURDIR)/addons/*/*CamDevice.cpp > $@
CamInternalSensors.h: $(wildcard sensors/*/*.cpp)
grep -h B_WEBCAM_DECLARE_SENSOR $(CURDIR)/sensors/*/*.cpp > $@
CamInternalColorSpaceTransforms.h: $(wildcard cstransforms/*.cpp)
grep -h B_WEBCAM_DECLARE_CSTRANSFORM $(CURDIR)/cstransforms/*.cpp > $@
@@ -0,0 +1,26 @@
/*
*/
#include "CamSensor.h"
class HDCS1000Sensor : public CamSensor {
public:
HDCS1000Sensor(CamDevice *_camera);
~HDCS1000Sensor();
};
// -----------------------------------------------------------------------------
HDCS1000Sensor::HDCS1000Sensor(CamDevice *_camera)
: CamSensor(_camera)
{
}
// -----------------------------------------------------------------------------
HDCS1000Sensor::~HDCS1000Sensor()
{
}
// -----------------------------------------------------------------------------
B_WEBCAM_DECLARE_SENSOR(HDCS1000Sensor, hdcs1000)
@@ -0,0 +1,35 @@
/*
*/
#include "CamSensor.h"
class HV7131E1Sensor : public CamSensor {
public:
HV7131E1Sensor(CamDevice *_camera);
~HV7131E1Sensor();
const char* Name();
};
// -----------------------------------------------------------------------------
HV7131E1Sensor::HV7131E1Sensor(CamDevice *_camera)
: CamSensor(_camera)
{
}
// -----------------------------------------------------------------------------
HV7131E1Sensor::~HV7131E1Sensor()
{
}
// -----------------------------------------------------------------------------
const char *
HV7131E1Sensor::Name()
{
return "Hynix hv7131e1";
}
// -----------------------------------------------------------------------------
B_WEBCAM_DECLARE_SENSOR(HV7131E1Sensor, hv7131e1)
@@ -0,0 +1,114 @@
/*
*/
#include "CamSensor.h"
#include "CamDebug.h"
#include "addons/sonix/SonixCamDevice.h"
class TAS5110C1BSensor : public CamSensor {
public:
TAS5110C1BSensor(CamDevice *_camera);
~TAS5110C1BSensor();
virtual status_t Setup();
const char *Name();
virtual bool Use400kHz() const { return false; };
virtual bool UseRealIIC() const { return false; };
virtual uint8 IICReadAddress() const { return 0x00; };
virtual uint8 IICWriteAddress() const { return 0xff; };
virtual int MaxWidth() const { return 352; };
virtual int MaxHeight() const { return 288; };
virtual status_t SetVideoFrame(BRect rect);
private:
bool fIsSonix;
};
// -----------------------------------------------------------------------------
TAS5110C1BSensor::TAS5110C1BSensor(CamDevice *_camera)
: CamSensor(_camera)
{
fIsSonix = (dynamic_cast<SonixCamDevice *>(_camera) != NULL);
if (fIsSonix) {
fInitStatus = B_OK;
} else {
PRINT((CH ": unknown camera device!" CT));
fInitStatus = ENODEV;
}
}
// -----------------------------------------------------------------------------
TAS5110C1BSensor::~TAS5110C1BSensor()
{
}
// -----------------------------------------------------------------------------
status_t
TAS5110C1BSensor::Setup()
{
PRINT((CH "()" CT));
if (InitCheck())
return InitCheck();
if (fIsSonix) {
Device()->WriteReg8(SN9C102_CHIP_CTRL, 0x01); /* power down the sensor */
Device()->WriteReg8(SN9C102_CHIP_CTRL, 0x44); /* power up the sensor, enable tx, sysclk@24MHz */
Device()->WriteReg8(SN9C102_R_B_GAIN, 0x00); /* red, blue gain = 1+0/8 = 1 */
Device()->WriteReg8(SN9C102_G_GAIN, 0x00); /* green gain = 1+0/8 = 1 */
Device()->WriteReg8(SN9C102_OFFSET, 0x0a); /* 10 pix offset */
Device()->WriteReg8(SN9C102_CLOCK_SEL, 0x60); /* enable sensor clk, and invert it */
Device()->WriteReg8(SN9C102_SYNC_N_SCALE, 0x06); /* no compression, normal curve,
* no scaling, vsync active low,
* v/hsync change at rising edge,
* falling edge of sensor pck */
Device()->WriteReg8(SN9C102_PIX_CLK, 0xfb); /* pixclk = 2 * masterclk, sensor is slave mode */
}
//sonix_i2c_write_multi(dev, dev->sensor->i2c_wid, 2, 0xc0, 0x80, 0, 0, 0); /* AEC = 0x203 ??? */
Device()->WriteIIC8(0xc0, 0x80); /* AEC = 0x203 ??? */
if (fIsSonix) {
// set crop
Device()->WriteReg8(SN9C102_H_SIZE, 69);
Device()->WriteReg8(SN9C102_V_SIZE, 9);
Device()->WriteReg8(SN9C102_PIX_CLK, 0xfb);
Device()->WriteReg8(SN9C102_HO_SIZE, 0x14);
Device()->WriteReg8(SN9C102_VO_SIZE, 0x0a);
fVideoFrame.Set(0, 0, 352-1, 288-1);
/* HACK: TEST IMAGE */
//Device()->WriteReg8(SN_CLOCK_SEL, 0x70); /* enable sensor clk, and invert it, test img */
}
//Device()->SetScale(1);
return B_OK;
}
// -----------------------------------------------------------------------------
const char *
TAS5110C1BSensor::Name()
{
return "TASC tas5110c1b";
}
// -----------------------------------------------------------------------------
status_t
TAS5110C1BSensor::SetVideoFrame(BRect rect)
{
if (fIsSonix) {
// set crop
Device()->WriteReg8(SN9C102_H_START, /*rect.left + */69);
Device()->WriteReg8(SN9C102_V_START, /*rect.top + */9);
Device()->WriteReg8(SN9C102_PIX_CLK, 0xfb);
Device()->WriteReg8(SN9C102_HO_SIZE, 0x14);
Device()->WriteReg8(SN9C102_VO_SIZE, 0x0a);
fVideoFrame = rect;
/* HACK: TEST IMAGE */
//Device()->WriteReg8(SN9C102_CLOCK_SEL, 0x70); /* enable sensor clk, and invert it, test img */
}
return B_OK;
}
// -----------------------------------------------------------------------------
B_WEBCAM_DECLARE_SENSOR(TAS5110C1BSensor, tas5110c1b)