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