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