1 /* 2 Copyright 1999, Be Incorporated. All Rights Reserved. 3 This file may be used under the terms of the Be Sample Code License. 4 */ 5 6 7 #include <fcntl.h> 8 #include <malloc.h> 9 #include <math.h> 10 #include <stdio.h> 11 #include <string.h> 12 #include <sys/uio.h> 13 #include <unistd.h> 14 15 #include <Buffer.h> 16 #include <BufferGroup.h> 17 #include <ParameterWeb.h> 18 #include <TimeSource.h> 19 20 #include <Autolock.h> 21 #include <Debug.h> 22 23 #define TOUCH(x) ((void)(x)) 24 25 #define PRINTF(a,b) \ 26 do { \ 27 if (a < 2) { \ 28 printf("VideoProducer::"); \ 29 printf b; \ 30 } \ 31 } while (0) 32 33 #include "Producer.h" 34 35 #define FIELD_RATE 30.f 36 37 VideoProducer::VideoProducer( 38 BMediaAddOn *addon, const char *name, int32 internal_id) 39 : BMediaNode(name), 40 BMediaEventLooper(), 41 BBufferProducer(B_MEDIA_RAW_VIDEO), 42 BControllable() 43 { 44 fInitStatus = B_NO_INIT; 45 46 fInternalID = internal_id; 47 fAddOn = addon; 48 49 fBufferGroup = NULL; 50 51 fThread = -1; 52 fFrameSync = -1; 53 fProcessingLatency = 0LL; 54 55 fRunning = false; 56 fConnected = false; 57 fEnabled = false; 58 59 fOutput.destination = media_destination::null; 60 61 fInitStatus = B_OK; 62 return; 63 } 64 65 VideoProducer::~VideoProducer() 66 { 67 if (fInitStatus == B_OK) { 68 /* Clean up after ourselves, in case the application didn't make us 69 * do so. */ 70 if (fConnected) 71 Disconnect(fOutput.source, fOutput.destination); 72 if (fRunning) 73 HandleStop(); 74 } 75 } 76 77 /* BMediaNode */ 78 79 port_id 80 VideoProducer::ControlPort() const 81 { 82 return BMediaNode::ControlPort(); 83 } 84 85 BMediaAddOn * 86 VideoProducer::AddOn(int32 *internal_id) const 87 { 88 if (internal_id) 89 *internal_id = fInternalID; 90 return fAddOn; 91 } 92 93 status_t 94 VideoProducer::HandleMessage(int32 message, const void *data, size_t size) 95 { 96 return B_ERROR; 97 } 98 99 void 100 VideoProducer::Preroll() 101 { 102 /* This hook may be called before the node is started to give the hardware 103 * a chance to start. */ 104 } 105 106 void 107 VideoProducer::SetTimeSource(BTimeSource *time_source) 108 { 109 /* Tell frame generation thread to recalculate delay value */ 110 release_sem(fFrameSync); 111 } 112 113 status_t 114 VideoProducer::RequestCompleted(const media_request_info &info) 115 { 116 return BMediaNode::RequestCompleted(info); 117 } 118 119 /* BMediaEventLooper */ 120 121 void 122 VideoProducer::NodeRegistered() 123 { 124 if (fInitStatus != B_OK) { 125 ReportError(B_NODE_IN_DISTRESS); 126 return; 127 } 128 129 /* Set up the parameter web */ 130 BParameterWeb *web = new BParameterWeb(); 131 BParameterGroup *main = web->MakeGroup(Name()); 132 BDiscreteParameter *state = main->MakeDiscreteParameter( 133 P_COLOR, B_MEDIA_RAW_VIDEO, "Color", "Color"); 134 state->AddItem(B_HOST_TO_LENDIAN_INT32(0xff000000), "Block"); 135 state->AddItem(B_HOST_TO_LENDIAN_INT32(0x00ff0000), "Red"); 136 state->AddItem(B_HOST_TO_LENDIAN_INT32(0x0000ff00), "Green"); 137 state->AddItem(B_HOST_TO_LENDIAN_INT32(0x000000ff), "Blue"); 138 139 fColor = B_HOST_TO_LENDIAN_INT32(0x00ff0000); 140 fLastColorChange = system_time(); 141 142 /* After this call, the BControllable owns the BParameterWeb object and 143 * will delete it for you */ 144 SetParameterWeb(web); 145 146 fOutput.node = Node(); 147 fOutput.source.port = ControlPort(); 148 fOutput.source.id = 0; 149 fOutput.destination = media_destination::null; 150 strcpy(fOutput.name, Name()); 151 152 /* Tailor these for the output of your device */ 153 fOutput.format.type = B_MEDIA_RAW_VIDEO; 154 fOutput.format.u.raw_video = media_raw_video_format::wildcard; 155 fOutput.format.u.raw_video.interlace = 1; 156 fOutput.format.u.raw_video.display.format = B_RGB32; 157 158 /* Start the BMediaEventLooper control loop running */ 159 Run(); 160 } 161 162 void 163 VideoProducer::Start(bigtime_t performance_time) 164 { 165 BMediaEventLooper::Start(performance_time); 166 } 167 168 void 169 VideoProducer::Stop(bigtime_t performance_time, bool immediate) 170 { 171 BMediaEventLooper::Stop(performance_time, immediate); 172 } 173 174 void 175 VideoProducer::Seek(bigtime_t media_time, bigtime_t performance_time) 176 { 177 BMediaEventLooper::Seek(media_time, performance_time); 178 } 179 180 void 181 VideoProducer::TimeWarp(bigtime_t at_real_time, bigtime_t to_performance_time) 182 { 183 BMediaEventLooper::TimeWarp(at_real_time, to_performance_time); 184 } 185 186 status_t 187 VideoProducer::AddTimer(bigtime_t at_performance_time, int32 cookie) 188 { 189 return BMediaEventLooper::AddTimer(at_performance_time, cookie); 190 } 191 192 void 193 VideoProducer::SetRunMode(run_mode mode) 194 { 195 BMediaEventLooper::SetRunMode(mode); 196 } 197 198 void 199 VideoProducer::HandleEvent(const media_timed_event *event, 200 bigtime_t lateness, bool realTimeEvent) 201 { 202 TOUCH(lateness); TOUCH(realTimeEvent); 203 204 switch(event->type) 205 { 206 case BTimedEventQueue::B_START: 207 HandleStart(event->event_time); 208 break; 209 case BTimedEventQueue::B_STOP: 210 HandleStop(); 211 break; 212 case BTimedEventQueue::B_WARP: 213 HandleTimeWarp(event->bigdata); 214 break; 215 case BTimedEventQueue::B_SEEK: 216 HandleSeek(event->bigdata); 217 break; 218 case BTimedEventQueue::B_HANDLE_BUFFER: 219 case BTimedEventQueue::B_DATA_STATUS: 220 case BTimedEventQueue::B_PARAMETER: 221 default: 222 PRINTF(-1, ("HandleEvent: Unhandled event -- %" B_PRIx32 "\n", 223 event->type)); 224 break; 225 } 226 } 227 228 void 229 VideoProducer::CleanUpEvent(const media_timed_event *event) 230 { 231 BMediaEventLooper::CleanUpEvent(event); 232 } 233 234 bigtime_t 235 VideoProducer::OfflineTime() 236 { 237 return BMediaEventLooper::OfflineTime(); 238 } 239 240 void 241 VideoProducer::ControlLoop() 242 { 243 BMediaEventLooper::ControlLoop(); 244 } 245 246 status_t 247 VideoProducer::DeleteHook(BMediaNode * node) 248 { 249 return BMediaEventLooper::DeleteHook(node); 250 } 251 252 /* BBufferProducer */ 253 254 status_t 255 VideoProducer::FormatSuggestionRequested( 256 media_type type, int32 quality, media_format *format) 257 { 258 if (type != B_MEDIA_ENCODED_VIDEO) 259 return B_MEDIA_BAD_FORMAT; 260 261 TOUCH(quality); 262 263 if (fOutput.format.u.raw_video.display.line_width == 0) 264 fOutput.format.u.raw_video.display.line_width = 320; 265 if (fOutput.format.u.raw_video.display.line_count == 0) 266 fOutput.format.u.raw_video.display.line_count = 240; 267 if (fOutput.format.u.raw_video.field_rate == 0) 268 fOutput.format.u.raw_video.field_rate = 29.97f; 269 270 *format = fOutput.format; 271 return B_OK; 272 } 273 274 status_t 275 VideoProducer::FormatProposal(const media_source &output, media_format *format) 276 { 277 status_t err; 278 279 if (!format) 280 return B_BAD_VALUE; 281 282 if (output != fOutput.source) 283 return B_MEDIA_BAD_SOURCE; 284 285 err = format_is_compatible(*format, fOutput.format) ? 286 B_OK : B_MEDIA_BAD_FORMAT; 287 *format = fOutput.format; 288 289 return err; 290 } 291 292 status_t 293 VideoProducer::FormatChangeRequested(const media_source &source, 294 const media_destination &destination, media_format *io_format, 295 int32 *_deprecated_) 296 { 297 TOUCH(destination); TOUCH(io_format); TOUCH(_deprecated_); 298 if (source != fOutput.source) 299 return B_MEDIA_BAD_SOURCE; 300 301 return B_ERROR; 302 } 303 304 status_t 305 VideoProducer::GetNextOutput(int32 *cookie, media_output *out_output) 306 { 307 if (!out_output) 308 return B_BAD_VALUE; 309 310 if ((*cookie) != 0) 311 return B_BAD_INDEX; 312 313 *out_output = fOutput; 314 (*cookie)++; 315 return B_OK; 316 } 317 318 status_t 319 VideoProducer::DisposeOutputCookie(int32 cookie) 320 { 321 TOUCH(cookie); 322 323 return B_OK; 324 } 325 326 status_t 327 VideoProducer::SetBufferGroup(const media_source &for_source, 328 BBufferGroup *group) 329 { 330 TOUCH(for_source); TOUCH(group); 331 332 return B_ERROR; 333 } 334 335 status_t 336 VideoProducer::VideoClippingChanged(const media_source &for_source, 337 int16 num_shorts, int16 *clip_data, 338 const media_video_display_info &display, int32 *_deprecated_) 339 { 340 TOUCH(for_source); TOUCH(num_shorts); TOUCH(clip_data); 341 TOUCH(display); TOUCH(_deprecated_); 342 343 return B_ERROR; 344 } 345 346 status_t 347 VideoProducer::GetLatency(bigtime_t *out_latency) 348 { 349 *out_latency = EventLatency() + SchedulingLatency(); 350 return B_OK; 351 } 352 353 status_t 354 VideoProducer::PrepareToConnect(const media_source &source, 355 const media_destination &destination, media_format *format, 356 media_source *out_source, char *out_name) 357 { 358 PRINTF(1, ("PrepareToConnect() %" B_PRIu32 "x%" B_PRIu32 "\n", \ 359 format->u.raw_video.display.line_width, \ 360 format->u.raw_video.display.line_count)); 361 362 if (fConnected) { 363 PRINTF(0, ("PrepareToConnect: Already connected\n")); 364 return EALREADY; 365 } 366 367 if (source != fOutput.source) 368 return B_MEDIA_BAD_SOURCE; 369 370 if (fOutput.destination != media_destination::null) 371 return B_MEDIA_ALREADY_CONNECTED; 372 373 /* The format parameter comes in with the suggested format, and may be 374 * specialized as desired by the node */ 375 if (!format_is_compatible(*format, fOutput.format)) { 376 *format = fOutput.format; 377 return B_MEDIA_BAD_FORMAT; 378 } 379 380 if (format->u.raw_video.display.line_width == 0) 381 format->u.raw_video.display.line_width = 320; 382 if (format->u.raw_video.display.line_count == 0) 383 format->u.raw_video.display.line_count = 240; 384 if (format->u.raw_video.field_rate == 0) 385 format->u.raw_video.field_rate = 29.97f; 386 387 *out_source = fOutput.source; 388 strcpy(out_name, fOutput.name); 389 390 fOutput.destination = destination; 391 392 return B_OK; 393 } 394 395 void 396 VideoProducer::Connect(status_t error, const media_source &source, 397 const media_destination &destination, const media_format &format, 398 char *io_name) 399 { 400 PRINTF(1, ("Connect() %" B_PRIu32 "x%" B_PRIu32 "\n", \ 401 format.u.raw_video.display.line_width, \ 402 format.u.raw_video.display.line_count)); 403 404 if (fConnected) { 405 PRINTF(0, ("Connect: Already connected\n")); 406 return; 407 } 408 409 if ( (source != fOutput.source) || (error < B_OK) || 410 !const_cast<media_format *>(&format)->Matches(&fOutput.format)) { 411 PRINTF(1, ("Connect: Connect error\n")); 412 return; 413 } 414 415 fOutput.destination = destination; 416 strcpy(io_name, fOutput.name); 417 418 if (fOutput.format.u.raw_video.field_rate != 0.0f) { 419 fPerformanceTimeBase = fPerformanceTimeBase + 420 (bigtime_t) 421 ((fFrame - fFrameBase) * 422 (1000000 / fOutput.format.u.raw_video.field_rate)); 423 fFrameBase = fFrame; 424 } 425 426 fConnectedFormat = format.u.raw_video; 427 428 /* get the latency */ 429 bigtime_t latency = 0; 430 media_node_id tsID = 0; 431 FindLatencyFor(fOutput.destination, &latency, &tsID); 432 #define NODE_LATENCY 1000 433 SetEventLatency(latency + NODE_LATENCY); 434 435 uint32 *buffer, *p, f = 3; 436 p = buffer = (uint32 *)malloc(4 * fConnectedFormat.display.line_count * 437 fConnectedFormat.display.line_width); 438 if (!buffer) { 439 PRINTF(0, ("Connect: Out of memory\n")); 440 return; 441 } 442 bigtime_t now = system_time(); 443 for (uint32 y = 0; y < fConnectedFormat.display.line_count; y++) 444 for (uint32 x = 0; x < fConnectedFormat.display.line_width; x++) 445 *(p++) = ((((x+y)^0^x)+f) & 0xff) * (0x01010101 & fColor); 446 fProcessingLatency = system_time() - now; 447 free(buffer); 448 449 /* Create the buffer group */ 450 fBufferGroup = new BBufferGroup(4 * fConnectedFormat.display.line_width * 451 fConnectedFormat.display.line_count, 8); 452 if (fBufferGroup->InitCheck() < B_OK) { 453 delete fBufferGroup; 454 fBufferGroup = NULL; 455 return; 456 } 457 458 fConnected = true; 459 fEnabled = true; 460 461 /* Tell frame generation thread to recalculate delay value */ 462 release_sem(fFrameSync); 463 } 464 465 void 466 VideoProducer::Disconnect(const media_source &source, 467 const media_destination &destination) 468 { 469 PRINTF(1, ("Disconnect()\n")); 470 471 if (!fConnected) { 472 PRINTF(0, ("Disconnect: Not connected\n")); 473 return; 474 } 475 476 if ((source != fOutput.source) || (destination != fOutput.destination)) { 477 PRINTF(0, ("Disconnect: Bad source and/or destination\n")); 478 return; 479 } 480 481 fEnabled = false; 482 fOutput.destination = media_destination::null; 483 484 fLock.Lock(); 485 delete fBufferGroup; 486 fBufferGroup = NULL; 487 fLock.Unlock(); 488 489 fConnected = false; 490 } 491 492 void 493 VideoProducer::LateNoticeReceived(const media_source &source, 494 bigtime_t how_much, bigtime_t performance_time) 495 { 496 TOUCH(source); TOUCH(how_much); TOUCH(performance_time); 497 } 498 499 void 500 VideoProducer::EnableOutput(const media_source &source, bool enabled, 501 int32 *_deprecated_) 502 { 503 TOUCH(_deprecated_); 504 505 if (source != fOutput.source) 506 return; 507 508 fEnabled = enabled; 509 } 510 511 status_t 512 VideoProducer::SetPlayRate(int32 numer, int32 denom) 513 { 514 TOUCH(numer); TOUCH(denom); 515 516 return B_ERROR; 517 } 518 519 void 520 VideoProducer::AdditionalBufferRequested(const media_source &source, 521 media_buffer_id prev_buffer, bigtime_t prev_time, 522 const media_seek_tag *prev_tag) 523 { 524 TOUCH(source); TOUCH(prev_buffer); TOUCH(prev_time); TOUCH(prev_tag); 525 } 526 527 void 528 VideoProducer::LatencyChanged(const media_source &source, 529 const media_destination &destination, bigtime_t new_latency, 530 uint32 flags) 531 { 532 TOUCH(source); TOUCH(destination); TOUCH(new_latency); TOUCH(flags); 533 } 534 535 /* BControllable */ 536 537 status_t 538 VideoProducer::GetParameterValue( 539 int32 id, bigtime_t *last_change, void *value, size_t *size) 540 { 541 if (id != P_COLOR) 542 return B_BAD_VALUE; 543 544 *last_change = fLastColorChange; 545 *size = sizeof(uint32); 546 *((uint32 *)value) = fColor; 547 548 return B_OK; 549 } 550 551 void 552 VideoProducer::SetParameterValue( 553 int32 id, bigtime_t when, const void *value, size_t size) 554 { 555 if ((id != P_COLOR) || !value || (size != sizeof(uint32))) 556 return; 557 558 if (*(uint32 *)value == fColor) 559 return; 560 561 fColor = *(uint32 *)value; 562 fLastColorChange = when; 563 564 BroadcastNewParameterValue( 565 fLastColorChange, P_COLOR, &fColor, sizeof(fColor)); 566 } 567 568 status_t 569 VideoProducer::StartControlPanel(BMessenger *out_messenger) 570 { 571 return BControllable::StartControlPanel(out_messenger); 572 } 573 574 /* VideoProducer */ 575 576 void 577 VideoProducer::HandleStart(bigtime_t performance_time) 578 { 579 /* Start producing frames, even if the output hasn't been connected yet. */ 580 581 PRINTF(1, ("HandleStart(%" B_PRIdBIGTIME ")\n", performance_time)); 582 583 if (fRunning) { 584 PRINTF(-1, ("HandleStart: Node already started\n")); 585 return; 586 } 587 588 fFrame = 0; 589 fFrameBase = 0; 590 fPerformanceTimeBase = performance_time; 591 592 fFrameSync = create_sem(0, "frame synchronization"); 593 if (fFrameSync < B_OK) 594 goto err1; 595 596 fThread = spawn_thread(_frame_generator_, "frame generator", 597 B_NORMAL_PRIORITY, this); 598 if (fThread < B_OK) 599 goto err2; 600 601 resume_thread(fThread); 602 603 fRunning = true; 604 return; 605 606 err2: 607 delete_sem(fFrameSync); 608 err1: 609 return; 610 } 611 612 void 613 VideoProducer::HandleStop(void) 614 { 615 PRINTF(1, ("HandleStop()\n")); 616 617 if (!fRunning) { 618 PRINTF(-1, ("HandleStop: Node isn't running\n")); 619 return; 620 } 621 622 delete_sem(fFrameSync); 623 wait_for_thread(fThread, &fThread); 624 625 fRunning = false; 626 } 627 628 void 629 VideoProducer::HandleTimeWarp(bigtime_t performance_time) 630 { 631 fPerformanceTimeBase = performance_time; 632 fFrameBase = fFrame; 633 634 /* Tell frame generation thread to recalculate delay value */ 635 release_sem(fFrameSync); 636 } 637 638 void 639 VideoProducer::HandleSeek(bigtime_t performance_time) 640 { 641 fPerformanceTimeBase = performance_time; 642 fFrameBase = fFrame; 643 644 /* Tell frame generation thread to recalculate delay value */ 645 release_sem(fFrameSync); 646 } 647 648 /* The following functions form the thread that generates frames. You should 649 * replace this with the code that interfaces to your hardware. */ 650 int32 651 VideoProducer::FrameGenerator() 652 { 653 bigtime_t wait_until = system_time(); 654 655 while (1) { 656 status_t err = acquire_sem_etc(fFrameSync, 1, B_ABSOLUTE_TIMEOUT, 657 wait_until); 658 659 /* The only acceptable responses are B_OK and B_TIMED_OUT. Everything 660 * else means the thread should quit. Deleting the semaphore, as in 661 * VideoProducer::HandleStop(), will trigger this behavior. */ 662 if ((err != B_OK) && (err != B_TIMED_OUT)) 663 break; 664 665 fFrame++; 666 667 /* Recalculate the time until the thread should wake up to begin 668 * processing the next frame. Subtract fProcessingLatency so that 669 * the frame is sent in time. */ 670 wait_until = TimeSource()->RealTimeFor(fPerformanceTimeBase + 671 (bigtime_t) 672 ((fFrame - fFrameBase) * 673 (1000000 / fConnectedFormat.field_rate)), 0) - 674 fProcessingLatency; 675 676 /* Drop frame if it's at least a frame late */ 677 if (wait_until < system_time()) 678 continue; 679 680 /* If the semaphore was acquired successfully, it means something 681 * changed the timing information (see VideoProducer::Connect()) and 682 * so the thread should go back to sleep until the newly-calculated 683 * wait_until time. */ 684 if (err == B_OK) 685 continue; 686 687 /* Send buffers only if the node is running and the output has been 688 * enabled */ 689 if (!fRunning || !fEnabled) 690 continue; 691 692 BAutolock _(fLock); 693 694 /* Fetch a buffer from the buffer group */ 695 BBuffer *buffer = fBufferGroup->RequestBuffer( 696 4 * fConnectedFormat.display.line_width * 697 fConnectedFormat.display.line_count, 0LL); 698 if (!buffer) 699 continue; 700 701 /* Fill out the details about this buffer. */ 702 media_header *h = buffer->Header(); 703 h->type = B_MEDIA_RAW_VIDEO; 704 h->time_source = TimeSource()->ID(); 705 h->size_used = 4 * fConnectedFormat.display.line_width * 706 fConnectedFormat.display.line_count; 707 /* For a buffer originating from a device, you might want to calculate 708 * this based on the PerformanceTimeFor the time your buffer arrived at 709 * the hardware (plus any applicable adjustments). */ 710 h->start_time = fPerformanceTimeBase + 711 (bigtime_t) 712 ((fFrame - fFrameBase) * 713 (1000000 / fConnectedFormat.field_rate)); 714 h->file_pos = 0; 715 h->orig_size = 0; 716 h->data_offset = 0; 717 h->u.raw_video.field_gamma = 1.0; 718 h->u.raw_video.field_sequence = fFrame; 719 h->u.raw_video.field_number = 0; 720 h->u.raw_video.pulldown_number = 0; 721 h->u.raw_video.first_active_line = 1; 722 h->u.raw_video.line_count = fConnectedFormat.display.line_count; 723 724 if (fColor == 0xff000000) { 725 // display a gray block that moves 726 uint32 *p = (uint32 *)buffer->Data(); 727 for (uint32 y = 0; y < fConnectedFormat.display.line_count; y++) 728 for (uint32 x = 0; x < fConnectedFormat.display.line_width; x++) { 729 if (x > (fFrame & 0xff) && x < (fFrame & 0xff) + 60 && y > 90 && y < 150) { 730 *(p++) = 0xff777777; 731 } else { 732 *(p++) = 0x00000000; 733 } 734 } 735 } else { 736 737 /* Fill in a pattern */ 738 uint32 *p = (uint32 *)buffer->Data(); 739 for (uint32 y = 0; y < fConnectedFormat.display.line_count; y++) 740 for (uint32 x = 0; x < fConnectedFormat.display.line_width; x++) 741 *(p++) = ((((x+y)^0^x)+fFrame) & 0xff) * (0x01010101 & fColor); 742 } 743 744 /* Send the buffer on down to the consumer */ 745 if (SendBuffer(buffer, fOutput.source, fOutput.destination) < B_OK) { 746 PRINTF(-1, ("FrameGenerator: Error sending buffer\n")); 747 /* If there is a problem sending the buffer, return it to its 748 * buffer group. */ 749 buffer->Recycle(); 750 } 751 } 752 753 return B_OK; 754 } 755 756 int32 757 VideoProducer::_frame_generator_(void *data) 758 { 759 return ((VideoProducer *)data)->FrameGenerator(); 760 } 761