1/*
2 * Copyright 2004-2008, Fran��ois Revol, <revol@free.fr>.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include <fcntl.h>
7#include <malloc.h>
8#include <math.h>
9#include <stdio.h>
10#include <string.h>
11#include <sys/uio.h>
12#include <unistd.h>
13
14#include <media/Buffer.h>
15#include <media/BufferGroup.h>
16#include <media/ParameterWeb.h>
17#include <media/TimeSource.h>
18
19#include <support/Autolock.h>
20#include <support/Debug.h>
21
22//XXX: change interface
23#include <interface/Bitmap.h>
24
25#include "CamDevice.h"
26#include "CamSensor.h"
27
28// don't separate parameters from addon, device and sensor
29#define SINGLE_PARAMETER_GROUP 1
30
31// CodyCam and eXposer prefer 320x240
32#define FORCE_320_240 1
33//#define FORCE_160_120 1
34//#define FORCE_MAX_FRAME 1
35
36#define TOUCH(x) ((void)(x))
37
38#define PRINTF(a,b) \
39		do { \
40			if (a < 2) { \
41				printf("VideoProducer::"); \
42				printf b; \
43			} \
44		} while (0)
45
46#include "Producer.h"
47
48//#define FIELD_RATE 30.f
49//#define FIELD_RATE 29.97f
50#define FIELD_RATE 5.f
51
52
53int32 VideoProducer::fInstances = 0;
54
55
56VideoProducer::VideoProducer(
57		BMediaAddOn *addon, CamDevice *dev, const char *name, int32 internal_id)
58	: BMediaNode(name),
59	BMediaEventLooper(),
60	BBufferProducer(B_MEDIA_RAW_VIDEO),
61	BControllable()
62{
63//	status_t err;
64
65	fInitStatus = B_NO_INIT;
66
67	/* Only allow one instance of the node to exist at any time */
68	if (atomic_add(&fInstances, 1) != 0)
69		return;
70
71	fInternalID = internal_id;
72	fAddOn = addon;
73	fCamDevice = dev;
74
75	fBufferGroup = NULL;
76
77	fThread = -1;
78	fFrameSync = -1;
79	fProcessingLatency = 0LL;
80
81	fRunning = false;
82	fConnected = false;
83	fEnabled = false;
84
85	fOutput.destination = media_destination::null;
86
87	AddNodeKind(B_PHYSICAL_INPUT);
88
89	fInitStatus = B_OK;
90	return;
91}
92
93
94VideoProducer::~VideoProducer()
95{
96	if (fInitStatus == B_OK) {
97		/* Clean up after ourselves, in case the application didn't make us
98		 * do so. */
99		if (fConnected)
100			Disconnect(fOutput.source, fOutput.destination);
101		if (fRunning)
102			HandleStop();
103	}
104
105	atomic_add(&fInstances, -1);
106}
107
108
109/* BMediaNode */
110port_id
111VideoProducer::ControlPort() const
112{
113	return BMediaNode::ControlPort();
114}
115
116
117BMediaAddOn *
118VideoProducer::AddOn(int32 *internal_id) const
119{
120	if (internal_id)
121		*internal_id = fInternalID;
122	return fAddOn;
123}
124
125
126status_t
127VideoProducer::HandleMessage(int32 /*message*/, const void* /*data*/, size_t /*size*/)
128{
129	return B_ERROR;
130}
131
132
133void
134VideoProducer::Preroll()
135{
136	/* This hook may be called before the node is started to give the hardware
137	 * a chance to start. */
138}
139
140
141void
142VideoProducer::SetTimeSource(BTimeSource* /*time_source*/)
143{
144	/* Tell frame generation thread to recalculate delay value */
145	release_sem(fFrameSync);
146}
147
148
149status_t
150VideoProducer::RequestCompleted(const media_request_info &info)
151{
152	return BMediaNode::RequestCompleted(info);
153}
154
155
156/* BMediaEventLooper */
157
158
159void
160VideoProducer::NodeRegistered()
161{
162	if (fInitStatus != B_OK) {
163		ReportError(B_NODE_IN_DISTRESS);
164		return;
165	}
166
167	/* Set up the parameter web */
168
169	//TODO: remove and put sensible stuff there
170	BParameterWeb *web = new BParameterWeb();
171	BParameterGroup *main = web->MakeGroup(Name());
172	BParameterGroup *g;
173
174	/*
175	g = main->MakeGroup("Color");
176	BDiscreteParameter *state = g->MakeDiscreteParameter(
177			P_COLOR, B_MEDIA_RAW_VIDEO, "Color", "Color");
178	state->AddItem(B_HOST_TO_LENDIAN_INT32(0x00ff0000), "Red");
179	state->AddItem(B_HOST_TO_LENDIAN_INT32(0x0000ff00), "Green");
180	state->AddItem(B_HOST_TO_LENDIAN_INT32(0x000000ff), "Blue");
181	*/
182
183	BParameter *p;
184	g = main->MakeGroup("Info");
185	p = g->MakeTextParameter(
186		P_INFO, B_MEDIA_RAW_VIDEO, "", "Info", 256);
187
188	int32 id = P_LAST;
189	if (fCamDevice) {
190#ifndef SINGLE_PARAMETER_GROUP
191		main = web->MakeGroup("Device");
192#endif
193		fCamDevice->AddParameters(main, id);
194		if (fCamDevice->Sensor()) {
195#ifndef SINGLE_PARAMETER_GROUP
196			main = web->MakeGroup("Sensor");
197#endif
198			fCamDevice->Sensor()->AddParameters(main, id);
199		}
200	}
201
202	fColor = B_HOST_TO_LENDIAN_INT32(0x00ff0000);
203	fLastColorChange = system_time();
204
205	/* After this call, the BControllable owns the BParameterWeb object and
206	 * will delete it for you */
207	SetParameterWeb(web);
208
209	fOutput.node = Node();
210	fOutput.source.port = ControlPort();
211	fOutput.source.id = 0;
212	fOutput.destination = media_destination::null;
213	strcpy(fOutput.name, Name());
214
215	/* Tailor these for the output of your device */
216	fOutput.format.type = B_MEDIA_RAW_VIDEO;
217	fOutput.format.u.raw_video = media_raw_video_format::wildcard;
218	fOutput.format.u.raw_video.interlace = 1;
219	fOutput.format.u.raw_video.display.format = B_RGB32;
220	fOutput.format.u.raw_video.field_rate = FIELD_RATE; // XXX: mmu
221
222	/* Start the BMediaEventLooper control loop running */
223	Run();
224}
225
226
227void
228VideoProducer::Start(bigtime_t performance_time)
229{
230	BMediaEventLooper::Start(performance_time);
231}
232
233
234void
235VideoProducer::Stop(bigtime_t performance_time, bool immediate)
236{
237	BMediaEventLooper::Stop(performance_time, immediate);
238}
239
240
241void
242VideoProducer::Seek(bigtime_t media_time, bigtime_t performance_time)
243{
244	BMediaEventLooper::Seek(media_time, performance_time);
245}
246
247
248void
249VideoProducer::TimeWarp(bigtime_t at_real_time, bigtime_t to_performance_time)
250{
251	BMediaEventLooper::TimeWarp(at_real_time, to_performance_time);
252}
253
254
255status_t
256VideoProducer::AddTimer(bigtime_t at_performance_time, int32 cookie)
257{
258	return BMediaEventLooper::AddTimer(at_performance_time, cookie);
259}
260
261
262void
263VideoProducer::SetRunMode(run_mode mode)
264{
265	BMediaEventLooper::SetRunMode(mode);
266}
267
268
269void
270VideoProducer::HandleEvent(const media_timed_event *event,
271		bigtime_t lateness, bool realTimeEvent)
272{
273	TOUCH(lateness); TOUCH(realTimeEvent);
274
275	switch(event->type) {
276		case BTimedEventQueue::B_START:
277			HandleStart(event->event_time);
278			break;
279		case BTimedEventQueue::B_STOP:
280			HandleStop();
281			break;
282		case BTimedEventQueue::B_WARP:
283			HandleTimeWarp(event->bigdata);
284			break;
285		case BTimedEventQueue::B_SEEK:
286			HandleSeek(event->bigdata);
287			break;
288		case BTimedEventQueue::B_HANDLE_BUFFER:
289		case BTimedEventQueue::B_DATA_STATUS:
290		case BTimedEventQueue::B_PARAMETER:
291		default:
292			PRINTF(-1, ("HandleEvent: Unhandled event -- %" B_PRIx32 "\n",
293				event->type));
294			break;
295	}
296}
297
298
299void
300VideoProducer::CleanUpEvent(const media_timed_event *event)
301{
302	BMediaEventLooper::CleanUpEvent(event);
303}
304
305
306bigtime_t
307VideoProducer::OfflineTime()
308{
309	return BMediaEventLooper::OfflineTime();
310}
311
312
313void
314VideoProducer::ControlLoop()
315{
316	BMediaEventLooper::ControlLoop();
317}
318
319
320status_t
321VideoProducer::DeleteHook(BMediaNode * node)
322{
323	return BMediaEventLooper::DeleteHook(node);
324}
325
326
327/* BBufferProducer */
328
329
330status_t
331VideoProducer::FormatSuggestionRequested(
332		media_type type, int32 quality, media_format *format)
333{
334	if (type != B_MEDIA_ENCODED_VIDEO)
335		return B_MEDIA_BAD_FORMAT;
336
337	TOUCH(quality);
338
339	PRINTF(1, ("FormatSuggestionRequested() %" B_PRIu32 "x%" B_PRIu32 "\n", \
340			format->u.raw_video.display.line_width, \
341			format->u.raw_video.display.line_count));
342
343	*format = fOutput.format;
344	uint32 width, height;
345	if (fCamDevice && fCamDevice->SuggestVideoFrame(width, height) == B_OK) {
346		format->u.raw_video.display.line_width = width;
347		format->u.raw_video.display.line_count = height;
348	}
349	format->u.raw_video.field_rate = FIELD_RATE;
350	return B_OK;
351}
352
353
354status_t
355VideoProducer::FormatProposal(const media_source &output, media_format *format)
356{
357	status_t err;
358
359	if (!format)
360		return B_BAD_VALUE;
361
362	if (output != fOutput.source)
363		return B_MEDIA_BAD_SOURCE;
364
365	PRINTF(1, ("FormatProposal() %" B_PRIu32 "x%" B_PRIu32 "\n", \
366			format->u.raw_video.display.line_width, \
367			format->u.raw_video.display.line_count));
368
369	err = format_is_compatible(*format, fOutput.format) ?
370			B_OK : B_MEDIA_BAD_FORMAT;
371
372	uint32 width = format->u.raw_video.display.line_width;
373	uint32 height = format->u.raw_video.display.line_count;
374
375	*format = fOutput.format;
376
377	if (err == B_OK && fCamDevice) {
378		err = fCamDevice->AcceptVideoFrame(width, height);
379		if (err >= B_OK) {
380			format->u.raw_video.display.line_width = width;
381			format->u.raw_video.display.line_count = height;
382		}
383	}
384
385	PRINTF(1, ("FormatProposal: %" B_PRIu32 "x%" B_PRIu32 "\n", \
386			format->u.raw_video.display.line_width, \
387			format->u.raw_video.display.line_count));
388
389	return err;
390
391}
392
393
394status_t
395VideoProducer::FormatChangeRequested(const media_source &source,
396		const media_destination &destination, media_format *io_format,
397		int32 *_deprecated_)
398{
399	TOUCH(destination); TOUCH(io_format); TOUCH(_deprecated_);
400	if (source != fOutput.source)
401		return B_MEDIA_BAD_SOURCE;
402
403	return B_ERROR;
404}
405
406
407status_t
408VideoProducer::GetNextOutput(int32 *cookie, media_output *out_output)
409{
410	if (!out_output)
411		return B_BAD_VALUE;
412
413	if ((*cookie) != 0)
414		return B_BAD_INDEX;
415
416	*out_output = fOutput;
417	(*cookie)++;
418	return B_OK;
419}
420
421
422status_t
423VideoProducer::DisposeOutputCookie(int32 cookie)
424{
425	TOUCH(cookie);
426
427	return B_OK;
428}
429
430
431status_t
432VideoProducer::SetBufferGroup(const media_source &for_source,
433		BBufferGroup *group)
434{
435	TOUCH(for_source); TOUCH(group);
436
437	return B_ERROR;
438}
439
440
441status_t
442VideoProducer::VideoClippingChanged(const media_source &for_source,
443		int16 num_shorts, int16 *clip_data,
444		const media_video_display_info &display, int32 *_deprecated_)
445{
446	TOUCH(for_source); TOUCH(num_shorts); TOUCH(clip_data);
447	TOUCH(display); TOUCH(_deprecated_);
448
449	return B_ERROR;
450}
451
452
453status_t
454VideoProducer::GetLatency(bigtime_t *out_latency)
455{
456	*out_latency = EventLatency() + SchedulingLatency();
457	return B_OK;
458}
459
460
461status_t
462VideoProducer::PrepareToConnect(const media_source &source,
463		const media_destination &destination, media_format *format,
464		media_source *out_source, char *out_name)
465{
466	status_t err;
467
468	PRINTF(1, ("PrepareToConnect() %" B_PRIu32 "x%" B_PRIu32 "\n", \
469			format->u.raw_video.display.line_width, \
470			format->u.raw_video.display.line_count));
471
472	if (fConnected) {
473		PRINTF(0, ("PrepareToConnect: Already connected\n"));
474		return EALREADY;
475	}
476
477	if (source != fOutput.source)
478		return B_MEDIA_BAD_SOURCE;
479
480	if (fOutput.destination != media_destination::null)
481		return B_MEDIA_ALREADY_CONNECTED;
482
483	/* The format parameter comes in with the suggested format, and may be
484	 * specialized as desired by the node */
485	if (!format_is_compatible(*format, fOutput.format)) {
486		*format = fOutput.format;
487		return B_MEDIA_BAD_FORMAT;
488	}
489
490//XXX:FIXME
491#if 0
492//	if (format->u.raw_video.display.line_width == 0)
493		format->u.raw_video.display.line_width = 352;//320;
494		format->u.raw_video.display.line_width = 320;
495//	if (format->u.raw_video.display.line_count == 0)
496		format->u.raw_video.display.line_count = 288;//240;
497		format->u.raw_video.display.line_count = 240;
498#endif
499
500#ifdef FORCE_320_240
501	{
502		format->u.raw_video.display.line_width = 320;
503		format->u.raw_video.display.line_count = 240;
504	}
505#endif
506#ifdef FORCE_160_120
507	{
508		format->u.raw_video.display.line_width = 160;
509		format->u.raw_video.display.line_count = 120;
510	}
511#endif
512#ifdef FORCE_MAX_FRAME
513	{
514		format->u.raw_video.display.line_width = 0;
515		format->u.raw_video.display.line_count = 0;
516	}
517#endif
518	if (fCamDevice) {
519		err = fCamDevice->AcceptVideoFrame(
520			format->u.raw_video.display.line_width,
521			format->u.raw_video.display.line_count);
522		if (err < B_OK)
523			return err;
524	}
525
526	if (format->u.raw_video.field_rate == 0)
527		format->u.raw_video.field_rate = FIELD_RATE;
528
529	*out_source = fOutput.source;
530	strcpy(out_name, fOutput.name);
531
532	fOutput.destination = destination;
533
534	return B_OK;
535}
536
537
538void
539VideoProducer::Connect(status_t error, const media_source &source,
540		const media_destination &destination, const media_format &format,
541		char *io_name)
542{
543	PRINTF(1, ("Connect() %" B_PRIu32 "x%" B_PRIu32 "\n", \
544			format.u.raw_video.display.line_width, \
545			format.u.raw_video.display.line_count));
546
547	if (fConnected) {
548		PRINTF(0, ("Connect: Already connected\n"));
549		return;
550	}
551
552	BAutolock lock(fCamDevice->Locker());
553	if (!fCamDevice->IsPlugged()) {
554		PRINTF(0, ("Connect: Device unplugged\n"));
555		return;
556	}
557
558	if (source != fOutput.source || error < B_OK
559		|| !const_cast<media_format *>(&format)->Matches(&fOutput.format)) {
560		PRINTF(1, ("Connect: Connect error\n"));
561		return;
562	}
563
564	fOutput.destination = destination;
565	strcpy(io_name, fOutput.name);
566
567	if (fOutput.format.u.raw_video.field_rate != 0.0f) {
568		fPerformanceTimeBase = fPerformanceTimeBase +
569				(bigtime_t)
570					((fFrame - fFrameBase) *
571					(1000000 / fOutput.format.u.raw_video.field_rate));
572		fFrameBase = fFrame;
573	}
574
575	fConnectedFormat = format.u.raw_video;
576
577	/* get the latency */
578	bigtime_t latency = 0;
579	media_node_id tsID = 0;
580	FindLatencyFor(fOutput.destination, &latency, &tsID);
581	#define NODE_LATENCY 1000
582	SetEventLatency(latency + NODE_LATENCY);
583
584	uint32 *buffer, *p, f = 3;
585	p = buffer = (uint32 *)malloc(4 * fConnectedFormat.display.line_count *
586			fConnectedFormat.display.line_width);
587	if (!buffer) {
588		PRINTF(0, ("Connect: Out of memory\n"));
589		return;
590	}
591	bigtime_t now = system_time();
592	for (uint32 y=0;y<fConnectedFormat.display.line_count;y++)
593		for (uint32 x=0;x<fConnectedFormat.display.line_width;x++)
594			*(p++) = ((((x+y)^0^x)+f) & 0xff) * (0x01010101 & fColor);
595	fProcessingLatency = system_time() - now;
596	free(buffer);
597
598	/* Create the buffer group */
599	fBufferGroup = new BBufferGroup(4 * fConnectedFormat.display.line_width *
600			fConnectedFormat.display.line_count, 8);
601	if (fBufferGroup->InitCheck() < B_OK) {
602		delete fBufferGroup;
603		fBufferGroup = NULL;
604		return;
605	}
606
607	fConnected = true;
608	fEnabled = true;
609
610	/* Tell frame generation thread to recalculate delay value */
611	release_sem(fFrameSync);
612}
613
614void
615VideoProducer::Disconnect(const media_source &source,
616		const media_destination &destination)
617{
618	PRINTF(1, ("Disconnect()\n"));
619
620	if (!fConnected) {
621		PRINTF(0, ("Disconnect: Not connected\n"));
622		return;
623	}
624
625	if ((source != fOutput.source) || (destination != fOutput.destination)) {
626		PRINTF(0, ("Disconnect: Bad source and/or destination\n"));
627		return;
628	}
629
630#if 1
631	/* Some dumb apps don't stop nodes before disconnecting... */
632	if (fRunning)
633		HandleStop();
634#endif
635
636	fEnabled = false;
637	fOutput.destination = media_destination::null;
638
639	fLock.Lock();
640		delete fBufferGroup;
641		fBufferGroup = NULL;
642	fLock.Unlock();
643
644	fConnected = false;
645}
646
647
648void
649VideoProducer::LateNoticeReceived(const media_source &source,
650		bigtime_t how_much, bigtime_t performance_time)
651{
652	TOUCH(source); TOUCH(how_much); TOUCH(performance_time);
653}
654
655
656void
657VideoProducer::EnableOutput(const media_source &source, bool enabled,
658		int32 *_deprecated_)
659{
660	TOUCH(_deprecated_);
661
662	if (source != fOutput.source)
663		return;
664
665	fEnabled = enabled;
666}
667
668
669status_t
670VideoProducer::SetPlayRate(int32 numer, int32 denom)
671{
672	TOUCH(numer); TOUCH(denom);
673
674	return B_ERROR;
675}
676
677
678void
679VideoProducer::AdditionalBufferRequested(const media_source &source,
680		media_buffer_id prev_buffer, bigtime_t prev_time,
681		const media_seek_tag *prev_tag)
682{
683	TOUCH(source); TOUCH(prev_buffer); TOUCH(prev_time); TOUCH(prev_tag);
684}
685
686
687void
688VideoProducer::LatencyChanged(const media_source &source,
689		const media_destination &destination, bigtime_t new_latency,
690		uint32 flags)
691{
692	TOUCH(source); TOUCH(destination); TOUCH(new_latency); TOUCH(flags);
693}
694
695
696/* BControllable */
697
698
699status_t
700VideoProducer::GetParameterValue(
701	int32 id, bigtime_t *last_change, void *value, size_t *size)
702{
703	status_t err;
704
705	switch (id) {
706		case P_COLOR:
707			//return B_BAD_VALUE;
708
709			*last_change = fLastColorChange;
710			*size = sizeof(uint32);
711			*((uint32 *)value) = fColor;
712			return B_OK;
713		case P_INFO:
714			if (*size < (size_t)(fInfoString.Length() + 1))
715				return EINVAL;
716			*last_change = fLastColorChange;
717			*size = fInfoString.Length() + 1;
718			memcpy(value, fInfoString.String(), *size);
719			return B_OK;
720	}
721
722	if (fCamDevice) {
723		BAutolock lock(fCamDevice->Locker());
724		err = fCamDevice->GetParameterValue(id, last_change, value, size);
725		if (err >= B_OK)
726			return err;
727		if (fCamDevice->Sensor()) {
728			err = fCamDevice->Sensor()->GetParameterValue(id, last_change, value, size);
729			if (err >= B_OK)
730				return err;
731		}
732	}
733
734	return B_BAD_VALUE;
735}
736
737
738void
739VideoProducer::SetParameterValue(
740	int32 id, bigtime_t when, const void *value, size_t size)
741{
742	status_t err = B_OK;
743
744	switch (id) {
745		case P_COLOR:
746			if (!value || (size != sizeof(uint32)))
747				return;
748
749			if (*(uint32 *)value == fColor)
750				return;
751
752			fColor = *(uint32 *)value;
753			fLastColorChange = when;
754			break;
755		case P_INFO:
756			// forbidden
757			return;
758		default:
759			if (fCamDevice == NULL)
760				return;
761
762			BAutolock lock(fCamDevice->Locker());
763			err = fCamDevice->SetParameterValue(id, when, value, size);
764			if ((err < B_OK) && (fCamDevice->Sensor())) {
765				err = fCamDevice->Sensor()->SetParameterValue(id, when, value, size);
766			}
767	}
768
769	if (err >= B_OK)
770		BroadcastNewParameterValue(when, id, (void *)value, size);
771}
772
773
774status_t
775VideoProducer::StartControlPanel(BMessenger *out_messenger)
776{
777	return BControllable::StartControlPanel(out_messenger);
778}
779
780
781/* VideoProducer */
782
783
784void
785VideoProducer::HandleStart(bigtime_t performance_time)
786{
787	/* Start producing frames, even if the output hasn't been connected yet. */
788
789	PRINTF(1, ("HandleStart(%" B_PRIdBIGTIME ")\n", performance_time));
790
791	if (fRunning) {
792		PRINTF(-1, ("HandleStart: Node already started\n"));
793		return;
794	}
795
796	fFrame = 0;
797	fFrameBase = 0;
798	fPerformanceTimeBase = performance_time;
799
800	fFrameSync = create_sem(0, "frame synchronization");
801	if (fFrameSync < B_OK)
802		goto err1;
803
804	fThread = spawn_thread(_frame_generator_, "frame generator",
805			B_NORMAL_PRIORITY, this);
806	if (fThread < B_OK)
807		goto err2;
808
809	resume_thread(fThread);
810
811	{
812		BAutolock lock(fCamDevice->Locker());
813		fCamDevice->StartTransfer();
814	}
815
816	fRunning = true;
817	return;
818
819err2:
820	delete_sem(fFrameSync);
821err1:
822	return;
823}
824
825
826void
827VideoProducer::HandleStop(void)
828{
829	PRINTF(1, ("HandleStop()\n"));
830
831	if (!fRunning) {
832		PRINTF(-1, ("HandleStop: Node isn't running\n"));
833		return;
834	}
835
836	delete_sem(fFrameSync);
837	wait_for_thread(fThread, &fThread);
838
839	BAutolock lock(fCamDevice->Locker());
840	fCamDevice->StopTransfer();
841
842	fRunning = false;
843}
844
845
846void
847VideoProducer::HandleTimeWarp(bigtime_t performance_time)
848{
849	fPerformanceTimeBase = performance_time;
850	fFrameBase = fFrame;
851
852	/* Tell frame generation thread to recalculate delay value */
853	release_sem(fFrameSync);
854}
855
856
857void
858VideoProducer::HandleSeek(bigtime_t performance_time)
859{
860	fPerformanceTimeBase = performance_time;
861	fFrameBase = fFrame;
862
863	/* Tell frame generation thread to recalculate delay value */
864	release_sem(fFrameSync);
865}
866
867
868void
869VideoProducer::_UpdateStats()
870{
871	float fps = (fStats[0].frames - fStats[1].frames) * 1000000LL
872				/ (double)(fStats[0].stamp - fStats[1].stamp);
873	float rfps = (fStats[0].actual - fStats[1].actual) * 1000000LL
874				/ (double)(fStats[0].stamp - fStats[1].stamp);
875	fInfoString = "FPS: ";
876	fInfoString << fps << " virt, "
877		<< rfps << " real, missed: " << fStats[0].missed;
878	memcpy(&fStats[1], &fStats[0], sizeof(fStats[0]));
879	fLastColorChange = system_time();
880	BroadcastNewParameterValue(fLastColorChange, P_INFO,
881		(void *)fInfoString.String(), fInfoString.Length()+1);
882}
883
884
885/* The following functions form the thread that generates frames. You should
886 * replace this with the code that interfaces to your hardware. */
887int32
888VideoProducer::FrameGenerator()
889{
890	bigtime_t wait_until = system_time();
891
892	while (1) {
893		PRINTF(1, ("FrameGenerator: " \
894			"acquire_sem_etc() until %" B_PRIdBIGTIME "��s " \
895			"(in %" B_PRIdBIGTIME "��s)\n", \
896			wait_until, wait_until - system_time()));
897		status_t err = acquire_sem_etc(fFrameSync, 1, B_ABSOLUTE_TIMEOUT,
898				wait_until);
899
900		/* The only acceptable responses are B_OK and B_TIMED_OUT. Everything
901		 * else means the thread should quit. Deleting the semaphore, as in
902		 * VideoProducer::HandleStop(), will trigger this behavior. */
903		if ((err != B_OK) && (err != B_TIMED_OUT))
904			break;
905
906		fFrame++;
907
908		/* Recalculate the time until the thread should wake up to begin
909		 * processing the next frame. Subtract fProcessingLatency so that
910		 * the frame is sent in time. */
911		wait_until = TimeSource()->RealTimeFor(fPerformanceTimeBase, 0) +
912				(bigtime_t)
913						((fFrame - fFrameBase) *
914						(1000000 / fConnectedFormat.field_rate)) -
915				fProcessingLatency;
916		PRINT(("PS: %" B_PRIdBIGTIME "\n", fProcessingLatency));
917
918		/* Drop frame if it's at least a frame late */
919		if (wait_until < system_time())
920			continue;
921
922		PRINTF(1, ("FrameGenerator: wait until %" B_PRIdBIGTIME ", "
923			"%ctimed out, %crunning, %cenabled.\n",
924			wait_until,
925			(err == B_OK)?'!':' ',
926			(fRunning)?' ':'!',
927			(fEnabled)?' ':'!'));
928
929		/* If the semaphore was acquired successfully, it means something
930		 * changed the timing information (see VideoProducer::Connect()) and
931		 * so the thread should go back to sleep until the newly-calculated
932		 * wait_until time. */
933		if (err == B_OK)
934			continue;
935
936		/* Send buffers only if the node is running and the output has been
937		 * enabled */
938		if (!fRunning || !fEnabled)
939			continue;
940
941		BAutolock _(fLock);
942
943		/* Fetch a buffer from the buffer group */
944		BBuffer *buffer = fBufferGroup->RequestBuffer(
945						4 * fConnectedFormat.display.line_width *
946						fConnectedFormat.display.line_count, 0LL);
947		if (!buffer)
948			continue;
949
950		/* Fill out the details about this buffer. */
951		media_header *h = buffer->Header();
952		h->type = B_MEDIA_RAW_VIDEO;
953		h->time_source = TimeSource()->ID();
954		h->size_used = 4 * fConnectedFormat.display.line_width *
955						fConnectedFormat.display.line_count;
956		/* For a buffer originating from a device, you might want to calculate
957		 * this based on the PerformanceTimeFor the time your buffer arrived at
958		 * the hardware (plus any applicable adjustments). */
959		/*
960		h->start_time = fPerformanceTimeBase +
961						(bigtime_t)
962							((fFrame - fFrameBase) *
963							(1000000 / fConnectedFormat.field_rate));
964		*/
965		h->file_pos = 0;
966		h->orig_size = 0;
967		h->data_offset = 0;
968		h->u.raw_video.field_gamma = 1.0;
969		h->u.raw_video.field_sequence = fFrame;
970		h->u.raw_video.field_number = 0;
971		h->u.raw_video.pulldown_number = 0;
972		h->u.raw_video.first_active_line = 1;
973		h->u.raw_video.line_count = fConnectedFormat.display.line_count;
974
975		// This is where we fill the video buffer.
976
977#if 0
978		uint32 *p = (uint32 *)buffer->Data();
979		/* Fill in a pattern */
980		for (uint32 y=0;y<fConnectedFormat.display.line_count;y++)
981			for (uint32 x=0;x<fConnectedFormat.display.line_width;x++)
982				*(p++) = ((((x+y)^0^x)+fFrame) & 0xff) * (0x01010101 & fColor);
983#endif
984
985		//NO! must be called without lock!
986		//BAutolock lock(fCamDevice->Locker());
987
988		bigtime_t now = system_time();
989		bigtime_t stamp;
990//#ifdef UseFillFrameBuffer
991		err = fCamDevice->FillFrameBuffer(buffer, &stamp);
992		if (err < B_OK) {
993			;//XXX handle error
994			fStats[0].missed++;
995		}
996//#endif
997#ifdef UseGetFrameBitmap
998		BBitmap *bm;
999		err = fCamDevice->GetFrameBitmap(&bm, &stamp);
1000		if (err >= B_OK) {
1001			;//XXX handle error
1002			fStats[0].missed++;
1003		}
1004#endif
1005		fStats[0].frames = fFrame;
1006		fStats[0].actual++;;
1007		fStats[0].stamp = system_time();
1008
1009		//PRINTF(1, ("FrameGenerator: stamp %lld vs %lld\n", stamp, h->start_time));
1010		//XXX: that's what we should be doing, but CodyCam drops all frames as they are late. (maybe add latency ??)
1011		//h->start_time = TimeSource()->PerformanceTimeFor(stamp);
1012		h->start_time = TimeSource()->PerformanceTimeFor(system_time());
1013
1014
1015		// update processing latency
1016		// XXX: should I ??
1017		fProcessingLatency = system_time() - now;
1018		fProcessingLatency /= 10;
1019
1020		PRINTF(1, ("FrameGenerator: SendBuffer...\n"));
1021		/* Send the buffer on down to the consumer */
1022		if (SendBuffer(buffer, fOutput.source, fOutput.destination) < B_OK) {
1023			PRINTF(-1, ("FrameGenerator: Error sending buffer\n"));
1024			/* If there is a problem sending the buffer, return it to its
1025			 * buffer group. */
1026			buffer->Recycle();
1027		}
1028
1029		_UpdateStats();
1030	}
1031
1032	PRINTF(1, ("FrameGenerator: thread existed.\n"));
1033	return B_OK;
1034}
1035
1036
1037int32
1038VideoProducer::_frame_generator_(void *data)
1039{
1040	return ((VideoProducer *)data)->FrameGenerator();
1041}
1042