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
CVideoIn(const char * dev_name)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
~CVideoIn()93 CVideoIn::~CVideoIn()
94 {
95 Trace("CVideoIn::~CVideoIn()");
96
97 FreeBuffers();
98 }
99
InitCheck()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
Capabilities() const140 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
Start(video_in_source source,video_in_standard standard,video_in_capture_mode mode,int width,int height)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
Stop()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
FreeBuffers()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
SetBrightness(int brightness)242 void CVideoIn::SetBrightness(int brightness)
243 {
244 Trace("CVideoIn::SetBrightness()");
245
246 fTheater->SetBrightness(brightness);
247 }
248
SetContrast(int contrast)249 void CVideoIn::SetContrast(int contrast)
250 {
251 Trace("CVideoIn::SetContrast()");
252
253 fTheater->SetContrast(contrast);
254 }
255
SetSaturation(int saturation)256 void CVideoIn::SetSaturation(int saturation)
257 {
258 Trace("CVideoIn::SetSaturation()");
259
260 fTheater->SetSaturation(saturation);
261 }
262
SetHue(int hue)263 void CVideoIn::SetHue(int hue)
264 {
265 Trace("CVideoIn::SetHue()");
266
267 fTheater->SetHue(hue);
268 }
269
SetSharpness(int sharpness)270 void CVideoIn::SetSharpness(int sharpness)
271 {
272 Trace("CVideoIn::SetSharpness()");
273
274 fTheater->SetSharpness(sharpness);
275 }
276
SetFrequency(float frequency,float picture)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
FrequencyForChannel(int channel,video_in_standard standard)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
SetChannel(int channel,video_in_standard standard)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
Capture(color_space colorSpace,void * bits,int bitsLength,int bytesPerRow,int * sequence,short * number,bigtime_t * when)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
Trace(const char * message) const453 void CVideoIn::Trace(const char * message) const
454 {
455 PRINT(("\x1b[0;30;34m%s\x1b[0;30;47m\n", message));
456 }
457
getFrameRate(video_in_standard standard)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
getActiveRange(video_in_standard standard,CRadeonRect & rect)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