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