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 -- %" B_PRIx32 "\n", 293 event->type)); 294 break; 295 } 296 } 297 298 299 void 300 VideoProducer::CleanUpEvent(const media_timed_event *event) 301 { 302 BMediaEventLooper::CleanUpEvent(event); 303 } 304 305 306 bigtime_t 307 VideoProducer::OfflineTime() 308 { 309 return BMediaEventLooper::OfflineTime(); 310 } 311 312 313 void 314 VideoProducer::ControlLoop() 315 { 316 BMediaEventLooper::ControlLoop(); 317 } 318 319 320 status_t 321 VideoProducer::DeleteHook(BMediaNode * node) 322 { 323 return BMediaEventLooper::DeleteHook(node); 324 } 325 326 327 /* BBufferProducer */ 328 329 330 status_t 331 VideoProducer::FormatSuggestionRequested( 332 media_type type, int32 quality, media_format *format) 333 { 334 if (type != B_MEDIA_ENCODED_VIDEO) 335 return B_MEDIA_BAD_FORMAT; 336 337 TOUCH(quality); 338 339 PRINTF(1, ("FormatSuggestionRequested() %" B_PRIu32 "x%" B_PRIu32 "\n", \ 340 format->u.raw_video.display.line_width, \ 341 format->u.raw_video.display.line_count)); 342 343 *format = fOutput.format; 344 uint32 width, height; 345 if (fCamDevice && fCamDevice->SuggestVideoFrame(width, height) == B_OK) { 346 format->u.raw_video.display.line_width = width; 347 format->u.raw_video.display.line_count = height; 348 } 349 format->u.raw_video.field_rate = FIELD_RATE; 350 return B_OK; 351 } 352 353 354 status_t 355 VideoProducer::FormatProposal(const media_source &output, media_format *format) 356 { 357 status_t err; 358 359 if (!format) 360 return B_BAD_VALUE; 361 362 if (output != fOutput.source) 363 return B_MEDIA_BAD_SOURCE; 364 365 PRINTF(1, ("FormatProposal() %" B_PRIu32 "x%" B_PRIu32 "\n", \ 366 format->u.raw_video.display.line_width, \ 367 format->u.raw_video.display.line_count)); 368 369 err = format_is_compatible(*format, fOutput.format) ? 370 B_OK : B_MEDIA_BAD_FORMAT; 371 372 uint32 width = format->u.raw_video.display.line_width; 373 uint32 height = format->u.raw_video.display.line_count; 374 375 *format = fOutput.format; 376 377 if (err == B_OK && fCamDevice) { 378 err = fCamDevice->AcceptVideoFrame(width, height); 379 if (err >= B_OK) { 380 format->u.raw_video.display.line_width = width; 381 format->u.raw_video.display.line_count = height; 382 } 383 } 384 385 PRINTF(1, ("FormatProposal: %" B_PRIu32 "x%" B_PRIu32 "\n", \ 386 format->u.raw_video.display.line_width, \ 387 format->u.raw_video.display.line_count)); 388 389 return err; 390 391 } 392 393 394 status_t 395 VideoProducer::FormatChangeRequested(const media_source &source, 396 const media_destination &destination, media_format *io_format, 397 int32 *_deprecated_) 398 { 399 TOUCH(destination); TOUCH(io_format); TOUCH(_deprecated_); 400 if (source != fOutput.source) 401 return B_MEDIA_BAD_SOURCE; 402 403 return B_ERROR; 404 } 405 406 407 status_t 408 VideoProducer::GetNextOutput(int32 *cookie, media_output *out_output) 409 { 410 if (!out_output) 411 return B_BAD_VALUE; 412 413 if ((*cookie) != 0) 414 return B_BAD_INDEX; 415 416 *out_output = fOutput; 417 (*cookie)++; 418 return B_OK; 419 } 420 421 422 status_t 423 VideoProducer::DisposeOutputCookie(int32 cookie) 424 { 425 TOUCH(cookie); 426 427 return B_OK; 428 } 429 430 431 status_t 432 VideoProducer::SetBufferGroup(const media_source &for_source, 433 BBufferGroup *group) 434 { 435 TOUCH(for_source); TOUCH(group); 436 437 return B_ERROR; 438 } 439 440 441 status_t 442 VideoProducer::VideoClippingChanged(const media_source &for_source, 443 int16 num_shorts, int16 *clip_data, 444 const media_video_display_info &display, int32 *_deprecated_) 445 { 446 TOUCH(for_source); TOUCH(num_shorts); TOUCH(clip_data); 447 TOUCH(display); TOUCH(_deprecated_); 448 449 return B_ERROR; 450 } 451 452 453 status_t 454 VideoProducer::GetLatency(bigtime_t *out_latency) 455 { 456 *out_latency = EventLatency() + SchedulingLatency(); 457 return B_OK; 458 } 459 460 461 status_t 462 VideoProducer::PrepareToConnect(const media_source &source, 463 const media_destination &destination, media_format *format, 464 media_source *out_source, char *out_name) 465 { 466 status_t err; 467 468 PRINTF(1, ("PrepareToConnect() %" B_PRIu32 "x%" B_PRIu32 "\n", \ 469 format->u.raw_video.display.line_width, \ 470 format->u.raw_video.display.line_count)); 471 472 if (fConnected) { 473 PRINTF(0, ("PrepareToConnect: Already connected\n")); 474 return EALREADY; 475 } 476 477 if (source != fOutput.source) 478 return B_MEDIA_BAD_SOURCE; 479 480 if (fOutput.destination != media_destination::null) 481 return B_MEDIA_ALREADY_CONNECTED; 482 483 /* The format parameter comes in with the suggested format, and may be 484 * specialized as desired by the node */ 485 if (!format_is_compatible(*format, fOutput.format)) { 486 *format = fOutput.format; 487 return B_MEDIA_BAD_FORMAT; 488 } 489 490 //XXX:FIXME 491 #if 0 492 // if (format->u.raw_video.display.line_width == 0) 493 format->u.raw_video.display.line_width = 352;//320; 494 format->u.raw_video.display.line_width = 320; 495 // if (format->u.raw_video.display.line_count == 0) 496 format->u.raw_video.display.line_count = 288;//240; 497 format->u.raw_video.display.line_count = 240; 498 #endif 499 500 #ifdef FORCE_320_240 501 { 502 format->u.raw_video.display.line_width = 320; 503 format->u.raw_video.display.line_count = 240; 504 } 505 #endif 506 #ifdef FORCE_160_120 507 { 508 format->u.raw_video.display.line_width = 160; 509 format->u.raw_video.display.line_count = 120; 510 } 511 #endif 512 #ifdef FORCE_MAX_FRAME 513 { 514 format->u.raw_video.display.line_width = 0; 515 format->u.raw_video.display.line_count = 0; 516 } 517 #endif 518 if (fCamDevice) { 519 err = fCamDevice->AcceptVideoFrame( 520 format->u.raw_video.display.line_width, 521 format->u.raw_video.display.line_count); 522 if (err < B_OK) 523 return err; 524 } 525 526 if (format->u.raw_video.field_rate == 0) 527 format->u.raw_video.field_rate = FIELD_RATE; 528 529 *out_source = fOutput.source; 530 strcpy(out_name, fOutput.name); 531 532 fOutput.destination = destination; 533 534 return B_OK; 535 } 536 537 538 void 539 VideoProducer::Connect(status_t error, const media_source &source, 540 const media_destination &destination, const media_format &format, 541 char *io_name) 542 { 543 PRINTF(1, ("Connect() %" B_PRIu32 "x%" B_PRIu32 "\n", \ 544 format.u.raw_video.display.line_width, \ 545 format.u.raw_video.display.line_count)); 546 547 if (fConnected) { 548 PRINTF(0, ("Connect: Already connected\n")); 549 return; 550 } 551 552 BAutolock lock(fCamDevice->Locker()); 553 if (!fCamDevice->IsPlugged()) { 554 PRINTF(0, ("Connect: Device unplugged\n")); 555 return; 556 } 557 558 if (source != fOutput.source || error < B_OK 559 || !const_cast<media_format *>(&format)->Matches(&fOutput.format)) { 560 PRINTF(1, ("Connect: Connect error\n")); 561 return; 562 } 563 564 fOutput.destination = destination; 565 strcpy(io_name, fOutput.name); 566 567 if (fOutput.format.u.raw_video.field_rate != 0.0f) { 568 fPerformanceTimeBase = fPerformanceTimeBase + 569 (bigtime_t) 570 ((fFrame - fFrameBase) * 571 (1000000 / fOutput.format.u.raw_video.field_rate)); 572 fFrameBase = fFrame; 573 } 574 575 fConnectedFormat = format.u.raw_video; 576 577 /* get the latency */ 578 bigtime_t latency = 0; 579 media_node_id tsID = 0; 580 FindLatencyFor(fOutput.destination, &latency, &tsID); 581 #define NODE_LATENCY 1000 582 SetEventLatency(latency + NODE_LATENCY); 583 584 uint32 *buffer, *p, f = 3; 585 p = buffer = (uint32 *)malloc(4 * fConnectedFormat.display.line_count * 586 fConnectedFormat.display.line_width); 587 if (!buffer) { 588 PRINTF(0, ("Connect: Out of memory\n")); 589 return; 590 } 591 bigtime_t now = system_time(); 592 for (uint32 y=0;y<fConnectedFormat.display.line_count;y++) 593 for (uint32 x=0;x<fConnectedFormat.display.line_width;x++) 594 *(p++) = ((((x+y)^0^x)+f) & 0xff) * (0x01010101 & fColor); 595 fProcessingLatency = system_time() - now; 596 free(buffer); 597 598 /* Create the buffer group */ 599 fBufferGroup = new BBufferGroup(4 * fConnectedFormat.display.line_width * 600 fConnectedFormat.display.line_count, 8); 601 if (fBufferGroup->InitCheck() < B_OK) { 602 delete fBufferGroup; 603 fBufferGroup = NULL; 604 return; 605 } 606 607 fConnected = true; 608 fEnabled = true; 609 610 /* Tell frame generation thread to recalculate delay value */ 611 release_sem(fFrameSync); 612 } 613 614 void 615 VideoProducer::Disconnect(const media_source &source, 616 const media_destination &destination) 617 { 618 PRINTF(1, ("Disconnect()\n")); 619 620 if (!fConnected) { 621 PRINTF(0, ("Disconnect: Not connected\n")); 622 return; 623 } 624 625 if ((source != fOutput.source) || (destination != fOutput.destination)) { 626 PRINTF(0, ("Disconnect: Bad source and/or destination\n")); 627 return; 628 } 629 630 #if 1 631 /* Some dumb apps don't stop nodes before disconnecting... */ 632 if (fRunning) 633 HandleStop(); 634 #endif 635 636 fEnabled = false; 637 fOutput.destination = media_destination::null; 638 639 fLock.Lock(); 640 delete fBufferGroup; 641 fBufferGroup = NULL; 642 fLock.Unlock(); 643 644 fConnected = false; 645 } 646 647 648 void 649 VideoProducer::LateNoticeReceived(const media_source &source, 650 bigtime_t how_much, bigtime_t performance_time) 651 { 652 TOUCH(source); TOUCH(how_much); TOUCH(performance_time); 653 } 654 655 656 void 657 VideoProducer::EnableOutput(const media_source &source, bool enabled, 658 int32 *_deprecated_) 659 { 660 TOUCH(_deprecated_); 661 662 if (source != fOutput.source) 663 return; 664 665 fEnabled = enabled; 666 } 667 668 669 status_t 670 VideoProducer::SetPlayRate(int32 numer, int32 denom) 671 { 672 TOUCH(numer); TOUCH(denom); 673 674 return B_ERROR; 675 } 676 677 678 void 679 VideoProducer::AdditionalBufferRequested(const media_source &source, 680 media_buffer_id prev_buffer, bigtime_t prev_time, 681 const media_seek_tag *prev_tag) 682 { 683 TOUCH(source); TOUCH(prev_buffer); TOUCH(prev_time); TOUCH(prev_tag); 684 } 685 686 687 void 688 VideoProducer::LatencyChanged(const media_source &source, 689 const media_destination &destination, bigtime_t new_latency, 690 uint32 flags) 691 { 692 TOUCH(source); TOUCH(destination); TOUCH(new_latency); TOUCH(flags); 693 } 694 695 696 /* BControllable */ 697 698 699 status_t 700 VideoProducer::GetParameterValue( 701 int32 id, bigtime_t *last_change, void *value, size_t *size) 702 { 703 status_t err; 704 705 switch (id) { 706 case P_COLOR: 707 //return B_BAD_VALUE; 708 709 *last_change = fLastColorChange; 710 *size = sizeof(uint32); 711 *((uint32 *)value) = fColor; 712 return B_OK; 713 case P_INFO: 714 if (*size < (size_t)(fInfoString.Length() + 1)) 715 return EINVAL; 716 *last_change = fLastColorChange; 717 *size = fInfoString.Length() + 1; 718 memcpy(value, fInfoString.String(), *size); 719 return B_OK; 720 } 721 722 if (fCamDevice) { 723 BAutolock lock(fCamDevice->Locker()); 724 err = fCamDevice->GetParameterValue(id, last_change, value, size); 725 if (err >= B_OK) 726 return err; 727 if (fCamDevice->Sensor()) { 728 err = fCamDevice->Sensor()->GetParameterValue(id, last_change, value, size); 729 if (err >= B_OK) 730 return err; 731 } 732 } 733 734 return B_BAD_VALUE; 735 } 736 737 738 void 739 VideoProducer::SetParameterValue( 740 int32 id, bigtime_t when, const void *value, size_t size) 741 { 742 status_t err = B_OK; 743 744 switch (id) { 745 case P_COLOR: 746 if (!value || (size != sizeof(uint32))) 747 return; 748 749 if (*(uint32 *)value == fColor) 750 return; 751 752 fColor = *(uint32 *)value; 753 fLastColorChange = when; 754 break; 755 case P_INFO: 756 // forbidden 757 return; 758 default: 759 if (fCamDevice == NULL) 760 return; 761 762 BAutolock lock(fCamDevice->Locker()); 763 err = fCamDevice->SetParameterValue(id, when, value, size); 764 if ((err < B_OK) && (fCamDevice->Sensor())) { 765 err = fCamDevice->Sensor()->SetParameterValue(id, when, value, size); 766 } 767 } 768 769 if (err >= B_OK) 770 BroadcastNewParameterValue(when, id, (void *)value, size); 771 } 772 773 774 status_t 775 VideoProducer::StartControlPanel(BMessenger *out_messenger) 776 { 777 return BControllable::StartControlPanel(out_messenger); 778 } 779 780 781 /* VideoProducer */ 782 783 784 void 785 VideoProducer::HandleStart(bigtime_t performance_time) 786 { 787 /* Start producing frames, even if the output hasn't been connected yet. */ 788 789 PRINTF(1, ("HandleStart(%" B_PRIdBIGTIME ")\n", performance_time)); 790 791 if (fRunning) { 792 PRINTF(-1, ("HandleStart: Node already started\n")); 793 return; 794 } 795 796 fFrame = 0; 797 fFrameBase = 0; 798 fPerformanceTimeBase = performance_time; 799 800 fFrameSync = create_sem(0, "frame synchronization"); 801 if (fFrameSync < B_OK) 802 goto err1; 803 804 fThread = spawn_thread(_frame_generator_, "frame generator", 805 B_NORMAL_PRIORITY, this); 806 if (fThread < B_OK) 807 goto err2; 808 809 resume_thread(fThread); 810 811 { 812 BAutolock lock(fCamDevice->Locker()); 813 fCamDevice->StartTransfer(); 814 } 815 816 fRunning = true; 817 return; 818 819 err2: 820 delete_sem(fFrameSync); 821 err1: 822 return; 823 } 824 825 826 void 827 VideoProducer::HandleStop(void) 828 { 829 PRINTF(1, ("HandleStop()\n")); 830 831 if (!fRunning) { 832 PRINTF(-1, ("HandleStop: Node isn't running\n")); 833 return; 834 } 835 836 delete_sem(fFrameSync); 837 wait_for_thread(fThread, &fThread); 838 839 BAutolock lock(fCamDevice->Locker()); 840 fCamDevice->StopTransfer(); 841 842 fRunning = false; 843 } 844 845 846 void 847 VideoProducer::HandleTimeWarp(bigtime_t performance_time) 848 { 849 fPerformanceTimeBase = performance_time; 850 fFrameBase = fFrame; 851 852 /* Tell frame generation thread to recalculate delay value */ 853 release_sem(fFrameSync); 854 } 855 856 857 void 858 VideoProducer::HandleSeek(bigtime_t performance_time) 859 { 860 fPerformanceTimeBase = performance_time; 861 fFrameBase = fFrame; 862 863 /* Tell frame generation thread to recalculate delay value */ 864 release_sem(fFrameSync); 865 } 866 867 868 void 869 VideoProducer::_UpdateStats() 870 { 871 float fps = (fStats[0].frames - fStats[1].frames) * 1000000LL 872 / (double)(fStats[0].stamp - fStats[1].stamp); 873 float rfps = (fStats[0].actual - fStats[1].actual) * 1000000LL 874 / (double)(fStats[0].stamp - fStats[1].stamp); 875 fInfoString = "FPS: "; 876 fInfoString << fps << " virt, " 877 << rfps << " real, missed: " << fStats[0].missed; 878 memcpy(&fStats[1], &fStats[0], sizeof(fStats[0])); 879 fLastColorChange = system_time(); 880 BroadcastNewParameterValue(fLastColorChange, P_INFO, 881 (void *)fInfoString.String(), fInfoString.Length()+1); 882 } 883 884 885 /* The following functions form the thread that generates frames. You should 886 * replace this with the code that interfaces to your hardware. */ 887 int32 888 VideoProducer::FrameGenerator() 889 { 890 bigtime_t wait_until = system_time(); 891 892 while (1) { 893 PRINTF(1, ("FrameGenerator: " \ 894 "acquire_sem_etc() until %" B_PRIdBIGTIME "µs " \ 895 "(in %" B_PRIdBIGTIME "µs)\n", \ 896 wait_until, wait_until - system_time())); 897 status_t err = acquire_sem_etc(fFrameSync, 1, B_ABSOLUTE_TIMEOUT, 898 wait_until); 899 900 /* The only acceptable responses are B_OK and B_TIMED_OUT. Everything 901 * else means the thread should quit. Deleting the semaphore, as in 902 * VideoProducer::HandleStop(), will trigger this behavior. */ 903 if ((err != B_OK) && (err != B_TIMED_OUT)) 904 break; 905 906 fFrame++; 907 908 /* Recalculate the time until the thread should wake up to begin 909 * processing the next frame. Subtract fProcessingLatency so that 910 * the frame is sent in time. */ 911 wait_until = TimeSource()->RealTimeFor(fPerformanceTimeBase, 0) + 912 (bigtime_t) 913 ((fFrame - fFrameBase) * 914 (1000000 / fConnectedFormat.field_rate)) - 915 fProcessingLatency; 916 PRINT(("PS: %" B_PRIdBIGTIME "\n", fProcessingLatency)); 917 918 /* Drop frame if it's at least a frame late */ 919 if (wait_until < system_time()) 920 continue; 921 922 PRINTF(1, ("FrameGenerator: wait until %" B_PRIdBIGTIME ", " 923 "%ctimed out, %crunning, %cenabled.\n", 924 wait_until, 925 (err == B_OK)?'!':' ', 926 (fRunning)?' ':'!', 927 (fEnabled)?' ':'!')); 928 929 /* If the semaphore was acquired successfully, it means something 930 * changed the timing information (see VideoProducer::Connect()) and 931 * so the thread should go back to sleep until the newly-calculated 932 * wait_until time. */ 933 if (err == B_OK) 934 continue; 935 936 /* Send buffers only if the node is running and the output has been 937 * enabled */ 938 if (!fRunning || !fEnabled) 939 continue; 940 941 BAutolock _(fLock); 942 943 /* Fetch a buffer from the buffer group */ 944 BBuffer *buffer = fBufferGroup->RequestBuffer( 945 4 * fConnectedFormat.display.line_width * 946 fConnectedFormat.display.line_count, 0LL); 947 if (!buffer) 948 continue; 949 950 /* Fill out the details about this buffer. */ 951 media_header *h = buffer->Header(); 952 h->type = B_MEDIA_RAW_VIDEO; 953 h->time_source = TimeSource()->ID(); 954 h->size_used = 4 * fConnectedFormat.display.line_width * 955 fConnectedFormat.display.line_count; 956 /* For a buffer originating from a device, you might want to calculate 957 * this based on the PerformanceTimeFor the time your buffer arrived at 958 * the hardware (plus any applicable adjustments). */ 959 /* 960 h->start_time = fPerformanceTimeBase + 961 (bigtime_t) 962 ((fFrame - fFrameBase) * 963 (1000000 / fConnectedFormat.field_rate)); 964 */ 965 h->file_pos = 0; 966 h->orig_size = 0; 967 h->data_offset = 0; 968 h->u.raw_video.field_gamma = 1.0; 969 h->u.raw_video.field_sequence = fFrame; 970 h->u.raw_video.field_number = 0; 971 h->u.raw_video.pulldown_number = 0; 972 h->u.raw_video.first_active_line = 1; 973 h->u.raw_video.line_count = fConnectedFormat.display.line_count; 974 975 // This is where we fill the video buffer. 976 977 #if 0 978 uint32 *p = (uint32 *)buffer->Data(); 979 /* Fill in a pattern */ 980 for (uint32 y=0;y<fConnectedFormat.display.line_count;y++) 981 for (uint32 x=0;x<fConnectedFormat.display.line_width;x++) 982 *(p++) = ((((x+y)^0^x)+fFrame) & 0xff) * (0x01010101 & fColor); 983 #endif 984 985 //NO! must be called without lock! 986 //BAutolock lock(fCamDevice->Locker()); 987 988 bigtime_t now = system_time(); 989 bigtime_t stamp; 990 //#ifdef UseFillFrameBuffer 991 err = fCamDevice->FillFrameBuffer(buffer, &stamp); 992 if (err < B_OK) { 993 ;//XXX handle error 994 fStats[0].missed++; 995 } 996 //#endif 997 #ifdef UseGetFrameBitmap 998 BBitmap *bm; 999 err = fCamDevice->GetFrameBitmap(&bm, &stamp); 1000 if (err >= B_OK) { 1001 ;//XXX handle error 1002 fStats[0].missed++; 1003 } 1004 #endif 1005 fStats[0].frames = fFrame; 1006 fStats[0].actual++;; 1007 fStats[0].stamp = system_time(); 1008 1009 //PRINTF(1, ("FrameGenerator: stamp %Ld vs %Ld\n", stamp, h->start_time)); 1010 //XXX: that's what we should be doing, but CodyCam drops all frames as they are late. (maybe add latency ??) 1011 //h->start_time = TimeSource()->PerformanceTimeFor(stamp); 1012 h->start_time = TimeSource()->PerformanceTimeFor(system_time()); 1013 1014 1015 // update processing latency 1016 // XXX: should I ?? 1017 fProcessingLatency = system_time() - now; 1018 fProcessingLatency /= 10; 1019 1020 PRINTF(1, ("FrameGenerator: SendBuffer...\n")); 1021 /* Send the buffer on down to the consumer */ 1022 if (SendBuffer(buffer, fOutput.source, fOutput.destination) < B_OK) { 1023 PRINTF(-1, ("FrameGenerator: Error sending buffer\n")); 1024 /* If there is a problem sending the buffer, return it to its 1025 * buffer group. */ 1026 buffer->Recycle(); 1027 } 1028 1029 _UpdateStats(); 1030 } 1031 1032 PRINTF(1, ("FrameGenerator: thread existed.\n")); 1033 return B_OK; 1034 } 1035 1036 1037 int32 1038 VideoProducer::_frame_generator_(void *data) 1039 { 1040 return ((VideoProducer *)data)->FrameGenerator(); 1041 } 1042