1 /****************************************************************************** 2 / 3 / File: RadeonProducer.cpp 4 / 5 / Description: ATI Radeon Video Producer media node. 6 / 7 / Copyright 2001, Carlos Hasan 8 / 9 *******************************************************************************/ 10 11 #include <fcntl.h> 12 #include <malloc.h> 13 #include <math.h> 14 #include <stdio.h> 15 #include <string.h> 16 #include <sys/uio.h> 17 #include <unistd.h> 18 #include <scheduler.h> 19 20 #include <media/Buffer.h> 21 #include <media/BufferGroup.h> 22 #include <media/ParameterWeb.h> 23 #include <media/TimeSource.h> 24 25 #include <support/Autolock.h> 26 #include <support/Debug.h> 27 28 #include <app/Message.h> 29 30 #include "RadeonAddOn.h" 31 #include "VideoIn.h" 32 33 #define DPRINT(args) { PRINT(("\x1b[0;30;35m")); PRINT(args); PRINT(("\x1b[0;30;47m")); } 34 35 #define TOUCH(x) ((void)(x)) 36 37 #define PRINTF(a,b) \ 38 do { \ 39 if (a < 2) { \ 40 printf("CRadeonProducer::"); \ 41 printf b; \ 42 } \ 43 } while (0) 44 45 #include "RadeonProducer.h" 46 47 // convert Be video standard to video-in standard; 48 // Be forgot some standards and define code 7 and 8 to be MPEG1/2, i.e. 49 // didn't leave any space for enhancements, so I chose to use 101 and up; 50 // this way, we get a scattered list of video standards, needing special 51 // functions to convert to scattered Be-code to the compact video-in-code 52 video_in_standard BeToVideoInStandard( int32 be_standard ) 53 { 54 55 DPRINT(("BeToVideoInStandard %d \n", be_standard)); 56 switch( be_standard ) { 57 case 1: return C_VIDEO_IN_NTSC; 58 case 2: return C_VIDEO_IN_NTSC_JAPAN; 59 case 3: return C_VIDEO_IN_PAL_BDGHI; 60 case 4: return C_VIDEO_IN_PAL_M; 61 case 5: return C_VIDEO_IN_PAL_N; 62 case 6: return C_VIDEO_IN_SECAM; 63 case 101: return C_VIDEO_IN_NTSC_443; 64 case 102: return C_VIDEO_IN_PAL_60; 65 case 103: return C_VIDEO_IN_PAL_NC; 66 default: return C_VIDEO_IN_NTSC; 67 } 68 } 69 70 int32 VideoInStandardToBe( video_in_standard standard ) 71 { 72 DPRINT(("VideoInStandardToBe %d \n", standard)); 73 switch( standard ) { 74 case C_VIDEO_IN_NTSC: return 1; 75 case C_VIDEO_IN_NTSC_JAPAN: return 2; 76 case C_VIDEO_IN_PAL_BDGHI: return 3; 77 case C_VIDEO_IN_PAL_M: return 4; 78 case C_VIDEO_IN_PAL_N: return 5; 79 case C_VIDEO_IN_SECAM: return 6; 80 case C_VIDEO_IN_NTSC_443: return 101; 81 case C_VIDEO_IN_PAL_60: return 102; 82 case C_VIDEO_IN_PAL_NC: return 103; 83 default: return 1; 84 } 85 } 86 87 status_t CRadeonProducer::FindInt32( 88 BMessage *config, EOptions option, int32 min_value, int32 max_value, 89 int32 default_value, int32 *value ) 90 { 91 char name[5]; 92 status_t res; 93 94 *value = default_value; 95 96 *(int32 *)name = option; 97 name[4] = 0; 98 99 res = config->FindInt32( name, value ); 100 if( res == B_NAME_NOT_FOUND ) 101 return B_OK; 102 103 if( res != B_OK ) 104 return res; 105 106 *value = MAX( *value, min_value ); 107 *value = MIN( *value, max_value ); 108 return B_OK; 109 } 110 111 CRadeonProducer::CRadeonProducer( 112 CRadeonAddOn *addon, const char *name, const char *device_name, int32 internal_id, 113 BMessage *config ) 114 : BMediaNode(name), 115 BMediaEventLooper(), 116 BBufferProducer(B_MEDIA_RAW_VIDEO), 117 BControllable(), 118 fVideoIn( device_name ) 119 { 120 DPRINT(("CRadeonProducer::CRadeonProducer()\n")); 121 122 fInitStatus = B_NO_INIT; 123 124 fInternalID = internal_id; 125 fAddOn = addon; 126 127 fBufferGroup = NULL; 128 //fUsedBufferGroup = NULL; 129 130 fProcessingLatency = 0LL; 131 132 //fConnected = false; 133 fEnabled = true; 134 135 AddNodeKind(B_PHYSICAL_INPUT); 136 137 if( fVideoIn.InitCheck() == B_OK ) 138 fInitStatus = B_OK; 139 140 fSource = ((fVideoIn.Capabilities() & C_VIDEO_IN_HAS_TUNER) != 0 ? C_VIDEO_IN_TUNER : C_VIDEO_IN_COMPOSITE); 141 fStandard = C_VIDEO_IN_NTSC; 142 fMode = C_VIDEO_IN_WEAVE; 143 fFormat = B_RGB32; 144 fResolution = 4; 145 fTuner = 25; 146 fBrightness = 0; 147 fContrast = 0; 148 fSaturation = 0; 149 fHue = 0; 150 fSharpness = 0; 151 152 if( config != NULL ) { 153 status_t res; 154 int32 standard; 155 156 if( (res = FindInt32( config, P_SOURCE, 0, C_VIDEO_IN_SOURCE_MAX, 157 (fVideoIn.Capabilities() & C_VIDEO_IN_HAS_TUNER) != 0 ? C_VIDEO_IN_TUNER : C_VIDEO_IN_COMPOSITE, 158 &fSource )) != B_OK || 159 (res = FindInt32( config, P_STANDARD, 0, C_VIDEO_IN_STANDARD_MAX, 160 C_VIDEO_IN_NTSC, &standard )) != B_OK || 161 (res = FindInt32( config, P_MODE, 0, C_VIDEO_IN_CAPTURE_MODE_MAX, 162 C_VIDEO_IN_FIELD, &fMode )) != B_OK || 163 (res = FindInt32( config, P_FORMAT, -2147483647L-1, 2147483647L, 164 B_RGB16, &fFormat )) != B_OK || 165 (res = FindInt32( config, P_RESOLUTION, 0, C_RESOLUTION_MAX, 166 4, &fResolution )) != B_OK || 167 (res = FindInt32( config, P_TUNER, 0, C_CHANNEL_MAX, 168 25, &fTuner )) != B_OK || 169 (res = FindInt32( config, P_BRIGHTNESS, -100, +100, 170 0, &fBrightness )) != B_OK || 171 (res = FindInt32( config, P_CONTRAST, 0, 100, 172 0, &fContrast )) != B_OK || 173 (res = FindInt32( config, P_SATURATION, -100, +100, 174 0, &fSaturation )) != B_OK || 175 (res = FindInt32( config, P_HUE, -90, +90, 176 0, &fHue )) != B_OK || 177 (res = FindInt32( config, P_SHARPNESS, 0, 15, 178 0, &fSharpness )) != B_OK ) 179 { 180 DPRINT(("Corrupted settings (%s)\n", strerror( res ))); 181 } 182 183 // standard is stored as internal code (which has no "holes" in its numbering); 184 // time to convert it 185 // if this value comes from our setup web is it not already linear? 186 fStandard = VideoInStandardToBe( (video_in_standard)standard ); 187 188 // if there is no tuner, force composite input 189 if( (fVideoIn.Capabilities() & C_VIDEO_IN_HAS_TUNER) == 0 ) 190 fSource = C_VIDEO_IN_COMPOSITE; 191 192 // format ids are scattered, so we must verify them manually 193 switch( fFormat ) { 194 case B_YCbCr422: 195 case B_GRAY8: 196 case B_RGB15: 197 case B_RGB16: 198 case B_RGB32: 199 break; 200 default: 201 fFormat = B_RGB16; 202 } 203 } 204 205 fSourceLastChange = 206 fStandardLastChange = 207 fModeLastChange = 208 fFormatLastChange = 209 fResolutionLastChange = 210 fTunerLastChange = 211 fBrightnessLastChange = 212 fContrastLastChange = 213 fSaturationLastChange = 214 fHueLastChange = 215 fSharpnessLastChange = system_time(); 216 217 fOutput.destination = media_destination::null; 218 strcpy(fOutput.name, Name()); 219 220 // we provide interlaced raw video in any format 221 fOutput.format.type = B_MEDIA_RAW_VIDEO; 222 fOutput.format.u.raw_video = media_raw_video_format::wildcard; 223 } 224 225 226 void CRadeonProducer::setupWeb() 227 { 228 /////////// 229 /* Set up the parameter web */ 230 231 // in "kind" value of parameters is "stampTV-compatible", i.e. 232 // if not defined, we use the name used in stampTV 233 BParameterWeb *web = new BParameterWeb(); 234 BParameterGroup *controls = web->MakeGroup("Controls"); 235 BParameterGroup *options = web->MakeGroup("Video"); 236 /*BParameterGroup *audio = web->MakeGroup("Audio");*/ 237 238 BParameterGroup *controls1 = controls->MakeGroup("Controls1"); 239 BParameterGroup *controls2 = controls->MakeGroup("Controls2"); 240 BParameterGroup *controls3 = controls->MakeGroup("Controls3"); 241 242 BParameterGroup *options1 = options->MakeGroup("Options1"); 243 BParameterGroup *options2 = options->MakeGroup("Options2"); 244 245 /*BParameterGroup *audio1 = audio->MakeGroup("Audio1"); 246 BParameterGroup *audio2 = audio->MakeGroup("Audio2");*/ 247 248 249 // Controls 250 if ((fVideoIn.Capabilities() & C_VIDEO_IN_HAS_TUNER) != 0) { 251 // Controls.Channel 252 BDiscreteParameter *tuner = controls1->MakeDiscreteParameter( 253 P_TUNER, B_MEDIA_NO_TYPE, "Channel:", B_TUNER_CHANNEL); 254 255 for (int channel = 0; channel <= 125; channel++) { 256 char buffer[32]; 257 sprintf(buffer, "%d", channel); 258 tuner->AddItem(channel, buffer); 259 } 260 } 261 262 // Controls.Source 263 BDiscreteParameter *source = controls1->MakeDiscreteParameter( 264 P_SOURCE, B_MEDIA_RAW_VIDEO, "Video Input:", "Video Input:"); 265 266 if ((fVideoIn.Capabilities() & C_VIDEO_IN_HAS_TUNER) != 0) 267 source->AddItem(C_VIDEO_IN_TUNER, "Tuner"); 268 if ((fVideoIn.Capabilities() & C_VIDEO_IN_HAS_COMPOSITE) != 0) 269 source->AddItem(C_VIDEO_IN_COMPOSITE, "Composite"); 270 if ((fVideoIn.Capabilities() & C_VIDEO_IN_HAS_SVIDEO) != 0) 271 source->AddItem(C_VIDEO_IN_SVIDEO, "SVideo"); 272 273 // TODO: 274 BDiscreteParameter *source2 = controls1->MakeDiscreteParameter( 275 P_AUDIO_SOURCE, B_MEDIA_RAW_VIDEO, "Audio Input:", "Audio Input:"); 276 if ((fVideoIn.Capabilities() & C_VIDEO_IN_HAS_TUNER) != 0) 277 source2->AddItem(C_VIDEO_IN_TUNER, "Tuner"); 278 /* if ((fVideoIn.Capabilities() & C_VIDEO_IN_HAS_COMPOSITE) != 0) 279 source2->AddItem(C_VIDEO_IN_COMPOSITE, "Composite"); 280 if ((fVideoIn.Capabilities() & C_VIDEO_IN_HAS_SVIDEO) != 0) 281 source2->AddItem(C_VIDEO_IN_SVIDEO, "SVideo"); 282 */ 283 284 // Controls.Brightness/Contrast/Saturation/Hue 285 controls2->MakeContinuousParameter(P_BRIGHTNESS, B_MEDIA_RAW_VIDEO,"Brightness", "BRIGHTNESS", "", -100, 100, 1); 286 controls2->MakeContinuousParameter(P_CONTRAST, B_MEDIA_RAW_VIDEO, "Contrast", "CONTRAST", "", 0, 100, 1); 287 controls2->MakeContinuousParameter(P_SHARPNESS, B_MEDIA_RAW_VIDEO, "Sharpness", B_LEVEL, "dB", 0, 15, 1); 288 289 controls3->MakeContinuousParameter(P_SATURATION, B_MEDIA_RAW_VIDEO, "Saturation", "SATURATION", "", -100, 100, 1); 290 controls3->MakeContinuousParameter(P_HUE, B_MEDIA_RAW_VIDEO, "Hue", B_LEVEL, "°", -90, 90, 1); 291 292 293 // Options.Resolution 294 BDiscreteParameter *resolution = options1->MakeDiscreteParameter( 295 P_RESOLUTION, B_MEDIA_RAW_VIDEO, "Default Image Size:", B_RESOLUTION); 296 297 resolution->AddItem(6, "768x576"); 298 resolution->AddItem(5, "720x576"); 299 resolution->AddItem(4, "720x480"); 300 resolution->AddItem(0, "640x480"); 301 resolution->AddItem(3, "480x360"); 302 resolution->AddItem(1, "320x240"); 303 resolution->AddItem(2, "160x120"); 304 305 // Options.Format 306 BDiscreteParameter *format = options1->MakeDiscreteParameter( 307 P_FORMAT, B_MEDIA_RAW_VIDEO, "Default Colors:", B_COLOR_SPACE); 308 309 format->AddItem(B_YCbCr422, "YCbCr422 (fastest)"); 310 format->AddItem(B_GRAY8, "8 Bits/Pixel (gray)"); 311 format->AddItem(B_RGB15, "15 Bits/Pixel"); 312 format->AddItem(B_RGB16, "16 Bits/Pixel"); 313 format->AddItem(B_RGB32, "32 Bits/Pixel"); 314 315 // Options.Standard 316 BDiscreteParameter *standard = options2->MakeDiscreteParameter( 317 P_STANDARD, B_MEDIA_RAW_VIDEO, "Video Format:", B_VIDEO_FORMAT); 318 319 standard->AddItem(1, "NTSC"); 320 standard->AddItem(2, "NTSC Japan"); 321 standard->AddItem(101, "NTSC 443"); 322 standard->AddItem(4, "PAL M"); 323 standard->AddItem(3, "PAL BDGHI"); 324 standard->AddItem(5, "PAL N"); 325 standard->AddItem(102, "PAL 60"); 326 standard->AddItem(103, "PAL NC"); 327 standard->AddItem(6, "SECAM"); 328 329 // Options.Mode 330 BDiscreteParameter *mode = options2->MakeDiscreteParameter( 331 P_MODE, B_MEDIA_RAW_VIDEO, "Video Interlace:", B_GENERIC); 332 333 mode->AddItem(C_VIDEO_IN_FIELD, "Field"); 334 mode->AddItem(C_VIDEO_IN_BOB, "Bob"); 335 mode->AddItem(C_VIDEO_IN_WEAVE, "Weave"); 336 337 338 // TODO: 339 /* 340 BDiscreteParameter *standard2 = audio1->MakeDiscreteParameter( 341 P_AUDIO_FORMAT, B_MEDIA_RAW_VIDEO, "Audio Format:", B_VIDEO_FORMAT); 342 standard2->AddItem(0, "Stereo"); 343 standard2->AddItem(1, "Mono"); 344 standard2->AddItem(2, "NICAM"); 345 346 BDiscreteParameter *audioSource = audio1->MakeDiscreteParameter( 347 P_AUDIO_FORMAT, B_MEDIA_RAW_VIDEO, "Audio Source:", B_VIDEO_FORMAT); 348 audioSource->AddItem(0, "FM"); 349 audioSource->AddItem(1, "Stereo"); 350 audioSource->AddItem(2, "SCART"); 351 audioSource->AddItem(3, "Language A"); 352 audioSource->AddItem(4, "Language B"); 353 354 BDiscreteParameter *matrix= audio2->MakeDiscreteParameter( 355 P_AUDIO_FORMAT, B_MEDIA_RAW_VIDEO, "Audio Matrix:", B_VIDEO_FORMAT); 356 matrix->AddItem(0, "Sound A"); 357 matrix->AddItem(1, "Sound B"); 358 matrix->AddItem(2, "Stereo"); 359 matrix->AddItem(3, "Mono");*/ 360 361 /* After this call, the BControllable owns the BParameterWeb object and 362 * will delete it for you */ 363 SetParameterWeb(web); 364 ///////// 365 } 366 367 CRadeonProducer::~CRadeonProducer() 368 { 369 DPRINT(("CRadeonProducer::~CRadeonProducer()\n")); 370 371 if (fInitStatus == B_OK) { 372 /* Clean up after ourselves, in case the application didn't make us 373 * do so. */ 374 /*if (fConnected) 375 Disconnect(fOutput.source, fOutput.destination);*/ 376 377 HandleStop(); 378 } 379 380 delete fBufferGroup; 381 fBufferGroup = NULL; 382 383 BMessage settings; 384 385 GetConfiguration( &settings ); 386 387 fAddOn->UnregisterNode( this, &settings ); 388 389 Quit(); 390 } 391 392 /* BMediaNode */ 393 394 port_id 395 CRadeonProducer::ControlPort() const 396 { 397 return BMediaNode::ControlPort(); 398 } 399 400 BMediaAddOn * 401 CRadeonProducer::AddOn(int32 *internal_id) const 402 { 403 if (internal_id) 404 *internal_id = fInternalID; 405 return fAddOn; 406 } 407 408 status_t 409 CRadeonProducer::HandleMessage(int32 message, const void *data, size_t size) 410 { 411 //DPRINT(("CRadeonProducer::HandleMessage()\n")); 412 413 switch( message ) { 414 case C_GET_CONFIGURATION: { 415 const configuration_msg *request = (const configuration_msg *)data; 416 BMessage msg; 417 configuration_msg_reply *reply; 418 size_t reply_size, config_size; 419 status_t res; 420 421 if( size < sizeof( configuration_msg )) 422 return B_ERROR; 423 424 res = GetConfiguration( &msg ); 425 426 config_size = msg.FlattenedSize(); 427 reply_size = sizeof( *reply ) + config_size; 428 reply = (configuration_msg_reply *)malloc( reply_size ); 429 if( reply == NULL ) 430 return B_NO_MEMORY; 431 432 reply->res = res; 433 reply->config_size = config_size; 434 msg.Flatten( &reply->config, config_size ); 435 436 write_port_etc( request->reply_port, C_GET_CONFIGURATION_REPLY, 437 reply, reply_size, B_TIMEOUT, 0 ); 438 439 free( reply ); 440 return B_OK; 441 } 442 default: 443 return B_ERROR; 444 // return BControllable::HandleMessage(message, data, size); 445 } 446 } 447 448 void 449 CRadeonProducer::Preroll() 450 { 451 /* This hook may be called before the node is started to give the hardware 452 * a chance to start. */ 453 DPRINT(("CRadeonProducer::Preroll()\n")); 454 } 455 456 void 457 CRadeonProducer::SetTimeSource(BTimeSource *time_source) 458 { 459 DPRINT(("CRadeonProducer::SetTimeSource()\n")); 460 461 /* Tell frame generation thread to recalculate delay value */ 462 //release_sem(fFrameSync); 463 } 464 465 status_t 466 CRadeonProducer::RequestCompleted(const media_request_info &info) 467 { 468 DPRINT(("CRadeonProducer::RequestCompleted()\n")); 469 470 return BMediaNode::RequestCompleted(info); 471 } 472 473 /* BMediaEventLooper */ 474 475 void 476 CRadeonProducer::NodeRegistered() 477 { 478 DPRINT(("CRadeonProducer::NodeRegistered()\n")); 479 480 if (fInitStatus != B_OK) { 481 ReportError(B_NODE_IN_DISTRESS); 482 return; 483 } 484 485 setupWeb(); 486 //!!// 487 488 fOutput.node = Node(); 489 fOutput.source.port = ControlPort(); 490 fOutput.source.id = 0; 491 492 /* Tailor these for the output of your device */ 493 /******** 494 fOutput.format.type = B_MEDIA_RAW_VIDEO; 495 fOutput.format.u.raw_video = media_raw_video_format::wildcard; 496 // fOutput.format.u.raw_video.interlace = 1; 497 fOutput.format.u.raw_video.display.format = B_RGB32; 498 ********/ 499 500 // up to 60 frames (NTSC); jitter less then half a frame; processing time 501 // depends on whether colour space conversion is required, let's say half 502 // a frame in worst case 503 SetPriority( suggest_thread_priority( B_VIDEO_RECORDING, 60, 8000, 8000 )); 504 505 /* Start the BMediaEventLooper control loop running */ 506 Run(); 507 } 508 509 void 510 CRadeonProducer::Start(bigtime_t performance_time) 511 { 512 DPRINT(("CRadeonProducer::Start()\n")); 513 514 BMediaEventLooper::Start(performance_time); 515 } 516 517 void 518 CRadeonProducer::Stop(bigtime_t performance_time, bool immediate) 519 { 520 DPRINT(("CRadeonProducer::Stop()\n")); 521 522 BMediaEventLooper::Stop(performance_time, immediate); 523 } 524 525 void 526 CRadeonProducer::Seek(bigtime_t media_time, bigtime_t performance_time) 527 { 528 DPRINT(("CRadeonProducer::Seek()\n")); 529 530 BMediaEventLooper::Seek(media_time, performance_time); 531 } 532 533 void 534 CRadeonProducer::TimeWarp(bigtime_t at_real_time, bigtime_t to_performance_time) 535 { 536 DPRINT(("CRadeonProducer::TimeWarp()\n")); 537 538 BMediaEventLooper::TimeWarp(at_real_time, to_performance_time); 539 } 540 541 status_t 542 CRadeonProducer::AddTimer(bigtime_t at_performance_time, int32 cookie) 543 { 544 DPRINT(("CRadeonProducer::AddTimer()\n")); 545 546 return BMediaEventLooper::AddTimer(at_performance_time, cookie); 547 } 548 549 void 550 CRadeonProducer::SetRunMode(run_mode mode) 551 { 552 DPRINT(("CRadeonProducer::SetRunMode()\n")); 553 554 BMediaEventLooper::SetRunMode(mode); 555 } 556 557 void 558 CRadeonProducer::HandleEvent(const media_timed_event *event, 559 bigtime_t lateness, bool realTimeEvent) 560 { 561 //DPRINT(("CRadeonProducer::HandleEvent()\n")); 562 563 TOUCH(lateness); TOUCH(realTimeEvent); 564 565 switch(event->type) 566 { 567 case BTimedEventQueue::B_START: 568 HandleStart(event->event_time); 569 break; 570 case BTimedEventQueue::B_STOP: 571 HandleStop(); 572 break; 573 case BTimedEventQueue::B_WARP: 574 HandleTimeWarp(event->bigdata); 575 break; 576 case BTimedEventQueue::B_SEEK: 577 HandleSeek(event->bigdata); 578 break; 579 case BTimedEventQueue::B_HANDLE_BUFFER: 580 case BTimedEventQueue::B_DATA_STATUS: 581 case BTimedEventQueue::B_PARAMETER: 582 default: 583 PRINTF(-1, ("HandleEvent: Unhandled event -- %lx\n", event->type)); 584 break; 585 case BTimedEventQueue::B_HARDWARE: 586 HandleHardware(); 587 break; 588 } 589 590 //DPRINT(("CRadeonProducer::HandleEvent() done\n")); 591 } 592 593 void 594 CRadeonProducer::CleanUpEvent(const media_timed_event *event) 595 { 596 DPRINT(("CRadeonProducer::CleanUpEvent()\n")); 597 598 BMediaEventLooper::CleanUpEvent(event); 599 } 600 601 bigtime_t 602 CRadeonProducer::OfflineTime() 603 { 604 return BMediaEventLooper::OfflineTime(); 605 } 606 607 void 608 CRadeonProducer::ControlLoop() 609 { 610 BMediaEventLooper::ControlLoop(); 611 } 612 613 status_t 614 CRadeonProducer::DeleteHook(BMediaNode * node) 615 { 616 DPRINT(("CRadeonProducer::DeleteHook()\n")); 617 618 return BMediaEventLooper::DeleteHook(node); 619 } 620 621 622 623 /* BBufferProducer */ 624 625 // choose capture mode according to format and update format according to that 626 status_t 627 CRadeonProducer::verifySetMode( media_format *format ) 628 { 629 float frame_rate = fVideoIn.getFrameRate( 630 BeToVideoInStandard( fStandard )) / 1000.0f; 631 632 if( format->u.raw_video.interlace == media_raw_video_format::wildcard.interlace ) { 633 if( format->u.raw_video.field_rate == media_raw_video_format::wildcard.field_rate ) { 634 format->u.raw_video.interlace = fMode == C_VIDEO_IN_BOB ? 2 : 1; 635 format->u.raw_video.field_rate = frame_rate * format->u.raw_video.interlace; 636 } else { 637 if( format->u.raw_video.field_rate == frame_rate ) 638 format->u.raw_video.interlace = 1; 639 else if( format->u.raw_video.field_rate == frame_rate * 2 ) 640 format->u.raw_video.interlace = 2; 641 else { 642 DPRINT(( "Unsupported field rate for active TV standard (%f)\n", 643 format->u.raw_video.field_rate )); 644 return B_MEDIA_BAD_FORMAT; 645 } 646 } 647 648 } else if( format->u.raw_video.interlace == 1 ) { 649 if( format->u.raw_video.field_rate == media_raw_video_format::wildcard.field_rate ) 650 format->u.raw_video.field_rate = frame_rate; 651 else { 652 // don't compare directly - there are rounding errors 653 if( fabs(format->u.raw_video.field_rate - frame_rate) > 0.001 ) { 654 DPRINT(( "Wrong field rate for active TV standard (%f) in progressive mode (expected %f)\n", 655 format->u.raw_video.field_rate - 29.976, 656 frame_rate - 29.976 )); 657 return B_MEDIA_BAD_FORMAT; 658 } 659 } 660 661 } else if( format->u.raw_video.interlace == 2 ) { 662 if( format->u.raw_video.field_rate == media_raw_video_format::wildcard.field_rate ) 663 format->u.raw_video.field_rate = frame_rate * 2; 664 else { 665 if( fabs(format->u.raw_video.field_rate - frame_rate * 2) > 0.001 ) { 666 DPRINT(( "Wrong field rate for active TV standard (%f) in interlace mode\n", 667 format->u.raw_video.field_rate )); 668 return B_MEDIA_BAD_FORMAT; 669 } 670 } 671 672 } else { 673 DPRINT(( "Invalid interlace mode (%d)\n", format->u.raw_video.interlace )); 674 return B_MEDIA_BAD_FORMAT; 675 } 676 677 return B_OK; 678 } 679 680 /* 681 Map BeOS capture mode to internal capture mode. 682 */ 683 int32 684 CRadeonProducer::extractCaptureMode( const media_format *format ) 685 { 686 // if application requests interlace, it always gets BOB; 687 // if is requests non-interlace, it may get WEAVE or FIELD - 688 // if the user selected one of them, we are fine; else, 689 // we always choose WEAVE (could choose FIELD as well, make 690 // it dependant on resolution, but the more magic the more problems) 691 if( format->u.raw_video.interlace == 2 ) 692 return C_VIDEO_IN_BOB; 693 else if( fMode == C_VIDEO_IN_BOB ) 694 return C_VIDEO_IN_WEAVE; 695 else 696 return fMode; 697 } 698 699 // check pixel aspect of format and set it if it's wildcarded 700 status_t 701 CRadeonProducer::verifySetPixelAspect( media_format *format ) 702 { 703 // for simplicity, we always assume 1:1 aspect 704 if( format->u.raw_video.pixel_width_aspect != media_raw_video_format::wildcard.pixel_width_aspect || 705 format->u.raw_video.pixel_height_aspect != media_raw_video_format::wildcard.pixel_height_aspect ) 706 { 707 if( format->u.raw_video.pixel_width_aspect != 708 format->u.raw_video.pixel_height_aspect ) 709 { 710 DPRINT(( "Unsupported pixel aspect (%d:%d)\n", 711 format->u.raw_video.pixel_width_aspect, 712 format->u.raw_video.pixel_height_aspect )); 713 return B_MEDIA_BAD_FORMAT; 714 } 715 } else { 716 format->u.raw_video.pixel_width_aspect = 1; 717 format->u.raw_video.pixel_height_aspect = 1; 718 } 719 720 return B_OK; 721 722 #if 0 723 // we assume 1:2 for interlaced and 1:1 for deinterlaced video 724 // (this is not really true as it depends on TV standard and 725 // resolution, but it should be enough for start) 726 if( format->u.raw_video.pixel_width_aspect != media_raw_video_format::wildcard.pixel_width_aspect || 727 format->u.raw_video.pixel_height_aspect != media_raw_video_format::wildcard.pixel_height_aspect ) 728 { 729 double ratio = mode == C_VIDEO_IN_WEAVE ? 1 : 0.5; 730 731 if( (float)format->u.raw_video.pixel_width_aspect / 732 format->u.raw_video.pixel_height_aspect != ratio ) 733 { 734 DPRINT(( "Unsupported pixel aspect (%d:%d)\n", 735 format->u.raw_video.pixel_width_aspect, 736 format->u.raw_video.pixel_height_aspect )); 737 return B_MEDIA_BAD_FORMAT; 738 } 739 } else { 740 format->u.raw_video.pixel_width_aspect = 1; 741 format->u.raw_video.pixel_height_aspect = 742 mode == C_VIDEO_IN_WEAVE ? 1 : 2; 743 } 744 745 return B_OK; 746 #endif 747 } 748 749 // verify active range defined as format 750 status_t 751 CRadeonProducer::verifyActiveRange( media_format *format ) 752 { 753 CRadeonRect active_rect; 754 755 fVideoIn.getActiveRange( BeToVideoInStandard( fStandard ), active_rect ); 756 757 if( format->u.raw_video.first_active != media_raw_video_format::wildcard.first_active ) { 758 if( (int32)format->u.raw_video.first_active < 0 ) { 759 DPRINT(( "Unsupported first_active (%d)\n", format->u.raw_video.first_active )); 760 return B_MEDIA_BAD_FORMAT; 761 } 762 } 763 764 // don't care about last_active much - some programs set it to number of 765 // captured lines, which is really something different 766 // (I have the feeling, noone really knows how to use this value properly) 767 if( format->u.raw_video.last_active != media_raw_video_format::wildcard.last_active ) { 768 if( format->u.raw_video.last_active >= (uint32)active_rect.Height() ) { 769 DPRINT(( "Unsupported last_active (%d)\n", format->u.raw_video.last_active )); 770 return B_MEDIA_BAD_FORMAT; 771 } 772 } 773 774 return B_OK; 775 } 776 777 778 // set active range in format if yet undefined 779 void 780 CRadeonProducer::setActiveRange( media_format *format ) 781 { 782 CRadeonRect active_rect; 783 784 fVideoIn.getActiveRange( BeToVideoInStandard( fStandard ), active_rect ); 785 786 if( format->u.raw_video.first_active == media_raw_video_format::wildcard.first_active ) 787 format->u.raw_video.first_active = 0; 788 789 if( format->u.raw_video.last_active == media_raw_video_format::wildcard.last_active ) 790 format->u.raw_video.last_active = (uint32)active_rect.Height() - 1; 791 } 792 793 794 // verify requested orientation 795 status_t 796 CRadeonProducer::verifyOrientation( media_format *format ) 797 { 798 if( format->u.raw_video.orientation != media_raw_video_format::wildcard.orientation ) { 799 if( format->u.raw_video.orientation != B_VIDEO_TOP_LEFT_RIGHT ) { 800 DPRINT(( "Unsupported orientation (%d)\n", format->u.raw_video.orientation )); 801 return B_MEDIA_BAD_FORMAT; 802 } 803 } 804 805 return B_OK; 806 } 807 808 809 // set image orientation if yet undefined 810 void 811 CRadeonProducer::setOrientation( media_format *format ) 812 { 813 if( format->u.raw_video.orientation == media_raw_video_format::wildcard.orientation ) 814 format->u.raw_video.orientation = B_VIDEO_TOP_LEFT_RIGHT; 815 } 816 817 818 // verify requested pixel format 819 status_t 820 CRadeonProducer::verifyPixelFormat( media_format *format ) 821 { 822 if( format->u.raw_video.display.format != 823 media_raw_video_format::wildcard.display.format ) 824 { 825 switch( format->u.raw_video.display.format ) { 826 case B_RGB32: 827 case B_RGB16: 828 case B_RGB15: 829 case B_YCbCr422: 830 case B_GRAY8: 831 break; 832 833 default: 834 DPRINT(("Unsupported colour space (%x)\n", 835 format->u.raw_video.display.format )); 836 return B_MEDIA_BAD_FORMAT; 837 } 838 } 839 840 return B_OK; 841 } 842 843 844 // set pixel format to user-defined default if not set yet 845 void 846 CRadeonProducer::setPixelFormat( media_format *format ) 847 { 848 if( format->u.raw_video.display.format == 849 media_raw_video_format::wildcard.display.format ) 850 format->u.raw_video.display.format = (color_space)fFormat; 851 } 852 853 /* 854 Verify video size and set it if undefined. 855 */ 856 status_t 857 CRadeonProducer::verifySetSize( 858 media_format *format, int32 mode, bool set_bytes_per_row ) 859 { 860 CRadeonRect active_rect; 861 862 fVideoIn.getActiveRange( BeToVideoInStandard( fStandard ), active_rect ); 863 864 // width and height must both be defined, else we define it ourself, 865 // i.e. if the application leaves one of them wildcarded, we 866 // set both 867 if( format->u.raw_video.display.line_width != 868 media_raw_video_format::wildcard.display.line_width && 869 format->u.raw_video.display.line_count != 870 media_raw_video_format::wildcard.display.line_count ) 871 { 872 uint32 max_height = active_rect.Height(); 873 874 if( mode != C_VIDEO_IN_WEAVE ) 875 max_height /= 2; 876 877 if( format->u.raw_video.display.line_width > (uint32)active_rect.Width() || 878 format->u.raw_video.display.line_count > max_height ) 879 { 880 DPRINT(("Requested image size is too high (%dx%d)\n", 881 format->u.raw_video.display.line_width, 882 format->u.raw_video.display.line_count)); 883 return B_MEDIA_BAD_FORMAT; 884 } 885 886 // our format converters do up to 8 pixels at a time (grey8); 887 // to be absolutely sure we don't get trouble there, refuse 888 // any width that is not a multiple of 8 889 890 if( (format->u.raw_video.display.line_width & 7) != 0 ) { 891 DPRINT(( "Request image width is not multiple of 8 (%d)\n", 892 format->u.raw_video.display.line_width )); 893 return B_MEDIA_BAD_FORMAT; 894 } 895 896 } else { 897 switch (fResolution) { 898 case 0: 899 format->u.raw_video.display.line_width = 640; 900 format->u.raw_video.display.line_count = 480; 901 break; 902 case 3: 903 format->u.raw_video.display.line_width = 480; 904 format->u.raw_video.display.line_count = 360; 905 break; 906 case 4: 907 format->u.raw_video.display.line_width = 720; 908 format->u.raw_video.display.line_count = 480; 909 break; 910 case 5: 911 format->u.raw_video.display.line_width = 720; 912 format->u.raw_video.display.line_count = 576; 913 break; 914 case 6: 915 format->u.raw_video.display.line_width = 768; 916 format->u.raw_video.display.line_count = 576; 917 break; 918 case 1: 919 format->u.raw_video.display.line_width = 320; 920 format->u.raw_video.display.line_count = 240; 921 break; 922 case 2: 923 format->u.raw_video.display.line_width = 160; 924 format->u.raw_video.display.line_count = 120; 925 break; 926 } 927 928 if( format->u.raw_video.display.line_width > (uint32)active_rect.Width() ) 929 format->u.raw_video.display.line_width = (uint32)active_rect.Width() & ~7; 930 931 if( format->u.raw_video.display.line_count > (uint32)active_rect.Height() ) 932 format->u.raw_video.display.line_count = (uint32)active_rect.Height(); 933 934 // BOB and FIELD modes provide only field, which has half height 935 if( mode != C_VIDEO_IN_WEAVE ) { 936 if( format->u.raw_video.display.line_count > (uint32)active_rect.Height() / 2 ) 937 format->u.raw_video.display.line_count = (uint32)active_rect.Height() / 2; 938 } 939 } 940 941 if( format->u.raw_video.display.format != media_raw_video_format::wildcard.display.format ) { 942 uint32 bytes_per_row; 943 944 switch( format->u.raw_video.display.format ) { 945 case B_RGB32: 946 bytes_per_row = 947 format->u.raw_video.display.line_width * 4; 948 break; 949 950 case B_RGB15: 951 case B_RGB16: 952 case B_YCbCr422: 953 bytes_per_row = 954 format->u.raw_video.display.line_width * 2; 955 break; 956 957 default: 958 bytes_per_row = 959 format->u.raw_video.display.line_width; 960 } 961 962 if( format->u.raw_video.display.bytes_per_row != 963 media_raw_video_format::wildcard.display.bytes_per_row ) 964 { 965 if( format->u.raw_video.display.bytes_per_row < bytes_per_row ) { 966 DPRINT(( "Requested bytes per row are too small", 967 format->u.raw_video.display.bytes_per_row )); 968 return B_MEDIA_BAD_FORMAT; 969 } 970 } else if( set_bytes_per_row ) 971 format->u.raw_video.display.bytes_per_row = bytes_per_row; 972 } 973 974 975 return B_OK; 976 } 977 978 979 // verify "offset" parameters of format 980 status_t 981 CRadeonProducer::verifyFormatOffsets( media_format *format ) 982 { 983 if( format->u.raw_video.display.pixel_offset != 984 media_raw_video_format::wildcard.display.pixel_offset && 985 format->u.raw_video.display.pixel_offset != 0 ) 986 { 987 DPRINT(( "Requested pixel offset is not zero" )); 988 return B_MEDIA_BAD_FORMAT; 989 } 990 991 if( format->u.raw_video.display.line_offset != 992 media_raw_video_format::wildcard.display.line_offset && 993 format->u.raw_video.display.line_offset != 0 ) 994 { 995 DPRINT(( "Requested line offset is not zero" )); 996 return B_MEDIA_BAD_FORMAT; 997 } 998 999 return B_OK; 1000 } 1001 1002 1003 // set "offset" parameters of format if not set yet 1004 void 1005 CRadeonProducer::setFormatOffsets( media_format *format ) 1006 { 1007 if( format->u.raw_video.display.pixel_offset == 1008 media_raw_video_format::wildcard.display.pixel_offset ) 1009 format->u.raw_video.display.pixel_offset = 0; 1010 1011 if( format->u.raw_video.display.line_offset == 1012 media_raw_video_format::wildcard.display.line_offset ) 1013 format->u.raw_video.display.line_offset = 0; 1014 } 1015 1016 1017 // verify "flags" parameter of format 1018 status_t 1019 CRadeonProducer::verifyFormatFlags( media_format *format ) 1020 { 1021 if( format->u.raw_video.display.flags != 1022 media_raw_video_format::wildcard.display.flags && 1023 format->u.raw_video.display.flags != 0 ) 1024 { 1025 DPRINT(( "Requested display flags are not zero" )); 1026 return B_MEDIA_BAD_FORMAT; 1027 } 1028 1029 return B_OK; 1030 } 1031 1032 1033 // set "flags" parameter of format if not set yet 1034 void 1035 CRadeonProducer::setFormatFlags( media_format *format ) 1036 { 1037 if( format->u.raw_video.display.flags == 1038 media_raw_video_format::wildcard.display.flags ) 1039 format->u.raw_video.display.flags = 0; 1040 } 1041 1042 1043 /* 1044 * Fill out all wildcards in a format descriptor. 1045 */ 1046 status_t 1047 CRadeonProducer::finalizeFormat( media_format *format ) 1048 { 1049 if (format->type != B_MEDIA_RAW_VIDEO) 1050 return B_MEDIA_BAD_FORMAT; 1051 1052 status_t res; 1053 1054 res = verifySetMode( format ); 1055 if( res != B_OK ) 1056 return res; 1057 1058 int32 mode = extractCaptureMode( format ); 1059 1060 res = verifyActiveRange( format ); 1061 if( res != B_OK ) 1062 return res; 1063 1064 setActiveRange( format ); 1065 1066 res = verifyOrientation( format ); 1067 if( res != B_OK ) 1068 return res; 1069 1070 res = verifySetPixelAspect( format ); 1071 if( res != B_OK ) 1072 return res; 1073 1074 res = verifyPixelFormat( format ); 1075 if( res != B_OK ) 1076 return res; 1077 1078 setPixelFormat( format); 1079 1080 res = verifySetSize( format, mode, true ); 1081 if( res != B_OK ) 1082 return res; 1083 1084 res = verifyFormatOffsets( format ); 1085 if( res != B_OK ) 1086 return res; 1087 1088 setFormatOffsets( format ); 1089 1090 res = verifyFormatFlags( format ); 1091 if( res != B_OK ) 1092 return res; 1093 1094 setFormatFlags( format ); 1095 return res; 1096 } 1097 1098 1099 /* 1100 * Someone has no idea what format we usually provide and asks us. 1101 * 1102 * It's not really clear whether we are allowed to return wildcards. 1103 */ 1104 status_t 1105 CRadeonProducer::FormatSuggestionRequested( 1106 media_type type, int32 quality, media_format *format) 1107 { 1108 DPRINT(("CRadeonProducer::FormatSuggestionRequested()\n")); 1109 1110 if (type != B_MEDIA_RAW_VIDEO) 1111 return B_MEDIA_BAD_FORMAT; 1112 1113 TOUCH(quality); 1114 1115 format->type = B_MEDIA_RAW_VIDEO; 1116 1117 format->u.raw_video = media_raw_video_format::wildcard; 1118 1119 finalizeFormat( format ); 1120 1121 return B_OK; 1122 } 1123 1124 1125 /* 1126 Initial format proposal as part of a connection establishment. 1127 1128 First, the application defines a format with many wildcards in it; 1129 this format is passed to us, so we can restrict it if necessary; 1130 we should leave as many wildcards as possible, because in the next 1131 step the consumer is asked, and he will not be happy if he has no choice left . 1132 */ 1133 status_t 1134 CRadeonProducer::FormatProposal(const media_source &output, media_format *format) 1135 { 1136 char buffer[256]; 1137 1138 DPRINT(("CRadeonProducer::FormatProposal()\n")); 1139 1140 if( format == NULL ) 1141 return B_BAD_VALUE; 1142 1143 if( output != fOutput.source ) 1144 return B_MEDIA_BAD_SOURCE; 1145 1146 string_for_format(*format, buffer, sizeof(buffer)); 1147 1148 DPRINT(("CRadeonProducer::FormatProposal() - in=%s\n", buffer)); 1149 1150 if( format->type == B_MEDIA_NO_TYPE ) { 1151 // if there is not even a type, set raw video 1152 format->type = B_MEDIA_RAW_VIDEO; 1153 format->u.raw_video = media_raw_video_format::wildcard; 1154 } 1155 1156 if (format->type != B_MEDIA_RAW_VIDEO) 1157 return B_MEDIA_BAD_FORMAT; 1158 1159 status_t res; 1160 1161 // first, choose capture mode, so we know the maximum video size 1162 res = verifySetMode( format ); 1163 if( res != B_OK ) 1164 return res; 1165 1166 int32 mode = extractCaptureMode( format ); 1167 1168 res = verifyActiveRange( format ); 1169 if( res != B_OK ) 1170 return res; 1171 1172 res = verifyOrientation( format ); 1173 if( res != B_OK ) 1174 return res; 1175 1176 setOrientation( format ); 1177 1178 // simple aspect calculation: we always use 1:1, so setting this is easy 1179 res = verifySetPixelAspect( format ); 1180 if( res != B_OK ) 1181 return res; 1182 1183 res = verifyPixelFormat( format ); 1184 if( res != B_OK ) 1185 return res; 1186 1187 // if we don't set it, the consumer usually chooses a stupid format 1188 setPixelFormat( format ); 1189 1190 // verify size and set if if undefined; 1191 // do that now, else the consumer will set it (making the defaults 1192 // set via preferences useless) 1193 // leave bytes_per_lines untouched, though 1194 // (we don't really care but the consumer may have some alignment restrictions) 1195 res = verifySetSize( format, mode, false ); 1196 if( res != B_OK ) 1197 return res; 1198 1199 res = verifyFormatOffsets( format ); 1200 if( res != B_OK ) 1201 return res; 1202 1203 res = verifyFormatFlags( format ); 1204 if( res != B_OK ) 1205 return res; 1206 1207 string_for_format(*format, buffer, sizeof(buffer)); 1208 1209 DPRINT(("CRadeonProducer::FormatProposal() - out=%s\n", buffer)); 1210 1211 return B_OK; 1212 } 1213 1214 status_t 1215 CRadeonProducer::FormatChangeRequested(const media_source &source, 1216 const media_destination &destination, media_format *io_format, 1217 int32 *_deprecated_) 1218 { 1219 DPRINT(("CRadeonProducer::FormatChangeRequested()\n")); 1220 1221 TOUCH(destination); TOUCH(io_format); TOUCH(_deprecated_); 1222 if (source != fOutput.source) 1223 return B_MEDIA_BAD_SOURCE; 1224 1225 return B_ERROR; 1226 } 1227 1228 status_t 1229 CRadeonProducer::GetNextOutput(int32 *cookie, media_output *out_output) 1230 { 1231 DPRINT(("CRadeonProducer::GetNextOutput()\n")); 1232 1233 if (!out_output) 1234 return B_BAD_VALUE; 1235 1236 if ((*cookie) != 0) 1237 return B_BAD_INDEX; 1238 1239 *out_output = fOutput; 1240 (*cookie)++; 1241 return B_OK; 1242 } 1243 1244 status_t 1245 CRadeonProducer::DisposeOutputCookie(int32 cookie) 1246 { 1247 DPRINT(("CRadeonProducer::DisposeOutputCookie()\n")); 1248 1249 TOUCH(cookie); 1250 1251 return B_OK; 1252 } 1253 1254 status_t 1255 CRadeonProducer::SetBufferGroup(const media_source &for_source, 1256 BBufferGroup *group) 1257 { 1258 DPRINT(("CRadeonProducer::SetBufferGroup()\n")); 1259 1260 if( for_source != fOutput.source ) 1261 return B_MEDIA_BAD_SOURCE; 1262 1263 if( group != NULL ) { 1264 delete fBufferGroup; 1265 fBufferGroup = group; 1266 } 1267 1268 return B_OK; 1269 } 1270 1271 status_t 1272 CRadeonProducer::VideoClippingChanged(const media_source &for_source, 1273 int16 num_shorts, int16 *clip_data, 1274 const media_video_display_info &display, int32 *_deprecated_) 1275 { 1276 DPRINT(("CRadeonProducer::VideoClippingChanged()\n")); 1277 1278 TOUCH(for_source); TOUCH(num_shorts); TOUCH(clip_data); 1279 TOUCH(display); TOUCH(_deprecated_); 1280 1281 return B_ERROR; 1282 } 1283 1284 status_t 1285 CRadeonProducer::GetLatency(bigtime_t *out_latency) 1286 { 1287 DPRINT(("CRadeonProducer::GetLatency()\n")); 1288 1289 // delay is one frame for capturing, a scheduling latency, the 1290 // DMA copying, the format conversion and the output nodes latency; 1291 // scheduling, DMA copying and format conversion is summed up in 1292 // fProcessingLatency 1293 bigtime_t capture_latency = (bigtime_t)(1000000.0 / fOutput.format.u.raw_video.field_rate); 1294 1295 // HACK: (see HandleHardware()) 1296 // to be compatible to existing software, we write the ending time of 1297 // capture instead of the beginning time into buffers, thus we 1298 // have no capture delay 1299 capture_latency = 0; 1300 1301 bigtime_t buffer_latency = fProcessingLatency; 1302 BBufferProducer::GetLatency( &buffer_latency ); 1303 1304 *out_latency = SchedulingLatency() + capture_latency + fProcessingLatency + 1305 buffer_latency; 1306 1307 DPRINT(("latency=%lld\n", *out_latency)); 1308 1309 return B_OK; 1310 } 1311 1312 1313 status_t 1314 CRadeonProducer::PrepareToConnect(const media_source &source, 1315 const media_destination &destination, media_format *format, 1316 media_source *out_source, char *out_name) 1317 { 1318 DPRINT(("CRadeonProducer::PrepareToConnect()\n")); 1319 1320 PRINTF(1, ("PrepareToConnect() %ldx%ld\n", \ 1321 format->u.raw_video.display.line_width, \ 1322 format->u.raw_video.display.line_count)); 1323 1324 if (source != fOutput.source) { 1325 DPRINT(("bad source\n")); 1326 return B_MEDIA_BAD_SOURCE; 1327 } 1328 1329 if (fOutput.destination != media_destination::null) { 1330 DPRINT(("already connected\n")); 1331 return B_MEDIA_ALREADY_CONNECTED; 1332 } 1333 1334 char buffer[256]; 1335 string_for_format(*format, buffer, sizeof(buffer)); 1336 DPRINT(("CRadeonProducer::PrepareToConnect() - in=%s\n", buffer)); 1337 1338 status_t res = finalizeFormat( format ); 1339 if( res != B_OK ) 1340 return res; 1341 1342 *out_source = fOutput.source; 1343 strcpy(out_name, fOutput.name); 1344 1345 string_for_format(*format, buffer, sizeof(buffer)); 1346 DPRINT(("CRadeonProducer::PrepareToConnect() - out=%s\n", buffer)); 1347 1348 // reserve connection 1349 fOutput.destination = destination; 1350 1351 DPRINT(("finished\n")); 1352 1353 return B_OK; 1354 } 1355 1356 void 1357 CRadeonProducer::setDefaultBufferGroup() 1358 { 1359 DPRINT(("CRadeonProducer::setDefaultBufferGroup()\n")); 1360 /*delete fBufferGroup; 1361 fBufferGroup = NULL;*/ 1362 if( fBufferGroup != NULL ) { 1363 DPRINT(("Buffer already set\n")); 1364 return; 1365 } 1366 1367 fBufferGroup = new BBufferGroup( 1368 fOutput.format.u.raw_video.display.bytes_per_row * 1369 fOutput.format.u.raw_video.display.line_count, 1370 3, B_ANY_ADDRESS, B_FULL_LOCK ); 1371 1372 if (fBufferGroup->InitCheck() < B_OK) { 1373 delete fBufferGroup; 1374 fBufferGroup = NULL; 1375 return; 1376 } 1377 } 1378 1379 void 1380 CRadeonProducer::startCapturing() 1381 { 1382 if( RunState() != BMediaEventLooper::B_STARTED || 1383 fOutput.destination == media_destination::null ) 1384 return; 1385 1386 fVideoIn.SetChannel(fTuner, C_VIDEO_IN_NTSC); // was hardcoded to NTSC 1387 fVideoIn.SetBrightness(fBrightness); 1388 fVideoIn.SetContrast(fContrast); 1389 fVideoIn.SetSaturation(fSaturation); 1390 fVideoIn.SetHue(fHue); 1391 fVideoIn.SetSharpness(fSharpness); 1392 1393 fVideoIn.Start(video_in_source(fSource), BeToVideoInStandard( fStandard ), 1394 video_in_capture_mode(fCurMode), 1395 fOutput.format.u.raw_video.display.line_width, 1396 fOutput.format.u.raw_video.display.line_count); 1397 1398 char *tmp_buffer; 1399 tmp_buffer = (char *)malloc( 1400 fOutput.format.u.raw_video.display.bytes_per_row * 1401 fOutput.format.u.raw_video.display.line_count ); 1402 1403 int field_sequence; 1404 short field_number; 1405 bigtime_t capture_time; 1406 1407 // do a real capturing to prime everything 1408 fVideoIn.Capture( 1409 tmp_buffer != NULL ? fOutput.format.u.raw_video.display.format : B_NO_COLOR_SPACE, 1410 tmp_buffer, 1411 fOutput.format.u.raw_video.display.bytes_per_row * fOutput.format.u.raw_video.display.line_count, 1412 fOutput.format.u.raw_video.display.bytes_per_row, 1413 &field_sequence, &field_number, &capture_time ); 1414 1415 // capture some frames to be sure there are no pending buffers; 1416 // discard captured data to safe time (we want to catch up and not fall behind) 1417 for( int i = 0; i < 3; ++i ) { 1418 fVideoIn.Capture( 1419 B_NO_COLOR_SPACE, 1420 NULL, 1421 fOutput.format.u.raw_video.display.bytes_per_row * fOutput.format.u.raw_video.display.line_count, 1422 fOutput.format.u.raw_video.display.bytes_per_row, 1423 &field_sequence, &field_number, &capture_time ); 1424 1425 DPRINT(("Captured: %lld, current: %lld\n", capture_time, system_time() )); 1426 } 1427 1428 // do a real capturing to see how long it takes until the 1429 // buffer is ready, i.e. including DMA and colour conversion 1430 fVideoIn.Capture( 1431 tmp_buffer != NULL ? fOutput.format.u.raw_video.display.format : B_NO_COLOR_SPACE, 1432 tmp_buffer, 1433 fOutput.format.u.raw_video.display.bytes_per_row * fOutput.format.u.raw_video.display.line_count, 1434 fOutput.format.u.raw_video.display.bytes_per_row, 1435 &field_sequence, &field_number, &capture_time ); 1436 1437 DPRINT(("Captured: %lld, current: %lld\n", capture_time, system_time() )); 1438 1439 // now we know our internal latency 1440 fProcessingLatency = system_time() - capture_time; 1441 1442 DPRINT(("Processing latency: %dµs\n", fProcessingLatency )); 1443 1444 // store field sequence to start with zero 1445 // (capture-internal field sequence always counts up) 1446 fFieldSequenceBase = field_sequence; 1447 1448 if( tmp_buffer != NULL ) 1449 free(tmp_buffer); 1450 1451 // tell latence MediaEventLooper so it can schedule events properly ahead 1452 bigtime_t total_latency; 1453 1454 GetLatency( &total_latency ); 1455 SetEventLatency( total_latency ); 1456 1457 // Create the buffer group 1458 setDefaultBufferGroup(); 1459 1460 //fUsedBufferGroup = fBufferGroup; 1461 1462 // schedule a capture event after one field's time 1463 RealTimeQueue()->FlushEvents( 0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HARDWARE ); 1464 1465 media_timed_event event( 1466 capture_time + 1000000 / fOutput.format.u.raw_video.field_rate, 1467 BTimedEventQueue::B_HARDWARE); 1468 1469 RealTimeQueue()->AddEvent(event); 1470 } 1471 1472 void 1473 CRadeonProducer::Connect(status_t error, const media_source &source, 1474 const media_destination &destination, const media_format &format, 1475 char *io_name) 1476 { 1477 // we even get called if consumer reported error in AcceptFormat; 1478 // in this case, we must release the source already reserved by 1479 // PrepareToConnect 1480 if( error != B_OK ) { 1481 DPRINT(( "Connect: Consumer returned error (%s) - releasing source", 1482 strerror( error ))); 1483 fOutput.destination = media_destination::null; 1484 return; 1485 } 1486 1487 if( source != fOutput.source ) { 1488 DPRINT(( "Connect: Wrong source specified\n")); 1489 return; 1490 } 1491 1492 fOutput.destination = destination; 1493 fOutput.format = format; 1494 fCurMode = extractCaptureMode( &format ); 1495 1496 char buffer[256]; 1497 string_for_format(format, buffer, sizeof(buffer)); 1498 1499 DPRINT(("CRadeonProducer::Connect() - %s\n", buffer)); 1500 1501 strcpy(io_name, fOutput.name); 1502 1503 startCapturing(); 1504 1505 DPRINT(("CRadeonProducer::Connect() done\n")); 1506 } 1507 1508 void 1509 CRadeonProducer::Disconnect(const media_source &source, 1510 const media_destination &destination) 1511 { 1512 DPRINT(("Disconnect()\n")); 1513 1514 if( source != fOutput.source || destination != fOutput.destination ) { 1515 DPRINT(("Disconnect: Bad source and/or destination\n")); 1516 return; 1517 } 1518 1519 fOutput.destination = media_destination::null; 1520 1521 delete fBufferGroup; 1522 fBufferGroup = NULL; 1523 1524 fVideoIn.Stop(); 1525 1526 RealTimeQueue()->FlushEvents( 0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HARDWARE ); 1527 1528 // reset format to get rid of any connection left-overs 1529 fOutput.format.type = B_MEDIA_RAW_VIDEO; 1530 fOutput.format.u.raw_video = media_raw_video_format::wildcard; 1531 1532 DPRINT(("Disconnect() done\n")); 1533 } 1534 1535 void 1536 CRadeonProducer::LateNoticeReceived(const media_source &source, 1537 bigtime_t how_much, bigtime_t performance_time) 1538 { 1539 DPRINT(("CRadeonProducer::LateNoticeReceived()\n")); 1540 1541 TOUCH(source); TOUCH(how_much); TOUCH(performance_time); 1542 } 1543 1544 void 1545 CRadeonProducer::EnableOutput(const media_source &source, bool enabled, 1546 int32 *_deprecated_) 1547 { 1548 DPRINT(("CRadeonProducer::EnableOutput()\n")); 1549 1550 TOUCH(_deprecated_); 1551 1552 if (source != fOutput.source) 1553 return; 1554 1555 fEnabled = enabled; 1556 } 1557 1558 status_t 1559 CRadeonProducer::SetPlayRate(int32 numer, int32 denom) 1560 { 1561 DPRINT(("CRadeonProducer::SetPlayRate()\n")); 1562 1563 TOUCH(numer); TOUCH(denom); 1564 1565 return B_ERROR; 1566 } 1567 1568 void 1569 CRadeonProducer::AdditionalBufferRequested(const media_source &source, 1570 media_buffer_id prev_buffer, bigtime_t prev_time, 1571 const media_seek_tag *prev_tag) 1572 { 1573 DPRINT(("CRadeonProducer::AdditionalBufferRequested()\n")); 1574 1575 TOUCH(source); TOUCH(prev_buffer); TOUCH(prev_time); TOUCH(prev_tag); 1576 } 1577 1578 void 1579 CRadeonProducer::LatencyChanged(const media_source &source, 1580 const media_destination &destination, bigtime_t new_latency, 1581 uint32 flags) 1582 { 1583 DPRINT(("CRadeonProducer::LatencyChanged()\n")); 1584 BBufferProducer::LatencyChanged( source, destination, new_latency, flags ); 1585 } 1586 1587 1588 1589 /* BControllable */ 1590 1591 status_t 1592 CRadeonProducer::GetParameterValue( 1593 int32 id, bigtime_t *last_change, void *value, size_t *size) 1594 { 1595 DPRINT(("CRadeonProducer::GetParameterValue(%d)\n", id)); 1596 1597 switch (id) { 1598 case P_SOURCE: 1599 *last_change = fSourceLastChange; 1600 *size = sizeof(fSource); 1601 *((uint32 *) value) = fSource; 1602 break; 1603 case P_STANDARD: 1604 *last_change = fStandardLastChange; 1605 *size = sizeof(fStandard); 1606 *((uint32 *) value) = fStandard; 1607 break; 1608 case P_MODE: 1609 *last_change = fModeLastChange; 1610 *size = sizeof(fMode); 1611 *((uint32 *) value) = fMode; 1612 break; 1613 case P_FORMAT: 1614 *last_change = fFormatLastChange; 1615 *size = sizeof(fFormat); 1616 *((uint32 *) value) = fFormat; 1617 break; 1618 case P_RESOLUTION: 1619 *last_change = fResolutionLastChange; 1620 *size = sizeof(fResolution); 1621 *((uint32 *) value) = fResolution; 1622 break; 1623 case P_TUNER: 1624 *last_change = fTunerLastChange; 1625 *size = sizeof(fTuner); 1626 *((uint32 *) value) = fTuner; 1627 break; 1628 case P_BRIGHTNESS: 1629 *last_change = fBrightnessLastChange; 1630 *size = sizeof(fBrightness); 1631 *((float *) value) = fBrightness; 1632 break; 1633 case P_CONTRAST: 1634 *last_change = fContrastLastChange; 1635 *size = sizeof(fContrast); 1636 *((float *) value) = fContrast; 1637 break; 1638 case P_SATURATION: 1639 *last_change = fSaturationLastChange; 1640 *size = sizeof(fSaturation); 1641 *((float *) value) = fSaturation; 1642 break; 1643 case P_HUE: 1644 *last_change = fHueLastChange; 1645 *size = sizeof(fHue); 1646 *((float *) value) = fHue; 1647 break; 1648 case P_SHARPNESS: 1649 *last_change = fSharpnessLastChange; 1650 *size = sizeof(fSharpness); 1651 *((float *) value) = fSharpness; 1652 break; 1653 default: 1654 DPRINT(("Unknown parameter\n")); 1655 return B_BAD_VALUE; 1656 } 1657 1658 return B_OK; 1659 } 1660 1661 1662 /* 1663 * Change video format instantly. 1664 * 1665 * Used when user changes a settings that affect the video format. 1666 * The new format must be the current format with some values 1667 * replaced with wildcards. Don't put too many wildcards: 1668 * some settings (like video size) are normally chosen by the 1669 * application, so don't use this function as a secret override. 1670 */ 1671 void 1672 CRadeonProducer::instantFormatChange( media_format *new_format ) 1673 { 1674 if( fOutput.destination == media_destination::null ) 1675 return; 1676 1677 if( finalizeFormat( new_format ) != B_OK ) { 1678 DPRINT(("Current format does not allow to change interlace mode on-the-fly\n")); 1679 return; 1680 } 1681 1682 if( ChangeFormat( fOutput.source, fOutput.destination, new_format ) != B_OK ) { 1683 DPRINT(("Consumer does not allow to change interlace mode instantly\n")); 1684 return; 1685 } 1686 1687 fOutput.format = *new_format; 1688 fCurMode = extractCaptureMode( new_format ); 1689 1690 fVideoIn.Stop(); 1691 startCapturing(); 1692 } 1693 1694 void 1695 CRadeonProducer::SetParameterValue( 1696 int32 id, bigtime_t when, const void *value, size_t size) 1697 { 1698 DPRINT(("CRadeonProducer::SetParameterValue()\n")); 1699 1700 if (!value || size != sizeof(uint32)) 1701 return; 1702 1703 switch (id) { 1704 case P_SOURCE: 1705 if (*((const int32 *) value) == fSource) 1706 return; 1707 fSource = *((const uint32 *) value); 1708 fSourceLastChange = when; 1709 1710 // if there is no tuner, force composite input 1711 // (eXposer sets source manually to tuner, even if there is none) 1712 // if there is no tuner, it isn't in the list and can't be picked! 1713 //if( (fVideoIn.Capabilities() & C_VIDEO_IN_HAS_TUNER) == 0 ) 1714 // fSource = C_VIDEO_IN_COMPOSITE; 1715 1716 break; 1717 case P_STANDARD: { 1718 if (*((const int32 *) value) == fStandard) 1719 return; 1720 1721 fStandard = BeToVideoInStandard( *((const int32 *) value) ); 1722 1723 fStandardLastChange = when; 1724 1725 media_format new_format = fOutput.format; 1726 1727 new_format.u.raw_video.field_rate = media_raw_video_format::wildcard.field_rate; 1728 new_format.u.raw_video.interlace = media_raw_video_format::wildcard.interlace; 1729 new_format.u.raw_video.pixel_width_aspect = media_raw_video_format::wildcard.pixel_width_aspect; 1730 new_format.u.raw_video.pixel_height_aspect = media_raw_video_format::wildcard.pixel_height_aspect; 1731 new_format.u.raw_video.first_active = media_raw_video_format::wildcard.first_active; 1732 new_format.u.raw_video.last_active = media_raw_video_format::wildcard.last_active; 1733 1734 instantFormatChange( &new_format ); 1735 break; } 1736 case P_MODE: { 1737 if (*((const int32 *) value) == fMode) 1738 return; 1739 1740 fMode = *((const uint32 *) value); 1741 fModeLastChange = when; 1742 1743 media_format new_format = fOutput.format; 1744 1745 new_format.u.raw_video.field_rate = media_raw_video_format::wildcard.field_rate; 1746 new_format.u.raw_video.interlace = media_raw_video_format::wildcard.interlace; 1747 new_format.u.raw_video.pixel_width_aspect = media_raw_video_format::wildcard.pixel_width_aspect; 1748 new_format.u.raw_video.pixel_height_aspect = media_raw_video_format::wildcard.pixel_height_aspect; 1749 1750 instantFormatChange( &new_format ); 1751 1752 break; } 1753 case P_FORMAT: { 1754 if (*((const int32 *) value) == fFormat) 1755 return; 1756 fFormat = *((const uint32 *) value); 1757 fFormatLastChange = when; 1758 1759 media_format new_format = fOutput.format; 1760 1761 new_format.u.raw_video.display.format = media_raw_video_format::wildcard.display.format; 1762 new_format.u.raw_video.display.bytes_per_row = media_raw_video_format::wildcard.display.bytes_per_row; 1763 1764 instantFormatChange( &new_format ); 1765 break; } 1766 case P_RESOLUTION: 1767 if (*((const int32 *) value) == fResolution) 1768 return; 1769 fResolution = *((const uint32 *) value); 1770 fResolutionLastChange = when; 1771 // no live update - see instantFormatChange() 1772 break; 1773 case P_TUNER: 1774 if (*((const int32 *) value) == fTuner) 1775 return; 1776 fTuner = *((const uint32 *) value); 1777 fTunerLastChange = when; 1778 fVideoIn.SetChannel(fTuner, C_VIDEO_IN_NTSC); // was hardcoded to NTSC 1779 break; 1780 case P_BRIGHTNESS: 1781 if (*((const float *) value) == fBrightness) 1782 return; 1783 fBrightness = (int32)*((const float *) value); 1784 fBrightnessLastChange = when; 1785 fVideoIn.SetBrightness(fBrightness); 1786 break; 1787 case P_CONTRAST: 1788 if (*((const float *) value) == fContrast) 1789 return; 1790 fContrast = (int32)*((const float *) value); 1791 fContrastLastChange = when; 1792 fVideoIn.SetContrast(fContrast); 1793 break; 1794 case P_SATURATION: 1795 if (*((const float *) value) == fSaturation) 1796 return; 1797 fSaturation = (int32)*((const float *) value); 1798 fSaturationLastChange = when; 1799 fVideoIn.SetSaturation(fSaturation); 1800 break; 1801 case P_HUE: 1802 if (*((const float *) value) == fHue) 1803 return; 1804 fHue = (int32)*((const float *) value); 1805 fHueLastChange = when; 1806 fVideoIn.SetHue(fHue); 1807 break; 1808 case P_SHARPNESS: 1809 if (*((const float *) value) == fSharpness) 1810 return; 1811 fSharpness = (int32)*((const float *) value); 1812 fSharpnessLastChange = when; 1813 fVideoIn.SetSharpness(fSharpness); 1814 break; 1815 default: 1816 return; 1817 } 1818 1819 BroadcastNewParameterValue(when, id, const_cast<void *>(value), sizeof(uint32)); 1820 } 1821 1822 status_t 1823 CRadeonProducer::StartControlPanel(BMessenger *out_messenger) 1824 { 1825 return BControllable::StartControlPanel(out_messenger); 1826 } 1827 1828 status_t CRadeonProducer::AddInt32( 1829 BMessage *msg, EOptions option, int32 value ) 1830 { 1831 char name[5]; 1832 1833 *(int32 *)name = option; 1834 name[4] = 0; 1835 1836 return msg->AddInt32( name, value ); 1837 } 1838 1839 status_t 1840 CRadeonProducer::GetConfiguration( BMessage *out ) 1841 { 1842 status_t res; 1843 1844 if( (res = AddInt32( out, P_SOURCE, fSource )) != B_OK || 1845 (res = AddInt32( out, P_STANDARD, BeToVideoInStandard( fStandard ))) != B_OK || 1846 (res = AddInt32( out, P_MODE, fMode )) != B_OK || 1847 (res = AddInt32( out, P_FORMAT, fFormat )) != B_OK || 1848 (res = AddInt32( out, P_RESOLUTION, fResolution )) != B_OK || 1849 (res = AddInt32( out, P_TUNER, fTuner )) != B_OK || 1850 (res = AddInt32( out, P_BRIGHTNESS, fBrightness )) != B_OK || 1851 (res = AddInt32( out, P_CONTRAST, fContrast )) != B_OK || 1852 (res = AddInt32( out, P_SATURATION, fSaturation )) != B_OK || 1853 (res = AddInt32( out, P_HUE, fHue )) != B_OK || 1854 (res = AddInt32( out, P_SHARPNESS, fSharpness )) != B_OK ) 1855 return res; 1856 1857 return B_OK; 1858 } 1859 1860 1861 /* VideoProducer */ 1862 1863 void 1864 CRadeonProducer::HandleStart(bigtime_t performance_time) 1865 { 1866 /* Start producing frames, even if the output hasn't been connected yet. */ 1867 DPRINT(("CRadeonProducer::HandleStart()\n")); 1868 1869 if( RunState() != BMediaEventLooper::B_STOPPED ) { 1870 DPRINT(("already running\n")); 1871 return; 1872 } 1873 1874 SetRunState( BMediaEventLooper::B_STARTED ); 1875 1876 startCapturing(); 1877 } 1878 1879 void 1880 CRadeonProducer::HandleStop(void) 1881 { 1882 DPRINT(("CRadeonProducer::HandleStop()\n")); 1883 1884 fVideoIn.Stop(); 1885 1886 // discard pending capture event 1887 RealTimeQueue()->FlushEvents( 0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HARDWARE ); 1888 } 1889 1890 void 1891 CRadeonProducer::HandleTimeWarp(bigtime_t performance_time) 1892 { 1893 DPRINT(("CRadeonProducer::HandleTimeWarp()\n")); 1894 } 1895 1896 void 1897 CRadeonProducer::HandleSeek(bigtime_t performance_time) 1898 { 1899 DPRINT(("CRadeonProducer::HandleSeek()\n")); 1900 } 1901 1902 void 1903 CRadeonProducer::captureField( bigtime_t *capture_time ) 1904 { 1905 *capture_time = system_time(); 1906 1907 // don't capture if output is disabled 1908 if (!fEnabled) 1909 return; 1910 1911 BBuffer *buffer = fBufferGroup->RequestBuffer( 1912 fOutput.format.u.raw_video.display.bytes_per_row * 1913 fOutput.format.u.raw_video.display.line_count, 0LL); 1914 1915 if (!buffer) { 1916 DPRINT(( "No buffer\n" )); 1917 return; 1918 } 1919 1920 media_header *h = buffer->Header(); 1921 h->type = B_MEDIA_RAW_VIDEO; 1922 h->time_source = TimeSource()->ID(); 1923 h->size_used = fOutput.format.u.raw_video.display.bytes_per_row * 1924 fOutput.format.u.raw_video.display.line_count; 1925 h->file_pos = 0; 1926 h->orig_size = 0; 1927 h->data_offset = 0; 1928 h->u.raw_video.field_gamma = 1.0; 1929 h->u.raw_video.pulldown_number = 0; 1930 h->u.raw_video.first_active_line = fOutput.format.u.raw_video.first_active; 1931 h->u.raw_video.line_count = fOutput.format.u.raw_video.display.line_count; 1932 1933 int field_sequence; 1934 short field_number; 1935 1936 int dropped=fVideoIn.Capture( 1937 fOutput.format.u.raw_video.display.format, 1938 buffer->Data(), 1939 fOutput.format.u.raw_video.display.bytes_per_row * fOutput.format.u.raw_video.display.line_count, 1940 fOutput.format.u.raw_video.display.bytes_per_row, 1941 &field_sequence, 1942 &field_number, 1943 capture_time); 1944 1945 // HACK: store end instead of start time 1946 // obviously, the _start_ time of a frame is always in the past by one 1947 // frame; unfortunately, programs like stamptv expect the start time to 1948 // be in the present, else they drop the frame; therefore, we write the 1949 // _end_ time into the start time field, and everyone is happy (though 1950 // the time is wrong) 1951 // this leads to many tweaks in the code! 1952 h->start_time = TimeSource()->PerformanceTimeFor( *capture_time ); 1953 1954 h->u.raw_video.field_sequence = field_sequence - fFieldSequenceBase; 1955 h->u.raw_video.field_number = field_number; 1956 1957 if (dropped > 1) { 1958 PRINT(("%d frames dropped\n", dropped-1)); 1959 } 1960 1961 if (SendBuffer(buffer, fOutput.destination) < B_OK) { 1962 PRINTF(-1, ("FrameGenerator: Error sending buffer\n")); 1963 buffer->Recycle(); 1964 } 1965 } 1966 1967 void 1968 CRadeonProducer::HandleHardware() 1969 { 1970 bigtime_t capture_time; 1971 1972 //DPRINT(("Hi\n")); 1973 1974 if( RunState() != BMediaEventLooper::B_STARTED ) 1975 return; 1976 1977 captureField( &capture_time ); 1978 1979 // generate next event after next field 1980 media_timed_event event( 1981 capture_time + 1000000 / fOutput.format.u.raw_video.field_rate, 1982 BTimedEventQueue::B_HARDWARE); 1983 1984 RealTimeQueue()->AddEvent(event); 1985 } 1986