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