xref: /haiku/src/add-ons/media/media-add-ons/usb_webcam/Producer.cpp (revision 37c7d5d83a2372a6971e383411d5bacbeef0ebdc)
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 
53 int32 VideoProducer::fInstances = 0;
54 
55 
56 VideoProducer::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 
94 VideoProducer::~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 */
110 port_id
111 VideoProducer::ControlPort() const
112 {
113 	return BMediaNode::ControlPort();
114 }
115 
116 
117 BMediaAddOn *
118 VideoProducer::AddOn(int32 *internal_id) const
119 {
120 	if (internal_id)
121 		*internal_id = fInternalID;
122 	return fAddOn;
123 }
124 
125 
126 status_t
127 VideoProducer::HandleMessage(int32 /*message*/, const void* /*data*/, size_t /*size*/)
128 {
129 	return B_ERROR;
130 }
131 
132 
133 void
134 VideoProducer::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 
141 void
142 VideoProducer::SetTimeSource(BTimeSource* /*time_source*/)
143 {
144 	/* Tell frame generation thread to recalculate delay value */
145 	release_sem(fFrameSync);
146 }
147 
148 
149 status_t
150 VideoProducer::RequestCompleted(const media_request_info &info)
151 {
152 	return BMediaNode::RequestCompleted(info);
153 }
154 
155 
156 /* BMediaEventLooper */
157 
158 
159 void
160 VideoProducer::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 
227 void
228 VideoProducer::Start(bigtime_t performance_time)
229 {
230 	BMediaEventLooper::Start(performance_time);
231 }
232 
233 
234 void
235 VideoProducer::Stop(bigtime_t performance_time, bool immediate)
236 {
237 	BMediaEventLooper::Stop(performance_time, immediate);
238 }
239 
240 
241 void
242 VideoProducer::Seek(bigtime_t media_time, bigtime_t performance_time)
243 {
244 	BMediaEventLooper::Seek(media_time, performance_time);
245 }
246 
247 
248 void
249 VideoProducer::TimeWarp(bigtime_t at_real_time, bigtime_t to_performance_time)
250 {
251 	BMediaEventLooper::TimeWarp(at_real_time, to_performance_time);
252 }
253 
254 
255 status_t
256 VideoProducer::AddTimer(bigtime_t at_performance_time, int32 cookie)
257 {
258 	return BMediaEventLooper::AddTimer(at_performance_time, cookie);
259 }
260 
261 
262 void
263 VideoProducer::SetRunMode(run_mode mode)
264 {
265 	BMediaEventLooper::SetRunMode(mode);
266 }
267 
268 
269 void
270 VideoProducer::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 -- %lx\n", event->type));
293 			break;
294 	}
295 }
296 
297 
298 void
299 VideoProducer::CleanUpEvent(const media_timed_event *event)
300 {
301 	BMediaEventLooper::CleanUpEvent(event);
302 }
303 
304 
305 bigtime_t
306 VideoProducer::OfflineTime()
307 {
308 	return BMediaEventLooper::OfflineTime();
309 }
310 
311 
312 void
313 VideoProducer::ControlLoop()
314 {
315 	BMediaEventLooper::ControlLoop();
316 }
317 
318 
319 status_t
320 VideoProducer::DeleteHook(BMediaNode * node)
321 {
322 	return BMediaEventLooper::DeleteHook(node);
323 }
324 
325 
326 /* BBufferProducer */
327 
328 
329 status_t
330 VideoProducer::FormatSuggestionRequested(
331 		media_type type, int32 quality, media_format *format)
332 {
333 	if (type != B_MEDIA_ENCODED_VIDEO)
334 		return B_MEDIA_BAD_FORMAT;
335 
336 	TOUCH(quality);
337 
338 	PRINTF(1, ("FormatSuggestionRequested() %ldx%ld\n", \
339 			format->u.raw_video.display.line_width, \
340 			format->u.raw_video.display.line_count));
341 
342 	*format = fOutput.format;
343 	if (fCamDevice && fCamDevice->Sensor()) {
344 		format->u.raw_video.display.line_width = fCamDevice->Sensor()->MaxWidth();
345 		format->u.raw_video.display.line_count = fCamDevice->Sensor()->MaxHeight();
346 	}
347 	format->u.raw_video.field_rate = FIELD_RATE;
348 	return B_OK;
349 }
350 
351 
352 status_t
353 VideoProducer::FormatProposal(const media_source &output, media_format *format)
354 {
355 	status_t err;
356 
357 	if (!format)
358 		return B_BAD_VALUE;
359 
360 	if (output != fOutput.source)
361 		return B_MEDIA_BAD_SOURCE;
362 
363 	PRINTF(1, ("FormatProposal() %ldx%ld\n", \
364 			format->u.raw_video.display.line_width, \
365 			format->u.raw_video.display.line_count));
366 
367 	err = format_is_compatible(*format, fOutput.format) ?
368 			B_OK : B_MEDIA_BAD_FORMAT;
369 
370 	uint32 width = format->u.raw_video.display.line_width;
371 	uint32 height = format->u.raw_video.display.line_count;
372 
373 	*format = fOutput.format;
374 
375 	if (err == B_OK && fCamDevice) {
376 		err = fCamDevice->AcceptVideoFrame(width, height);
377 		if (err >= B_OK) {
378 			format->u.raw_video.display.line_width = width;
379 			format->u.raw_video.display.line_count = height;
380 		}
381 	}
382 
383 	PRINTF(1, ("FormatProposal: %ldx%ld\n", \
384 			format->u.raw_video.display.line_width, \
385 			format->u.raw_video.display.line_count));
386 
387 	return err;
388 
389 }
390 
391 
392 status_t
393 VideoProducer::FormatChangeRequested(const media_source &source,
394 		const media_destination &destination, media_format *io_format,
395 		int32 *_deprecated_)
396 {
397 	TOUCH(destination); TOUCH(io_format); TOUCH(_deprecated_);
398 	if (source != fOutput.source)
399 		return B_MEDIA_BAD_SOURCE;
400 
401 	return B_ERROR;
402 }
403 
404 
405 status_t
406 VideoProducer::GetNextOutput(int32 *cookie, media_output *out_output)
407 {
408 	if (!out_output)
409 		return B_BAD_VALUE;
410 
411 	if ((*cookie) != 0)
412 		return B_BAD_INDEX;
413 
414 	*out_output = fOutput;
415 	(*cookie)++;
416 	return B_OK;
417 }
418 
419 
420 status_t
421 VideoProducer::DisposeOutputCookie(int32 cookie)
422 {
423 	TOUCH(cookie);
424 
425 	return B_OK;
426 }
427 
428 
429 status_t
430 VideoProducer::SetBufferGroup(const media_source &for_source,
431 		BBufferGroup *group)
432 {
433 	TOUCH(for_source); TOUCH(group);
434 
435 	return B_ERROR;
436 }
437 
438 
439 status_t
440 VideoProducer::VideoClippingChanged(const media_source &for_source,
441 		int16 num_shorts, int16 *clip_data,
442 		const media_video_display_info &display, int32 *_deprecated_)
443 {
444 	TOUCH(for_source); TOUCH(num_shorts); TOUCH(clip_data);
445 	TOUCH(display); TOUCH(_deprecated_);
446 
447 	return B_ERROR;
448 }
449 
450 
451 status_t
452 VideoProducer::GetLatency(bigtime_t *out_latency)
453 {
454 	*out_latency = EventLatency() + SchedulingLatency();
455 	return B_OK;
456 }
457 
458 
459 status_t
460 VideoProducer::PrepareToConnect(const media_source &source,
461 		const media_destination &destination, media_format *format,
462 		media_source *out_source, char *out_name)
463 {
464 	status_t err;
465 
466 	PRINTF(1, ("PrepareToConnect() %ldx%ld\n", \
467 			format->u.raw_video.display.line_width, \
468 			format->u.raw_video.display.line_count));
469 
470 	if (fConnected) {
471 		PRINTF(0, ("PrepareToConnect: Already connected\n"));
472 		return EALREADY;
473 	}
474 
475 	if (source != fOutput.source)
476 		return B_MEDIA_BAD_SOURCE;
477 
478 	if (fOutput.destination != media_destination::null)
479 		return B_MEDIA_ALREADY_CONNECTED;
480 
481 	/* The format parameter comes in with the suggested format, and may be
482 	 * specialized as desired by the node */
483 	if (!format_is_compatible(*format, fOutput.format)) {
484 		*format = fOutput.format;
485 		return B_MEDIA_BAD_FORMAT;
486 	}
487 
488 //XXX:FIXME
489 #if 0
490 //	if (format->u.raw_video.display.line_width == 0)
491 		format->u.raw_video.display.line_width = 352;//320;
492 		format->u.raw_video.display.line_width = 320;
493 //	if (format->u.raw_video.display.line_count == 0)
494 		format->u.raw_video.display.line_count = 288;//240;
495 		format->u.raw_video.display.line_count = 240;
496 #endif
497 
498 #ifdef FORCE_320_240
499 	{
500 		format->u.raw_video.display.line_width = 320;
501 		format->u.raw_video.display.line_count = 240;
502 	}
503 #endif
504 #ifdef FORCE_160_120
505 	{
506 		format->u.raw_video.display.line_width = 160;
507 		format->u.raw_video.display.line_count = 120;
508 	}
509 #endif
510 #ifdef FORCE_MAX_FRAME
511 	{
512 		format->u.raw_video.display.line_width = 0;
513 		format->u.raw_video.display.line_count = 0;
514 	}
515 #endif
516 	if (fCamDevice) {
517 		err = fCamDevice->AcceptVideoFrame(
518 			format->u.raw_video.display.line_width,
519 			format->u.raw_video.display.line_count);
520 		if (err < B_OK)
521 			return err;
522 	}
523 
524 	if (format->u.raw_video.field_rate == 0)
525 		format->u.raw_video.field_rate = FIELD_RATE;
526 
527 	*out_source = fOutput.source;
528 	strcpy(out_name, fOutput.name);
529 
530 	fOutput.destination = destination;
531 
532 	return B_OK;
533 }
534 
535 
536 void
537 VideoProducer::Connect(status_t error, const media_source &source,
538 		const media_destination &destination, const media_format &format,
539 		char *io_name)
540 {
541 	PRINTF(1, ("Connect() %ldx%ld\n", \
542 			format.u.raw_video.display.line_width, \
543 			format.u.raw_video.display.line_count));
544 
545 	if (fConnected) {
546 		PRINTF(0, ("Connect: Already connected\n"));
547 		return;
548 	}
549 
550 	BAutolock lock(fCamDevice->Locker());
551 	if (!fCamDevice->IsPlugged()) {
552 		PRINTF(0, ("Connect: Device unplugged\n"));
553 		return;
554 	}
555 
556 	if (source != fOutput.source || error < B_OK
557 		|| !const_cast<media_format *>(&format)->Matches(&fOutput.format)) {
558 		PRINTF(1, ("Connect: Connect error\n"));
559 		return;
560 	}
561 
562 	fOutput.destination = destination;
563 	strcpy(io_name, fOutput.name);
564 
565 	if (fOutput.format.u.raw_video.field_rate != 0.0f) {
566 		fPerformanceTimeBase = fPerformanceTimeBase +
567 				(bigtime_t)
568 					((fFrame - fFrameBase) *
569 					(1000000 / fOutput.format.u.raw_video.field_rate));
570 		fFrameBase = fFrame;
571 	}
572 
573 	fConnectedFormat = format.u.raw_video;
574 
575 	/* get the latency */
576 	bigtime_t latency = 0;
577 	media_node_id tsID = 0;
578 	FindLatencyFor(fOutput.destination, &latency, &tsID);
579 	#define NODE_LATENCY 1000
580 	SetEventLatency(latency + NODE_LATENCY);
581 
582 	uint32 *buffer, *p, f = 3;
583 	p = buffer = (uint32 *)malloc(4 * fConnectedFormat.display.line_count *
584 			fConnectedFormat.display.line_width);
585 	if (!buffer) {
586 		PRINTF(0, ("Connect: Out of memory\n"));
587 		return;
588 	}
589 	bigtime_t now = system_time();
590 	for (uint32 y=0;y<fConnectedFormat.display.line_count;y++)
591 		for (uint32 x=0;x<fConnectedFormat.display.line_width;x++)
592 			*(p++) = ((((x+y)^0^x)+f) & 0xff) * (0x01010101 & fColor);
593 	fProcessingLatency = system_time() - now;
594 	free(buffer);
595 
596 	/* Create the buffer group */
597 	fBufferGroup = new BBufferGroup(4 * fConnectedFormat.display.line_width *
598 			fConnectedFormat.display.line_count, 8);
599 	if (fBufferGroup->InitCheck() < B_OK) {
600 		delete fBufferGroup;
601 		fBufferGroup = NULL;
602 		return;
603 	}
604 
605 	fConnected = true;
606 	fEnabled = true;
607 
608 	/* Tell frame generation thread to recalculate delay value */
609 	release_sem(fFrameSync);
610 }
611 
612 void
613 VideoProducer::Disconnect(const media_source &source,
614 		const media_destination &destination)
615 {
616 	PRINTF(1, ("Disconnect()\n"));
617 
618 	if (!fConnected) {
619 		PRINTF(0, ("Disconnect: Not connected\n"));
620 		return;
621 	}
622 
623 	if ((source != fOutput.source) || (destination != fOutput.destination)) {
624 		PRINTF(0, ("Disconnect: Bad source and/or destination\n"));
625 		return;
626 	}
627 
628 #if 1
629 	/* Some dumb apps don't stop nodes before disconnecting... */
630 	if (fRunning)
631 		HandleStop();
632 #endif
633 
634 	fEnabled = false;
635 	fOutput.destination = media_destination::null;
636 
637 	fLock.Lock();
638 		delete fBufferGroup;
639 		fBufferGroup = NULL;
640 	fLock.Unlock();
641 
642 	fConnected = false;
643 }
644 
645 
646 void
647 VideoProducer::LateNoticeReceived(const media_source &source,
648 		bigtime_t how_much, bigtime_t performance_time)
649 {
650 	TOUCH(source); TOUCH(how_much); TOUCH(performance_time);
651 }
652 
653 
654 void
655 VideoProducer::EnableOutput(const media_source &source, bool enabled,
656 		int32 *_deprecated_)
657 {
658 	TOUCH(_deprecated_);
659 
660 	if (source != fOutput.source)
661 		return;
662 
663 	fEnabled = enabled;
664 }
665 
666 
667 status_t
668 VideoProducer::SetPlayRate(int32 numer, int32 denom)
669 {
670 	TOUCH(numer); TOUCH(denom);
671 
672 	return B_ERROR;
673 }
674 
675 
676 void
677 VideoProducer::AdditionalBufferRequested(const media_source &source,
678 		media_buffer_id prev_buffer, bigtime_t prev_time,
679 		const media_seek_tag *prev_tag)
680 {
681 	TOUCH(source); TOUCH(prev_buffer); TOUCH(prev_time); TOUCH(prev_tag);
682 }
683 
684 
685 void
686 VideoProducer::LatencyChanged(const media_source &source,
687 		const media_destination &destination, bigtime_t new_latency,
688 		uint32 flags)
689 {
690 	TOUCH(source); TOUCH(destination); TOUCH(new_latency); TOUCH(flags);
691 }
692 
693 
694 /* BControllable */
695 
696 
697 status_t
698 VideoProducer::GetParameterValue(
699 	int32 id, bigtime_t *last_change, void *value, size_t *size)
700 {
701 	status_t err;
702 
703 	switch (id) {
704 		case P_COLOR:
705 			//return B_BAD_VALUE;
706 
707 			*last_change = fLastColorChange;
708 			*size = sizeof(uint32);
709 			*((uint32 *)value) = fColor;
710 			return B_OK;
711 		case P_INFO:
712 			if (*size < fInfoString.Length() + 1)
713 				return EINVAL;
714 			*last_change = fLastColorChange;
715 			*size = fInfoString.Length() + 1;
716 			memcpy(value, fInfoString.String(), *size);
717 			return B_OK;
718 	}
719 
720 	if (fCamDevice) {
721 		BAutolock lock(fCamDevice->Locker());
722 		err = fCamDevice->GetParameterValue(id, last_change, value, size);
723 		if (err >= B_OK)
724 			return err;
725 		if (fCamDevice->Sensor()) {
726 			err = fCamDevice->Sensor()->GetParameterValue(id, last_change, value, size);
727 			if (err >= B_OK)
728 				return err;
729 		}
730 	}
731 
732 	return B_BAD_VALUE;
733 }
734 
735 
736 void
737 VideoProducer::SetParameterValue(
738 	int32 id, bigtime_t when, const void *value, size_t size)
739 {
740 	status_t err = B_OK;
741 
742 	switch (id) {
743 		case P_COLOR:
744 			if (!value || (size != sizeof(uint32)))
745 				return;
746 
747 			if (*(uint32 *)value == fColor)
748 				return;
749 
750 			fColor = *(uint32 *)value;
751 			fLastColorChange = when;
752 			break;
753 		case P_INFO:
754 			// forbidden
755 			return;
756 		default:
757 			if (fCamDevice == NULL)
758 				return;
759 
760 			BAutolock lock(fCamDevice->Locker());
761 			err = fCamDevice->SetParameterValue(id, when, value, size);
762 			if ((err < B_OK) && (fCamDevice->Sensor())) {
763 				err = fCamDevice->Sensor()->SetParameterValue(id, when, value, size);
764 			}
765 	}
766 
767 	if (err >= B_OK)
768 		BroadcastNewParameterValue(when, id, (void *)value, size);
769 }
770 
771 
772 status_t
773 VideoProducer::StartControlPanel(BMessenger *out_messenger)
774 {
775 	return BControllable::StartControlPanel(out_messenger);
776 }
777 
778 
779 /* VideoProducer */
780 
781 
782 void
783 VideoProducer::HandleStart(bigtime_t performance_time)
784 {
785 	/* Start producing frames, even if the output hasn't been connected yet. */
786 
787 	PRINTF(1, ("HandleStart(%Ld)\n", performance_time));
788 
789 	if (fRunning) {
790 		PRINTF(-1, ("HandleStart: Node already started\n"));
791 		return;
792 	}
793 
794 	fFrame = 0;
795 	fFrameBase = 0;
796 	fPerformanceTimeBase = performance_time;
797 
798 	fFrameSync = create_sem(0, "frame synchronization");
799 	if (fFrameSync < B_OK)
800 		goto err1;
801 
802 	fThread = spawn_thread(_frame_generator_, "frame generator",
803 			B_NORMAL_PRIORITY, this);
804 	if (fThread < B_OK)
805 		goto err2;
806 
807 	resume_thread(fThread);
808 
809 	{
810 		BAutolock lock(fCamDevice->Locker());
811 		fCamDevice->StartTransfer();
812 	}
813 
814 	fRunning = true;
815 	return;
816 
817 err2:
818 	delete_sem(fFrameSync);
819 err1:
820 	return;
821 }
822 
823 
824 void
825 VideoProducer::HandleStop(void)
826 {
827 	PRINTF(1, ("HandleStop()\n"));
828 
829 	if (!fRunning) {
830 		PRINTF(-1, ("HandleStop: Node isn't running\n"));
831 		return;
832 	}
833 
834 	delete_sem(fFrameSync);
835 	wait_for_thread(fThread, &fThread);
836 
837 	BAutolock lock(fCamDevice->Locker());
838 	fCamDevice->StopTransfer();
839 
840 	fRunning = false;
841 }
842 
843 
844 void
845 VideoProducer::HandleTimeWarp(bigtime_t performance_time)
846 {
847 	fPerformanceTimeBase = performance_time;
848 	fFrameBase = fFrame;
849 
850 	/* Tell frame generation thread to recalculate delay value */
851 	release_sem(fFrameSync);
852 }
853 
854 
855 void
856 VideoProducer::HandleSeek(bigtime_t performance_time)
857 {
858 	fPerformanceTimeBase = performance_time;
859 	fFrameBase = fFrame;
860 
861 	/* Tell frame generation thread to recalculate delay value */
862 	release_sem(fFrameSync);
863 }
864 
865 
866 void
867 VideoProducer::_UpdateStats()
868 {
869 	float fps = (fStats[0].frames - fStats[1].frames) * 1000000LL
870 				/ (double)(fStats[0].stamp - fStats[1].stamp);
871 	float rfps = (fStats[0].actual - fStats[1].actual) * 1000000LL
872 				/ (double)(fStats[0].stamp - fStats[1].stamp);
873 	fInfoString = "FPS: ";
874 	fInfoString << fps << " virt, "
875 		<< rfps << " real, missed: " << fStats[0].missed;
876 	memcpy(&fStats[1], &fStats[0], sizeof(fStats[0]));
877 	fLastColorChange = system_time();
878 	BroadcastNewParameterValue(fLastColorChange, P_INFO,
879 		(void *)fInfoString.String(), fInfoString.Length()+1);
880 }
881 
882 
883 /* The following functions form the thread that generates frames. You should
884  * replace this with the code that interfaces to your hardware. */
885 int32
886 VideoProducer::FrameGenerator()
887 {
888 	bigtime_t wait_until = system_time();
889 
890 	while (1) {
891 		PRINTF(1, ("FrameGenerator: acquire_sem_etc() until %Ldµs (in %Ldµs)\n", wait_until, wait_until - system_time()));
892 		status_t err = acquire_sem_etc(fFrameSync, 1, B_ABSOLUTE_TIMEOUT,
893 				wait_until);
894 
895 		/* The only acceptable responses are B_OK and B_TIMED_OUT. Everything
896 		 * else means the thread should quit. Deleting the semaphore, as in
897 		 * VideoProducer::HandleStop(), will trigger this behavior. */
898 		if ((err != B_OK) && (err != B_TIMED_OUT))
899 			break;
900 
901 		fFrame++;
902 
903 		/* Recalculate the time until the thread should wake up to begin
904 		 * processing the next frame. Subtract fProcessingLatency so that
905 		 * the frame is sent in time. */
906 		wait_until = TimeSource()->RealTimeFor(fPerformanceTimeBase, 0) +
907 				(bigtime_t)
908 						((fFrame - fFrameBase) *
909 						(1000000 / fConnectedFormat.field_rate)) -
910 				fProcessingLatency;
911 PRINT(("PS: %Ld\n", fProcessingLatency));
912 
913 		/* Drop frame if it's at least a frame late */
914 		if (wait_until < system_time())
915 			continue;
916 
917 		PRINTF(1, ("FrameGenerator: wait until %Ld, %ctimed out, %crunning, %cenabled.\n",
918 					wait_until,
919 					(err == B_OK)?'!':' ',
920 					(fRunning)?' ':'!',
921 					(fEnabled)?' ':'!'));
922 
923 		/* If the semaphore was acquired successfully, it means something
924 		 * changed the timing information (see VideoProducer::Connect()) and
925 		 * so the thread should go back to sleep until the newly-calculated
926 		 * wait_until time. */
927 		if (err == B_OK)
928 			continue;
929 
930 		/* Send buffers only if the node is running and the output has been
931 		 * enabled */
932 		if (!fRunning || !fEnabled)
933 			continue;
934 
935 		BAutolock _(fLock);
936 
937 		/* Fetch a buffer from the buffer group */
938 		BBuffer *buffer = fBufferGroup->RequestBuffer(
939 						4 * fConnectedFormat.display.line_width *
940 						fConnectedFormat.display.line_count, 0LL);
941 		if (!buffer)
942 			continue;
943 
944 		/* Fill out the details about this buffer. */
945 		media_header *h = buffer->Header();
946 		h->type = B_MEDIA_RAW_VIDEO;
947 		h->time_source = TimeSource()->ID();
948 		h->size_used = 4 * fConnectedFormat.display.line_width *
949 						fConnectedFormat.display.line_count;
950 		/* For a buffer originating from a device, you might want to calculate
951 		 * this based on the PerformanceTimeFor the time your buffer arrived at
952 		 * the hardware (plus any applicable adjustments). */
953 		/*
954 		h->start_time = fPerformanceTimeBase +
955 						(bigtime_t)
956 							((fFrame - fFrameBase) *
957 							(1000000 / fConnectedFormat.field_rate));
958 		*/
959 		h->file_pos = 0;
960 		h->orig_size = 0;
961 		h->data_offset = 0;
962 		h->u.raw_video.field_gamma = 1.0;
963 		h->u.raw_video.field_sequence = fFrame;
964 		h->u.raw_video.field_number = 0;
965 		h->u.raw_video.pulldown_number = 0;
966 		h->u.raw_video.first_active_line = 1;
967 		h->u.raw_video.line_count = fConnectedFormat.display.line_count;
968 
969 		// This is where we fill the video buffer.
970 
971 #if 0
972 		uint32 *p = (uint32 *)buffer->Data();
973 		/* Fill in a pattern */
974 		for (uint32 y=0;y<fConnectedFormat.display.line_count;y++)
975 			for (uint32 x=0;x<fConnectedFormat.display.line_width;x++)
976 				*(p++) = ((((x+y)^0^x)+fFrame) & 0xff) * (0x01010101 & fColor);
977 #endif
978 
979 		//NO! must be called without lock!
980 		//BAutolock lock(fCamDevice->Locker());
981 
982 		bigtime_t now = system_time();
983 		bigtime_t stamp;
984 //#ifdef UseFillFrameBuffer
985 		err = fCamDevice->FillFrameBuffer(buffer, &stamp);
986 		if (err < B_OK) {
987 			;//XXX handle error
988 			fStats[0].missed++;
989 		}
990 //#endif
991 #ifdef UseGetFrameBitmap
992 		BBitmap *bm;
993 		err = fCamDevice->GetFrameBitmap(&bm, &stamp);
994 		if (err >= B_OK) {
995 			;//XXX handle error
996 			fStats[0].missed++;
997 		}
998 #endif
999 		fStats[0].frames = fFrame;
1000 		fStats[0].actual++;;
1001 		fStats[0].stamp = system_time();
1002 
1003 		//PRINTF(1, ("FrameGenerator: stamp %Ld vs %Ld\n", stamp, h->start_time));
1004 		//XXX: that's what we should be doing, but CodyCam drops all frames as they are late. (maybe add latency ??)
1005 		//h->start_time = TimeSource()->PerformanceTimeFor(stamp);
1006 		h->start_time = TimeSource()->PerformanceTimeFor(system_time());
1007 
1008 
1009 		// update processing latency
1010 		// XXX: should I ??
1011 		fProcessingLatency = system_time() - now;
1012 		fProcessingLatency /= 10;
1013 
1014 		PRINTF(1, ("FrameGenerator: SendBuffer...\n"));
1015 		/* Send the buffer on down to the consumer */
1016 		if (SendBuffer(buffer, fOutput.source, fOutput.destination) < B_OK) {
1017 			PRINTF(-1, ("FrameGenerator: Error sending buffer\n"));
1018 			/* If there is a problem sending the buffer, return it to its
1019 			 * buffer group. */
1020 			buffer->Recycle();
1021 		}
1022 
1023 		_UpdateStats();
1024 	}
1025 
1026 	PRINTF(1, ("FrameGenerator: thread existed.\n"));
1027 	return B_OK;
1028 }
1029 
1030 
1031 int32
1032 VideoProducer::_frame_generator_(void *data)
1033 {
1034 	return ((VideoProducer *)data)->FrameGenerator();
1035 }
1036