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