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