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 37VideoProducer::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 65VideoProducer::~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 79port_id 80VideoProducer::ControlPort() const 81{ 82 return BMediaNode::ControlPort(); 83} 84 85BMediaAddOn * 86VideoProducer::AddOn(int32 *internal_id) const 87{ 88 if (internal_id) 89 *internal_id = fInternalID; 90 return fAddOn; 91} 92 93status_t 94VideoProducer::HandleMessage(int32 message, const void *data, size_t size) 95{ 96 return B_ERROR; 97} 98 99void 100VideoProducer::Preroll() 101{ 102 /* This hook may be called before the node is started to give the hardware 103 * a chance to start. */ 104} 105 106void 107VideoProducer::SetTimeSource(BTimeSource *time_source) 108{ 109 /* Tell frame generation thread to recalculate delay value */ 110 release_sem(fFrameSync); 111} 112 113status_t 114VideoProducer::RequestCompleted(const media_request_info &info) 115{ 116 return BMediaNode::RequestCompleted(info); 117} 118 119/* BMediaEventLooper */ 120 121void 122VideoProducer::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 162void 163VideoProducer::Start(bigtime_t performance_time) 164{ 165 BMediaEventLooper::Start(performance_time); 166} 167 168void 169VideoProducer::Stop(bigtime_t performance_time, bool immediate) 170{ 171 BMediaEventLooper::Stop(performance_time, immediate); 172} 173 174void 175VideoProducer::Seek(bigtime_t media_time, bigtime_t performance_time) 176{ 177 BMediaEventLooper::Seek(media_time, performance_time); 178} 179 180void 181VideoProducer::TimeWarp(bigtime_t at_real_time, bigtime_t to_performance_time) 182{ 183 BMediaEventLooper::TimeWarp(at_real_time, to_performance_time); 184} 185 186status_t 187VideoProducer::AddTimer(bigtime_t at_performance_time, int32 cookie) 188{ 189 return BMediaEventLooper::AddTimer(at_performance_time, cookie); 190} 191 192void 193VideoProducer::SetRunMode(run_mode mode) 194{ 195 BMediaEventLooper::SetRunMode(mode); 196} 197 198void 199VideoProducer::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 228void 229VideoProducer::CleanUpEvent(const media_timed_event *event) 230{ 231 BMediaEventLooper::CleanUpEvent(event); 232} 233 234bigtime_t 235VideoProducer::OfflineTime() 236{ 237 return BMediaEventLooper::OfflineTime(); 238} 239 240void 241VideoProducer::ControlLoop() 242{ 243 BMediaEventLooper::ControlLoop(); 244} 245 246status_t 247VideoProducer::DeleteHook(BMediaNode * node) 248{ 249 return BMediaEventLooper::DeleteHook(node); 250} 251 252/* BBufferProducer */ 253 254status_t 255VideoProducer::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 274status_t 275VideoProducer::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 292status_t 293VideoProducer::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 304status_t 305VideoProducer::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 318status_t 319VideoProducer::DisposeOutputCookie(int32 cookie) 320{ 321 TOUCH(cookie); 322 323 return B_OK; 324} 325 326status_t 327VideoProducer::SetBufferGroup(const media_source &for_source, 328 BBufferGroup *group) 329{ 330 TOUCH(for_source); TOUCH(group); 331 332 return B_ERROR; 333} 334 335status_t 336VideoProducer::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 346status_t 347VideoProducer::GetLatency(bigtime_t *out_latency) 348{ 349 *out_latency = EventLatency() + SchedulingLatency(); 350 return B_OK; 351} 352 353status_t 354VideoProducer::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 395void 396VideoProducer::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 465void 466VideoProducer::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 492void 493VideoProducer::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 499void 500VideoProducer::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 511status_t 512VideoProducer::SetPlayRate(int32 numer, int32 denom) 513{ 514 TOUCH(numer); TOUCH(denom); 515 516 return B_ERROR; 517} 518 519void 520VideoProducer::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 527void 528VideoProducer::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 537status_t 538VideoProducer::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 551void 552VideoProducer::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 568status_t 569VideoProducer::StartControlPanel(BMessenger *out_messenger) 570{ 571 return BControllable::StartControlPanel(out_messenger); 572} 573 574/* VideoProducer */ 575 576void 577VideoProducer::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 606err2: 607 delete_sem(fFrameSync); 608err1: 609 return; 610} 611 612void 613VideoProducer::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 628void 629VideoProducer::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 638void 639VideoProducer::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. */ 650int32 651VideoProducer::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 756int32 757VideoProducer::_frame_generator_(void *data) 758{ 759 return ((VideoProducer *)data)->FrameGenerator(); 760} 761