1 /******************************************************************************
5 / Description: High-Level ATI Radeon Video Capture Interface.
7 / Copyright 2001, Carlos Hasan
9 *******************************************************************************/
15 static const theater_standard kStandard
[] = {
28 static const theater_source kSource
[] = {
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
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
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
64 { { 910, 525 }, { 112, 37, 755, 480 }, { 73, 13, 798, 22 } } // NTSC-Raw
70 CVideoIn::CVideoIn( const char *dev_name
)
71 : fRadeon( dev_name
),
81 convert_buffer( NULL
),
83 fBufferBytesPerRow(0),
89 Trace("CVideoIn::CVideoIn()");
95 Trace("CVideoIn::~CVideoIn()");
100 status_t
CVideoIn::InitCheck()
105 Trace("CVideoIn::InitCheck()");
106 if( (res
= fRadeon
.InitCheck()) != B_OK
)
109 if( (res
= fCapture
.InitCheck()) != B_OK
)
112 if( (res
= fI2CPort
.InitCheck()) != B_OK
)
116 // detect type of theatre and initialise specific theater class
117 if ((device
= fRadeon
.FindVIPDevice( C_THEATER100_VIP_DEVICE_ID
)) != -1)
119 Trace("CVideoIn::Found Rage Theater 100");
120 fTheater
= new CTheater100(fRadeon
, device
);
122 else if ((device
= fRadeon
.FindVIPDevice( C_THEATER200_VIP_DEVICE_ID
)) != -1)
124 Trace("CVideoIn::Found Rage Theater 200");
125 fTheater
= new CTheater200(fRadeon
, device
);
130 res
= fTheater
->InitCheck();
140 int CVideoIn::Capabilities() const
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);
147 void CVideoIn::Start(video_in_source source
, video_in_standard standard
,
148 video_in_capture_mode mode
, int width
, int height
)
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
);
160 case C_VIDEO_IN_FIELD
:
162 width
= Clamp(width
, 0, kTiming
[standard
].active
.width
);
163 height
= Clamp(height
, 0, kTiming
[standard
].active
.height
/ 2);
165 case C_VIDEO_IN_WEAVE
:
166 width
= Clamp(width
, 0, kTiming
[standard
].active
.width
);
167 height
= Clamp(height
, 0, kTiming
[standard
].active
.height
);
171 fBufferBytesPerRow
= (2 * width
+ 15) & ~15;
172 fBufferLength
= (fBufferBytesPerRow
* height
+ 15) & ~15;
177 // no error handling !!!!
178 fRadeon
.AllocateGraphicsMemory(
180 mode
== C_VIDEO_IN_BOB
? 4 * fBufferLength
: 2 * fBufferLength
,
181 &fBuffer0
, &fBuffer0Handle
);
183 fRadeon
.AllocateGraphicsMemory(
185 mode
== C_VIDEO_IN_BOB
? 2 * fBufferLength
: 1 * fBufferLength
,
186 &fBuffer1
, &fBuffer1Handle
);
188 convert_buffer
= malloc( mode
== C_VIDEO_IN_BOB
? 4 * fBufferLength
: 2 * fBufferLength
);
190 fBufferPeriod
= 1000000000LL / getFrameRate( standard
);
192 if( mode
== C_VIDEO_IN_BOB
)
195 fTheater
->SetStandard(kStandard
[standard
], kSource
[source
]);
196 fTheater
->SetSize(width
, (mode
!= C_VIDEO_IN_WEAVE
? 2 * height
: height
));
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);
201 fTheater
->SetEnable(true, false);
202 if( fSound
.InitCheck() == B_OK
)
203 fSound
.SetEnable(true);
204 fCapture
.SetInterrupts(true);
208 void CVideoIn::Stop()
210 Trace("CVideoIn::Stop()");
216 fCapture
.SetInterrupts(false);
217 if( fSound
.InitCheck() == B_OK
)
218 fSound
.SetEnable(false);
219 fTheater
->SetEnable(false, false);
224 void CVideoIn::FreeBuffers()
226 if( fBuffer0Handle
> 0 ) {
227 fRadeon
.FreeGraphicsMemory( mt_local
, fBuffer0Handle
);
231 if( fBuffer1Handle
> 0 ) {
232 fRadeon
.FreeGraphicsMemory( mt_local
, fBuffer1Handle
);
236 if( convert_buffer
!= NULL
) {
237 free( convert_buffer
);
238 convert_buffer
= NULL
;
242 void CVideoIn::SetBrightness(int brightness
)
244 Trace("CVideoIn::SetBrightness()");
246 fTheater
->SetBrightness(brightness
);
249 void CVideoIn::SetContrast(int contrast
)
251 Trace("CVideoIn::SetContrast()");
253 fTheater
->SetContrast(contrast
);
256 void CVideoIn::SetSaturation(int saturation
)
258 Trace("CVideoIn::SetSaturation()");
260 fTheater
->SetSaturation(saturation
);
263 void CVideoIn::SetHue(int hue
)
265 Trace("CVideoIn::SetHue()");
267 fTheater
->SetHue(hue
);
270 void CVideoIn::SetSharpness(int sharpness
)
272 Trace("CVideoIn::SetSharpness()");
274 fTheater
->SetSharpness(sharpness
);
277 void CVideoIn::SetFrequency(float frequency
, float picture
)
279 Trace("CVideoIn::SetFrequency()");
281 if (fTuner
.Type() != C_TUNER_NONE
)
282 fTuner
.SweepFrequency(frequency
, picture
);
285 float CVideoIn::FrequencyForChannel(int channel
, video_in_standard standard
)
289 Trace("CVideoIn::FrequencyForChannel()");
291 if (fTuner
.Type() != C_TUNER_NONE
) {
293 case C_VIDEO_IN_NTSC
:
294 case C_VIDEO_IN_NTSC_RAW
:
296 if (channel
>= 2 && channel
<= 6) {
297 frequency
= 55.25 + 6.00 * (channel
- 2);
299 else if (channel
>= 7 && channel
<= 13) {
300 frequency
= 175.25 + 6.00 * (channel
- 7);
302 else if (channel
>= 14 && channel
<= 22) {
303 frequency
= 121.25 + 6.00 * (channel
- 14);
305 else if (channel
>= 23 && channel
<= 36) {
306 frequency
= 217.25 + 6.00 * (channel
- 23);
308 else if (channel
>= 37 && channel
<= 62) {
309 frequency
= 301.25 + 6.00 * (channel
- 37);
311 else if (channel
>= 63 && channel
<= 94) {
312 frequency
= 457.25 + 6.00 * (channel
- 63);
314 else if (channel
>= 95 && channel
<= 99) {
315 frequency
= 91.25 + 6.00 * (channel
- 95);
317 else if (channel
>= 100 && channel
<= 125) {
318 frequency
= 649.25 + 6.00 * (channel
- 100);
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
:
338 bool CVideoIn::SetChannel(int channel
, video_in_standard standard
)
340 Trace("CVideoIn::SetChannel()");
342 if (fTuner
.Type() == C_TUNER_NONE
)
345 const float frequency
= FrequencyForChannel(channel
, 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
);
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
);
365 int CVideoIn::Capture(color_space colorSpace
, void * bits
, int bitsLength
,
366 int bytesPerRow
, int * sequence
, short * number
, bigtime_t
* when
)
368 // Trace("CVideoIn::Capture()");
372 if ((mask
= fCapture
.WaitInterrupts(sequence
, when
, fBufferPeriod
)) == 0)
375 *number
= ((mask
& (C_RADEON_CAPTURE_BUF0_INT
| C_RADEON_CAPTURE_BUF1_INT
)) != 0 ? 0 : 1);
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);
383 /*PRINT(("colorSpace:%x, bitsLength: %d, fBufferLength: %d, bytesPerRow: %d, fBufferBytesPerRow: %d\n",
384 colorSpace, bitsLength, fBufferLength, bytesPerRow, fBufferBytesPerRow ));*/
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 );
392 else if (colorSpace
== B_RGB32
&& bitsLength
<= 2 * fBufferLength
&& bytesPerRow
== 2 * fBufferBytesPerRow
) {
393 fRadeon
.DMACopy( captured_buffer
, convert_buffer
, fBufferLength
, true, false );
396 #include "yuv_converter.h"
401 else if (colorSpace
== B_RGB16
&& bitsLength
<= fBufferLength
&& bytesPerRow
== fBufferBytesPerRow
) {
402 fRadeon
.DMACopy( captured_buffer
, convert_buffer
, fBufferLength
, true, false );
405 #include "yuv_converter.h"
410 else if (colorSpace
== B_RGB15
&& bitsLength
<= fBufferLength
&& bytesPerRow
== fBufferBytesPerRow
) {
411 fRadeon
.DMACopy( captured_buffer
, convert_buffer
, fBufferLength
, true, false );
414 #include "yuv_converter.h"
418 else if (colorSpace
== B_GRAY8
&& 2 * bitsLength
<= fBufferLength
&& 2 * bytesPerRow
== fBufferBytesPerRow
) {
419 fRadeon
.DMACopy( captured_buffer
, convert_buffer
, fBufferLength
, true, false );
421 static const unsigned short mask
[] = {
422 0x00ff, 0x00ff, 0x00ff, 0x00ff };
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
438 : "r" (convert_buffer
), "r" (bits
), "r" (bitsLength
), "m" (mask
));
440 else if( colorSpace
== B_NO_COLOR_SPACE
) {
441 // special case: only wait for image but don't copy it
445 PRINT(("CVideoIn::Capture() - Bad buffer format\n"));
448 counter
= *sequence
- fBufferSequence
;
449 fBufferSequence
= *sequence
;
453 void CVideoIn::Trace(const char * message
) const
455 PRINT(("\x1b[0;30;34m%s\x1b[0;30;47m\n", message
));
458 int32
CVideoIn::getFrameRate( video_in_standard standard
)
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
465 return frame_rate
[standard
];
468 void CVideoIn::getActiveRange( video_in_standard standard
, CRadeonRect
&rect
)
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
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 );