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