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 53int32 VideoProducer::fInstances = 0; 54 55 56VideoProducer::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 94VideoProducer::~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 */ 110port_id 111VideoProducer::ControlPort() const 112{ 113 return BMediaNode::ControlPort(); 114} 115 116 117BMediaAddOn * 118VideoProducer::AddOn(int32 *internal_id) const 119{ 120 if (internal_id) 121 *internal_id = fInternalID; 122 return fAddOn; 123} 124 125 126status_t 127VideoProducer::HandleMessage(int32 /*message*/, const void* /*data*/, size_t /*size*/) 128{ 129 return B_ERROR; 130} 131 132 133void 134VideoProducer::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 141void 142VideoProducer::SetTimeSource(BTimeSource* /*time_source*/) 143{ 144 /* Tell frame generation thread to recalculate delay value */ 145 release_sem(fFrameSync); 146} 147 148 149status_t 150VideoProducer::RequestCompleted(const media_request_info &info) 151{ 152 return BMediaNode::RequestCompleted(info); 153} 154 155 156/* BMediaEventLooper */ 157 158 159void 160VideoProducer::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 227void 228VideoProducer::Start(bigtime_t performance_time) 229{ 230 BMediaEventLooper::Start(performance_time); 231} 232 233 234void 235VideoProducer::Stop(bigtime_t performance_time, bool immediate) 236{ 237 BMediaEventLooper::Stop(performance_time, immediate); 238} 239 240 241void 242VideoProducer::Seek(bigtime_t media_time, bigtime_t performance_time) 243{ 244 BMediaEventLooper::Seek(media_time, performance_time); 245} 246 247 248void 249VideoProducer::TimeWarp(bigtime_t at_real_time, bigtime_t to_performance_time) 250{ 251 BMediaEventLooper::TimeWarp(at_real_time, to_performance_time); 252} 253 254 255status_t 256VideoProducer::AddTimer(bigtime_t at_performance_time, int32 cookie) 257{ 258 return BMediaEventLooper::AddTimer(at_performance_time, cookie); 259} 260 261 262void 263VideoProducer::SetRunMode(run_mode mode) 264{ 265 BMediaEventLooper::SetRunMode(mode); 266} 267 268 269void 270VideoProducer::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 299void 300VideoProducer::CleanUpEvent(const media_timed_event *event) 301{ 302 BMediaEventLooper::CleanUpEvent(event); 303} 304 305 306bigtime_t 307VideoProducer::OfflineTime() 308{ 309 return BMediaEventLooper::OfflineTime(); 310} 311 312 313void 314VideoProducer::ControlLoop() 315{ 316 BMediaEventLooper::ControlLoop(); 317} 318 319 320status_t 321VideoProducer::DeleteHook(BMediaNode * node) 322{ 323 return BMediaEventLooper::DeleteHook(node); 324} 325 326 327/* BBufferProducer */ 328 329 330status_t 331VideoProducer::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 354status_t 355VideoProducer::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 394status_t 395VideoProducer::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 407status_t 408VideoProducer::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 422status_t 423VideoProducer::DisposeOutputCookie(int32 cookie) 424{ 425 TOUCH(cookie); 426 427 return B_OK; 428} 429 430 431status_t 432VideoProducer::SetBufferGroup(const media_source &for_source, 433 BBufferGroup *group) 434{ 435 TOUCH(for_source); TOUCH(group); 436 437 return B_ERROR; 438} 439 440 441status_t 442VideoProducer::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 453status_t 454VideoProducer::GetLatency(bigtime_t *out_latency) 455{ 456 *out_latency = EventLatency() + SchedulingLatency(); 457 return B_OK; 458} 459 460 461status_t 462VideoProducer::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 538void 539VideoProducer::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 614void 615VideoProducer::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 648void 649VideoProducer::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 656void 657VideoProducer::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 669status_t 670VideoProducer::SetPlayRate(int32 numer, int32 denom) 671{ 672 TOUCH(numer); TOUCH(denom); 673 674 return B_ERROR; 675} 676 677 678void 679VideoProducer::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 687void 688VideoProducer::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 699status_t 700VideoProducer::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 738void 739VideoProducer::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 774status_t 775VideoProducer::StartControlPanel(BMessenger *out_messenger) 776{ 777 return BControllable::StartControlPanel(out_messenger); 778} 779 780 781/* VideoProducer */ 782 783 784void 785VideoProducer::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 819err2: 820 delete_sem(fFrameSync); 821err1: 822 return; 823} 824 825 826void 827VideoProducer::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 846void 847VideoProducer::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 857void 858VideoProducer::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 868void 869VideoProducer::_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. */ 887int32 888VideoProducer::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 %lld vs %lld\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 1037int32 1038VideoProducer::_frame_generator_(void *data) 1039{ 1040 return ((VideoProducer *)data)->FrameGenerator(); 1041} 1042