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