1 /****************************************************************************** 2 / 3 / File: VideoIn.cpp 4 / 5 / Description: High-Level ATI Radeon Video Capture Interface. 6 / 7 / Copyright 2001, Carlos Hasan 8 / 9 *******************************************************************************/ 10 11 #include <Debug.h> 12 #include "VideoIn.h" 13 #include <memory.h> 14 15 static const theater_standard kStandard[] = { 16 C_THEATER_NTSC, 17 C_THEATER_NTSC_JAPAN, 18 C_THEATER_NTSC_443, 19 C_THEATER_PAL_M, 20 C_THEATER_PAL_BDGHI, 21 C_THEATER_PAL_N, 22 C_THEATER_PAL_60, 23 C_THEATER_PAL_NC, 24 C_THEATER_SECAM, 25 // C_THEATER_NTSC_RAW 26 }; 27 28 static const theater_source kSource[] = { 29 C_THEATER_TUNER, 30 C_THEATER_COMPOSITE, 31 C_THEATER_SVIDEO 32 }; 33 34 static const capture_buffer_mode kMode[] = { 35 C_RADEON_CAPTURE_FIELD_DOUBLE, 36 C_RADEON_CAPTURE_BOB_DOUBLE, 37 C_RADEON_CAPTURE_WEAVE_DOUBLE 38 }; 39 40 static const struct { 41 struct { 42 int width, height; 43 } total; 44 struct { 45 int left, top; 46 int width, height; 47 } active; 48 struct { 49 int left, top; 50 int width, height; 51 } vbi; 52 } kTiming[] = { 53 { { 910, 525 }, { 112, 37, 755, 480 }, { 73, 13, 798, 20 } }, // NTSC-M 54 { { 910, 525 }, { 112, 37, 755, 480 }, { 73, 13, 798, 24 } }, // NTSC-Japan 55 { { 1042, 525 }, { 126, 37, 940, 480 }, { 73, 13, 798, 24 } }, // NTSC-443 56 { { 910, 525 }, { 103, 37, 764, 480 }, { 73, 13, 798, 24 } }, // PAL-M 57 58 { { 1135, 625 }, { 154, 35, 928, 576 }, { 132, 11, 924, 24 } }, // PAL-BDGHI 59 { { 1135, 625 }, { 154, 35, 928, 576 }, { 132, 11, 924, 24 } }, // PAL-N 60 { { 1125, 625 }, { 132, 37, 736, 480 }, { 100, 13, 770, 26 } }, // PAL-60 61 { { 910, 625 }, { 125, 35, 957, 576 }, { 132, 11, 924, 24 } }, // PAL-NC 62 { { 1135, 625 }, { 149, 35, 933, 576 }, { 132, 11, 924, 24 } }, // SECAM 63 64 { { 910, 525 }, { 112, 37, 755, 480 }, { 73, 13, 798, 22 } } // NTSC-Raw 65 }; 66 67 class CTheater100; 68 class CTheater200; 69 70 CVideoIn::CVideoIn( const char *dev_name ) 71 : fRadeon( dev_name ), 72 fCapture(fRadeon), 73 fI2CPort(fRadeon), 74 fTheater(NULL), 75 fTuner(fI2CPort), 76 fSound(fI2CPort), 77 fBuffer0(0), 78 fBuffer1(0), 79 fBuffer0Handle(0), 80 fBuffer1Handle(0), 81 convert_buffer( NULL ), 82 fBufferLength(0), 83 fBufferBytesPerRow(0), 84 fBufferSequence(0), 85 fBufferPeriod(0), 86 started( false ) 87 { 88 89 Trace("CVideoIn::CVideoIn()"); 90 91 } 92 93 CVideoIn::~CVideoIn() 94 { 95 Trace("CVideoIn::~CVideoIn()"); 96 97 FreeBuffers(); 98 } 99 100 status_t CVideoIn::InitCheck() 101 { 102 status_t res; 103 int device; 104 105 Trace("CVideoIn::InitCheck()"); 106 if( (res = fRadeon.InitCheck()) != B_OK ) 107 return res; 108 109 if( (res = fCapture.InitCheck()) != B_OK ) 110 return res; 111 112 if( (res = fI2CPort.InitCheck()) != B_OK ) 113 return res; 114 115 //debugger("init"); 116 // detect type of theatre and initialise specific theater class 117 if ((device = fRadeon.FindVIPDevice( C_THEATER100_VIP_DEVICE_ID )) != -1) 118 { 119 Trace("CVideoIn::Found Rage Theater 100"); 120 fTheater = new CTheater100(fRadeon, device); 121 } 122 else if ((device = fRadeon.FindVIPDevice( C_THEATER200_VIP_DEVICE_ID )) != -1) 123 { 124 Trace("CVideoIn::Found Rage Theater 200"); 125 fTheater = new CTheater200(fRadeon, device); 126 } 127 128 if (fTheater) 129 { 130 res = fTheater->InitCheck(); 131 } 132 else 133 { 134 res = B_ERROR; 135 } 136 137 return res; 138 } 139 140 int CVideoIn::Capabilities() const 141 { 142 return fTheater->Capabilities() + 143 (fTuner.InitCheck() == B_OK ? C_VIDEO_IN_HAS_TUNER : 0) + 144 (fSound.InitCheck() == B_OK ? C_VIDEO_IN_HAS_SOUND : 0); 145 } 146 147 void CVideoIn::Start(video_in_source source, video_in_standard standard, 148 video_in_capture_mode mode, int width, int height) 149 { 150 char buffer[256]; 151 sprintf(buffer, "CVideoIn::Start(%s, %d, %d)", 152 mode == C_VIDEO_IN_FIELD ? "C_VIDEO_IN_FIELD" : 153 mode == C_VIDEO_IN_BOB ? "C_VIDEO_IN_BOB" : "C_VIDEO_IN_WEAVE", width, height); 154 Trace(buffer); 155 156 if( started ) 157 Stop(); 158 159 switch (mode) { 160 case C_VIDEO_IN_FIELD: 161 case C_VIDEO_IN_BOB: 162 width = Clamp(width, 0, kTiming[standard].active.width); 163 height = Clamp(height, 0, kTiming[standard].active.height / 2); 164 break; 165 case C_VIDEO_IN_WEAVE: 166 width = Clamp(width, 0, kTiming[standard].active.width); 167 height = Clamp(height, 0, kTiming[standard].active.height); 168 break; 169 } 170 171 fBufferBytesPerRow = (2 * width + 15) & ~15; 172 fBufferLength = (fBufferBytesPerRow * height + 15) & ~15; 173 174 FreeBuffers(); 175 176 // TBD: 177 // no error handling !!!! 178 fRadeon.AllocateGraphicsMemory( 179 mt_local, 180 mode == C_VIDEO_IN_BOB ? 4 * fBufferLength : 2 * fBufferLength, 181 &fBuffer0, &fBuffer0Handle ); 182 183 fRadeon.AllocateGraphicsMemory( 184 mt_local, 185 mode == C_VIDEO_IN_BOB ? 2 * fBufferLength : 1 * fBufferLength, 186 &fBuffer1, &fBuffer1Handle ); 187 188 convert_buffer = malloc( mode == C_VIDEO_IN_BOB ? 4 * fBufferLength : 2 * fBufferLength ); 189 190 fBufferPeriod = 1000000000LL / getFrameRate( standard ); 191 192 if( mode == C_VIDEO_IN_BOB ) 193 fBufferPeriod >>= 1; 194 195 fTheater->SetStandard(kStandard[standard], kSource[source]); 196 fTheater->SetSize(width, (mode != C_VIDEO_IN_WEAVE ? 2 * height : height)); 197 198 fCapture.SetBuffer(C_RADEON_CAPTURE_CCIR656, kMode[mode], fBuffer0, fBuffer1, fBufferLength, fBufferBytesPerRow >> 1); 199 fCapture.SetClip(0, kTiming[standard].vbi.height, width - 1, kTiming[standard].vbi.height + (mode != C_VIDEO_IN_WEAVE ? height : height >> 1) - 1); 200 201 fTheater->SetEnable(true, false); 202 if( fSound.InitCheck() == B_OK ) 203 fSound.SetEnable(true); 204 fCapture.SetInterrupts(true); 205 fCapture.Start(); 206 } 207 208 void CVideoIn::Stop() 209 { 210 Trace("CVideoIn::Stop()"); 211 212 if( !started ) 213 return; 214 215 fCapture.Stop(); 216 fCapture.SetInterrupts(false); 217 if( fSound.InitCheck() == B_OK ) 218 fSound.SetEnable(false); 219 fTheater->SetEnable(false, false); 220 221 FreeBuffers(); 222 } 223 224 void CVideoIn::FreeBuffers() 225 { 226 if( fBuffer0Handle > 0 ) { 227 fRadeon.FreeGraphicsMemory( mt_local, fBuffer0Handle ); 228 fBuffer0Handle = 0; 229 } 230 231 if( fBuffer1Handle > 0 ) { 232 fRadeon.FreeGraphicsMemory( mt_local, fBuffer1Handle ); 233 fBuffer1Handle = 0; 234 } 235 236 if( convert_buffer != NULL ) { 237 free( convert_buffer ); 238 convert_buffer = NULL; 239 } 240 } 241 242 void CVideoIn::SetBrightness(int brightness) 243 { 244 Trace("CVideoIn::SetBrightness()"); 245 246 fTheater->SetBrightness(brightness); 247 } 248 249 void CVideoIn::SetContrast(int contrast) 250 { 251 Trace("CVideoIn::SetContrast()"); 252 253 fTheater->SetContrast(contrast); 254 } 255 256 void CVideoIn::SetSaturation(int saturation) 257 { 258 Trace("CVideoIn::SetSaturation()"); 259 260 fTheater->SetSaturation(saturation); 261 } 262 263 void CVideoIn::SetHue(int hue) 264 { 265 Trace("CVideoIn::SetHue()"); 266 267 fTheater->SetHue(hue); 268 } 269 270 void CVideoIn::SetSharpness(int sharpness) 271 { 272 Trace("CVideoIn::SetSharpness()"); 273 274 fTheater->SetSharpness(sharpness); 275 } 276 277 void CVideoIn::SetFrequency(float frequency, float picture) 278 { 279 Trace("CVideoIn::SetFrequency()"); 280 281 if (fTuner.Type() != C_TUNER_NONE) 282 fTuner.SweepFrequency(frequency, picture); 283 } 284 285 float CVideoIn::FrequencyForChannel(int channel, video_in_standard standard) 286 { 287 float frequency = 0; 288 289 Trace("CVideoIn::FrequencyForChannel()"); 290 291 if (fTuner.Type() != C_TUNER_NONE) { 292 switch (standard) { 293 case C_VIDEO_IN_NTSC: 294 case C_VIDEO_IN_NTSC_RAW: 295 // NTSC Cable 296 if (channel >= 2 && channel <= 6) { 297 frequency = 55.25 + 6.00 * (channel - 2); 298 } 299 else if (channel >= 7 && channel <= 13) { 300 frequency = 175.25 + 6.00 * (channel - 7); 301 } 302 else if (channel >= 14 && channel <= 22) { 303 frequency = 121.25 + 6.00 * (channel - 14); 304 } 305 else if (channel >= 23 && channel <= 36) { 306 frequency = 217.25 + 6.00 * (channel - 23); 307 } 308 else if (channel >= 37 && channel <= 62) { 309 frequency = 301.25 + 6.00 * (channel - 37); 310 } 311 else if (channel >= 63 && channel <= 94) { 312 frequency = 457.25 + 6.00 * (channel - 63); 313 } 314 else if (channel >= 95 && channel <= 99) { 315 frequency = 91.25 + 6.00 * (channel - 95); 316 } 317 else if (channel >= 100 && channel <= 125) { 318 frequency = 649.25 + 6.00 * (channel - 100); 319 } 320 else { 321 frequency = 0; 322 } 323 break; 324 case C_VIDEO_IN_NTSC_JAPAN: 325 case C_VIDEO_IN_NTSC_443: 326 case C_VIDEO_IN_PAL_M: 327 case C_VIDEO_IN_PAL_BDGHI: 328 case C_VIDEO_IN_PAL_N: 329 case C_VIDEO_IN_PAL_60: 330 case C_VIDEO_IN_PAL_NC: 331 case C_VIDEO_IN_SECAM: 332 break; 333 } 334 } 335 return frequency; 336 } 337 338 bool CVideoIn::SetChannel(int channel, video_in_standard standard) 339 { 340 Trace("CVideoIn::SetChannel()"); 341 342 if (fTuner.Type() == C_TUNER_NONE) 343 return true; 344 345 const float frequency = FrequencyForChannel(channel, standard); 346 347 switch (standard) { 348 case C_VIDEO_IN_NTSC: 349 case C_VIDEO_IN_NTSC_RAW: 350 return fTuner.SweepFrequency(frequency, C_TUNER_NTSC_PICTURE_CARRIER / 100.0f); 351 break; 352 case C_VIDEO_IN_NTSC_JAPAN: 353 case C_VIDEO_IN_NTSC_443: 354 case C_VIDEO_IN_PAL_M: 355 case C_VIDEO_IN_PAL_BDGHI: 356 case C_VIDEO_IN_PAL_N: 357 case C_VIDEO_IN_PAL_60: 358 case C_VIDEO_IN_PAL_NC: 359 case C_VIDEO_IN_SECAM: 360 return fTuner.SweepFrequency(frequency, C_TUNER_PAL_PICTURE_CARRIER / 100.0f); 361 } 362 return false; 363 } 364 365 int CVideoIn::Capture(color_space colorSpace, void * bits, int bitsLength, 366 int bytesPerRow, int * sequence, short * number, bigtime_t * when) 367 { 368 // Trace("CVideoIn::Capture()"); 369 370 int mask, counter; 371 372 if ((mask = fCapture.WaitInterrupts(sequence, when, fBufferPeriod)) == 0) 373 return 0; 374 375 *number = ((mask & (C_RADEON_CAPTURE_BUF0_INT | C_RADEON_CAPTURE_BUF1_INT)) != 0 ? 0 : 1); 376 377 int32 captured_buffer = 378 ((mask & C_RADEON_CAPTURE_BUF0_INT) != 0 ? fBuffer0 : 379 (mask & C_RADEON_CAPTURE_BUF1_INT) != 0 ? fBuffer1 : 380 (mask & C_RADEON_CAPTURE_BUF0_EVEN_INT) != 0 ? fBuffer0 + fBufferLength : 381 (mask & C_RADEON_CAPTURE_BUF1_EVEN_INT) != 0 ? fBuffer1 + fBufferLength : 0); 382 383 /*PRINT(("colorSpace:%x, bitsLength: %d, fBufferLength: %d, bytesPerRow: %d, fBufferBytesPerRow: %d\n", 384 colorSpace, bitsLength, fBufferLength, bytesPerRow, fBufferBytesPerRow ));*/ 385 386 // always copy into main memory first, even if it must be converted by CPU - 387 // reading from graphics mem is incredibly slow 388 if (colorSpace == B_YCbCr422 && bitsLength <= fBufferLength && bytesPerRow == fBufferBytesPerRow) { 389 fRadeon.DMACopy( captured_buffer, bits, bitsLength, true, false ); 390 } 391 392 else if (colorSpace == B_RGB32 && bitsLength <= 2 * fBufferLength && bytesPerRow == 2 * fBufferBytesPerRow) { 393 fRadeon.DMACopy( captured_buffer, convert_buffer, fBufferLength, true, false ); 394 395 #define RGB32 396 #include "yuv_converter.h" 397 #undef RGB32 398 399 } 400 401 else if (colorSpace == B_RGB16 && bitsLength <= fBufferLength && bytesPerRow == fBufferBytesPerRow) { 402 fRadeon.DMACopy( captured_buffer, convert_buffer, fBufferLength, true, false ); 403 404 #define RGB16 405 #include "yuv_converter.h" 406 #undef RGB16 407 408 } 409 410 else if (colorSpace == B_RGB15 && bitsLength <= fBufferLength && bytesPerRow == fBufferBytesPerRow) { 411 fRadeon.DMACopy( captured_buffer, convert_buffer, fBufferLength, true, false ); 412 413 #define RGB15 414 #include "yuv_converter.h" 415 #undef RGB15 416 417 } 418 else if (colorSpace == B_GRAY8 && 2 * bitsLength <= fBufferLength && 2 * bytesPerRow == fBufferBytesPerRow) { 419 fRadeon.DMACopy( captured_buffer, convert_buffer, fBufferLength, true, false ); 420 421 static const unsigned short mask[] = { 422 0x00ff, 0x00ff, 0x00ff, 0x00ff }; 423 424 asm volatile( 425 "1:\n" 426 "movq 0x00(%0),%%mm0\n" // mm0 = Cr2' Y3 Cb2' Y2 Cr0' Y1 Cb0' Y0 427 "movq 0x08(%0),%%mm1\n" // mm1 = Cr6' Y7 Cb6' Y6 Cr4' Y5 Cb4' Y4 428 "pand %3,%%mm0\n" // mm0 = 00 Y3 00 Y2 00 Y1 00 Y0 429 "pand %3,%%mm1\n" // mm1 = 00 Y7 00 Y6 00 Y5 00 Y4 430 "packuswb %%mm1,%%mm0\n" // mm0 = Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 431 "movq %%mm0,(%1)\n" // destination[0] = mm0 432 "addl $0x10,%0\n" // source += 16 433 "addl $0x08,%1\n" // destination += 8 434 "subl $0x08,%2\n" 435 "jg 1b\n" 436 "emms\n" 437 : 438 : "r" (convert_buffer), "r" (bits), "r" (bitsLength), "m" (mask)); 439 } 440 else if( colorSpace == B_NO_COLOR_SPACE ) { 441 // special case: only wait for image but don't copy it 442 ; 443 } 444 else { 445 PRINT(("CVideoIn::Capture() - Bad buffer format\n")); 446 } 447 448 counter = *sequence - fBufferSequence; 449 fBufferSequence = *sequence; 450 return counter; 451 } 452 453 void CVideoIn::Trace(const char * message) const 454 { 455 PRINT(("\x1b[0;30;34m%s\x1b[0;30;47m\n", message)); 456 } 457 458 int32 CVideoIn::getFrameRate( video_in_standard standard ) 459 { 460 // TODO: I'm not really sure about these values 461 static const int32 frame_rate[] = { 462 29976, 29976, 29976, 25000, 25000, 25000, 29976, 25000, 25000, 29976 463 }; 464 465 return frame_rate[standard]; 466 } 467 468 void CVideoIn::getActiveRange( video_in_standard standard, CRadeonRect &rect ) 469 { 470 // in theory, we would ask fTheatre; 471 // in practice, values retrieved from there don't work; 472 // e.g. for PAL, according to Theatre, VBI height is 38, but you must set up the 473 // clipping unit to height 24! I have no clue what goes wrong there 474 rect.SetTo( 475 kTiming[standard].active.left, 476 kTiming[standard].active.top, 477 kTiming[standard].active.left + kTiming[standard].active.width - 1, 478 kTiming[standard].active.top + kTiming[standard].active.height - 1 ); 479 } 480