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