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