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