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:
@@ -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 Hsync 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 Vsync 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)
|
||||
|
||||
Reference in New Issue
Block a user