/*
 * Copyright 2004-2008, François Revol, <revol@free.fr>.
 * Distributed under the terms of the MIT License.
 */
 
#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"
#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) \
		do { \
			if (a < 2) { \
				printf("VideoProducer::"); \
				printf b; \
			} \
		} while (0)
 
#include "Producer.h"
 
//#define FIELD_RATE 30.f
//#define FIELD_RATE 29.97f
#define FIELD_RATE 5.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 */
 
	//TODO: remove and put sensible stuff there
	BParameterWeb *web = new BParameterWeb();
	BParameterGroup *main = web->MakeGroup(Name());
	BParameterGroup *g;
 
	/*
	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();
 
	/* 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 = FIELD_RATE; // 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);
 
	PRINTF(1, ("FormatSuggestionRequested() %ldx%ld\n", \
			format->u.raw_video.display.line_width, \
			format->u.raw_video.display.line_count));
 
	*format = fOutput.format;
	uint32 width, height;
	if (fCamDevice && fCamDevice->SuggestVideoFrame(width, height) == B_OK) {
		format->u.raw_video.display.line_width = width;
		format->u.raw_video.display.line_count = height;
	}
	format->u.raw_video.field_rate = FIELD_RATE;
	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;
 
	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;
 
}
 
 
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 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;
//	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;
#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) {
		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 = FIELD_RATE;
 
	*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;
	}
 
#if 1
	/* Some dumb apps don't stop nodes before disconnecting... */
	if (fRunning)
		HandleStop();
#endif
 
	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)
{
	status_t err;
 
	switch (id) {
		case P_COLOR:
			//return B_BAD_VALUE;
 
			*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) {
		BAutolock lock(fCamDevice->Locker());
		err = fCamDevice->GetParameterValue(id, last_change, value, size);
		if (err >= B_OK)
			return err;
		if (fCamDevice->Sensor()) {
			err = fCamDevice->Sensor()->GetParameterValue(id, last_change, value, size);
			if (err >= B_OK)
				return err;
		}
	}
 
	return B_BAD_VALUE;
}
 
 
void
VideoProducer::SetParameterValue(
	int32 id, bigtime_t when, const void *value, size_t size)
{
	status_t err = B_OK;
 
	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;
 
			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)
		BroadcastNewParameterValue(when, id, (void *)value, size);
}
 
 
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);
 
	{
		BAutolock lock(fCamDevice->Locker());
		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);
 
	BAutolock lock(fCamDevice->Locker());
	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);
}
 
 
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
VideoProducer::FrameGenerator()
{
	bigtime_t wait_until = system_time();
 
	while (1) {
		PRINTF(1, ("FrameGenerator: acquire_sem_etc() until %Ldµs (in %Ldµs)\n", wait_until, wait_until - system_time()));
		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;
PRINT(("PS: %Ld\n", fProcessingLatency));
 
		/* Drop frame if it's at least a frame late */
		if (wait_until < system_time())
			continue;
 
		PRINTF(1, ("FrameGenerator: wait until %Ld, %ctimed out, %crunning, %cenabled.\n",
					wait_until,
					(err == B_OK)?'!':' ',
					(fRunning)?' ':'!',
					(fEnabled)?' ':'!'));
 
		/* 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.
 
#if 0
		uint32 *p = (uint32 *)buffer->Data();
		/* 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
 
		//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
		BBitmap *bm;
		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.source, 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();
		}
 
		_UpdateStats();
	}
 
	PRINTF(1, ("FrameGenerator: thread existed.\n"));
	return B_OK;
}
 
 
int32
VideoProducer::_frame_generator_(void *data)
{
	return ((VideoProducer *)data)->FrameGenerator();
}

↑ V576 Incorrect format. Consider checking the second actual argument of the 'printf' function. The memsize type argument is expected.

↑ V576 Incorrect format. Consider checking the third actual argument of the 'printf' function. The memsize type argument is expected.

↑ V576 Potentially incorrect format string is passed to the 'printf' function. Prefix 'L' is not applicable to conversion specifier 'd'.

↑ V576 Potentially incorrect format string is passed to the 'printf' function. Prefix 'L' is not applicable to conversion specifier 'd'.

↑ V576 Incorrect format. Consider checking the second actual argument of the 'printf' function. The memsize type argument is expected.

↑ V576 Incorrect format. Consider checking the third actual argument of the 'printf' function. The memsize type argument is expected.

↑ V576 Incorrect format. Consider checking the third actual argument of the 'printf' function. The memsize type argument is expected.

↑ V576 Incorrect format. Consider checking the second actual argument of the 'printf' function. The memsize type argument is expected.

↑ V576 Incorrect format. Consider checking the second actual argument of the 'printf' function. The memsize type argument is expected.

↑ V576 Incorrect format. Consider checking the second actual argument of the 'printf' function. The memsize type argument is expected.

↑ V576 Incorrect format. Consider checking the third actual argument of the 'printf' function. The memsize type argument is expected.

↑ V576 Potentially incorrect format string is passed to the 'printf' function. Prefix 'L' is not applicable to conversion specifier 'd'.

↑ V576 Incorrect format. Consider checking the third actual argument of the 'printf' function. The memsize type argument is expected.

↑ V576 Incorrect format. Consider checking the second actual argument of the 'printf' function. The memsize type argument is expected.