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 -- %lx\n", event->type)); 223 break; 224 } 225 } 226 227 void 228 VideoProducer::CleanUpEvent(const media_timed_event *event) 229 { 230 BMediaEventLooper::CleanUpEvent(event); 231 } 232 233 bigtime_t 234 VideoProducer::OfflineTime() 235 { 236 return BMediaEventLooper::OfflineTime(); 237 } 238 239 void 240 VideoProducer::ControlLoop() 241 { 242 BMediaEventLooper::ControlLoop(); 243 } 244 245 status_t 246 VideoProducer::DeleteHook(BMediaNode * node) 247 { 248 return BMediaEventLooper::DeleteHook(node); 249 } 250 251 /* BBufferProducer */ 252 253 status_t 254 VideoProducer::FormatSuggestionRequested( 255 media_type type, int32 quality, media_format *format) 256 { 257 if (type != B_MEDIA_ENCODED_VIDEO) 258 return B_MEDIA_BAD_FORMAT; 259 260 TOUCH(quality); 261 262 if (fOutput.format.u.raw_video.display.line_width == 0) 263 fOutput.format.u.raw_video.display.line_width = 320; 264 if (fOutput.format.u.raw_video.display.line_count == 0) 265 fOutput.format.u.raw_video.display.line_count = 240; 266 if (fOutput.format.u.raw_video.field_rate == 0) 267 fOutput.format.u.raw_video.field_rate = 29.97f; 268 269 *format = fOutput.format; 270 return B_OK; 271 } 272 273 status_t 274 VideoProducer::FormatProposal(const media_source &output, media_format *format) 275 { 276 status_t err; 277 278 if (!format) 279 return B_BAD_VALUE; 280 281 if (output != fOutput.source) 282 return B_MEDIA_BAD_SOURCE; 283 284 err = format_is_compatible(*format, fOutput.format) ? 285 B_OK : B_MEDIA_BAD_FORMAT; 286 *format = fOutput.format; 287 288 return err; 289 } 290 291 status_t 292 VideoProducer::FormatChangeRequested(const media_source &source, 293 const media_destination &destination, media_format *io_format, 294 int32 *_deprecated_) 295 { 296 TOUCH(destination); TOUCH(io_format); TOUCH(_deprecated_); 297 if (source != fOutput.source) 298 return B_MEDIA_BAD_SOURCE; 299 300 return B_ERROR; 301 } 302 303 status_t 304 VideoProducer::GetNextOutput(int32 *cookie, media_output *out_output) 305 { 306 if (!out_output) 307 return B_BAD_VALUE; 308 309 if ((*cookie) != 0) 310 return B_BAD_INDEX; 311 312 *out_output = fOutput; 313 (*cookie)++; 314 return B_OK; 315 } 316 317 status_t 318 VideoProducer::DisposeOutputCookie(int32 cookie) 319 { 320 TOUCH(cookie); 321 322 return B_OK; 323 } 324 325 status_t 326 VideoProducer::SetBufferGroup(const media_source &for_source, 327 BBufferGroup *group) 328 { 329 TOUCH(for_source); TOUCH(group); 330 331 return B_ERROR; 332 } 333 334 status_t 335 VideoProducer::VideoClippingChanged(const media_source &for_source, 336 int16 num_shorts, int16 *clip_data, 337 const media_video_display_info &display, int32 *_deprecated_) 338 { 339 TOUCH(for_source); TOUCH(num_shorts); TOUCH(clip_data); 340 TOUCH(display); TOUCH(_deprecated_); 341 342 return B_ERROR; 343 } 344 345 status_t 346 VideoProducer::GetLatency(bigtime_t *out_latency) 347 { 348 *out_latency = EventLatency() + SchedulingLatency(); 349 return B_OK; 350 } 351 352 status_t 353 VideoProducer::PrepareToConnect(const media_source &source, 354 const media_destination &destination, media_format *format, 355 media_source *out_source, char *out_name) 356 { 357 PRINTF(1, ("PrepareToConnect() %ldx%ld\n", \ 358 format->u.raw_video.display.line_width, \ 359 format->u.raw_video.display.line_count)); 360 361 if (fConnected) { 362 PRINTF(0, ("PrepareToConnect: Already connected\n")); 363 return EALREADY; 364 } 365 366 if (source != fOutput.source) 367 return B_MEDIA_BAD_SOURCE; 368 369 if (fOutput.destination != media_destination::null) 370 return B_MEDIA_ALREADY_CONNECTED; 371 372 /* The format parameter comes in with the suggested format, and may be 373 * specialized as desired by the node */ 374 if (!format_is_compatible(*format, fOutput.format)) { 375 *format = fOutput.format; 376 return B_MEDIA_BAD_FORMAT; 377 } 378 379 if (format->u.raw_video.display.line_width == 0) 380 format->u.raw_video.display.line_width = 320; 381 if (format->u.raw_video.display.line_count == 0) 382 format->u.raw_video.display.line_count = 240; 383 if (format->u.raw_video.field_rate == 0) 384 format->u.raw_video.field_rate = 29.97f; 385 386 *out_source = fOutput.source; 387 strcpy(out_name, fOutput.name); 388 389 fOutput.destination = destination; 390 391 return B_OK; 392 } 393 394 void 395 VideoProducer::Connect(status_t error, const media_source &source, 396 const media_destination &destination, const media_format &format, 397 char *io_name) 398 { 399 PRINTF(1, ("Connect() %ldx%ld\n", \ 400 format.u.raw_video.display.line_width, \ 401 format.u.raw_video.display.line_count)); 402 403 if (fConnected) { 404 PRINTF(0, ("Connect: Already connected\n")); 405 return; 406 } 407 408 if ( (source != fOutput.source) || (error < B_OK) || 409 !const_cast<media_format *>(&format)->Matches(&fOutput.format)) { 410 PRINTF(1, ("Connect: Connect error\n")); 411 return; 412 } 413 414 fOutput.destination = destination; 415 strcpy(io_name, fOutput.name); 416 417 if (fOutput.format.u.raw_video.field_rate != 0.0f) { 418 fPerformanceTimeBase = fPerformanceTimeBase + 419 (bigtime_t) 420 ((fFrame - fFrameBase) * 421 (1000000 / fOutput.format.u.raw_video.field_rate)); 422 fFrameBase = fFrame; 423 } 424 425 fConnectedFormat = format.u.raw_video; 426 427 /* get the latency */ 428 bigtime_t latency = 0; 429 media_node_id tsID = 0; 430 FindLatencyFor(fOutput.destination, &latency, &tsID); 431 #define NODE_LATENCY 1000 432 SetEventLatency(latency + NODE_LATENCY); 433 434 uint32 *buffer, *p, f = 3; 435 p = buffer = (uint32 *)malloc(4 * fConnectedFormat.display.line_count * 436 fConnectedFormat.display.line_width); 437 if (!buffer) { 438 PRINTF(0, ("Connect: Out of memory\n")); 439 return; 440 } 441 bigtime_t now = system_time(); 442 for (int y = 0; y < (int)fConnectedFormat.display.line_count; y++) 443 for (int x = 0; x < (int)fConnectedFormat.display.line_width; x++) 444 *(p++) = ((((x+y)^0^x)+f) & 0xff) * (0x01010101 & fColor); 445 fProcessingLatency = system_time() - now; 446 free(buffer); 447 448 /* Create the buffer group */ 449 fBufferGroup = new BBufferGroup(4 * fConnectedFormat.display.line_width * 450 fConnectedFormat.display.line_count, 8); 451 if (fBufferGroup->InitCheck() < B_OK) { 452 delete fBufferGroup; 453 fBufferGroup = NULL; 454 return; 455 } 456 457 fConnected = true; 458 fEnabled = true; 459 460 /* Tell frame generation thread to recalculate delay value */ 461 release_sem(fFrameSync); 462 } 463 464 void 465 VideoProducer::Disconnect(const media_source &source, 466 const media_destination &destination) 467 { 468 PRINTF(1, ("Disconnect()\n")); 469 470 if (!fConnected) { 471 PRINTF(0, ("Disconnect: Not connected\n")); 472 return; 473 } 474 475 if ((source != fOutput.source) || (destination != fOutput.destination)) { 476 PRINTF(0, ("Disconnect: Bad source and/or destination\n")); 477 return; 478 } 479 480 fEnabled = false; 481 fOutput.destination = media_destination::null; 482 483 fLock.Lock(); 484 delete fBufferGroup; 485 fBufferGroup = NULL; 486 fLock.Unlock(); 487 488 fConnected = false; 489 } 490 491 void 492 VideoProducer::LateNoticeReceived(const media_source &source, 493 bigtime_t how_much, bigtime_t performance_time) 494 { 495 TOUCH(source); TOUCH(how_much); TOUCH(performance_time); 496 } 497 498 void 499 VideoProducer::EnableOutput(const media_source &source, bool enabled, 500 int32 *_deprecated_) 501 { 502 TOUCH(_deprecated_); 503 504 if (source != fOutput.source) 505 return; 506 507 fEnabled = enabled; 508 } 509 510 status_t 511 VideoProducer::SetPlayRate(int32 numer, int32 denom) 512 { 513 TOUCH(numer); TOUCH(denom); 514 515 return B_ERROR; 516 } 517 518 void 519 VideoProducer::AdditionalBufferRequested(const media_source &source, 520 media_buffer_id prev_buffer, bigtime_t prev_time, 521 const media_seek_tag *prev_tag) 522 { 523 TOUCH(source); TOUCH(prev_buffer); TOUCH(prev_time); TOUCH(prev_tag); 524 } 525 526 void 527 VideoProducer::LatencyChanged(const media_source &source, 528 const media_destination &destination, bigtime_t new_latency, 529 uint32 flags) 530 { 531 TOUCH(source); TOUCH(destination); TOUCH(new_latency); TOUCH(flags); 532 } 533 534 /* BControllable */ 535 536 status_t 537 VideoProducer::GetParameterValue( 538 int32 id, bigtime_t *last_change, void *value, size_t *size) 539 { 540 if (id != P_COLOR) 541 return B_BAD_VALUE; 542 543 *last_change = fLastColorChange; 544 *size = sizeof(uint32); 545 *((uint32 *)value) = fColor; 546 547 return B_OK; 548 } 549 550 void 551 VideoProducer::SetParameterValue( 552 int32 id, bigtime_t when, const void *value, size_t size) 553 { 554 if ((id != P_COLOR) || !value || (size != sizeof(uint32))) 555 return; 556 557 if (*(uint32 *)value == fColor) 558 return; 559 560 fColor = *(uint32 *)value; 561 fLastColorChange = when; 562 563 BroadcastNewParameterValue( 564 fLastColorChange, P_COLOR, &fColor, sizeof(fColor)); 565 } 566 567 status_t 568 VideoProducer::StartControlPanel(BMessenger *out_messenger) 569 { 570 return BControllable::StartControlPanel(out_messenger); 571 } 572 573 /* VideoProducer */ 574 575 void 576 VideoProducer::HandleStart(bigtime_t performance_time) 577 { 578 /* Start producing frames, even if the output hasn't been connected yet. */ 579 580 PRINTF(1, ("HandleStart(%Ld)\n", performance_time)); 581 582 if (fRunning) { 583 PRINTF(-1, ("HandleStart: Node already started\n")); 584 return; 585 } 586 587 fFrame = 0; 588 fFrameBase = 0; 589 fPerformanceTimeBase = performance_time; 590 591 fFrameSync = create_sem(0, "frame synchronization"); 592 if (fFrameSync < B_OK) 593 goto err1; 594 595 fThread = spawn_thread(_frame_generator_, "frame generator", 596 B_NORMAL_PRIORITY, this); 597 if (fThread < B_OK) 598 goto err2; 599 600 resume_thread(fThread); 601 602 fRunning = true; 603 return; 604 605 err2: 606 delete_sem(fFrameSync); 607 err1: 608 return; 609 } 610 611 void 612 VideoProducer::HandleStop(void) 613 { 614 PRINTF(1, ("HandleStop()\n")); 615 616 if (!fRunning) { 617 PRINTF(-1, ("HandleStop: Node isn't running\n")); 618 return; 619 } 620 621 delete_sem(fFrameSync); 622 wait_for_thread(fThread, &fThread); 623 624 fRunning = false; 625 } 626 627 void 628 VideoProducer::HandleTimeWarp(bigtime_t performance_time) 629 { 630 fPerformanceTimeBase = performance_time; 631 fFrameBase = fFrame; 632 633 /* Tell frame generation thread to recalculate delay value */ 634 release_sem(fFrameSync); 635 } 636 637 void 638 VideoProducer::HandleSeek(bigtime_t performance_time) 639 { 640 fPerformanceTimeBase = performance_time; 641 fFrameBase = fFrame; 642 643 /* Tell frame generation thread to recalculate delay value */ 644 release_sem(fFrameSync); 645 } 646 647 /* The following functions form the thread that generates frames. You should 648 * replace this with the code that interfaces to your hardware. */ 649 int32 650 VideoProducer::FrameGenerator() 651 { 652 bigtime_t wait_until = system_time(); 653 654 while (1) { 655 status_t err = acquire_sem_etc(fFrameSync, 1, B_ABSOLUTE_TIMEOUT, 656 wait_until); 657 658 /* The only acceptable responses are B_OK and B_TIMED_OUT. Everything 659 * else means the thread should quit. Deleting the semaphore, as in 660 * VideoProducer::HandleStop(), will trigger this behavior. */ 661 if ((err != B_OK) && (err != B_TIMED_OUT)) 662 break; 663 664 fFrame++; 665 666 /* Recalculate the time until the thread should wake up to begin 667 * processing the next frame. Subtract fProcessingLatency so that 668 * the frame is sent in time. */ 669 wait_until = TimeSource()->RealTimeFor(fPerformanceTimeBase + 670 (bigtime_t) 671 ((fFrame - fFrameBase) * 672 (1000000 / fConnectedFormat.field_rate)), 0) - 673 fProcessingLatency; 674 675 /* Drop frame if it's at least a frame late */ 676 if (wait_until < system_time()) 677 continue; 678 679 /* If the semaphore was acquired successfully, it means something 680 * changed the timing information (see VideoProducer::Connect()) and 681 * so the thread should go back to sleep until the newly-calculated 682 * wait_until time. */ 683 if (err == B_OK) 684 continue; 685 686 /* Send buffers only if the node is running and the output has been 687 * enabled */ 688 if (!fRunning || !fEnabled) 689 continue; 690 691 BAutolock _(fLock); 692 693 /* Fetch a buffer from the buffer group */ 694 BBuffer *buffer = fBufferGroup->RequestBuffer( 695 4 * fConnectedFormat.display.line_width * 696 fConnectedFormat.display.line_count, 0LL); 697 if (!buffer) 698 continue; 699 700 /* Fill out the details about this buffer. */ 701 media_header *h = buffer->Header(); 702 h->type = B_MEDIA_RAW_VIDEO; 703 h->time_source = TimeSource()->ID(); 704 h->size_used = 4 * fConnectedFormat.display.line_width * 705 fConnectedFormat.display.line_count; 706 /* For a buffer originating from a device, you might want to calculate 707 * this based on the PerformanceTimeFor the time your buffer arrived at 708 * the hardware (plus any applicable adjustments). */ 709 h->start_time = fPerformanceTimeBase + 710 (bigtime_t) 711 ((fFrame - fFrameBase) * 712 (1000000 / fConnectedFormat.field_rate)); 713 h->file_pos = 0; 714 h->orig_size = 0; 715 h->data_offset = 0; 716 h->u.raw_video.field_gamma = 1.0; 717 h->u.raw_video.field_sequence = fFrame; 718 h->u.raw_video.field_number = 0; 719 h->u.raw_video.pulldown_number = 0; 720 h->u.raw_video.first_active_line = 1; 721 h->u.raw_video.line_count = fConnectedFormat.display.line_count; 722 723 if (fColor == 0xff000000) { 724 // display a gray block that moves 725 uint32 *p = (uint32 *)buffer->Data(); 726 for (int y = 0; y < (int)fConnectedFormat.display.line_count; y++) 727 for (int x = 0; x < (int)fConnectedFormat.display.line_width; x++) { 728 if (x > (fFrame & 0xff) && x < (fFrame & 0xff) + 60 && y > 90 && y < 150) { 729 *(p++) = 0xff777777; 730 } else { 731 *(p++) = 0x00000000; 732 } 733 } 734 } else { 735 736 /* Fill in a pattern */ 737 uint32 *p = (uint32 *)buffer->Data(); 738 for (int y = 0; y < (int)fConnectedFormat.display.line_count; y++) 739 for (int x = 0; x < (int)fConnectedFormat.display.line_width; x++) 740 *(p++) = ((((x+y)^0^x)+fFrame) & 0xff) * (0x01010101 & fColor); 741 } 742 743 /* Send the buffer on down to the consumer */ 744 if (SendBuffer(buffer, fOutput.source, fOutput.destination) < B_OK) { 745 PRINTF(-1, ("FrameGenerator: Error sending buffer\n")); 746 /* If there is a problem sending the buffer, return it to its 747 * buffer group. */ 748 buffer->Recycle(); 749 } 750 } 751 752 return B_OK; 753 } 754 755 int32 756 VideoProducer::_frame_generator_(void *data) 757 { 758 return ((VideoProducer *)data)->FrameGenerator(); 759 } 760