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