xref: /haiku/src/add-ons/media/media-add-ons/usb_webcam/Producer.cpp (revision 4c8e85b316c35a9161f5a1c50ad70bc91c83a76f)
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 -- %" B_PRIx32 "\n",
293 				event->type));
294 			break;
295 	}
296 }
297 
298 
299 void
300 VideoProducer::CleanUpEvent(const media_timed_event *event)
301 {
302 	BMediaEventLooper::CleanUpEvent(event);
303 }
304 
305 
306 bigtime_t
307 VideoProducer::OfflineTime()
308 {
309 	return BMediaEventLooper::OfflineTime();
310 }
311 
312 
313 void
314 VideoProducer::ControlLoop()
315 {
316 	BMediaEventLooper::ControlLoop();
317 }
318 
319 
320 status_t
321 VideoProducer::DeleteHook(BMediaNode * node)
322 {
323 	return BMediaEventLooper::DeleteHook(node);
324 }
325 
326 
327 /* BBufferProducer */
328 
329 
330 status_t
331 VideoProducer::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 
354 status_t
355 VideoProducer::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 
394 status_t
395 VideoProducer::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 
407 status_t
408 VideoProducer::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 
422 status_t
423 VideoProducer::DisposeOutputCookie(int32 cookie)
424 {
425 	TOUCH(cookie);
426 
427 	return B_OK;
428 }
429 
430 
431 status_t
432 VideoProducer::SetBufferGroup(const media_source &for_source,
433 		BBufferGroup *group)
434 {
435 	TOUCH(for_source); TOUCH(group);
436 
437 	return B_ERROR;
438 }
439 
440 
441 status_t
442 VideoProducer::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 
453 status_t
454 VideoProducer::GetLatency(bigtime_t *out_latency)
455 {
456 	*out_latency = EventLatency() + SchedulingLatency();
457 	return B_OK;
458 }
459 
460 
461 status_t
462 VideoProducer::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 
538 void
539 VideoProducer::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 
614 void
615 VideoProducer::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 
648 void
649 VideoProducer::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 
656 void
657 VideoProducer::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 
669 status_t
670 VideoProducer::SetPlayRate(int32 numer, int32 denom)
671 {
672 	TOUCH(numer); TOUCH(denom);
673 
674 	return B_ERROR;
675 }
676 
677 
678 void
679 VideoProducer::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 
687 void
688 VideoProducer::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 
699 status_t
700 VideoProducer::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 
738 void
739 VideoProducer::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 
774 status_t
775 VideoProducer::StartControlPanel(BMessenger *out_messenger)
776 {
777 	return BControllable::StartControlPanel(out_messenger);
778 }
779 
780 
781 /* VideoProducer */
782 
783 
784 void
785 VideoProducer::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 
819 err2:
820 	delete_sem(fFrameSync);
821 err1:
822 	return;
823 }
824 
825 
826 void
827 VideoProducer::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 
846 void
847 VideoProducer::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 
857 void
858 VideoProducer::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 
868 void
869 VideoProducer::_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. */
887 int32
888 VideoProducer::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 %Ld vs %Ld\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 
1037 int32
1038 VideoProducer::_frame_generator_(void *data)
1039 {
1040 	return ((VideoProducer *)data)->FrameGenerator();
1041 }
1042