1 #include <fcntl.h> 2 #include <malloc.h> 3 #include <math.h> 4 #include <stdio.h> 5 #include <string.h> 6 #include <sys/uio.h> 7 #include <unistd.h> 8 9 #include <media/Buffer.h> 10 #include <media/BufferGroup.h> 11 #include <media/ParameterWeb.h> 12 #include <media/TimeSource.h> 13 14 #include <support/Autolock.h> 15 #include <support/Debug.h> 16 17 //XXX: change interface 18 #include <interface/Bitmap.h> 19 20 #include "CamDevice.h" 21 22 #define TOUCH(x) ((void)(x)) 23 24 #define PRINTF(a,b) \ 25 do { \ 26 if (a < 2) { \ 27 printf("VideoProducer::"); \ 28 printf b; \ 29 } \ 30 } while (0) 31 32 #include "Producer.h" 33 34 #define FIELD_RATE 30.f 35 36 int32 VideoProducer::fInstances = 0; 37 38 VideoProducer::VideoProducer( 39 BMediaAddOn *addon, CamDevice *dev, const char *name, int32 internal_id) 40 : BMediaNode(name), 41 BMediaEventLooper(), 42 BBufferProducer(B_MEDIA_RAW_VIDEO), 43 BControllable() 44 { 45 // status_t err; 46 47 fInitStatus = B_NO_INIT; 48 49 /* Only allow one instance of the node to exist at any time */ 50 if (atomic_add(&fInstances, 1) != 0) 51 return; 52 53 fInternalID = internal_id; 54 fAddOn = addon; 55 fCamDevice = dev; 56 57 fBufferGroup = NULL; 58 59 fThread = -1; 60 fFrameSync = -1; 61 fProcessingLatency = 0LL; 62 63 fRunning = false; 64 fConnected = false; 65 fEnabled = false; 66 67 fOutput.destination = media_destination::null; 68 69 AddNodeKind(B_PHYSICAL_INPUT); 70 71 fInitStatus = B_OK; 72 return; 73 } 74 75 VideoProducer::~VideoProducer() 76 { 77 if (fInitStatus == B_OK) { 78 /* Clean up after ourselves, in case the application didn't make us 79 * do so. */ 80 if (fConnected) 81 Disconnect(fOutput.source, fOutput.destination); 82 if (fRunning) 83 HandleStop(); 84 } 85 86 atomic_add(&fInstances, -1); 87 } 88 89 /* BMediaNode */ 90 91 port_id 92 VideoProducer::ControlPort() const 93 { 94 return BMediaNode::ControlPort(); 95 } 96 97 BMediaAddOn * 98 VideoProducer::AddOn(int32 *internal_id) const 99 { 100 if (internal_id) 101 *internal_id = fInternalID; 102 return fAddOn; 103 } 104 105 status_t 106 VideoProducer::HandleMessage(int32 /*message*/, const void* /*data*/, size_t /*size*/) 107 { 108 return B_ERROR; 109 } 110 111 void 112 VideoProducer::Preroll() 113 { 114 /* This hook may be called before the node is started to give the hardware 115 * a chance to start. */ 116 } 117 118 void 119 VideoProducer::SetTimeSource(BTimeSource* /*time_source*/) 120 { 121 /* Tell frame generation thread to recalculate delay value */ 122 release_sem(fFrameSync); 123 } 124 125 status_t 126 VideoProducer::RequestCompleted(const media_request_info &info) 127 { 128 return BMediaNode::RequestCompleted(info); 129 } 130 131 /* BMediaEventLooper */ 132 133 void 134 VideoProducer::NodeRegistered() 135 { 136 if (fInitStatus != B_OK) { 137 ReportError(B_NODE_IN_DISTRESS); 138 return; 139 } 140 141 /* Set up the parameter web */ 142 BParameterWeb *web = new BParameterWeb(); 143 BParameterGroup *main = web->MakeGroup(Name()); 144 BDiscreteParameter *state = main->MakeDiscreteParameter( 145 P_COLOR, B_MEDIA_RAW_VIDEO, "Color", "Color"); 146 state->AddItem(B_HOST_TO_LENDIAN_INT32(0x00ff0000), "Red"); 147 state->AddItem(B_HOST_TO_LENDIAN_INT32(0x0000ff00), "Green"); 148 state->AddItem(B_HOST_TO_LENDIAN_INT32(0x000000ff), "Blue"); 149 150 fColor = B_HOST_TO_LENDIAN_INT32(0x00ff0000); 151 fLastColorChange = system_time(); 152 153 /* After this call, the BControllable owns the BParameterWeb object and 154 * will delete it for you */ 155 SetParameterWeb(web); 156 157 fOutput.node = Node(); 158 fOutput.source.port = ControlPort(); 159 fOutput.source.id = 0; 160 fOutput.destination = media_destination::null; 161 strcpy(fOutput.name, Name()); 162 163 /* Tailor these for the output of your device */ 164 fOutput.format.type = B_MEDIA_RAW_VIDEO; 165 fOutput.format.u.raw_video = media_raw_video_format::wildcard; 166 fOutput.format.u.raw_video.interlace = 1; 167 fOutput.format.u.raw_video.display.format = B_RGB32; 168 fOutput.format.u.raw_video.field_rate = 29.97f; // XXX: mmu 169 170 /* Start the BMediaEventLooper control loop running */ 171 Run(); 172 } 173 174 void 175 VideoProducer::Start(bigtime_t performance_time) 176 { 177 BMediaEventLooper::Start(performance_time); 178 } 179 180 void 181 VideoProducer::Stop(bigtime_t performance_time, bool immediate) 182 { 183 BMediaEventLooper::Stop(performance_time, immediate); 184 } 185 186 void 187 VideoProducer::Seek(bigtime_t media_time, bigtime_t performance_time) 188 { 189 BMediaEventLooper::Seek(media_time, performance_time); 190 } 191 192 void 193 VideoProducer::TimeWarp(bigtime_t at_real_time, bigtime_t to_performance_time) 194 { 195 BMediaEventLooper::TimeWarp(at_real_time, to_performance_time); 196 } 197 198 status_t 199 VideoProducer::AddTimer(bigtime_t at_performance_time, int32 cookie) 200 { 201 return BMediaEventLooper::AddTimer(at_performance_time, cookie); 202 } 203 204 void 205 VideoProducer::SetRunMode(run_mode mode) 206 { 207 BMediaEventLooper::SetRunMode(mode); 208 } 209 210 void 211 VideoProducer::HandleEvent(const media_timed_event *event, 212 bigtime_t lateness, bool realTimeEvent) 213 { 214 TOUCH(lateness); TOUCH(realTimeEvent); 215 216 switch(event->type) 217 { 218 case BTimedEventQueue::B_START: 219 HandleStart(event->event_time); 220 break; 221 case BTimedEventQueue::B_STOP: 222 HandleStop(); 223 break; 224 case BTimedEventQueue::B_WARP: 225 HandleTimeWarp(event->bigdata); 226 break; 227 case BTimedEventQueue::B_SEEK: 228 HandleSeek(event->bigdata); 229 break; 230 case BTimedEventQueue::B_HANDLE_BUFFER: 231 case BTimedEventQueue::B_DATA_STATUS: 232 case BTimedEventQueue::B_PARAMETER: 233 default: 234 PRINTF(-1, ("HandleEvent: Unhandled event -- %lx\n", event->type)); 235 break; 236 } 237 } 238 239 void 240 VideoProducer::CleanUpEvent(const media_timed_event *event) 241 { 242 BMediaEventLooper::CleanUpEvent(event); 243 } 244 245 bigtime_t 246 VideoProducer::OfflineTime() 247 { 248 return BMediaEventLooper::OfflineTime(); 249 } 250 251 void 252 VideoProducer::ControlLoop() 253 { 254 BMediaEventLooper::ControlLoop(); 255 } 256 257 status_t 258 VideoProducer::DeleteHook(BMediaNode * node) 259 { 260 return BMediaEventLooper::DeleteHook(node); 261 } 262 263 /* BBufferProducer */ 264 265 status_t 266 VideoProducer::FormatSuggestionRequested( 267 media_type type, int32 quality, media_format *format) 268 { 269 if (type != B_MEDIA_ENCODED_VIDEO) 270 return B_MEDIA_BAD_FORMAT; 271 272 TOUCH(quality); 273 274 *format = fOutput.format; 275 format->u.raw_video.field_rate = 29.97f; 276 return B_OK; 277 } 278 279 status_t 280 VideoProducer::FormatProposal(const media_source &output, media_format *format) 281 { 282 status_t err; 283 284 if (!format) 285 return B_BAD_VALUE; 286 287 if (output != fOutput.source) 288 return B_MEDIA_BAD_SOURCE; 289 290 err = format_is_compatible(*format, fOutput.format) ? 291 B_OK : B_MEDIA_BAD_FORMAT; 292 *format = fOutput.format; 293 return err; 294 295 } 296 297 status_t 298 VideoProducer::FormatChangeRequested(const media_source &source, 299 const media_destination &destination, media_format *io_format, 300 int32 *_deprecated_) 301 { 302 TOUCH(destination); TOUCH(io_format); TOUCH(_deprecated_); 303 if (source != fOutput.source) 304 return B_MEDIA_BAD_SOURCE; 305 306 return B_ERROR; 307 } 308 309 status_t 310 VideoProducer::GetNextOutput(int32 *cookie, media_output *out_output) 311 { 312 if (!out_output) 313 return B_BAD_VALUE; 314 315 if ((*cookie) != 0) 316 return B_BAD_INDEX; 317 318 *out_output = fOutput; 319 (*cookie)++; 320 return B_OK; 321 } 322 323 status_t 324 VideoProducer::DisposeOutputCookie(int32 cookie) 325 { 326 TOUCH(cookie); 327 328 return B_OK; 329 } 330 331 status_t 332 VideoProducer::SetBufferGroup(const media_source &for_source, 333 BBufferGroup *group) 334 { 335 TOUCH(for_source); TOUCH(group); 336 337 return B_ERROR; 338 } 339 340 status_t 341 VideoProducer::VideoClippingChanged(const media_source &for_source, 342 int16 num_shorts, int16 *clip_data, 343 const media_video_display_info &display, int32 *_deprecated_) 344 { 345 TOUCH(for_source); TOUCH(num_shorts); TOUCH(clip_data); 346 TOUCH(display); TOUCH(_deprecated_); 347 348 return B_ERROR; 349 } 350 351 status_t 352 VideoProducer::GetLatency(bigtime_t *out_latency) 353 { 354 *out_latency = EventLatency() + SchedulingLatency(); 355 return B_OK; 356 } 357 358 status_t 359 VideoProducer::PrepareToConnect(const media_source &source, 360 const media_destination &destination, media_format *format, 361 media_source *out_source, char *out_name) 362 { 363 // status_t err; 364 365 PRINTF(1, ("PrepareToConnect() %ldx%ld\n", \ 366 format->u.raw_video.display.line_width, \ 367 format->u.raw_video.display.line_count)); 368 369 if (fConnected) { 370 PRINTF(0, ("PrepareToConnect: Already connected\n")); 371 return EALREADY; 372 } 373 374 if (source != fOutput.source) 375 return B_MEDIA_BAD_SOURCE; 376 377 if (fOutput.destination != media_destination::null) 378 return B_MEDIA_ALREADY_CONNECTED; 379 380 /* The format parameter comes in with the suggested format, and may be 381 * specialized as desired by the node */ 382 if (!format_is_compatible(*format, fOutput.format)) { 383 *format = fOutput.format; 384 return B_MEDIA_BAD_FORMAT; 385 } 386 387 //XXX:FIXME 388 // if (format->u.raw_video.display.line_width == 0) 389 format->u.raw_video.display.line_width = 352;//320; 390 format->u.raw_video.display.line_width = 320; 391 // if (format->u.raw_video.display.line_count == 0) 392 format->u.raw_video.display.line_count = 288;//240; 393 format->u.raw_video.display.line_count = 240; 394 if (format->u.raw_video.field_rate == 0) 395 format->u.raw_video.field_rate = 29.97f; 396 397 *out_source = fOutput.source; 398 strcpy(out_name, fOutput.name); 399 400 fOutput.destination = destination; 401 402 return B_OK; 403 } 404 405 void 406 VideoProducer::Connect(status_t error, const media_source &source, 407 const media_destination &destination, const media_format &format, 408 char *io_name) 409 { 410 PRINTF(1, ("Connect() %ldx%ld\n", \ 411 format.u.raw_video.display.line_width, \ 412 format.u.raw_video.display.line_count)); 413 414 if (fConnected) { 415 PRINTF(0, ("Connect: Already connected\n")); 416 return; 417 } 418 419 BAutolock lock(fCamDevice->Locker()); 420 if (!fCamDevice->IsPlugged()) { 421 PRINTF(0, ("Connect: Device unplugged\n")); 422 return; 423 } 424 425 if ( (source != fOutput.source) || (error < B_OK) || 426 !const_cast<media_format *>(&format)->Matches(&fOutput.format)) { 427 PRINTF(1, ("Connect: Connect error\n")); 428 return; 429 } 430 431 fOutput.destination = destination; 432 strcpy(io_name, fOutput.name); 433 434 if (fOutput.format.u.raw_video.field_rate != 0.0f) { 435 fPerformanceTimeBase = fPerformanceTimeBase + 436 (bigtime_t) 437 ((fFrame - fFrameBase) * 438 (1000000 / fOutput.format.u.raw_video.field_rate)); 439 fFrameBase = fFrame; 440 } 441 442 fConnectedFormat = format.u.raw_video; 443 444 /* get the latency */ 445 bigtime_t latency = 0; 446 media_node_id tsID = 0; 447 FindLatencyFor(fOutput.destination, &latency, &tsID); 448 #define NODE_LATENCY 1000 449 SetEventLatency(latency + NODE_LATENCY); 450 451 uint32 *buffer, *p, f = 3; 452 p = buffer = (uint32 *)malloc(4 * fConnectedFormat.display.line_count * 453 fConnectedFormat.display.line_width); 454 if (!buffer) { 455 PRINTF(0, ("Connect: Out of memory\n")); 456 return; 457 } 458 bigtime_t now = system_time(); 459 for (uint32 y=0;y<fConnectedFormat.display.line_count;y++) 460 for (uint32 x=0;x<fConnectedFormat.display.line_width;x++) 461 *(p++) = ((((x+y)^0^x)+f) & 0xff) * (0x01010101 & fColor); 462 fProcessingLatency = system_time() - now; 463 free(buffer); 464 465 /* Create the buffer group */ 466 fBufferGroup = new BBufferGroup(4 * fConnectedFormat.display.line_width * 467 fConnectedFormat.display.line_count, 8); 468 if (fBufferGroup->InitCheck() < B_OK) { 469 delete fBufferGroup; 470 fBufferGroup = NULL; 471 return; 472 } 473 474 fConnected = true; 475 fEnabled = true; 476 477 /* Tell frame generation thread to recalculate delay value */ 478 release_sem(fFrameSync); 479 } 480 481 void 482 VideoProducer::Disconnect(const media_source &source, 483 const media_destination &destination) 484 { 485 PRINTF(1, ("Disconnect()\n")); 486 487 if (!fConnected) { 488 PRINTF(0, ("Disconnect: Not connected\n")); 489 return; 490 } 491 492 if ((source != fOutput.source) || (destination != fOutput.destination)) { 493 PRINTF(0, ("Disconnect: Bad source and/or destination\n")); 494 return; 495 } 496 497 #if 0 498 /* Some dumb apps don't stop nodes before disconnecting... */ 499 if (fRunning) 500 HandleStop(); 501 #endif 502 503 fEnabled = false; 504 fOutput.destination = media_destination::null; 505 506 fLock.Lock(); 507 delete fBufferGroup; 508 fBufferGroup = NULL; 509 fLock.Unlock(); 510 511 fConnected = false; 512 } 513 514 void 515 VideoProducer::LateNoticeReceived(const media_source &source, 516 bigtime_t how_much, bigtime_t performance_time) 517 { 518 TOUCH(source); TOUCH(how_much); TOUCH(performance_time); 519 } 520 521 void 522 VideoProducer::EnableOutput(const media_source &source, bool enabled, 523 int32 *_deprecated_) 524 { 525 TOUCH(_deprecated_); 526 527 if (source != fOutput.source) 528 return; 529 530 fEnabled = enabled; 531 } 532 533 status_t 534 VideoProducer::SetPlayRate(int32 numer, int32 denom) 535 { 536 TOUCH(numer); TOUCH(denom); 537 538 return B_ERROR; 539 } 540 541 void 542 VideoProducer::AdditionalBufferRequested(const media_source &source, 543 media_buffer_id prev_buffer, bigtime_t prev_time, 544 const media_seek_tag *prev_tag) 545 { 546 TOUCH(source); TOUCH(prev_buffer); TOUCH(prev_time); TOUCH(prev_tag); 547 } 548 549 void 550 VideoProducer::LatencyChanged(const media_source &source, 551 const media_destination &destination, bigtime_t new_latency, 552 uint32 flags) 553 { 554 TOUCH(source); TOUCH(destination); TOUCH(new_latency); TOUCH(flags); 555 } 556 557 /* BControllable */ 558 559 status_t 560 VideoProducer::GetParameterValue( 561 int32 id, bigtime_t *last_change, void *value, size_t *size) 562 { 563 if (id != P_COLOR) 564 return B_BAD_VALUE; 565 566 *last_change = fLastColorChange; 567 *size = sizeof(uint32); 568 *((uint32 *)value) = fColor; 569 570 return B_OK; 571 } 572 573 void 574 VideoProducer::SetParameterValue( 575 int32 id, bigtime_t when, const void *value, size_t size) 576 { 577 if ((id != P_COLOR) || !value || (size != sizeof(uint32))) 578 return; 579 580 if (*(uint32 *)value == fColor) 581 return; 582 583 fColor = *(uint32 *)value; 584 fLastColorChange = when; 585 586 BroadcastNewParameterValue( 587 fLastColorChange, P_COLOR, &fColor, sizeof(fColor)); 588 } 589 590 status_t 591 VideoProducer::StartControlPanel(BMessenger *out_messenger) 592 { 593 return BControllable::StartControlPanel(out_messenger); 594 } 595 596 /* VideoProducer */ 597 598 void 599 VideoProducer::HandleStart(bigtime_t performance_time) 600 { 601 /* Start producing frames, even if the output hasn't been connected yet. */ 602 603 PRINTF(1, ("HandleStart(%Ld)\n", performance_time)); 604 605 if (fRunning) { 606 PRINTF(-1, ("HandleStart: Node already started\n")); 607 return; 608 } 609 610 fFrame = 0; 611 fFrameBase = 0; 612 fPerformanceTimeBase = performance_time; 613 614 fFrameSync = create_sem(0, "frame synchronization"); 615 if (fFrameSync < B_OK) 616 goto err1; 617 618 fThread = spawn_thread(_frame_generator_, "frame generator", 619 B_NORMAL_PRIORITY, this); 620 if (fThread < B_OK) 621 goto err2; 622 623 resume_thread(fThread); 624 625 { 626 BAutolock lock(fCamDevice->Locker()); 627 fCamDevice->StartTransfer(); 628 } 629 630 fRunning = true; 631 return; 632 633 err2: 634 delete_sem(fFrameSync); 635 err1: 636 return; 637 } 638 639 void 640 VideoProducer::HandleStop(void) 641 { 642 PRINTF(1, ("HandleStop()\n")); 643 644 if (!fRunning) { 645 PRINTF(-1, ("HandleStop: Node isn't running\n")); 646 return; 647 } 648 649 delete_sem(fFrameSync); 650 wait_for_thread(fThread, &fThread); 651 652 BAutolock lock(fCamDevice->Locker()); 653 fCamDevice->StopTransfer(); 654 655 fRunning = false; 656 } 657 658 void 659 VideoProducer::HandleTimeWarp(bigtime_t performance_time) 660 { 661 fPerformanceTimeBase = performance_time; 662 fFrameBase = fFrame; 663 664 /* Tell frame generation thread to recalculate delay value */ 665 release_sem(fFrameSync); 666 } 667 668 void 669 VideoProducer::HandleSeek(bigtime_t performance_time) 670 { 671 fPerformanceTimeBase = performance_time; 672 fFrameBase = fFrame; 673 674 /* Tell frame generation thread to recalculate delay value */ 675 release_sem(fFrameSync); 676 } 677 678 /* The following functions form the thread that generates frames. You should 679 * replace this with the code that interfaces to your hardware. */ 680 int32 681 VideoProducer::FrameGenerator() 682 { 683 bigtime_t wait_until = system_time(); 684 685 while (1) { 686 PRINTF(1, ("FrameGenerator: acquire_sem_etc() until %Ldµs (in %Ldµs)\n", wait_until, wait_until - system_time())); 687 status_t err = acquire_sem_etc(fFrameSync, 1, B_ABSOLUTE_TIMEOUT, 688 wait_until); 689 690 /* The only acceptable responses are B_OK and B_TIMED_OUT. Everything 691 * else means the thread should quit. Deleting the semaphore, as in 692 * VideoProducer::HandleStop(), will trigger this behavior. */ 693 if ((err != B_OK) && (err != B_TIMED_OUT)) 694 break; 695 696 fFrame++; 697 698 /* Recalculate the time until the thread should wake up to begin 699 * processing the next frame. Subtract fProcessingLatency so that 700 * the frame is sent in time. */ 701 wait_until = TimeSource()->RealTimeFor(fPerformanceTimeBase, 0) + 702 (bigtime_t) 703 ((fFrame - fFrameBase) * 704 (1000000 / fConnectedFormat.field_rate)) - 705 fProcessingLatency; 706 707 /* Drop frame if it's at least a frame late */ 708 if (wait_until < system_time()) 709 continue; 710 711 PRINTF(1, ("FrameGenerator: wait until %Ld, %ctimed out, %crunning, %cenabled.\n", 712 wait_until, 713 (err == B_OK)?'!':' ', 714 (fRunning)?' ':'!', 715 (fEnabled)?' ':'!')); 716 717 /* If the semaphore was acquired successfully, it means something 718 * changed the timing information (see VideoProducer::Connect()) and 719 * so the thread should go back to sleep until the newly-calculated 720 * wait_until time. */ 721 if (err == B_OK) 722 continue; 723 724 /* Send buffers only if the node is running and the output has been 725 * enabled */ 726 if (!fRunning || !fEnabled) 727 continue; 728 729 BAutolock _(fLock); 730 731 /* Fetch a buffer from the buffer group */ 732 BBuffer *buffer = fBufferGroup->RequestBuffer( 733 4 * fConnectedFormat.display.line_width * 734 fConnectedFormat.display.line_count, 0LL); 735 if (!buffer) 736 continue; 737 738 /* Fill out the details about this buffer. */ 739 media_header *h = buffer->Header(); 740 h->type = B_MEDIA_RAW_VIDEO; 741 h->time_source = TimeSource()->ID(); 742 h->size_used = 4 * fConnectedFormat.display.line_width * 743 fConnectedFormat.display.line_count; 744 /* For a buffer originating from a device, you might want to calculate 745 * this based on the PerformanceTimeFor the time your buffer arrived at 746 * the hardware (plus any applicable adjustments). */ 747 h->start_time = fPerformanceTimeBase + 748 (bigtime_t) 749 ((fFrame - fFrameBase) * 750 (1000000 / fConnectedFormat.field_rate)); 751 h->file_pos = 0; 752 h->orig_size = 0; 753 h->data_offset = 0; 754 h->u.raw_video.field_gamma = 1.0; 755 h->u.raw_video.field_sequence = fFrame; 756 h->u.raw_video.field_number = 0; 757 h->u.raw_video.pulldown_number = 0; 758 h->u.raw_video.first_active_line = 1; 759 h->u.raw_video.line_count = fConnectedFormat.display.line_count; 760 761 // This is where we fill the video buffer. 762 763 uint32 *p = (uint32 *)buffer->Data(); 764 #if 0 765 /* Fill in a pattern */ 766 for (uint32 y=0;y<fConnectedFormat.display.line_count;y++) 767 for (uint32 x=0;x<fConnectedFormat.display.line_width;x++) 768 *(p++) = ((((x+y)^0^x)+fFrame) & 0xff) * (0x01010101 & fColor); 769 #endif 770 771 //NO! must be called without lock! 772 //BAutolock lock(fCamDevice->Locker()); 773 774 //#ifdef UseFillFrameBuffer 775 err = fCamDevice->FillFrameBuffer(buffer); 776 if (err < B_OK) { 777 ;//XXX handle error 778 } 779 //#endif 780 #ifdef UseGetFrameBitmap 781 BBitmap *bm; 782 err = fCamDevice->GetFrameBitmap(&bm); 783 if (err >= B_OK) { 784 ;//XXX handle error 785 } 786 #endif 787 788 PRINTF(1, ("FrameGenerator: SendBuffer...\n")); 789 /* Send the buffer on down to the consumer */ 790 if (SendBuffer(buffer, fOutput.destination) < B_OK) { 791 PRINTF(-1, ("FrameGenerator: Error sending buffer\n")); 792 /* If there is a problem sending the buffer, return it to its 793 * buffer group. */ 794 buffer->Recycle(); 795 } 796 } 797 798 PRINTF(1, ("FrameGenerator: thread existed.\n")); 799 return B_OK; 800 } 801 802 int32 803 VideoProducer::_frame_generator_(void *data) 804 { 805 return ((VideoProducer *)data)->FrameGenerator(); 806 } 807