vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / media / media-add-ons / radeon / VideoIn.cpp
blobc2393b424bee1c20489bb0503affac97a62142ba
1 /******************************************************************************
3 / File: VideoIn.cpp
5 / Description: High-Level ATI Radeon Video Capture Interface.
7 / Copyright 2001, Carlos Hasan
9 *******************************************************************************/
11 #include <Debug.h>
12 #include "VideoIn.h"
13 #include <memory.h>
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
28 static const theater_source kSource[] = {
29 C_THEATER_TUNER,
30 C_THEATER_COMPOSITE,
31 C_THEATER_SVIDEO
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
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
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
67 class CTheater100;
68 class CTheater200;
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 )
89 Trace("CVideoIn::CVideoIn()");
93 CVideoIn::~CVideoIn()
95 Trace("CVideoIn::~CVideoIn()");
97 FreeBuffers();
100 status_t CVideoIn::InitCheck()
102 status_t res;
103 int device;
105 Trace("CVideoIn::InitCheck()");
106 if( (res = fRadeon.InitCheck()) != B_OK )
107 return res;
109 if( (res = fCapture.InitCheck()) != B_OK )
110 return res;
112 if( (res = fI2CPort.InitCheck()) != B_OK )
113 return res;
115 //debugger("init");
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);
128 if (fTheater)
130 res = fTheater->InitCheck();
132 else
134 res = B_ERROR;
137 return res;
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)
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);
156 if( started )
157 Stop();
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;
171 fBufferBytesPerRow = (2 * width + 15) & ~15;
172 fBufferLength = (fBufferBytesPerRow * height + 15) & ~15;
174 FreeBuffers();
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 );
183 fRadeon.AllocateGraphicsMemory(
184 mt_local,
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 )
193 fBufferPeriod >>= 1;
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);
205 fCapture.Start();
208 void CVideoIn::Stop()
210 Trace("CVideoIn::Stop()");
212 if( !started )
213 return;
215 fCapture.Stop();
216 fCapture.SetInterrupts(false);
217 if( fSound.InitCheck() == B_OK )
218 fSound.SetEnable(false);
219 fTheater->SetEnable(false, false);
221 FreeBuffers();
224 void CVideoIn::FreeBuffers()
226 if( fBuffer0Handle > 0 ) {
227 fRadeon.FreeGraphicsMemory( mt_local, fBuffer0Handle );
228 fBuffer0Handle = 0;
231 if( fBuffer1Handle > 0 ) {
232 fRadeon.FreeGraphicsMemory( mt_local, fBuffer1Handle );
233 fBuffer1Handle = 0;
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)
287 float frequency = 0;
289 Trace("CVideoIn::FrequencyForChannel()");
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);
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);
320 else {
321 frequency = 0;
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;
335 return frequency;
338 bool CVideoIn::SetChannel(int channel, video_in_standard standard)
340 Trace("CVideoIn::SetChannel()");
342 if (fTuner.Type() == C_TUNER_NONE)
343 return true;
345 const float frequency = FrequencyForChannel(channel, standard);
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);
362 return false;
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()");
370 int mask, counter;
372 if ((mask = fCapture.WaitInterrupts(sequence, when, fBufferPeriod)) == 0)
373 return 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 );
395 #define RGB32
396 #include "yuv_converter.h"
397 #undef RGB32
401 else if (colorSpace == B_RGB16 && bitsLength <= fBufferLength && bytesPerRow == fBufferBytesPerRow) {
402 fRadeon.DMACopy( captured_buffer, convert_buffer, fBufferLength, true, false );
404 #define RGB16
405 #include "yuv_converter.h"
406 #undef RGB16
410 else if (colorSpace == B_RGB15 && bitsLength <= fBufferLength && bytesPerRow == fBufferBytesPerRow) {
411 fRadeon.DMACopy( captured_buffer, convert_buffer, fBufferLength, true, false );
413 #define RGB15
414 #include "yuv_converter.h"
415 #undef RGB15
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 };
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"
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
444 else {
445 PRINT(("CVideoIn::Capture() - Bad buffer format\n"));
448 counter = *sequence - fBufferSequence;
449 fBufferSequence = *sequence;
450 return counter;
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
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 );