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