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 _HandleStop(); 174 break; 175 case BTimedEventQueue::B_WARP: 176 _HandleTimeWarp(event->bigdata); 177 break; 178 case BTimedEventQueue::B_SEEK: 179 _HandleSeek(event->bigdata); 180 break; 181 case BTimedEventQueue::B_HANDLE_BUFFER: 182 case BTimedEventQueue::B_DATA_STATUS: 183 case BTimedEventQueue::B_PARAMETER: 184 default: 185 TRACE("HandleEvent: Unhandled event -- %lx\n", event->type); 186 break; 187 } 188 } 189 190 191 status_t 192 VideoProducer::DeleteHook(BMediaNode* node) 193 { 194 return BMediaEventLooper::DeleteHook(node); 195 } 196 197 198 status_t 199 VideoProducer::FormatSuggestionRequested(media_type type, int32 quality, 200 media_format* _format) 201 { 202 FUNCTION("FormatSuggestionRequested\n"); 203 204 if (type != B_MEDIA_ENCODED_VIDEO) 205 return B_MEDIA_BAD_FORMAT; 206 207 TOUCH(quality); 208 209 *_format = fOutput.format; 210 return B_OK; 211 } 212 213 214 status_t 215 VideoProducer::FormatProposal(const media_source& output, media_format* format) 216 { 217 #ifdef TRACE_VIDEO_PRODUCER 218 char string[256]; 219 string_for_format(*format, string, 256); 220 FUNCTION("FormatProposal(%s)\n", string); 221 #endif 222 223 if (!format) 224 return B_BAD_VALUE; 225 226 if (output != fOutput.source) 227 return B_MEDIA_BAD_SOURCE; 228 229 status_t ret = format_is_compatible(*format, fOutput.format) ? 230 B_OK : B_MEDIA_BAD_FORMAT; 231 if (ret != B_OK) { 232 ERROR("FormatProposal() error: %s\n", strerror(ret)); 233 char string[512]; 234 string_for_format(*format, string, sizeof(string)); 235 ERROR(" requested: %s\n", string); 236 string_for_format(fOutput.format, string, sizeof(string)); 237 ERROR(" output: %s\n", string); 238 } 239 240 // change any wild cards to specific values 241 242 return ret; 243 244 } 245 246 247 status_t 248 VideoProducer::FormatChangeRequested(const media_source& source, 249 const media_destination& destination, media_format* ioFormat, 250 int32 *_deprecated_) 251 { 252 TOUCH(destination); TOUCH(ioFormat); TOUCH(_deprecated_); 253 254 if (source != fOutput.source) 255 return B_MEDIA_BAD_SOURCE; 256 257 return B_ERROR; 258 } 259 260 261 status_t 262 VideoProducer::GetNextOutput(int32* cookie, media_output* outOutput) 263 { 264 if (!outOutput) 265 return B_BAD_VALUE; 266 267 if ((*cookie) != 0) 268 return B_BAD_INDEX; 269 270 *outOutput = fOutput; 271 (*cookie)++; 272 273 return B_OK; 274 } 275 276 277 status_t 278 VideoProducer::DisposeOutputCookie(int32 cookie) 279 { 280 TOUCH(cookie); 281 282 return B_OK; 283 } 284 285 286 status_t 287 VideoProducer::SetBufferGroup(const media_source& forSource, 288 BBufferGroup *group) 289 { 290 if (forSource != fOutput.source) 291 return B_MEDIA_BAD_SOURCE; 292 293 TRACE("VideoProducer::SetBufferGroup() - using buffer group of " 294 "consumer.\n"); 295 fUsedBufferGroup = group; 296 297 return B_OK; 298 } 299 300 301 status_t 302 VideoProducer::VideoClippingChanged(const media_source& forSource, 303 int16 numShorts, int16* clipData, const media_video_display_info& display, 304 int32* _deprecated_) 305 { 306 TOUCH(forSource); TOUCH(numShorts); TOUCH(clipData); 307 TOUCH(display); TOUCH(_deprecated_); 308 309 return B_ERROR; 310 } 311 312 313 status_t 314 VideoProducer::GetLatency(bigtime_t* _latency) 315 { 316 if (!_latency) 317 return B_BAD_VALUE; 318 319 *_latency = EventLatency() + SchedulingLatency(); 320 321 return B_OK; 322 } 323 324 325 status_t 326 VideoProducer::PrepareToConnect(const media_source& source, 327 const media_destination& destination, media_format* format, 328 media_source* outSource, char* outName) 329 { 330 FUNCTION("PrepareToConnect() %ldx%ld\n", 331 format->u.raw_video.display.line_width, 332 format->u.raw_video.display.line_count); 333 334 if (fConnected) { 335 ERROR("PrepareToConnect() - already connected!\n"); 336 return B_MEDIA_ALREADY_CONNECTED; 337 } 338 339 if (source != fOutput.source) 340 return B_MEDIA_BAD_SOURCE; 341 342 if (fOutput.destination != media_destination::null) { 343 ERROR("PrepareToConnect() - destination != null.\n"); 344 return B_MEDIA_ALREADY_CONNECTED; 345 } 346 347 // The format parameter comes in with the suggested format, and may be 348 // specialized as desired by the node 349 if (!format_is_compatible(*format, fOutput.format)) { 350 ERROR("PrepareToConnect() - incompatible format.\n"); 351 *format = fOutput.format; 352 return B_MEDIA_BAD_FORMAT; 353 } 354 355 if (format->u.raw_video.display.line_width == 0) 356 format->u.raw_video.display.line_width = 384; 357 if (format->u.raw_video.display.line_count == 0) 358 format->u.raw_video.display.line_count = 288; 359 if (format->u.raw_video.field_rate == 0) 360 format->u.raw_video.field_rate = 25.0; 361 if (format->u.raw_video.display.bytes_per_row == 0) 362 format->u.raw_video.display.bytes_per_row = format->u.raw_video.display.line_width * 4; 363 364 *outSource = fOutput.source; 365 strcpy(outName, fOutput.name); 366 367 return B_OK; 368 } 369 370 371 void 372 VideoProducer::Connect(status_t error, const media_source& source, 373 const media_destination& destination, const media_format& format, 374 char* _name) 375 { 376 FUNCTION("Connect() %ldx%ld\n", 377 format.u.raw_video.display.line_width, 378 format.u.raw_video.display.line_count); 379 380 if (fConnected) { 381 ERROR("Connect() - already connected.\n"); 382 return; 383 } 384 385 if (source != fOutput.source) { 386 ERROR("Connect() - wrong source.\n"); 387 return; 388 } 389 if (error != B_OK) { 390 ERROR("Connect() - consumer error: %s\n", strerror(error)); 391 return; 392 } 393 if (!const_cast<media_format*>(&format)->Matches(&fOutput.format)) { 394 ERROR("Connect() - format mismatch.\n"); 395 return; 396 } 397 398 fOutput.destination = destination; 399 strcpy(_name, fOutput.name); 400 fConnectedFormat = format.u.raw_video; 401 fBufferDuration = 20000; 402 403 if (fConnectedFormat.field_rate != 0.0f) { 404 fPerformanceTimeBase = fPerformanceTimeBase 405 + (bigtime_t)((fFrame - fFrameBase) 406 * 1000000LL / fConnectedFormat.field_rate); 407 fFrameBase = fFrame; 408 fBufferDuration = bigtime_t(1000000LL / fConnectedFormat.field_rate); 409 } 410 411 if (fConnectedFormat.display.bytes_per_row == 0) { 412 ERROR("Connect() - connected format still has BPR wildcard!\n"); 413 fConnectedFormat.display.bytes_per_row 414 = 4 * fConnectedFormat.display.line_width; 415 } 416 417 // Create the buffer group 418 if (fUsedBufferGroup == NULL) { 419 fBufferGroup = new BBufferGroup(fConnectedFormat.display.bytes_per_row 420 * fConnectedFormat.display.line_count, BUFFER_COUNT); 421 status_t err = fBufferGroup->InitCheck(); 422 if (err < B_OK) { 423 delete fBufferGroup; 424 fBufferGroup = NULL; 425 ERROR("Connect() - buffer group error: %s\n", strerror(err)); 426 return; 427 } 428 fUsedBufferGroup = fBufferGroup; 429 } 430 431 // get the latency 432 fBufferLatency = (BUFFER_COUNT - 1) * fBufferDuration; 433 434 int32 bufferCount; 435 if (fUsedBufferGroup->CountBuffers(&bufferCount) == B_OK) { 436 // recompute the latency 437 fBufferLatency = (bufferCount - 1) * fBufferDuration; 438 } 439 440 bigtime_t latency = 0; 441 media_node_id tsID = 0; 442 FindLatencyFor(fOutput.destination, &latency, &tsID); 443 SetEventLatency(latency + fBufferLatency); 444 445 fConnected = true; 446 fEnabled = true; 447 448 // Tell frame generation thread to recalculate delay value 449 release_sem(fFrameSync); 450 } 451 452 453 void 454 VideoProducer::Disconnect(const media_source& source, 455 const media_destination& destination) 456 { 457 FUNCTION("Disconnect()\n"); 458 459 if (!fConnected) { 460 ERROR("Disconnect() - Not connected\n"); 461 return; 462 } 463 464 if ((source != fOutput.source) || (destination != fOutput.destination)) { 465 ERROR("Disconnect() - Bad source and/or destination\n"); 466 return; 467 } 468 469 fEnabled = false; 470 fOutput.destination = media_destination::null; 471 472 if (fLock.Lock()) { 473 // Always delete the buffer group, even if it is not ours. 474 // (See BeBook::SetBufferGroup()). 475 delete fUsedBufferGroup; 476 if (fBufferGroup != fUsedBufferGroup) 477 delete fBufferGroup; 478 fUsedBufferGroup = NULL; 479 fBufferGroup = NULL; 480 fLock.Unlock(); 481 } 482 483 fConnected = false; 484 TRACE("Disconnect() done\n"); 485 } 486 487 488 void 489 VideoProducer::LateNoticeReceived(const media_source &source, 490 bigtime_t how_much, bigtime_t performanceTime) 491 { 492 TOUCH(source); TOUCH(how_much); TOUCH(performanceTime); 493 TRACE("Late!!!\n"); 494 } 495 496 497 void 498 VideoProducer::EnableOutput(const media_source& source, bool enabled, 499 int32* _deprecated_) 500 { 501 TOUCH(_deprecated_); 502 503 if (source != fOutput.source) 504 return; 505 506 fEnabled = enabled; 507 } 508 509 510 status_t 511 VideoProducer::SetPlayRate(int32 numer, int32 denom) 512 { 513 TOUCH(numer); TOUCH(denom); 514 515 return B_ERROR; 516 } 517 518 519 void 520 VideoProducer::AdditionalBufferRequested(const media_source& source, 521 media_buffer_id prevBuffer, bigtime_t prevTime, 522 const media_seek_tag* prevTag) 523 { 524 TOUCH(source); TOUCH(prevBuffer); TOUCH(prevTime); TOUCH(prevTag); 525 } 526 527 528 void 529 VideoProducer::LatencyChanged(const media_source& source, 530 const media_destination& destination, 531 bigtime_t newLatency, uint32 flags) 532 { 533 TOUCH(source); TOUCH(destination); TOUCH(newLatency); TOUCH(flags); 534 TRACE("Latency changed!\n"); 535 } 536 537 538 // #pragma mark - 539 540 541 void 542 VideoProducer::_HandleStart(bigtime_t performanceTime) 543 { 544 // Start producing frames, even if the output hasn't been connected yet. 545 TRACE("_HandleStart(%Ld)\n", performanceTime); 546 547 if (fRunning) { 548 TRACE("_HandleStart: Node already started\n"); 549 return; 550 } 551 552 fFrame = 0; 553 fFrameBase = 0; 554 fPerformanceTimeBase = performanceTime; 555 556 fFrameSync = create_sem(0, "frame synchronization"); 557 if (fFrameSync < B_OK) 558 return; 559 560 fThread = spawn_thread(_FrameGeneratorThreadEntry, "frame generator", 561 B_NORMAL_PRIORITY, this); 562 if (fThread < B_OK) { 563 delete_sem(fFrameSync); 564 return; 565 } 566 567 resume_thread(fThread); 568 fRunning = true; 569 return; 570 } 571 572 573 void 574 VideoProducer::_HandleStop() 575 { 576 TRACE("_HandleStop()\n"); 577 578 if (!fRunning) { 579 TRACE("_HandleStop: Node isn't running\n"); 580 return; 581 } 582 583 delete_sem(fFrameSync); 584 wait_for_thread(fThread, &fThread); 585 586 fRunning = false; 587 } 588 589 590 void 591 VideoProducer::_HandleTimeWarp(bigtime_t performanceTime) 592 { 593 fPerformanceTimeBase = performanceTime; 594 fFrameBase = fFrame; 595 596 // Tell frame generation thread to recalculate delay value 597 release_sem(fFrameSync); 598 } 599 600 601 void 602 VideoProducer::_HandleSeek(bigtime_t performanceTime) 603 { 604 fPerformanceTimeBase = performanceTime; 605 fFrameBase = fFrame; 606 607 // Tell frame generation thread to recalculate delay value 608 release_sem(fFrameSync); 609 } 610 611 612 int32 613 VideoProducer::_FrameGeneratorThreadEntry(void* data) 614 { 615 return ((VideoProducer*)data)->_FrameGeneratorThread(); 616 } 617 618 619 int32 620 VideoProducer::_FrameGeneratorThread() 621 { 622 bool forceSendingBuffer = true; 623 int32 droppedFrames = 0; 624 const int32 kMaxDroppedFrames = 15; 625 bool running = true; 626 while (running) { 627 TRACE("_FrameGeneratorThread: loop: %Ld\n", fFrame); 628 // lock the node manager 629 status_t err = fManager->LockWithTimeout(10000); 630 bool ignoreEvent = false; 631 // Data to be retrieved from the node manager. 632 bigtime_t performanceTime = 0; 633 bigtime_t nextPerformanceTime = 0; 634 bigtime_t waitUntil = 0; 635 bigtime_t nextWaitUntil = 0; 636 int32 playingDirection = 0; 637 int64 playlistFrame = 0; 638 switch (err) { 639 case B_OK: { 640 TRACE("_FrameGeneratorThread: node manager successfully " 641 "locked\n"); 642 if (droppedFrames > 0) 643 fManager->FrameDropped(); 644 // get the times for the current and the next frame 645 performanceTime = fManager->TimeForFrame(fFrame); 646 nextPerformanceTime = fManager->TimeForFrame(fFrame + 1); 647 waitUntil = TimeSource()->RealTimeFor(fPerformanceTimeBase 648 + performanceTime, fBufferLatency); 649 nextWaitUntil = TimeSource()->RealTimeFor(fPerformanceTimeBase 650 + nextPerformanceTime, fBufferLatency); 651 // get playing direction and playlist frame for the current 652 // frame 653 bool newPlayingState; 654 playlistFrame = fManager->PlaylistFrameAtFrame(fFrame, 655 playingDirection, newPlayingState); 656 TRACE("_FrameGeneratorThread: performance time: %Ld, " 657 "playlist frame: %lld\n", performanceTime, playlistFrame); 658 forceSendingBuffer |= newPlayingState; 659 fManager->SetCurrentVideoTime(nextPerformanceTime); 660 fManager->Unlock(); 661 break; 662 } 663 case B_TIMED_OUT: 664 TRACE("_FrameGeneratorThread: Couldn't lock the node " 665 "manager.\n"); 666 ignoreEvent = true; 667 waitUntil = system_time() - 1; 668 break; 669 default: 670 ERROR("_FrameGeneratorThread: Couldn't lock the node manager. " 671 "Terminating video producer frame generator thread.\n"); 672 TRACE("_FrameGeneratorThread: frame generator thread done.\n"); 673 // do not access any member variables, since this could 674 // also mean the Node has been deleted 675 return B_OK; 676 } 677 678 TRACE("_FrameGeneratorThread: waiting (%Ld)...\n", waitUntil); 679 // wait until... 680 err = acquire_sem_etc(fFrameSync, 1, B_ABSOLUTE_TIMEOUT, waitUntil); 681 // The only acceptable responses are B_OK and B_TIMED_OUT. Everything 682 // else means the thread should quit. Deleting the semaphore, as in 683 // VideoProducer::_HandleStop(), will trigger this behavior. 684 switch (err) { 685 case B_OK: 686 TRACE("_FrameGeneratorThread: going back to sleep.\n"); 687 break; 688 case B_TIMED_OUT: 689 TRACE("_FrameGeneratorThread: timed out => event\n"); 690 // Catch the cases in which the node manager could not be 691 // locked and we therefore have no valid data to work with, 692 // or the producer is not running or enabled. 693 if (ignoreEvent || !fRunning || !fEnabled) { 694 TRACE("_FrameGeneratorThread: ignore event\n"); 695 // nothing to do 696 } else if (!forceSendingBuffer 697 && nextWaitUntil < system_time() - fBufferLatency 698 && droppedFrames < kMaxDroppedFrames) { 699 // Drop frame if it's at least a frame late. 700 if (playingDirection > 0) { 701 printf("VideoProducer: dropped frame (%" B_PRId64 702 ")\n", fFrame); 703 } 704 // next frame 705 droppedFrames++; 706 fFrame++; 707 } else if (playingDirection != 0 || forceSendingBuffer) { 708 // Send buffers only, if playing, the node is running and 709 // the output has been enabled 710 TRACE("_FrameGeneratorThread: produce frame\n"); 711 BAutolock _(fLock); 712 // Fetch a buffer from the buffer group 713 fUsedBufferGroup->WaitForBuffers(); 714 BBuffer* buffer = fUsedBufferGroup->RequestBuffer( 715 fConnectedFormat.display.bytes_per_row 716 * fConnectedFormat.display.line_count, 0LL); 717 if (buffer == NULL) { 718 // Wait until a buffer becomes available again 719 ERROR("_FrameGeneratorThread: no buffer!\n"); 720 break; 721 } 722 // Fill out the details about this buffer. 723 media_header* h = buffer->Header(); 724 h->type = B_MEDIA_RAW_VIDEO; 725 h->time_source = TimeSource()->ID(); 726 h->size_used = fConnectedFormat.display.bytes_per_row 727 * fConnectedFormat.display.line_count; 728 // For a buffer originating from a device, you might 729 // want to calculate this based on the 730 // PerformanceTimeFor the time your buffer arrived at 731 // the hardware (plus any applicable adjustments). 732 h->start_time = fPerformanceTimeBase + performanceTime; 733 h->file_pos = 0; 734 h->orig_size = 0; 735 h->data_offset = 0; 736 h->u.raw_video.field_gamma = 1.0; 737 h->u.raw_video.field_sequence = fFrame; 738 h->u.raw_video.field_number = 0; 739 h->u.raw_video.pulldown_number = 0; 740 h->u.raw_video.first_active_line = 1; 741 h->u.raw_video.line_count 742 = fConnectedFormat.display.line_count; 743 // Fill in a frame 744 TRACE("_FrameGeneratorThread: frame: %Ld, " 745 "playlistFrame: %Ld\n", fFrame, playlistFrame); 746 bool wasCached = false; 747 err = fSupplier->FillBuffer(playlistFrame, 748 buffer->Data(), fConnectedFormat, forceSendingBuffer, 749 wasCached); 750 if (err == B_TIMED_OUT) { 751 // Don't send the buffer if there was insufficient 752 // time for rendering, this will leave the last 753 // valid frame on screen until we catch up, instead 754 // of going black. 755 wasCached = true; 756 err = B_OK; 757 } 758 // clean the buffer if something went wrong 759 if (err != B_OK) { 760 // TODO: should use "back value" according 761 // to color space! 762 memset(buffer->Data(), 0, h->size_used); 763 err = B_OK; 764 } 765 // Send the buffer on down to the consumer 766 if (wasCached || (err = SendBuffer(buffer, fOutput.source, 767 fOutput.destination) != B_OK)) { 768 // If there is a problem sending the buffer, 769 // or if we don't send the buffer because its 770 // contents are the same as the last one, 771 // return it to its buffer group. 772 buffer->Recycle(); 773 // we tell the supplier to delete 774 // its caches if there was a problem sending 775 // the buffer 776 if (err != B_OK) { 777 ERROR("_FrameGeneratorThread: Error " 778 "sending buffer\n"); 779 fSupplier->DeleteCaches(); 780 } 781 } 782 // Only if everything went fine we clear the flag 783 // that forces us to send a buffer even if not 784 // playing. 785 if (err == B_OK) 786 forceSendingBuffer = false; 787 // next frame 788 fFrame++; 789 droppedFrames = 0; 790 } else { 791 TRACE("_FrameGeneratorThread: not playing\n"); 792 // next frame 793 fFrame++; 794 } 795 break; 796 default: 797 TRACE("_FrameGeneratorThread: Couldn't acquire semaphore. " 798 "Error: %s\n", strerror(err)); 799 running = false; 800 break; 801 } 802 } 803 TRACE("_FrameGeneratorThread: frame generator thread done.\n"); 804 return B_OK; 805 } 806 807