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 = 384; 362 if (format->u.raw_video.display.line_count == 0) 363 format->u.raw_video.display.line_count = 288; 364 if (format->u.raw_video.field_rate == 0) 365 format->u.raw_video.field_rate = 25.0; 366 if (format->u.raw_video.display.bytes_per_row == 0) 367 format->u.raw_video.display.bytes_per_row = format->u.raw_video.display.line_width * 4; 368 369 *outSource = fOutput.source; 370 strcpy(outName, fOutput.name); 371 372 return B_OK; 373 } 374 375 376 void 377 VideoProducer::Connect(status_t error, const media_source& source, 378 const media_destination& destination, const media_format& format, 379 char* _name) 380 { 381 FUNCTION("Connect() %ldx%ld\n", 382 format.u.raw_video.display.line_width, 383 format.u.raw_video.display.line_count); 384 385 if (fConnected) { 386 ERROR("Connect() - already connected.\n"); 387 return; 388 } 389 390 if (source != fOutput.source) { 391 ERROR("Connect() - wrong source.\n"); 392 return; 393 } 394 if (error != B_OK) { 395 ERROR("Connect() - consumer error: %s\n", strerror(error)); 396 return; 397 } 398 if (!const_cast<media_format*>(&format)->Matches(&fOutput.format)) { 399 ERROR("Connect() - format mismatch.\n"); 400 return; 401 } 402 403 fOutput.destination = destination; 404 strcpy(_name, fOutput.name); 405 fConnectedFormat = format.u.raw_video; 406 fBufferDuration = 20000; 407 408 if (fConnectedFormat.field_rate != 0.0f) { 409 fPerformanceTimeBase = fPerformanceTimeBase 410 + (bigtime_t)((fFrame - fFrameBase) 411 * 1000000LL / fConnectedFormat.field_rate); 412 fFrameBase = fFrame; 413 fBufferDuration = bigtime_t(1000000LL / fConnectedFormat.field_rate); 414 } 415 416 if (fConnectedFormat.display.bytes_per_row == 0) { 417 ERROR("Connect() - connected format still has BPR wildcard!\n"); 418 fConnectedFormat.display.bytes_per_row 419 = 4 * fConnectedFormat.display.line_width; 420 } 421 422 // Create the buffer group 423 if (fUsedBufferGroup == NULL) { 424 fBufferGroup = new BBufferGroup(fConnectedFormat.display.bytes_per_row 425 * fConnectedFormat.display.line_count, BUFFER_COUNT); 426 status_t err = fBufferGroup->InitCheck(); 427 if (err < B_OK) { 428 delete fBufferGroup; 429 fBufferGroup = NULL; 430 ERROR("Connect() - buffer group error: %s\n", strerror(err)); 431 return; 432 } 433 fUsedBufferGroup = fBufferGroup; 434 } 435 436 // get the latency 437 fBufferLatency = (BUFFER_COUNT - 1) * fBufferDuration; 438 439 int32 bufferCount; 440 if (fUsedBufferGroup->CountBuffers(&bufferCount) == B_OK) { 441 // recompute the latency 442 fBufferLatency = (bufferCount - 1) * fBufferDuration; 443 } 444 445 bigtime_t latency = 0; 446 media_node_id tsID = 0; 447 FindLatencyFor(fOutput.destination, &latency, &tsID); 448 SetEventLatency(latency + fBufferLatency); 449 450 fConnected = true; 451 fEnabled = true; 452 453 // Tell frame generation thread to recalculate delay value 454 release_sem(fFrameSync); 455 } 456 457 458 void 459 VideoProducer::Disconnect(const media_source& source, 460 const media_destination& destination) 461 { 462 FUNCTION("Disconnect()\n"); 463 464 if (!fConnected) { 465 ERROR("Disconnect() - Not connected\n"); 466 return; 467 } 468 469 if ((source != fOutput.source) || (destination != fOutput.destination)) { 470 ERROR("Disconnect() - Bad source and/or destination\n"); 471 return; 472 } 473 474 fEnabled = false; 475 fOutput.destination = media_destination::null; 476 477 if (fLock.Lock()) { 478 // Always delete the buffer group, even if it is not ours. 479 // (See BeBook::SetBufferGroup()). 480 delete fUsedBufferGroup; 481 if (fBufferGroup != fUsedBufferGroup) 482 delete fBufferGroup; 483 fUsedBufferGroup = NULL; 484 fBufferGroup = NULL; 485 fLock.Unlock(); 486 } 487 488 fConnected = false; 489 TRACE("Disconnect() done\n"); 490 } 491 492 493 void 494 VideoProducer::LateNoticeReceived(const media_source &source, 495 bigtime_t how_much, bigtime_t performanceTime) 496 { 497 TOUCH(source); TOUCH(how_much); TOUCH(performanceTime); 498 TRACE("Late!!!\n"); 499 } 500 501 502 void 503 VideoProducer::EnableOutput(const media_source& source, bool enabled, 504 int32* _deprecated_) 505 { 506 TOUCH(_deprecated_); 507 508 if (source != fOutput.source) 509 return; 510 511 fEnabled = enabled; 512 } 513 514 515 status_t 516 VideoProducer::SetPlayRate(int32 numer, int32 denom) 517 { 518 TOUCH(numer); TOUCH(denom); 519 520 return B_ERROR; 521 } 522 523 524 void 525 VideoProducer::AdditionalBufferRequested(const media_source& source, 526 media_buffer_id prevBuffer, bigtime_t prevTime, 527 const media_seek_tag* prevTag) 528 { 529 TOUCH(source); TOUCH(prevBuffer); TOUCH(prevTime); TOUCH(prevTag); 530 } 531 532 533 void 534 VideoProducer::LatencyChanged(const media_source& source, 535 const media_destination& destination, 536 bigtime_t newLatency, uint32 flags) 537 { 538 TOUCH(source); TOUCH(destination); TOUCH(newLatency); TOUCH(flags); 539 TRACE("Latency changed!\n"); 540 } 541 542 543 // #pragma mark - 544 545 546 void 547 VideoProducer::_HandleStart(bigtime_t performanceTime) 548 { 549 // Start producing frames, even if the output hasn't been connected yet. 550 TRACE("_HandleStart(%Ld)\n", performanceTime); 551 552 if (fRunning) { 553 TRACE("_HandleStart: Node already started\n"); 554 return; 555 } 556 557 fFrame = 0; 558 fFrameBase = 0; 559 fPerformanceTimeBase = performanceTime; 560 561 fFrameSync = create_sem(0, "frame synchronization"); 562 if (fFrameSync < B_OK) 563 return; 564 565 fThread = spawn_thread(_FrameGeneratorThreadEntry, "frame generator", 566 B_NORMAL_PRIORITY, this); 567 if (fThread < B_OK) { 568 delete_sem(fFrameSync); 569 return; 570 } 571 572 resume_thread(fThread); 573 fRunning = true; 574 return; 575 } 576 577 578 void 579 VideoProducer::_HandleStop() 580 { 581 TRACE("_HandleStop()\n"); 582 583 if (!fRunning) { 584 TRACE("_HandleStop: Node isn't running\n"); 585 return; 586 } 587 588 delete_sem(fFrameSync); 589 wait_for_thread(fThread, &fThread); 590 591 fRunning = false; 592 } 593 594 595 void 596 VideoProducer::_HandleTimeWarp(bigtime_t performanceTime) 597 { 598 fPerformanceTimeBase = performanceTime; 599 fFrameBase = fFrame; 600 601 // Tell frame generation thread to recalculate delay value 602 release_sem(fFrameSync); 603 } 604 605 606 void 607 VideoProducer::_HandleSeek(bigtime_t performanceTime) 608 { 609 fPerformanceTimeBase = performanceTime; 610 fFrameBase = fFrame; 611 612 // Tell frame generation thread to recalculate delay value 613 release_sem(fFrameSync); 614 } 615 616 617 int32 618 VideoProducer::_FrameGeneratorThreadEntry(void* data) 619 { 620 return ((VideoProducer*)data)->_FrameGeneratorThread(); 621 } 622 623 624 int32 625 VideoProducer::_FrameGeneratorThread() 626 { 627 bool forceSendingBuffer = true; 628 int32 droppedFrames = 0; 629 const int32 kMaxDroppedFrames = 15; 630 bool running = true; 631 while (running) { 632 TRACE("_FrameGeneratorThread: loop: %Ld\n", fFrame); 633 // lock the node manager 634 status_t err = fManager->LockWithTimeout(10000); 635 bool ignoreEvent = false; 636 // Data to be retrieved from the node manager. 637 bigtime_t performanceTime = 0; 638 bigtime_t nextPerformanceTime = 0; 639 bigtime_t waitUntil = 0; 640 bigtime_t nextWaitUntil = 0; 641 int32 playingDirection = 0; 642 int64 playlistFrame = 0; 643 switch (err) { 644 case B_OK: { 645 TRACE("_FrameGeneratorThread: node manager successfully " 646 "locked\n"); 647 if (droppedFrames > 0) 648 fManager->FrameDropped(); 649 // get the times for the current and the next frame 650 performanceTime = fManager->TimeForFrame(fFrame); 651 nextPerformanceTime = fManager->TimeForFrame(fFrame + 1); 652 waitUntil = TimeSource()->RealTimeFor(fPerformanceTimeBase 653 + performanceTime, fBufferLatency); 654 nextWaitUntil = TimeSource()->RealTimeFor(fPerformanceTimeBase 655 + nextPerformanceTime, fBufferLatency); 656 // get playing direction and playlist frame for the current 657 // frame 658 bool newPlayingState; 659 playlistFrame = fManager->PlaylistFrameAtFrame(fFrame, 660 playingDirection, newPlayingState); 661 TRACE("_FrameGeneratorThread: performance time: %Ld, " 662 "playlist frame: %lld\n", performanceTime, playlistFrame); 663 forceSendingBuffer |= newPlayingState; 664 fManager->SetCurrentVideoTime(nextPerformanceTime); 665 fManager->Unlock(); 666 break; 667 } 668 case B_TIMED_OUT: 669 TRACE("_FrameGeneratorThread: Couldn't lock the node " 670 "manager.\n"); 671 ignoreEvent = true; 672 waitUntil = system_time() - 1; 673 break; 674 default: 675 ERROR("_FrameGeneratorThread: Couldn't lock the node manager. " 676 "Terminating video producer frame generator thread.\n"); 677 TRACE("_FrameGeneratorThread: frame generator thread done.\n"); 678 // do not access any member variables, since this could 679 // also mean the Node has been deleted 680 return B_OK; 681 } 682 683 TRACE("_FrameGeneratorThread: waiting (%Ld)...\n", waitUntil); 684 // wait until... 685 err = acquire_sem_etc(fFrameSync, 1, B_ABSOLUTE_TIMEOUT, waitUntil); 686 // The only acceptable responses are B_OK and B_TIMED_OUT. Everything 687 // else means the thread should quit. Deleting the semaphore, as in 688 // VideoProducer::_HandleStop(), will trigger this behavior. 689 switch (err) { 690 case B_OK: 691 TRACE("_FrameGeneratorThread: going back to sleep.\n"); 692 break; 693 case B_TIMED_OUT: 694 TRACE("_FrameGeneratorThread: timed out => event\n"); 695 // Catch the cases in which the node manager could not be 696 // locked and we therefore have no valid data to work with, 697 // or the producer is not running or enabled. 698 if (ignoreEvent || !fRunning || !fEnabled) { 699 TRACE("_FrameGeneratorThread: ignore event\n"); 700 // nothing to do 701 } else if (!forceSendingBuffer 702 && nextWaitUntil < system_time() - fBufferLatency 703 && droppedFrames < kMaxDroppedFrames) { 704 // Drop frame if it's at least a frame late. 705 if (playingDirection > 0) { 706 printf("VideoProducer: dropped frame (%" B_PRId64 707 ") (perf. time %" B_PRIdBIGTIME ")\n", fFrame, 708 performanceTime); 709 } 710 // next frame 711 droppedFrames++; 712 fFrame++; 713 } else if (playingDirection != 0 || forceSendingBuffer) { 714 // Send buffers only, if playing, the node is running and 715 // the output has been enabled 716 TRACE("_FrameGeneratorThread: produce frame\n"); 717 BAutolock _(fLock); 718 // Fetch a buffer from the buffer group 719 fUsedBufferGroup->WaitForBuffers(); 720 BBuffer* buffer = fUsedBufferGroup->RequestBuffer( 721 fConnectedFormat.display.bytes_per_row 722 * fConnectedFormat.display.line_count, 0LL); 723 if (buffer == NULL) { 724 // Wait until a buffer becomes available again 725 ERROR("_FrameGeneratorThread: no buffer!\n"); 726 break; 727 } 728 // Fill out the details about this buffer. 729 media_header* h = buffer->Header(); 730 h->type = B_MEDIA_RAW_VIDEO; 731 h->time_source = TimeSource()->ID(); 732 h->size_used = fConnectedFormat.display.bytes_per_row 733 * fConnectedFormat.display.line_count; 734 // For a buffer originating from a device, you might 735 // want to calculate this based on the 736 // PerformanceTimeFor the time your buffer arrived at 737 // the hardware (plus any applicable adjustments). 738 h->start_time = fPerformanceTimeBase + performanceTime; 739 h->file_pos = 0; 740 h->orig_size = 0; 741 h->data_offset = 0; 742 h->u.raw_video.field_gamma = 1.0; 743 h->u.raw_video.field_sequence = fFrame; 744 h->u.raw_video.field_number = 0; 745 h->u.raw_video.pulldown_number = 0; 746 h->u.raw_video.first_active_line = 1; 747 h->u.raw_video.line_count 748 = fConnectedFormat.display.line_count; 749 // Fill in a frame 750 TRACE("_FrameGeneratorThread: frame: %Ld, " 751 "playlistFrame: %Ld\n", fFrame, playlistFrame); 752 bool wasCached = false; 753 err = fSupplier->FillBuffer(playlistFrame, 754 buffer->Data(), fConnectedFormat, forceSendingBuffer, 755 wasCached); 756 if (err == B_TIMED_OUT) { 757 // Don't send the buffer if there was insufficient 758 // time for rendering, this will leave the last 759 // valid frame on screen until we catch up, instead 760 // of going black. 761 wasCached = true; 762 err = B_OK; 763 } 764 // clean the buffer if something went wrong 765 if (err != B_OK) { 766 // TODO: should use "back value" according 767 // to color space! 768 memset(buffer->Data(), 0, h->size_used); 769 err = B_OK; 770 } 771 // Send the buffer on down to the consumer 772 if (wasCached || (err = SendBuffer(buffer, fOutput.source, 773 fOutput.destination) != B_OK)) { 774 // If there is a problem sending the buffer, 775 // or if we don't send the buffer because its 776 // contents are the same as the last one, 777 // return it to its buffer group. 778 buffer->Recycle(); 779 // we tell the supplier to delete 780 // its caches if there was a problem sending 781 // the buffer 782 if (err != B_OK) { 783 ERROR("_FrameGeneratorThread: Error " 784 "sending buffer\n"); 785 fSupplier->DeleteCaches(); 786 } 787 } 788 // Only if everything went fine we clear the flag 789 // that forces us to send a buffer even if not 790 // playing. 791 if (err == B_OK) 792 forceSendingBuffer = false; 793 // next frame 794 fFrame++; 795 droppedFrames = 0; 796 } else { 797 TRACE("_FrameGeneratorThread: not playing\n"); 798 // next frame 799 fFrame++; 800 } 801 break; 802 default: 803 TRACE("_FrameGeneratorThread: Couldn't acquire semaphore. " 804 "Error: %s\n", strerror(err)); 805 running = false; 806 break; 807 } 808 } 809 TRACE("_FrameGeneratorThread: frame generator thread done.\n"); 810 return B_OK; 811 } 812 813