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