xref: /haiku/src/apps/mediaplayer/media_node_framework/video/VideoProducer.cpp (revision 981f1b1135291a4fca290fbdf69910dc2f24abdd)
1 /*	Copyright (c) 1998-99, Be Incorporated, All Rights Reserved.
2  *	Distributed under the terms of the Be Sample Code license.
3  *
4  *	Copyright (c) 2000-2008, Ingo Weinhold <ingo_weinhold@gmx.de>,
5  *	Copyright (c) 2000-2008, Stephan Aßmus <superstippi@gmx.de>,
6  *	All Rights Reserved. Distributed under the terms of the MIT license.
7  */
8 #include "VideoProducer.h"
9 
10 #include <stdio.h>
11 #include <string.h>
12 
13 #include <Autolock.h>
14 #include <Buffer.h>
15 #include <BufferGroup.h>
16 #include <TimeSource.h>
17 
18 #include "NodeManager.h"
19 #include "VideoSupplier.h"
20 
21 
22 // debugging
23 //#define TRACE_VIDEO_PRODUCER
24 #ifdef TRACE_VIDEO_PRODUCER
25 # define	TRACE(x...) printf("VideoProducer::"); printf(x)
26 # define	FUNCTION(x...) TRACE(x)
27 # define	ERROR(x...) fprintf(stderr, "VideoProducer::"); fprintf(stderr, x)
28 #else
29 # define	TRACE(x...)
30 # define	FUNCTION(x...)
31 # define	ERROR(x...) fprintf(stderr, "VideoProducer::"); fprintf(stderr, x)
32 #endif
33 
34 
35 #define BUFFER_COUNT 3
36 
37 #define TOUCH(x) ((void)(x))
38 
39 
40 VideoProducer::VideoProducer(BMediaAddOn* addon, const char* name,
41 		int32 internalId, NodeManager* manager, VideoSupplier* supplier)
42 	: BMediaNode(name),
43 	  BMediaEventLooper(),
44 	  BBufferProducer(B_MEDIA_RAW_VIDEO),
45 	  fInitStatus(B_NO_INIT),
46 	  fInternalID(internalId),
47 	  fAddOn(addon),
48 	  fBufferGroup(NULL),
49 	  fUsedBufferGroup(NULL),
50 	  fThread(-1),
51 	  fFrameSync(-1),
52 	  fRunning(false),
53 	  fConnected(false),
54 	  fEnabled(false),
55 	  fManager(manager),
56 	  fSupplier(supplier)
57 {
58 	fOutput.destination = media_destination::null;
59 	fInitStatus = B_OK;
60 }
61 
62 
63 VideoProducer::~VideoProducer()
64 {
65 	if (fInitStatus == B_OK) {
66 		// Clean up after ourselves, in case the application didn't make us
67 		// do so.
68 		if (fConnected)
69 			Disconnect(fOutput.source, fOutput.destination);
70 		if (fRunning)
71 			_HandleStop();
72 	}
73 	Quit();
74 }
75 
76 
77 port_id
78 VideoProducer::ControlPort() const
79 {
80 	return BMediaNode::ControlPort();
81 }
82 
83 
84 BMediaAddOn*
85 VideoProducer::AddOn(int32* _internalId) const
86 {
87 	if (_internalId)
88 		*_internalId = fInternalID;
89 	return fAddOn;
90 }
91 
92 
93 status_t
94 VideoProducer::HandleMessage(int32 message, const void* data, size_t size)
95 {
96 	return B_ERROR;
97 }
98 
99 
100 void
101 VideoProducer::SetTimeSource(BTimeSource* timeSource)
102 {
103 	// Tell frame generation thread to recalculate delay value
104 	release_sem(fFrameSync);
105 }
106 
107 
108 status_t
109 VideoProducer::RequestCompleted(const media_request_info& info)
110 {
111 	return BMediaNode::RequestCompleted(info);
112 }
113 
114 
115 void
116 VideoProducer::NodeRegistered()
117 {
118 	if (fInitStatus != B_OK) {
119 		ReportError(B_NODE_IN_DISTRESS);
120 		return;
121 	}
122 
123 	fOutput.node = Node();
124 	fOutput.source.port = ControlPort();
125 	fOutput.source.id = 0;
126 	fOutput.destination = media_destination::null;
127 	strcpy(fOutput.name, Name());
128 
129 	// fill with wild cards at this point in time
130 	fOutput.format.type = B_MEDIA_RAW_VIDEO;
131 	fOutput.format.u.raw_video = media_raw_video_format::wildcard;
132 	fOutput.format.u.raw_video.interlace = 1;
133 	fOutput.format.u.raw_video.display.format = B_NO_COLOR_SPACE;
134 	fOutput.format.u.raw_video.display.bytes_per_row = 0;
135 	fOutput.format.u.raw_video.display.line_width = 0;
136 	fOutput.format.u.raw_video.display.line_count = 0;
137 
138 	// start the BMediaEventLooper control loop running
139 	Run();
140 }
141 
142 
143 void
144 VideoProducer::Start(bigtime_t performanceTime)
145 {
146 	// notify the manager in case we were started from the outside world
147 //	fManager->StartPlaying();
148 
149 	BMediaEventLooper::Start(performanceTime);
150 }
151 
152 
153 void
154 VideoProducer::Stop(bigtime_t performanceTime, bool immediate)
155 {
156 	// notify the manager in case we were stopped from the outside world
157 //	fManager->StopPlaying();
158 
159 	BMediaEventLooper::Stop(performanceTime, immediate);
160 }
161 
162 
163 void
164 VideoProducer::Seek(bigtime_t media_time, bigtime_t performanceTime)
165 {
166 	BMediaEventLooper::Seek(media_time, performanceTime);
167 }
168 
169 
170 void
171 VideoProducer::TimeWarp(bigtime_t at_real_time, bigtime_t to_performance_time)
172 {
173 	BMediaEventLooper::TimeWarp(at_real_time, to_performance_time);
174 }
175 
176 
177 status_t
178 VideoProducer::AddTimer(bigtime_t at_performance_time, int32 cookie)
179 {
180 	return BMediaEventLooper::AddTimer(at_performance_time, cookie);
181 }
182 
183 
184 void
185 VideoProducer::SetRunMode(run_mode mode)
186 {
187 printf("VideoProducer::SetRunMode(%d)\n", mode);
188 	TRACE("SetRunMode(%d)\n", mode);
189 	BMediaEventLooper::SetRunMode(mode);
190 }
191 
192 
193 void
194 VideoProducer::HandleEvent(const media_timed_event* event,
195 		bigtime_t lateness, bool realTimeEvent)
196 {
197 	TOUCH(lateness); TOUCH(realTimeEvent);
198 
199 	switch(event->type) {
200 		case BTimedEventQueue::B_START:
201 			_HandleStart(event->event_time);
202 			break;
203 		case BTimedEventQueue::B_STOP:
204 			_HandleStop();
205 			break;
206 		case BTimedEventQueue::B_WARP:
207 			_HandleTimeWarp(event->bigdata);
208 			break;
209 		case BTimedEventQueue::B_SEEK:
210 			_HandleSeek(event->bigdata);
211 			break;
212 		case BTimedEventQueue::B_HANDLE_BUFFER:
213 		case BTimedEventQueue::B_DATA_STATUS:
214 		case BTimedEventQueue::B_PARAMETER:
215 		default:
216 			TRACE("HandleEvent: Unhandled event -- %lx\n", event->type);
217 			break;
218 	}
219 }
220 
221 
222 void
223 VideoProducer::CleanUpEvent(const media_timed_event *event)
224 {
225 	BMediaEventLooper::CleanUpEvent(event);
226 }
227 
228 
229 bigtime_t
230 VideoProducer::OfflineTime()
231 {
232 	return BMediaEventLooper::OfflineTime();
233 }
234 
235 
236 void
237 VideoProducer::ControlLoop()
238 {
239 	BMediaEventLooper::ControlLoop();
240 }
241 
242 
243 status_t
244 VideoProducer::DeleteHook(BMediaNode* node)
245 {
246 	return BMediaEventLooper::DeleteHook(node);
247 }
248 
249 
250 status_t
251 VideoProducer::FormatSuggestionRequested(media_type type, int32 quality,
252 	media_format* _format)
253 {
254 	FUNCTION("FormatSuggestionRequested\n");
255 
256 	if (type != B_MEDIA_ENCODED_VIDEO)
257 		return B_MEDIA_BAD_FORMAT;
258 
259 	TOUCH(quality);
260 
261 	*_format = fOutput.format;
262 	return B_OK;
263 }
264 
265 
266 status_t
267 VideoProducer::FormatProposal(const media_source& output, media_format* format)
268 {
269 	#ifdef TRACE_VIDEO_PRODUCER
270 		char string[256];
271 		string_for_format(*format, string, 256);
272 		FUNCTION("FormatProposal(%s)\n", string);
273 	#endif
274 
275 	if (!format)
276 		return B_BAD_VALUE;
277 
278 	if (output != fOutput.source)
279 		return B_MEDIA_BAD_SOURCE;
280 
281 	status_t ret = format_is_compatible(*format, fOutput.format) ?
282 		B_OK : B_MEDIA_BAD_FORMAT;
283 	if (ret != B_OK) {
284 		ERROR("FormatProposal() error: %s\n", strerror(ret));
285 		char string[512];
286 		string_for_format(*format, string, sizeof(string));
287 		ERROR("  requested: %s\n", string);
288 		string_for_format(fOutput.format, string, sizeof(string));
289 		ERROR("  output:    %s\n", string);
290 	}
291 
292 	// change any wild cards to specific values
293 
294 	return ret;
295 
296 }
297 
298 
299 status_t
300 VideoProducer::FormatChangeRequested(const media_source& source,
301 	const media_destination& destination, media_format* ioFormat,
302 	int32 *_deprecated_)
303 {
304 	TOUCH(destination); TOUCH(ioFormat); TOUCH(_deprecated_);
305 
306 	if (source != fOutput.source)
307 		return B_MEDIA_BAD_SOURCE;
308 
309 	return B_ERROR;
310 }
311 
312 
313 status_t
314 VideoProducer::GetNextOutput(int32* cookie, media_output* outOutput)
315 {
316 	if (!outOutput)
317 		return B_BAD_VALUE;
318 
319 	if ((*cookie) != 0)
320 		return B_BAD_INDEX;
321 
322 	*outOutput = fOutput;
323 	(*cookie)++;
324 
325 	return B_OK;
326 }
327 
328 
329 status_t
330 VideoProducer::DisposeOutputCookie(int32 cookie)
331 {
332 	TOUCH(cookie);
333 
334 	return B_OK;
335 }
336 
337 
338 status_t
339 VideoProducer::SetBufferGroup(const media_source& forSource,
340 	BBufferGroup *group)
341 {
342 	if (forSource != fOutput.source)
343 		return B_MEDIA_BAD_SOURCE;
344 
345 	TRACE("VideoProducer::SetBufferGroup() - using buffer group of "
346 		"consumer.\n");
347 	fUsedBufferGroup = group;
348 
349 	return B_OK;
350 }
351 
352 
353 status_t
354 VideoProducer::VideoClippingChanged(const media_source& forSource,
355 	int16 numShorts, int16* clipData, const media_video_display_info& display,
356 	int32* _deprecated_)
357 {
358 	TOUCH(forSource); TOUCH(numShorts); TOUCH(clipData);
359 	TOUCH(display); TOUCH(_deprecated_);
360 
361 	return B_ERROR;
362 }
363 
364 
365 status_t
366 VideoProducer::GetLatency(bigtime_t* _latency)
367 {
368 	if (!_latency)
369 		return B_BAD_VALUE;
370 
371 	*_latency = EventLatency() + SchedulingLatency();
372 
373 	return B_OK;
374 }
375 
376 
377 status_t
378 VideoProducer::PrepareToConnect(const media_source& source,
379 	const media_destination& destination, media_format* format,
380 	media_source* outSource, char* outName)
381 {
382 	FUNCTION("PrepareToConnect() %ldx%ld\n",
383 		format->u.raw_video.display.line_width,
384 		format->u.raw_video.display.line_count);
385 
386 	if (fConnected) {
387 		ERROR("PrepareToConnect() - already connected!\n");
388 		return B_MEDIA_ALREADY_CONNECTED;
389 	}
390 
391 	if (source != fOutput.source)
392 		return B_MEDIA_BAD_SOURCE;
393 
394 	if (fOutput.destination != media_destination::null) {
395 		ERROR("PrepareToConnect() - destination != null.\n");
396 		return B_MEDIA_ALREADY_CONNECTED;
397 	}
398 
399 	// The format parameter comes in with the suggested format, and may be
400 	// specialized as desired by the node
401 	if (!format_is_compatible(*format, fOutput.format)) {
402 		ERROR("PrepareToConnect() - incompatible format.\n");
403 		*format = fOutput.format;
404 		return B_MEDIA_BAD_FORMAT;
405 	}
406 
407 	if (format->u.raw_video.display.line_width == 0)
408 		format->u.raw_video.display.line_width = 384;
409 	if (format->u.raw_video.display.line_count == 0)
410 		format->u.raw_video.display.line_count = 288;
411 	if (format->u.raw_video.field_rate == 0)
412 		format->u.raw_video.field_rate = 25.0;
413 	if (format->u.raw_video.display.bytes_per_row == 0)
414 		format->u.raw_video.display.bytes_per_row = format->u.raw_video.display.line_width * 4;
415 
416 	*outSource = fOutput.source;
417 	strcpy(outName, fOutput.name);
418 
419 	return B_OK;
420 }
421 
422 
423 #define NODE_LATENCY 20000
424 
425 
426 void
427 VideoProducer::Connect(status_t error, const media_source& source,
428 	const media_destination& destination, const media_format& format,
429 	char* _name)
430 {
431 	FUNCTION("Connect() %ldx%ld\n",
432 		format.u.raw_video.display.line_width,
433 		format.u.raw_video.display.line_count);
434 
435 	if (fConnected) {
436 		ERROR("Connect() - already connected.\n");
437 		return;
438 	}
439 
440 	if (source != fOutput.source) {
441 		ERROR("Connect() - wrong source.\n");
442 		return;
443 	}
444 	if (error < B_OK) {
445 		ERROR("Connect() - consumer error: %s\n", strerror(error));
446 		return;
447 	}
448 	if (!const_cast<media_format*>(&format)->Matches(&fOutput.format)) {
449 		ERROR("Connect() - format mismatch.\n");
450 		return;
451 	}
452 
453 	fOutput.destination = destination;
454 	strcpy(_name, fOutput.name);
455 
456 	if (fOutput.format.u.raw_video.field_rate != 0.0f) {
457 		fPerformanceTimeBase = fPerformanceTimeBase
458 			+ (bigtime_t)((fFrame - fFrameBase)
459 				* 1000000 / fOutput.format.u.raw_video.field_rate);
460 		fFrameBase = fFrame;
461 	}
462 
463 	fConnectedFormat = format.u.raw_video;
464 	if (fConnectedFormat.display.bytes_per_row == 0) {
465 		ERROR("Connect() - connected format still has BPR wildcard!\n");
466 		fConnectedFormat.display.bytes_per_row
467 			= 4 * fConnectedFormat.display.line_width;
468 	}
469 
470 	// get the latency
471 	bigtime_t latency = 0;
472 	media_node_id tsID = 0;
473 	FindLatencyFor(fOutput.destination, &latency, &tsID);
474 	SetEventLatency(latency + NODE_LATENCY);
475 
476 	// Create the buffer group
477 	if (!fUsedBufferGroup) {
478 		fBufferGroup = new BBufferGroup(fConnectedFormat.display.bytes_per_row
479 			* fConnectedFormat.display.line_count, BUFFER_COUNT);
480 		status_t err = fBufferGroup->InitCheck();
481 		if (err < B_OK) {
482 			delete fBufferGroup;
483 			fBufferGroup = NULL;
484 			ERROR("Connect() - buffer group error: %s\n", strerror(err));
485 			return;
486 		}
487 		fUsedBufferGroup = fBufferGroup;
488 	}
489 
490 	fConnected = true;
491 	fEnabled = true;
492 
493 	// Tell frame generation thread to recalculate delay value
494 	release_sem(fFrameSync);
495 }
496 
497 
498 void
499 VideoProducer::Disconnect(const media_source& source,
500 	const media_destination& destination)
501 {
502 	FUNCTION("Disconnect()\n");
503 
504 	if (!fConnected) {
505 		ERROR("Disconnect() - Not connected\n");
506 		return;
507 	}
508 
509 	if ((source != fOutput.source) || (destination != fOutput.destination)) {
510 		ERROR("Disconnect() - Bad source and/or destination\n");
511 		return;
512 	}
513 
514 	fEnabled = false;
515 	fOutput.destination = media_destination::null;
516 
517 	if (fLock.Lock()) {
518 		// Always delete the buffer group, even if it is not ours.
519 		// (See BeBook::SetBufferGroup()).
520 		delete fUsedBufferGroup;
521 		fUsedBufferGroup = NULL;
522 		fBufferGroup = NULL;
523 		fLock.Unlock();
524 	}
525 
526 	fConnected = false;
527 	TRACE("Disconnect() done\n");
528 }
529 
530 
531 void
532 VideoProducer::LateNoticeReceived(const media_source &source,
533 		bigtime_t how_much, bigtime_t performanceTime)
534 {
535 	TOUCH(source); TOUCH(how_much); TOUCH(performanceTime);
536 	TRACE("Late!!!\n");
537 }
538 
539 
540 void
541 VideoProducer::EnableOutput(const media_source& source, bool enabled,
542 	int32* _deprecated_)
543 {
544 	TOUCH(_deprecated_);
545 
546 	if (source != fOutput.source)
547 		return;
548 
549 	fEnabled = enabled;
550 }
551 
552 
553 status_t
554 VideoProducer::SetPlayRate(int32 numer, int32 denom)
555 {
556 	TOUCH(numer); TOUCH(denom);
557 
558 	return B_ERROR;
559 }
560 
561 
562 void
563 VideoProducer::AdditionalBufferRequested(const media_source& source,
564 	media_buffer_id prevBuffer, bigtime_t prevTime,
565 	const media_seek_tag* prevTag)
566 {
567 	TOUCH(source); TOUCH(prevBuffer); TOUCH(prevTime); TOUCH(prevTag);
568 }
569 
570 
571 void
572 VideoProducer::LatencyChanged(const media_source& source,
573 	const media_destination& destination,
574 	bigtime_t newLatency, uint32 flags)
575 {
576 	TOUCH(source); TOUCH(destination); TOUCH(newLatency); TOUCH(flags);
577 	TRACE("Latency changed!\n");
578 }
579 
580 
581 // #pragma mark -
582 
583 
584 void
585 VideoProducer::_HandleStart(bigtime_t performanceTime)
586 {
587 	// Start producing frames, even if the output hasn't been connected yet.
588 	TRACE("_HandleStart(%Ld)\n", performanceTime);
589 
590 	if (fRunning) {
591 		TRACE("_HandleStart: Node already started\n");
592 		return;
593 	}
594 
595 	fFrame = 0;
596 	fFrameBase = 0;
597 	fPerformanceTimeBase = performanceTime;
598 
599 	fFrameSync = create_sem(0, "frame synchronization");
600 	if (fFrameSync < B_OK)
601 		return;
602 
603 	fThread = spawn_thread(_FrameGeneratorThreadEntry, "frame generator",
604 		B_NORMAL_PRIORITY, this);
605 	if (fThread < B_OK) {
606 		delete_sem(fFrameSync);
607 		return;
608 	}
609 
610 	resume_thread(fThread);
611 	fRunning = true;
612 	return;
613 }
614 
615 
616 void
617 VideoProducer::_HandleStop()
618 {
619 	TRACE("_HandleStop()\n");
620 
621 	if (!fRunning) {
622 		TRACE("_HandleStop: Node isn't running\n");
623 		return;
624 	}
625 
626 	delete_sem(fFrameSync);
627 	wait_for_thread(fThread, &fThread);
628 
629 	fRunning = false;
630 }
631 
632 
633 void
634 VideoProducer::_HandleTimeWarp(bigtime_t performanceTime)
635 {
636 	fPerformanceTimeBase = performanceTime;
637 	fFrameBase = fFrame;
638 
639 	// Tell frame generation thread to recalculate delay value
640 	release_sem(fFrameSync);
641 }
642 
643 
644 void
645 VideoProducer::_HandleSeek(bigtime_t performanceTime)
646 {
647 	fPerformanceTimeBase = performanceTime;
648 	fFrameBase = fFrame;
649 
650 	// Tell frame generation thread to recalculate delay value
651 	release_sem(fFrameSync);
652 }
653 
654 
655 int32
656 VideoProducer::_FrameGeneratorThreadEntry(void* data)
657 {
658 	return ((VideoProducer*)data)->_FrameGeneratorThread();
659 }
660 
661 
662 int32
663 VideoProducer::_FrameGeneratorThread()
664 {
665 	bool forceSendingBuffer = true;
666 	bigtime_t lastFrameSentAt = 0;
667 	int64 lastPlaylistFrame = 0;
668 	bool running = true;
669 	while (running) {
670 		TRACE("_FrameGeneratorThread: loop: %Ld\n", fFrame);
671 		// lock the node manager
672 		status_t err = fManager->LockWithTimeout(10000);
673 		bool ignoreEvent = false;
674 		// Data to be retrieved from the node manager.
675 		bigtime_t performanceTime = 0;
676 		bigtime_t nextPerformanceTime = 0;
677 		bigtime_t waitUntil = 0;
678 		bigtime_t nextWaitUntil = 0;
679 		bigtime_t maxRenderTime = 0;
680 		int32 playingDirection = 0;
681 		int32 playingMode = 0;
682 		int64 playlistFrame = 0;
683 		switch (err) {
684 			case B_OK: {
685 				TRACE("_FrameGeneratorThread: node manager successfully "
686 					"locked\n");
687 				// get the times for the current and the next frame
688 				performanceTime = fManager->TimeForFrame(fFrame);
689 				nextPerformanceTime = fManager->TimeForFrame(fFrame + 1);
690 				maxRenderTime = min_c(bigtime_t(33334 * 0.9),
691 					max_c(fSupplier->ProcessingLatency(), maxRenderTime));
692 				playingMode = fManager->PlayModeAtFrame(fFrame);
693 
694 				waitUntil = TimeSource()->RealTimeFor(fPerformanceTimeBase
695 					+ performanceTime, 0) - maxRenderTime;
696 				nextWaitUntil = TimeSource()->RealTimeFor(fPerformanceTimeBase
697 					+ nextPerformanceTime, 0) - maxRenderTime;
698 				// get playing direction and playlist frame for the current
699 				// frame
700 				bool newPlayingState;
701 				playlistFrame = fManager->PlaylistFrameAtFrame(fFrame,
702 					playingDirection, newPlayingState);
703 				TRACE("_FrameGeneratorThread: performance time: %Ld, "
704 					"playlist frame: %lld\n", performanceTime, playlistFrame);
705 				forceSendingBuffer |= newPlayingState;
706 				if (lastPlaylistFrame != playlistFrame) {
707 					forceSendingBuffer = true;
708 					lastPlaylistFrame = playlistFrame;
709 				}
710 				fManager->SetCurrentVideoTime(nextPerformanceTime);
711 				fManager->Unlock();
712 				break;
713 			}
714 			case B_TIMED_OUT:
715 				TRACE("_FrameGeneratorThread: Couldn't lock the node "
716 					"manager.\n");
717 				ignoreEvent = true;
718 				waitUntil = system_time() - 1;
719 				break;
720 			default:
721 				ERROR("_FrameGeneratorThread: Couldn't lock the node manager. "
722 					"Terminating video producer frame generator thread.\n");
723 				TRACE("_FrameGeneratorThread: frame generator thread done.\n");
724 				// do not access any member variables, since this could
725 				// also mean the Node has been deleted
726 				return B_OK;
727 		}
728 
729 		TRACE("_FrameGeneratorThread: waiting (%Ld)...\n", waitUntil);
730 		// wait until...
731 		err = acquire_sem_etc(fFrameSync, 1, B_ABSOLUTE_TIMEOUT, waitUntil);
732 		// The only acceptable responses are B_OK and B_TIMED_OUT. Everything
733 		// else means the thread should quit. Deleting the semaphore, as in
734 		// VideoProducer::_HandleStop(), will trigger this behavior.
735 		switch (err) {
736 			case B_OK:
737 				TRACE("_FrameGeneratorThread: going back to sleep.\n");
738 				break;
739 			case B_TIMED_OUT:
740 				TRACE("_FrameGeneratorThread: timed out => event\n");
741 				// Catch the cases in which the node manager could not be
742 				// locked and we therefore have no valid data to work with,
743 				// or the producer is not running or enabled.
744 				if (ignoreEvent || !fRunning || !fEnabled) {
745 					TRACE("_FrameGeneratorThread: ignore event\n");
746 					// nothing to do
747 				} else if (nextWaitUntil < system_time()) {
748 					// Drop frame if it's at least a frame late.
749 					printf("VideoProducer: dropped frame (%Ld)\n", fFrame);
750 					if (fManager->LockWithTimeout(10000) == B_OK) {
751 						fManager->FrameDropped();
752 						fManager->Unlock();
753 					}
754 					// next frame
755 					fFrame++;
756 				} else if (playingDirection != 0 || forceSendingBuffer) {
757 					// Send buffers only, if playing, the node is running and
758 					// the output has been enabled
759 					TRACE("_FrameGeneratorThread: produce frame\n");
760 					BAutolock _(fLock);
761 					// Fetch a buffer from the buffer group
762 					BBuffer *buffer = fUsedBufferGroup->RequestBuffer(
763 						fConnectedFormat.display.bytes_per_row
764 						* fConnectedFormat.display.line_count, 0LL);
765 					if (buffer) {
766 						// Fill out the details about this buffer.
767 						media_header *h = buffer->Header();
768 						h->type = B_MEDIA_RAW_VIDEO;
769 						h->time_source = TimeSource()->ID();
770 						h->size_used = fConnectedFormat.display.bytes_per_row
771 									   * fConnectedFormat.display.line_count;
772 						// For a buffer originating from a device, you might
773 						// want to calculate this based on the
774 						// PerformanceTimeFor the time your buffer arrived at
775 						// the hardware (plus any applicable adjustments).
776 						h->start_time = fPerformanceTimeBase + performanceTime;
777 // TODO: Fix the runmode stuff! Setting the consumer to B_OFFLINE does
778 // not do the trick. I made the VideoConsumer check the performance
779 // time of the buffer and if it is 0, it plays it regardless.
780 if (playingMode < 0) {
781 h->start_time = 0;
782 }
783 						h->file_pos = 0;
784 						h->orig_size = 0;
785 						h->data_offset = 0;
786 						h->u.raw_video.field_gamma = 1.0;
787 						h->u.raw_video.field_sequence = fFrame;
788 						h->u.raw_video.field_number = 0;
789 						h->u.raw_video.pulldown_number = 0;
790 						h->u.raw_video.first_active_line = 1;
791 						h->u.raw_video.line_count
792 							= fConnectedFormat.display.line_count;
793 						// Fill in a frame
794 						media_format mf;
795 						mf.type = B_MEDIA_RAW_VIDEO;
796 						mf.u.raw_video = fConnectedFormat;
797 						TRACE("_FrameGeneratorThread: frame: %Ld, "
798 							"playlistFrame: %Ld\n", fFrame, playlistFrame);
799 						bool forceOrWasCached = forceSendingBuffer;
800 
801 						err = fSupplier->FillBuffer(playlistFrame,
802 							buffer->Data(), &mf, forceOrWasCached);
803 						// clean the buffer if something went wrong
804 						if (err != B_OK) {
805 							// TODO: should use "back value" according
806 							// to color space!
807 							memset(buffer->Data(), 0, h->size_used);
808 							err = B_OK;
809 						}
810 						// Send the buffer on down to the consumer
811 						if (SendBuffer(buffer, fOutput.destination) < B_OK) {
812 							ERROR("_FrameGeneratorThread: Error "
813 								"sending buffer\n");
814 							// If there is a problem sending the buffer,
815 							// or if we don't send the buffer because its
816 							// contents are the same as the last one,
817 							// return it to its buffer group.
818 							buffer->Recycle();
819 							// we tell the supplier to delete
820 							// its caches if there was a problem sending
821 							// the buffer
822 							fSupplier->DeleteCaches();
823 						}
824 						// Only if everything went fine we clear the flag
825 						// that forces us to send a buffer even if not
826 						// playing.
827 						if (err == B_OK) {
828 							forceSendingBuffer = false;
829 							lastFrameSentAt = performanceTime;
830 						}
831 					} else {
832 						TRACE("_FrameGeneratorThread: no buffer!\n");
833 //						ERROR("_FrameGeneratorThread: no buffer!\n");
834 					}
835 					// next frame
836 					fFrame++;
837 				} else {
838 					TRACE("_FrameGeneratorThread: not playing\n");
839 					// next frame
840 					fFrame++;
841 				}
842 				break;
843 			default:
844 				TRACE("_FrameGeneratorThread: Couldn't acquire semaphore. "
845 					"Error: %s\n", strerror(err));
846 				running = false;
847 				break;
848 		}
849 	}
850 	TRACE("_FrameGeneratorThread: frame generator thread done.\n");
851 	return B_OK;
852 }
853 
854