xref: /haiku/src/add-ons/media/media-add-ons/radeon/VideoIn.cpp (revision 863634b83f627a5950315df1added5f754d42c23)
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