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
42VideoProducer::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
69VideoProducer::~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
83BMediaAddOn*
84VideoProducer::AddOn(int32* _internalId) const
85{
86	if (_internalId)
87		*_internalId = fInternalID;
88	return fAddOn;
89}
90
91
92status_t
93VideoProducer::HandleMessage(int32 message, const void* data, size_t size)
94{
95	return B_ERROR;
96}
97
98
99void
100VideoProducer::SetTimeSource(BTimeSource* timeSource)
101{
102	// Tell frame generation thread to recalculate delay value
103	release_sem(fFrameSync);
104}
105
106
107void
108VideoProducer::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
135void
136VideoProducer::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
145void
146VideoProducer::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
155void
156VideoProducer::Seek(bigtime_t media_time, bigtime_t performanceTime)
157{
158	BMediaEventLooper::Seek(media_time, performanceTime);
159}
160
161
162void
163VideoProducer::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
196status_t
197VideoProducer::DeleteHook(BMediaNode* node)
198{
199	return BMediaEventLooper::DeleteHook(node);
200}
201
202
203status_t
204VideoProducer::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
219status_t
220VideoProducer::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
252status_t
253VideoProducer::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
266status_t
267VideoProducer::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
282status_t
283VideoProducer::DisposeOutputCookie(int32 cookie)
284{
285	TOUCH(cookie);
286
287	return B_OK;
288}
289
290
291status_t
292VideoProducer::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
306status_t
307VideoProducer::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
318status_t
319VideoProducer::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
330status_t
331VideoProducer::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
384void
385VideoProducer::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
466void
467VideoProducer::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
501void
502VideoProducer::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
510void
511VideoProducer::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
523status_t
524VideoProducer::SetPlayRate(int32 numer, int32 denom)
525{
526	TOUCH(numer); TOUCH(denom);
527
528	return B_ERROR;
529}
530
531
532void
533VideoProducer::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
541void
542VideoProducer::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
554void
555VideoProducer::_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
586void
587VideoProducer::_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
603void
604VideoProducer::_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
614void
615VideoProducer::_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
625int32
626VideoProducer::_FrameGeneratorThreadEntry(void* data)
627{
628	return ((VideoProducer*)data)->_FrameGeneratorThread();
629}
630
631
632int32
633VideoProducer::_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