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