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
VideoProducer(BMediaAddOn * addon,const char * name,int32 internalId,NodeManager * manager,VideoSupplier * supplier)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
~VideoProducer()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*
AddOn(int32 * _internalId) const84 VideoProducer::AddOn(int32* _internalId) const
85 {
86 if (_internalId)
87 *_internalId = fInternalID;
88 return fAddOn;
89 }
90
91
92 status_t
HandleMessage(int32 message,const void * data,size_t size)93 VideoProducer::HandleMessage(int32 message, const void* data, size_t size)
94 {
95 return B_ERROR;
96 }
97
98
99 void
SetTimeSource(BTimeSource * timeSource)100 VideoProducer::SetTimeSource(BTimeSource* timeSource)
101 {
102 // Tell frame generation thread to recalculate delay value
103 release_sem(fFrameSync);
104 }
105
106
107 void
NodeRegistered()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
Start(bigtime_t performanceTime)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
Stop(bigtime_t performanceTime,bool immediate)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
Seek(bigtime_t media_time,bigtime_t performanceTime)156 VideoProducer::Seek(bigtime_t media_time, bigtime_t performanceTime)
157 {
158 BMediaEventLooper::Seek(media_time, performanceTime);
159 }
160
161
162 void
HandleEvent(const media_timed_event * event,bigtime_t lateness,bool realTimeEvent)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
DeleteHook(BMediaNode * node)197 VideoProducer::DeleteHook(BMediaNode* node)
198 {
199 return BMediaEventLooper::DeleteHook(node);
200 }
201
202
203 status_t
FormatSuggestionRequested(media_type type,int32 quality,media_format * _format)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
FormatProposal(const media_source & output,media_format * format)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
FormatChangeRequested(const media_source & source,const media_destination & destination,media_format * ioFormat,int32 * _deprecated_)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
GetNextOutput(int32 * cookie,media_output * outOutput)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
DisposeOutputCookie(int32 cookie)283 VideoProducer::DisposeOutputCookie(int32 cookie)
284 {
285 TOUCH(cookie);
286
287 return B_OK;
288 }
289
290
291 status_t
SetBufferGroup(const media_source & forSource,BBufferGroup * group)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
VideoClippingChanged(const media_source & forSource,int16 numShorts,int16 * clipData,const media_video_display_info & display,int32 * _deprecated_)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
GetLatency(bigtime_t * _latency)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
PrepareToConnect(const media_source & source,const media_destination & destination,media_format * format,media_source * outSource,char * outName)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
Connect(status_t error,const media_source & source,const media_destination & destination,const media_format & format,char * _name)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
Disconnect(const media_source & source,const media_destination & destination)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
LateNoticeReceived(const media_source & source,bigtime_t how_much,bigtime_t performanceTime)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
EnableOutput(const media_source & source,bool enabled,int32 * _deprecated_)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
SetPlayRate(int32 numer,int32 denom)524 VideoProducer::SetPlayRate(int32 numer, int32 denom)
525 {
526 TOUCH(numer); TOUCH(denom);
527
528 return B_ERROR;
529 }
530
531
532 void
AdditionalBufferRequested(const media_source & source,media_buffer_id prevBuffer,bigtime_t prevTime,const media_seek_tag * prevTag)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
LatencyChanged(const media_source & source,const media_destination & destination,bigtime_t newLatency,uint32 flags)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
_HandleStart(bigtime_t performanceTime)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
_HandleStop()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
_HandleTimeWarp(bigtime_t performanceTime)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
_HandleSeek(bigtime_t performanceTime)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
_FrameGeneratorThreadEntry(void * data)626 VideoProducer::_FrameGeneratorThreadEntry(void* data)
627 {
628 return ((VideoProducer*)data)->_FrameGeneratorThread();
629 }
630
631
632 int32
_FrameGeneratorThread()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