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