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