diff --git a/src/add-ons/media/media-add-ons/usb_webcam/CamDevice.cpp b/src/add-ons/media/media-add-ons/usb_webcam/CamDevice.cpp index 501b75de23..b84a621d7f 100644 --- a/src/add-ons/media/media-add-ons/usb_webcam/CamDevice.cpp +++ b/src/add-ons/media/media-add-ons/usb_webcam/CamDevice.cpp @@ -184,6 +184,19 @@ CamDevice::StopTransfer() } +status_t +CamDevice::AcceptVideoFrame(uint32 &width, uint32 &height) +{ + status_t err = ENOSYS; + if (Sensor()) + err = Sensor()->AcceptVideoFrame(width, height); + if (err < B_OK) + return err; + fVideoFrame = BRect(0, 0, width - 1, height - 1); + return B_OK; +} + + status_t CamDevice::SetVideoFrame(BRect frame) { diff --git a/src/add-ons/media/media-add-ons/usb_webcam/CamDevice.h b/src/add-ons/media/media-add-ons/usb_webcam/CamDevice.h index d6657de30c..6f830d8192 100644 --- a/src/add-ons/media/media-add-ons/usb_webcam/CamDevice.h +++ b/src/add-ons/media/media-add-ons/usb_webcam/CamDevice.h @@ -57,6 +57,7 @@ class CamDevice { virtual status_t StopTransfer(); virtual bool TransferEnabled() const { return fTransferEnabled; }; + virtual status_t AcceptVideoFrame(uint32 &width, uint32 &height); virtual status_t SetVideoFrame(BRect rect); virtual BRect VideoFrame() const { return fVideoFrame; }; virtual status_t SetScale(float scale); diff --git a/src/add-ons/media/media-add-ons/usb_webcam/CamSensor.cpp b/src/add-ons/media/media-add-ons/usb_webcam/CamSensor.cpp index e0ba6179bc..756d15ca80 100644 --- a/src/add-ons/media/media-add-ons/usb_webcam/CamSensor.cpp +++ b/src/add-ons/media/media-add-ons/usb_webcam/CamSensor.cpp @@ -65,6 +65,22 @@ CamSensor::StopTransfer() } +status_t +CamSensor::AcceptVideoFrame(uint32 &width, uint32 &height) +{ + // minimum sanity + if (width < 1) + width = MaxWidth(); + if (height < 1) + height = MaxHeight(); + if (width > MaxWidth()) + width = MaxWidth(); + if (height > MaxHeight()) + height = MaxHeight(); + return B_OK; +} + + status_t CamSensor::SetVideoFrame(BRect rect) { diff --git a/src/add-ons/media/media-add-ons/usb_webcam/CamSensor.h b/src/add-ons/media/media-add-ons/usb_webcam/CamSensor.h index fc46cc79f2..baf6276fac 100644 --- a/src/add-ons/media/media-add-ons/usb_webcam/CamSensor.h +++ b/src/add-ons/media/media-add-ons/usb_webcam/CamSensor.h @@ -28,10 +28,11 @@ class CamSensor 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 AcceptVideoFrame(uint32 &width, uint32 &height); 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); diff --git a/src/add-ons/media/media-add-ons/usb_webcam/Producer.cpp b/src/add-ons/media/media-add-ons/usb_webcam/Producer.cpp index 6f00e10d96..4142092ba4 100644 --- a/src/add-ons/media/media-add-ons/usb_webcam/Producer.cpp +++ b/src/add-ons/media/media-add-ons/usb_webcam/Producer.cpp @@ -20,6 +20,14 @@ #include "CamDevice.h" #include "CamSensor.h" +// don't separate parameters from addon, device and sensor +#define SINGLE_PARAMETER_GROUP 1 + +// CodyCam and eXposer prefer 320x240 +//#define FORCE_320_240 1 +//#define FORCE_160_120 1 +//#define FORCE_MAX_FRAME 1 + #define TOUCH(x) ((void)(x)) #define PRINTF(a,b) \ @@ -32,7 +40,9 @@ #include "Producer.h" -#define FIELD_RATE 30.f +//#define FIELD_RATE 30.f +//#define FIELD_RATE 29.97f +#define FIELD_RATE 5.f int32 VideoProducer::fInstances = 0; @@ -139,27 +149,40 @@ VideoProducer::NodeRegistered() return; } - int32 id = P_COLOR; /* Set up the parameter web */ //TODO: remove and put sensible stuff there BParameterWeb *web = new BParameterWeb(); BParameterGroup *main = web->MakeGroup(Name()); - BDiscreteParameter *state = main->MakeDiscreteParameter( - P_COLOR, B_MEDIA_RAW_VIDEO, "Color", "Color"); + BParameterGroup *g; - id++; - if (fCamDevice) { - BParameterGroup *dev = web->MakeGroup("Device"); - fCamDevice->AddParameters(dev, id); - if (fCamDevice->Sensor()) { - BParameterGroup *sensor = web->MakeGroup("Sensor"); - fCamDevice->Sensor()->AddParameters(sensor, id); - } - } + /* + g = main->MakeGroup("Color"); + BDiscreteParameter *state = g->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"); + */ + + BParameter *p; + g = main->MakeGroup("Info"); + p = g->MakeTextParameter( + P_INFO, B_MEDIA_RAW_VIDEO, "", "Info", 256); + + int32 id = P_LAST; + if (fCamDevice) { +#ifndef SINGLE_PARAMETER_GROUP + main = web->MakeGroup("Device"); +#endif + fCamDevice->AddParameters(main, id); + if (fCamDevice->Sensor()) { +#ifndef SINGLE_PARAMETER_GROUP + main = web->MakeGroup("Sensor"); +#endif + fCamDevice->Sensor()->AddParameters(main, id); + } + } fColor = B_HOST_TO_LENDIAN_INT32(0x00ff0000); fLastColorChange = system_time(); @@ -179,7 +202,7 @@ VideoProducer::NodeRegistered() 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 + fOutput.format.u.raw_video.field_rate = FIELD_RATE; // XXX: mmu /* Start the BMediaEventLooper control loop running */ Run(); @@ -285,8 +308,16 @@ VideoProducer::FormatSuggestionRequested( TOUCH(quality); + PRINTF(1, ("FormatSuggestionRequested() %ldx%ld\n", \ + format->u.raw_video.display.line_width, \ + format->u.raw_video.display.line_count)); + *format = fOutput.format; - format->u.raw_video.field_rate = 29.97f; + if (fCamDevice && fCamDevice->Sensor()) { + format->u.raw_video.display.line_width = fCamDevice->Sensor()->MaxWidth(); + format->u.raw_video.display.line_count = fCamDevice->Sensor()->MaxHeight(); + } + format->u.raw_video.field_rate = FIELD_RATE; return B_OK; } @@ -301,9 +332,30 @@ VideoProducer::FormatProposal(const media_source &output, media_format *format) if (output != fOutput.source) return B_MEDIA_BAD_SOURCE; + PRINTF(1, ("FormatProposal() %ldx%ld\n", \ + format->u.raw_video.display.line_width, \ + format->u.raw_video.display.line_count)); + err = format_is_compatible(*format, fOutput.format) ? B_OK : B_MEDIA_BAD_FORMAT; + + uint32 width = format->u.raw_video.display.line_width; + uint32 height = format->u.raw_video.display.line_count; + *format = fOutput.format; + + if (err == B_OK && fCamDevice) { + err = fCamDevice->AcceptVideoFrame(width, height); + if (err >= B_OK) { + format->u.raw_video.display.line_width = width; + format->u.raw_video.display.line_count = height; + } + } + + PRINTF(1, ("FormatProposal: %ldx%ld\n", \ + format->u.raw_video.display.line_width, \ + format->u.raw_video.display.line_count)); + return err; } @@ -374,7 +426,7 @@ VideoProducer::PrepareToConnect(const media_source &source, const media_destination &destination, media_format *format, media_source *out_source, char *out_name) { -// status_t err; + status_t err; PRINTF(1, ("PrepareToConnect() %ldx%ld\n", \ format->u.raw_video.display.line_width, \ @@ -399,7 +451,7 @@ VideoProducer::PrepareToConnect(const media_source &source, } //XXX:FIXME -#if 1 +#if 0 // 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; @@ -408,13 +460,34 @@ VideoProducer::PrepareToConnect(const media_source &source, format->u.raw_video.display.line_count = 240; #endif +#ifdef FORCE_320_240 + { + format->u.raw_video.display.line_width = 320; + format->u.raw_video.display.line_count = 240; + } +#endif +#ifdef FORCE_160_120 + { + format->u.raw_video.display.line_width = 160; + format->u.raw_video.display.line_count = 120; + } +#endif +#ifdef FORCE_MAX_FRAME + { + format->u.raw_video.display.line_width = 0; + format->u.raw_video.display.line_count = 0; + } +#endif if (fCamDevice) { - format->u.raw_video.display.line_width = fCamDevice->VideoFrame().IntegerWidth() + 1; - format->u.raw_video.display.line_count = fCamDevice->VideoFrame().IntegerHeight() + 1; + err = fCamDevice->AcceptVideoFrame( + format->u.raw_video.display.line_width, + format->u.raw_video.display.line_count); + if (err < B_OK) + return err; } if (format->u.raw_video.field_rate == 0) - format->u.raw_video.field_rate = 29.97f; + format->u.raw_video.field_rate = FIELD_RATE; *out_source = fOutput.source; strcpy(out_name, fOutput.name); @@ -584,13 +657,21 @@ VideoProducer::GetParameterValue( { status_t err; - if (id == P_COLOR) { - //return B_BAD_VALUE; + switch (id) { + case P_COLOR: + //return B_BAD_VALUE; - *last_change = fLastColorChange; - *size = sizeof(uint32); - *((uint32 *)value) = fColor; - return B_OK; + *last_change = fLastColorChange; + *size = sizeof(uint32); + *((uint32 *)value) = fColor; + return B_OK; + case P_INFO: + if (*size < fInfoString.Length() + 1) + return EINVAL; + *last_change = fLastColorChange; + *size = fInfoString.Length() + 1; + memcpy(value, fInfoString.String(), *size); + return B_OK; } if (fCamDevice) { @@ -614,22 +695,29 @@ VideoProducer::SetParameterValue( { status_t err = B_OK; - if (id == P_COLOR) { - if (!value || (size != sizeof(uint32))) + switch (id) { + case P_COLOR: + if (!value || (size != sizeof(uint32))) + return; + + if (*(uint32 *)value == fColor) + return; + + fColor = *(uint32 *)value; + fLastColorChange = when; + break; + case P_INFO: + // forbidden return; + default: + if (fCamDevice == NULL) + return; - if (*(uint32 *)value == fColor) - return; - - fColor = *(uint32 *)value; - fLastColorChange = when; - - } else if (fCamDevice) { - BAutolock lock(fCamDevice->Locker()); - err = fCamDevice->SetParameterValue(id, when, value, size); - if ((err < B_OK) && (fCamDevice->Sensor())) { - err = fCamDevice->Sensor()->SetParameterValue(id, when, value, size); - } + BAutolock lock(fCamDevice->Locker()); + err = fCamDevice->SetParameterValue(id, when, value, size); + if ((err < B_OK) && (fCamDevice->Sensor())) { + err = fCamDevice->Sensor()->SetParameterValue(id, when, value, size); + } } if (err >= B_OK) @@ -724,6 +812,24 @@ VideoProducer::HandleSeek(bigtime_t performance_time) release_sem(fFrameSync); } +void +VideoProducer::_UpdateStats() +{ + float fps = (fStats[0].frames - fStats[1].frames) * 1000000LL + / (double)(fStats[0].stamp - fStats[1].stamp); + float rfps = (fStats[0].actual - fStats[1].actual) * 1000000LL + / (double)(fStats[0].stamp - fStats[1].stamp); + fInfoString = "FPS: "; + fInfoString << fps << " virt, " + << rfps << " real, missed: " << fStats[0].missed; + memcpy(&fStats[1], &fStats[0], sizeof(fStats[0])); + fLastColorChange = system_time(); + BroadcastNewParameterValue(fLastColorChange, P_INFO, + (void *)fInfoString.String(), fInfoString.Length()+1); +} + + + /* The following functions form the thread that generates frames. You should * replace this with the code that interfaces to your hardware. */ int32 @@ -752,6 +858,7 @@ VideoProducer::FrameGenerator() ((fFrame - fFrameBase) * (1000000 / fConnectedFormat.field_rate)) - fProcessingLatency; +PRINT(("PS: %Ld\n", fProcessingLatency)); /* Drop frame if it's at least a frame late */ if (wait_until < system_time()) @@ -822,11 +929,13 @@ VideoProducer::FrameGenerator() //NO! must be called without lock! //BAutolock lock(fCamDevice->Locker()); + bigtime_t now = system_time(); bigtime_t stamp; //#ifdef UseFillFrameBuffer err = fCamDevice->FillFrameBuffer(buffer, &stamp); if (err < B_OK) { ;//XXX handle error + fStats[0].missed++; } //#endif #ifdef UseGetFrameBitmap @@ -834,13 +943,24 @@ VideoProducer::FrameGenerator() err = fCamDevice->GetFrameBitmap(&bm, &stamp); if (err >= B_OK) { ;//XXX handle error + fStats[0].missed++; } #endif + fStats[0].frames = fFrame; + fStats[0].actual++;; + fStats[0].stamp = system_time(); + //PRINTF(1, ("FrameGenerator: stamp %Ld vs %Ld\n", stamp, h->start_time)); //XXX: that's what we should be doing, but CodyCam drops all frames as they are late. (maybe add latency ??) //h->start_time = TimeSource()->PerformanceTimeFor(stamp); h->start_time = TimeSource()->PerformanceTimeFor(system_time()); + + // update processing latency + // XXX: should I ?? + fProcessingLatency = system_time() - now; + fProcessingLatency /= 10; + PRINTF(1, ("FrameGenerator: SendBuffer...\n")); /* Send the buffer on down to the consumer */ if (SendBuffer(buffer, fOutput.destination) < B_OK) { @@ -849,6 +969,8 @@ VideoProducer::FrameGenerator() * buffer group. */ buffer->Recycle(); } + + _UpdateStats(); } PRINTF(1, ("FrameGenerator: thread existed.\n")); diff --git a/src/add-ons/media/media-add-ons/usb_webcam/Producer.h b/src/add-ons/media/media-add-ons/usb_webcam/Producer.h index 4e6b89b70c..60cc0ae85c 100644 --- a/src/add-ons/media/media-add-ons/usb_webcam/Producer.h +++ b/src/add-ons/media/media-add-ons/usb_webcam/Producer.h @@ -8,8 +8,11 @@ #include #include #include +#include class CamDevice; +class BParameter; +class BTextParameter; class VideoProducer : public virtual BMediaEventLooper, @@ -106,6 +109,8 @@ private: void HandleTimeWarp(bigtime_t performance_time); void HandleSeek(bigtime_t performance_time); + void _UpdateStats(); + static int32 fInstances; status_t fInitStatus; @@ -134,9 +139,21 @@ static int32 _frame_generator_(void *data); bool fConnected; bool fEnabled; - enum { P_COLOR }; + enum { + P_COLOR, + P_INFO, + P_LAST // first available for addons + }; uint32 fColor; - bigtime_t fLastColorChange; + BString fInfoString; + bigtime_t fLastColorChange; // TODO: rename + + struct { + uint32 frames; + uint32 actual; + uint32 missed; + bigtime_t stamp; + } fStats[2]; }; #endif diff --git a/src/add-ons/media/media-add-ons/usb_webcam/README.txt b/src/add-ons/media/media-add-ons/usb_webcam/README.txt index c8d33e8b4e..26a4553071 100644 --- a/src/add-ons/media/media-add-ons/usb_webcam/README.txt +++ b/src/add-ons/media/media-add-ons/usb_webcam/README.txt @@ -54,6 +54,9 @@ controls ? References and other drivers of interest: +* Logitech opensource effort: +http://www.quickcamteam.net/ + * Sonix linux drivers (several of them): http://sourceforge.net/projects/sonix/ -- http://sonix.sourceforge.net/ http://freshmeat.net/projects/sonic-snap/?branch_id=55324&release_id=183982 diff --git a/src/add-ons/media/media-add-ons/usb_webcam/addons/sonix/SonixCamDevice.cpp b/src/add-ons/media/media-add-ons/usb_webcam/addons/sonix/SonixCamDevice.cpp index 4fd161ad13..5fb46c5687 100644 --- a/src/add-ons/media/media-add-ons/usb_webcam/addons/sonix/SonixCamDevice.cpp +++ b/src/add-ons/media/media-add-ons/usb_webcam/addons/sonix/SonixCamDevice.cpp @@ -163,7 +163,7 @@ SonixCamDevice::StartTransfer() SetScale(1); if (Sensor()) - SetVideoFrame(BRect(0, 0, Sensor()->MaxWidth()-1, Sensor()->MaxHeight()-1)); + SetVideoFrame(fVideoFrame); //SetVideoFrame(BRect(0, 0, 320-1, 240-1)); diff --git a/src/add-ons/media/media-add-ons/usb_webcam/sensors/tas5110c1b.cpp b/src/add-ons/media/media-add-ons/usb_webcam/sensors/tas5110c1b.cpp index c0302f1395..03afad8c7d 100644 --- a/src/add-ons/media/media-add-ons/usb_webcam/sensors/tas5110c1b.cpp +++ b/src/add-ons/media/media-add-ons/usb_webcam/sensors/tas5110c1b.cpp @@ -19,8 +19,11 @@ public: virtual bool UseRealIIC() const { return false; }; virtual uint8 IICReadAddress() const { return 0x00; }; virtual uint8 IICWriteAddress() const { return 0x11; /*0xff;*/ }; + virtual int MaxWidth() const { return 352; }; virtual int MaxHeight() const { return 288; }; + + virtual status_t AcceptVideoFrame(uint32 &width, uint32 &height); virtual status_t SetVideoFrame(BRect rect); virtual void AddParameters(BParameterGroup *group, int32 &firstID); virtual status_t GetParameterValue(int32 id, bigtime_t *last_change, void *value, size_t *size); @@ -31,7 +34,7 @@ private: float fGain; }; -// ----------------------------------------------------------------------------- + TAS5110C1BSensor::TAS5110C1BSensor(CamDevice *_camera) : CamSensor(_camera) { @@ -45,12 +48,12 @@ TAS5110C1BSensor::TAS5110C1BSensor(CamDevice *_camera) fGain = (float)0x40; // default } -// ----------------------------------------------------------------------------- + TAS5110C1BSensor::~TAS5110C1BSensor() { } -// ----------------------------------------------------------------------------- + status_t TAS5110C1BSensor::Setup() { @@ -93,14 +96,30 @@ TAS5110C1BSensor::Setup() return B_OK; } -// ----------------------------------------------------------------------------- + const char * TAS5110C1BSensor::Name() { return "TASC tas5110c1b"; } -// ----------------------------------------------------------------------------- + +status_t +TAS5110C1BSensor::AcceptVideoFrame(uint32 &width, uint32 &height) +{ + // default sanity checks + status_t err = CamSensor::AcceptVideoFrame(width, height); + if (err < B_OK) + return err; + // must be modulo 16 + width /= 16; + width *= 16; + height /= 16; + height *= 16; + return B_OK; +} + + status_t TAS5110C1BSensor::SetVideoFrame(BRect rect) { @@ -120,6 +139,7 @@ TAS5110C1BSensor::SetVideoFrame(BRect rect) return B_OK; } + void TAS5110C1BSensor::AddParameters(BParameterGroup *group, int32 &index) { @@ -128,7 +148,8 @@ TAS5110C1BSensor::AddParameters(BParameterGroup *group, int32 &index) #ifdef ENABLE_GAIN // NON-FUNCTIONAL - p = group->MakeContinuousParameter(index++, + BParameterGroup *g = group->MakeGroup("global gain"); + p = g->MakeContinuousParameter(index++, B_MEDIA_RAW_VIDEO, "global gain", B_GAIN, "", (float)0x00, (float)0xf6, (float)1); #endif @@ -148,6 +169,7 @@ TAS5110C1BSensor::GetParameterValue(int32 id, bigtime_t *last_change, void *valu return B_BAD_VALUE; } + status_t TAS5110C1BSensor::SetParameterValue(int32 id, bigtime_t when, const void *value, size_t size) { @@ -179,6 +201,6 @@ TAS5110C1BSensor::SetParameterValue(int32 id, bigtime_t when, const void *value, } -// ----------------------------------------------------------------------------- + B_WEBCAM_DECLARE_SENSOR(TAS5110C1BSensor, tas5110c1b)