#include <Debug.h>
#include "VideoIn.h"
#include <memory.h>
static const theater_standard kStandard[] = {
C_THEATER_NTSC,
C_THEATER_NTSC_JAPAN,
C_THEATER_NTSC_443,
C_THEATER_PAL_M,
C_THEATER_PAL_BDGHI,
C_THEATER_PAL_N,
C_THEATER_PAL_60,
C_THEATER_PAL_NC,
C_THEATER_SECAM,
};
static const theater_source kSource[] = {
C_THEATER_TUNER,
C_THEATER_COMPOSITE,
C_THEATER_SVIDEO
};
static const capture_buffer_mode kMode[] = {
C_RADEON_CAPTURE_FIELD_DOUBLE,
C_RADEON_CAPTURE_BOB_DOUBLE,
C_RADEON_CAPTURE_WEAVE_DOUBLE
};
static const struct {
struct {
int width, height;
} total;
struct {
int left, top;
int width, height;
} active;
struct {
int left, top;
int width, height;
} vbi;
} kTiming[] = {
{ { 910, 525 }, { 112, 37, 755, 480 }, { 73, 13, 798, 20 } },
{ { 910, 525 }, { 112, 37, 755, 480 }, { 73, 13, 798, 24 } },
{ { 1042, 525 }, { 126, 37, 940, 480 }, { 73, 13, 798, 24 } },
{ { 910, 525 }, { 103, 37, 764, 480 }, { 73, 13, 798, 24 } },
{ { 1135, 625 }, { 154, 35, 928, 576 }, { 132, 11, 924, 24 } },
{ { 1135, 625 }, { 154, 35, 928, 576 }, { 132, 11, 924, 24 } },
{ { 1125, 625 }, { 132, 37, 736, 480 }, { 100, 13, 770, 26 } },
{ { 910, 625 }, { 125, 35, 957, 576 }, { 132, 11, 924, 24 } },
{ { 1135, 625 }, { 149, 35, 933, 576 }, { 132, 11, 924, 24 } },
{ { 910, 525 }, { 112, 37, 755, 480 }, { 73, 13, 798, 22 } }
};
class CTheater100;
class CTheater200;
CVideoIn::CVideoIn( const char *dev_name )
: fRadeon( dev_name ),
fCapture(fRadeon),
fI2CPort(fRadeon),
fTheater(NULL),
fTuner(fI2CPort),
fSound(fI2CPort),
fBuffer0(0),
fBuffer1(0),
fBuffer0Handle(0),
fBuffer1Handle(0),
convert_buffer( NULL ),
fBufferLength(0),
fBufferBytesPerRow(0),
fBufferSequence(0),
fBufferPeriod(0),
started( false )
{
Trace("CVideoIn::CVideoIn()");
}
CVideoIn::~CVideoIn()
{
Trace("CVideoIn::~CVideoIn()");
FreeBuffers();
}
status_t CVideoIn::InitCheck()
{
status_t res;
int device;
Trace("CVideoIn::InitCheck()");
if( (res = fRadeon.InitCheck()) != B_OK )
return res;
if( (res = fCapture.InitCheck()) != B_OK )
return res;
if( (res = fI2CPort.InitCheck()) != B_OK )
return res;
if ((device = fRadeon.FindVIPDevice( C_THEATER100_VIP_DEVICE_ID )) != -1)
{
Trace("CVideoIn::Found Rage Theater 100");
fTheater = new CTheater100(fRadeon, device);
}
else if ((device = fRadeon.FindVIPDevice( C_THEATER200_VIP_DEVICE_ID )) != -1)
{
Trace("CVideoIn::Found Rage Theater 200");
fTheater = new CTheater200(fRadeon, device);
}
if (fTheater)
{
res = fTheater->InitCheck();
}
else
{
res = B_ERROR;
}
return res;
}
int CVideoIn::Capabilities() const
{
return fTheater->Capabilities() +
(fTuner.InitCheck() == B_OK ? C_VIDEO_IN_HAS_TUNER : 0) +
(fSound.InitCheck() == B_OK ? C_VIDEO_IN_HAS_SOUND : 0);
}
void CVideoIn::Start(video_in_source source, video_in_standard standard,
video_in_capture_mode mode, int width, int height)
{
char buffer[256];
sprintf(buffer, "CVideoIn::Start(%s, %d, %d)",
mode == C_VIDEO_IN_FIELD ? "C_VIDEO_IN_FIELD" :
mode == C_VIDEO_IN_BOB ? "C_VIDEO_IN_BOB" : "C_VIDEO_IN_WEAVE", width, height);
Trace(buffer);
if( started )
Stop();
switch (mode) {
case C_VIDEO_IN_FIELD:
case C_VIDEO_IN_BOB:
width = Clamp(width, 0, kTiming[standard].active.width);
height = Clamp(height, 0, kTiming[standard].active.height / 2);
break;
case C_VIDEO_IN_WEAVE:
width = Clamp(width, 0, kTiming[standard].active.width);
height = Clamp(height, 0, kTiming[standard].active.height);
break;
}
fBufferBytesPerRow = (2 * width + 15) & ~15;
fBufferLength = (fBufferBytesPerRow * height + 15) & ~15;
FreeBuffers();
fRadeon.AllocateGraphicsMemory(
mt_local,
mode == C_VIDEO_IN_BOB ? 4 * fBufferLength : 2 * fBufferLength,
&fBuffer0, &fBuffer0Handle );
fRadeon.AllocateGraphicsMemory(
mt_local,
mode == C_VIDEO_IN_BOB ? 2 * fBufferLength : 1 * fBufferLength,
&fBuffer1, &fBuffer1Handle );
convert_buffer = malloc( mode == C_VIDEO_IN_BOB ? 4 * fBufferLength : 2 * fBufferLength );
fBufferPeriod = 1000000000LL / getFrameRate( standard );
if( mode == C_VIDEO_IN_BOB )
fBufferPeriod >>= 1;
fTheater->SetStandard(kStandard[standard], kSource[source]);
fTheater->SetSize(width, (mode != C_VIDEO_IN_WEAVE ? 2 * height : height));
fCapture.SetBuffer(C_RADEON_CAPTURE_CCIR656, kMode[mode], fBuffer0, fBuffer1, fBufferLength, fBufferBytesPerRow >> 1);
fCapture.SetClip(0, kTiming[standard].vbi.height, width - 1, kTiming[standard].vbi.height + (mode != C_VIDEO_IN_WEAVE ? height : height >> 1) - 1);
fTheater->SetEnable(true, false);
if( fSound.InitCheck() == B_OK )
fSound.SetEnable(true);
fCapture.SetInterrupts(true);
fCapture.Start();
}
void CVideoIn::Stop()
{
Trace("CVideoIn::Stop()");
if( !started )
return;
fCapture.Stop();
fCapture.SetInterrupts(false);
if( fSound.InitCheck() == B_OK )
fSound.SetEnable(false);
fTheater->SetEnable(false, false);
FreeBuffers();
}
void CVideoIn::FreeBuffers()
{
if( fBuffer0Handle > 0 ) {
fRadeon.FreeGraphicsMemory( mt_local, fBuffer0Handle );
fBuffer0Handle = 0;
}
if( fBuffer1Handle > 0 ) {
fRadeon.FreeGraphicsMemory( mt_local, fBuffer1Handle );
fBuffer1Handle = 0;
}
if( convert_buffer != NULL ) {
free( convert_buffer );
convert_buffer = NULL;
}
}
void CVideoIn::SetBrightness(int brightness)
{
Trace("CVideoIn::SetBrightness()");
fTheater->SetBrightness(brightness);
}
void CVideoIn::SetContrast(int contrast)
{
Trace("CVideoIn::SetContrast()");
fTheater->SetContrast(contrast);
}
void CVideoIn::SetSaturation(int saturation)
{
Trace("CVideoIn::SetSaturation()");
fTheater->SetSaturation(saturation);
}
void CVideoIn::SetHue(int hue)
{
Trace("CVideoIn::SetHue()");
fTheater->SetHue(hue);
}
void CVideoIn::SetSharpness(int sharpness)
{
Trace("CVideoIn::SetSharpness()");
fTheater->SetSharpness(sharpness);
}
void CVideoIn::SetFrequency(float frequency, float picture)
{
Trace("CVideoIn::SetFrequency()");
if (fTuner.Type() != C_TUNER_NONE)
fTuner.SweepFrequency(frequency, picture);
}
float CVideoIn::FrequencyForChannel(int channel, video_in_standard standard)
{
float frequency = 0;
Trace("CVideoIn::FrequencyForChannel()");
if (fTuner.Type() != C_TUNER_NONE) {
switch (standard) {
case C_VIDEO_IN_NTSC:
case C_VIDEO_IN_NTSC_RAW:
if (channel >= 2 && channel <= 6) {
frequency = 55.25 + 6.00 * (channel - 2);
}
else if (channel >= 7 && channel <= 13) {
frequency = 175.25 + 6.00 * (channel - 7);
}
else if (channel >= 14 && channel <= 22) {
frequency = 121.25 + 6.00 * (channel - 14);
}
else if (channel >= 23 && channel <= 36) {
frequency = 217.25 + 6.00 * (channel - 23);
}
else if (channel >= 37 && channel <= 62) {
frequency = 301.25 + 6.00 * (channel - 37);
}
else if (channel >= 63 && channel <= 94) {
frequency = 457.25 + 6.00 * (channel - 63);
}
else if (channel >= 95 && channel <= 99) {
frequency = 91.25 + 6.00 * (channel - 95);
}
else if (channel >= 100 && channel <= 125) {
frequency = 649.25 + 6.00 * (channel - 100);
}
else {
frequency = 0;
}
break;
case C_VIDEO_IN_NTSC_JAPAN:
case C_VIDEO_IN_NTSC_443:
case C_VIDEO_IN_PAL_M:
case C_VIDEO_IN_PAL_BDGHI:
case C_VIDEO_IN_PAL_N:
case C_VIDEO_IN_PAL_60:
case C_VIDEO_IN_PAL_NC:
case C_VIDEO_IN_SECAM:
break;
}
}
return frequency;
}
bool CVideoIn::SetChannel(int channel, video_in_standard standard)
{
Trace("CVideoIn::SetChannel()");
if (fTuner.Type() == C_TUNER_NONE)
return true;
const float frequency = FrequencyForChannel(channel, standard);
switch (standard) {
case C_VIDEO_IN_NTSC:
case C_VIDEO_IN_NTSC_RAW:
return fTuner.SweepFrequency(frequency, C_TUNER_NTSC_PICTURE_CARRIER / 100.0f);
break;
case C_VIDEO_IN_NTSC_JAPAN:
case C_VIDEO_IN_NTSC_443:
case C_VIDEO_IN_PAL_M:
case C_VIDEO_IN_PAL_BDGHI:
case C_VIDEO_IN_PAL_N:
case C_VIDEO_IN_PAL_60:
case C_VIDEO_IN_PAL_NC:
case C_VIDEO_IN_SECAM:
return fTuner.SweepFrequency(frequency, C_TUNER_PAL_PICTURE_CARRIER / 100.0f);
}
return false;
}
int CVideoIn::Capture(color_space colorSpace, void * bits, int bitsLength,
int bytesPerRow, int * sequence, short * number, bigtime_t * when)
{
int mask, counter;
if ((mask = fCapture.WaitInterrupts(sequence, when, fBufferPeriod)) == 0)
return 0;
*number = ((mask & (C_RADEON_CAPTURE_BUF0_INT | C_RADEON_CAPTURE_BUF1_INT)) != 0 ? 0 : 1);
int32 captured_buffer =
((mask & C_RADEON_CAPTURE_BUF0_INT) != 0 ? fBuffer0 :
(mask & C_RADEON_CAPTURE_BUF1_INT) != 0 ? fBuffer1 :
(mask & C_RADEON_CAPTURE_BUF0_EVEN_INT) != 0 ? fBuffer0 + fBufferLength :
(mask & C_RADEON_CAPTURE_BUF1_EVEN_INT) != 0 ? fBuffer1 + fBufferLength : 0);
if (colorSpace == B_YCbCr422 && bitsLength <= fBufferLength && bytesPerRow == fBufferBytesPerRow) {
fRadeon.DMACopy( captured_buffer, bits, bitsLength, true, false );
}
else if (colorSpace == B_RGB32 && bitsLength <= 2 * fBufferLength && bytesPerRow == 2 * fBufferBytesPerRow) {
fRadeon.DMACopy( captured_buffer, convert_buffer, fBufferLength, true, false );
#define RGB32
#include "yuv_converter.h"
#undef RGB32
}
else if (colorSpace == B_RGB16 && bitsLength <= fBufferLength && bytesPerRow == fBufferBytesPerRow) {
fRadeon.DMACopy( captured_buffer, convert_buffer, fBufferLength, true, false );
#define RGB16
#include "yuv_converter.h"
#undef RGB16
}
else if (colorSpace == B_RGB15 && bitsLength <= fBufferLength && bytesPerRow == fBufferBytesPerRow) {
fRadeon.DMACopy( captured_buffer, convert_buffer, fBufferLength, true, false );
#define RGB15
#include "yuv_converter.h"
#undef RGB15
}
else if (colorSpace == B_GRAY8 && 2 * bitsLength <= fBufferLength && 2 * bytesPerRow == fBufferBytesPerRow) {
fRadeon.DMACopy( captured_buffer, convert_buffer, fBufferLength, true, false );
static const unsigned short mask[] = {
0x00ff, 0x00ff, 0x00ff, 0x00ff };
asm volatile(
"1:\n"
"movq 0x00(%0),%%mm0\n"
"movq 0x08(%0),%%mm1\n"
"pand %3,%%mm0\n"
"pand %3,%%mm1\n"
"packuswb %%mm1,%%mm0\n"
"movq %%mm0,(%1)\n"
"addl $0x10,%0\n"
"addl $0x08,%1\n"
"subl $0x08,%2\n"
"jg 1b\n"
"emms\n"
:
: "r" (convert_buffer), "r" (bits), "r" (bitsLength), "m" (mask));
}
else if( colorSpace == B_NO_COLOR_SPACE ) {
;
}
else {
PRINT(("CVideoIn::Capture() - Bad buffer format\n"));
}
counter = *sequence - fBufferSequence;
fBufferSequence = *sequence;
return counter;
}
void CVideoIn::Trace(const char * message) const
{
PRINT(("\x1b[0;30;34m%s\x1b[0;30;47m\n", message));
}
int32 CVideoIn::getFrameRate( video_in_standard standard )
{
static const int32 frame_rate[] = {
29976, 29976, 29976, 25000, 25000, 25000, 29976, 25000, 25000, 29976
};
return frame_rate[standard];
}
void CVideoIn::getActiveRange( video_in_standard standard, CRadeonRect &rect )
{
rect.SetTo(
kTiming[standard].active.left,
kTiming[standard].active.top,
kTiming[standard].active.left + kTiming[standard].active.width - 1,
kTiming[standard].active.top + kTiming[standard].active.height - 1 );
}