diff options
Diffstat (limited to 'device.c')
-rw-r--r-- | device.c | 1705 |
1 files changed, 1705 insertions, 0 deletions
diff --git a/device.c b/device.c new file mode 100644 index 0000000..30069dd --- /dev/null +++ b/device.c @@ -0,0 +1,1705 @@ +#include "common.h" +#include <linux/dvb/video.h> +#include <stdlib.h> + +#if VDRVERSNUM > 10712 +#define PVR_SOURCEPARAMS +#include <vdr/sourceparams.h> +#endif + +char DRIVERNAME[][15] = { + "undef", "ivtv", "cx18", "pvrusb2", "cx88_blackbird", "hdpvr" +}; + +char CARDNAME[][9] = { + "undef", "PVR150", "PVR250", "PVR350", "PVR500#1", "PVR500#2", "HVR1300", "HVR1600", "HVR1900", "HVR1950", "PVRUSB2", "HDPVR" +}; + +cPvrDevice * PvrDevices[kMaxPvrDevices]; + +cString cPvrDevice::externChannelSwitchScript; + +#ifdef PVR_SOURCEPARAMS +class cPvrSourceParam : public cSourceParam { +private: + int param; + int input; + int standard; + int card; + cChannel data; +public: + cPvrSourceParam(); + cString ParametersToString(void) const; + void ParseParameters(const char* Parameters); + virtual void SetData(cChannel *Channel); + virtual void GetData(cChannel *Channel); + virtual cOsdItem *GetOsdItem(void); + + static bool IsPvr(int Code); + + static const char sSourceID = 'V'; + static const uint stPvr = (sSourceID << 24); + static const int sNumInputs = 12; + static const char* sInputs[]; + static const int sNumCards = 9; + static const char* sCards[]; + static const int sNumStandards = 31; + static const char* sStandards[]; +}; + +const char* cPvrSourceParam::sInputs[] = { + "TV", + "RADIO", + "COMPOSITE0", + "COMPOSITE1", + "COMPOSITE2", + "COMPOSITE3", + "COMPOSITE4", + "SVIDEO0", + "SVIDEO1", + "SVIDEO2", + "SVIDEO3", + "COMPONENT" +}; + +const char* cPvrSourceParam::sCards[] = { + "", + "CARD0", + "CARD1", + "CARD2", + "CARD3", + "CARD4", + "CARD5", + "CARD6", + "CARD7" +}; + +const char* cPvrSourceParam::sStandards[] = { + "", + "PAL", + "NTSC", + "SECAM", + "NTSC_M", + "NTSC_M_JP", + "NTSC_443", + "NTSC_M_KR", + "PAL_M", + "PAL_N", + "PAL_NC", + "PAL_B", + "PAL_B1", + "PAL_G", + "PAL_BG", + "PAL_D", + "PAL_D1", + "PAL_K", + "PAL_DK", + "PAL_H", + "PAL_I", + "PAL_60", + "SECAM_B", + "SECAM_D", + "SECAM_G", + "SECAM_H", + "SECAM_K", + "SECAM_K1", + "SECAM_DK", + "SECAM_L", + "SECAM_LC" +}; + +cPvrSourceParam::cPvrSourceParam() +:cSourceParam(sSourceID, "analog (pvrinput)") +{ + param = 0; + input = 0; + standard = 0; + card = 0; +} + +cString cPvrSourceParam::ParametersToString(void) const +{ + if ((standard == 0) && (card == 0)) + return cString::sprintf("%s", sInputs[input]); + if ((standard == 0) && (card != 0)) + return cString::sprintf("%s|%s", sInputs[input], sCards[card]); + if ((standard != 0) && (card == 0)) + return cString::sprintf("%s|%s", sInputs[input], sStandards[standard]); + return cString::sprintf("%s|%s|%s", sInputs[input], sCards[card], sStandards[standard]); +} + +void cPvrSourceParam::ParseParameters(const char* Parameters) { + char * Input = NULL; + char * optArg[2] = { NULL, NULL }; + int NumArgs = sscanf(Parameters, "%a[^|]|%a[^|]|%a[^:\n]", &Input, &optArg[0], &optArg[1]); + input = 0; + card = 0; + standard = 0; + if (NumArgs >= 1) { + for (int i = 0; sInputs[i]; i++) { + if (!strcasecmp (Input, sInputs[i])) { + input = i; + break; + } + } + } + for (int opt = 0; opt < 2; opt++) { + if (NumArgs >= (opt + 2)) { + bool parsed = false; + for (int c = 1; sCards[c]; c++) { + if (!strcasecmp (optArg[opt], sCards[c])) { + card = c; + parsed = true; + break; + } + } + if (!parsed) { + for (int s = 1; sStandards[s]; s++) { + if (!strcasecmp (optArg[opt], sStandards[s])) { + standard = s; + break; + } + } + } + } + } +} + +void cPvrSourceParam::SetData(cChannel *Channel) +{ + data = *Channel; + ParseParameters(data.Parameters()); + param = 0; +} + +void cPvrSourceParam::GetData(cChannel *Channel) +{ + data.SetTransponderData(Channel->Source(), Channel->Frequency(), Channel->Srate(), ParametersToString(), true); + *Channel = data; +} + +cOsdItem *cPvrSourceParam::GetOsdItem(void) +{ + switch (param++) { + case 0: return new cMenuEditStraItem(tr("SourceParam.pvrinput$Input"), &input, sNumInputs, sInputs); + case 1: return new cMenuEditStraItem(tr("SourceParam.pvrinput$Card"), &card, sNumCards, sCards); + case 2: return new cMenuEditStraItem(tr("SourceParam.pvrinput$Standard"), &standard, sNumStandards, sStandards); + default: return NULL; + } + return NULL; +} + +bool cPvrSourceParam::IsPvr(int Code) { + return (Code & cSource::st_Mask) == stPvr; +} +#endif + +static void cPvrSourceParam_Initialize() { +#ifdef PVR_SOURCEPARAMS + new cPvrSourceParam(); +#endif +} + +cPvrDevice::cPvrDevice(int DeviceNumber) +: number(DeviceNumber), + frequency(-1), + CurrentNorm(0), //uint64_t can't be negative + CurrentLinesPerFrame(-1), + CurrentInput(-1), + SupportsSlicedVBI(false), + hasDecoder(false), + hasTuner(true), + streamType(0), + dvrOpen(false), + delivered(false), + isClosing(false), + readThreadRunning(false), + ChannelSettingsDone(false), + FirstChannelSwitch(true), + pvrusb2_ready(true), + driver(undef), + cardname(UNDEF), + tsBufferPrefill(0), + readThread(0) +{ log(pvrDEBUG2,"new cPvrDevice (%d)", number); + v4l2_fd = mpeg_fd = radio_fd = -1; + v4l2_dev = mpeg_dev = radio_dev = -1; + vpid = apid = tpid = -1; + cString devName; + struct v4l2_capability video_vcap; + struct v4l2_capability capability; + struct v4l2_input input; + int i; + // put all values here that are set only *once* + v4l2_dev = number; + devName = cString::sprintf("/dev/video%d", v4l2_dev); + v4l2_fd = open(devName, O_RDWR); + if (v4l2_fd < 0) { + log(pvrERROR, "cPvrDevice::cPvrDevice(): error opening video device %s: %s", + *devName, strerror(errno)); + } + memset(&video_vcap, 0, sizeof(video_vcap)); + IOCTL(v4l2_fd, VIDIOC_QUERYCAP, &video_vcap); + log(pvrDEBUG1, "%s = %s", *devName, video_vcap.card); + BusID = cString::sprintf("%s", video_vcap.bus_info); + log(pvrDEBUG1, "BusID = %s", *BusID); + if (!memcmp(video_vcap.driver, "ivtv",4)) driver = ivtv; + else if (!memcmp(video_vcap.driver, "cx18",4)) driver = cx18; + else if (!memcmp(video_vcap.driver, "pvrusb2",7)) driver = pvrusb2; + else if (!memcmp(video_vcap.driver, "cx88_blackbird",14)) driver = cx88_blackbird; + else if (!memcmp(video_vcap.driver, "hdpvr",5)) driver = hdpvr; + else { driver = undef; log(pvrDEBUG1, "unknown video driver: \"%s\"",video_vcap.driver); } + if (!memcmp(video_vcap.card, "Hauppauge WinTV PVR-150",23)) cardname = PVR150; + else if (!memcmp(video_vcap.card, "Hauppauge WinTV PVR-250",23)) cardname = PVR250; + else if (!memcmp(video_vcap.card, "Hauppauge WinTV PVR-350",23)) cardname = PVR350; + else if (!memcmp(video_vcap.card, "WinTV PVR 500 (unit #1)",23)) cardname = PVR500_1; + else if (!memcmp(video_vcap.card, "WinTV PVR 500 (unit #2)",23)) cardname = PVR500_2; + else if (!memcmp(video_vcap.card, "Hauppauge WinTV-HVR1300",23)) cardname = HVR1300; + else if (!memcmp(video_vcap.card, "Hauppauge HVR-1600",18)) cardname = HVR1600; + else if (!memcmp(video_vcap.card, "WinTV HVR-1900",14)) cardname = HVR1900; + else if (!memcmp(video_vcap.card, "WinTV HVR-1950",14)) cardname = HVR1950; + else if (!memcmp(video_vcap.card, "WinTV PVR USB2",14)) cardname = PVRUSB2; + else if (!memcmp(video_vcap.card, "Hauppauge HD PVR",16)) cardname = HDPVR; + else if (!memcmp(video_vcap.card, "Haupauge HD PVR",15)) cardname = HDPVR; // spelling error in hdpvr + else { cardname = UNDEF; log(pvrDEBUG1, "unknown video card: \"%s\"",video_vcap.card); } + if (cardname == HVR1300) log(pvrERROR, "HVR 1300 is not really supported yet"); + driver_apiversion = video_vcap.version; + log(pvrDEBUG1, "%s Version 0x%06x", DRIVERNAME[driver], driver_apiversion); + if ((video_vcap.capabilities & V4L2_CAP_SLICED_VBI_CAPTURE) && (driver != pvrusb2)) { + /*The pvrusb2 driver advertises vbi capability, although it isn't there. + This was fixed in v4l-dvb hg in 01/2009 and will hopefully be in Kernel 2.6.30*/ + SupportsSlicedVBI = true; + log(pvrDEBUG1, "%s supports sliced VBI Capture",*devName); + } + if (video_vcap.capabilities & V4L2_CAP_VIDEO_OUTPUT_OVERLAY) { + hasDecoder = true; //can only be a PVR350 + } + for (i=0; i<kMaxPvrDevices; i++) { + if (radio_dev<0 && (video_vcap.capabilities & V4L2_CAP_RADIO)) { //searching matching radio dev + devName = cString::sprintf("/dev/radio%d", i); + radio_fd = open(devName, O_RDWR); + if (radio_fd >= 0) { + memset(&capability, 0, sizeof(capability)); + if (IOCTL(radio_fd, VIDIOC_QUERYCAP, &capability) !=0) { + log(pvrERROR, "VIDIOC_QUERYCAP failed, %d:%s", + errno, strerror(errno)); + } + if (!strncmp(*BusID,(const char *) capability.bus_info, strlen(*BusID)-1)) { + radio_dev=i; // store info for later + log(pvrDEBUG1, "/dev/radio%d = FM radio dev",radio_dev); + } + close(radio_fd); // a pvrusb2 will remain on input 3. The bool FirstChannelSwitch will solve this later + radio_fd = -1; + } + } + if (mpeg_dev<0 && (driver == cx88_blackbird)) { // the blackbird uses two (!) different devices, search the other one. + close(v4l2_fd); + v4l2_fd = -1; + devName = cString::sprintf("/dev/video%d", i); + mpeg_fd = open(devName, O_RDWR); + if (mpeg_fd) { + memset(&capability, 0, sizeof(capability)); + IOCTL(mpeg_fd, VIDIOC_QUERYCAP, &capability); + if (!strncmp(*BusID ,(const char *) capability.bus_info, strlen(*BusID)-1) && + !strcmp("cx8800",(const char *) capability.driver)) { + mpeg_dev = v4l2_dev; //for this driver we found mpeg_dev up to now. + v4l2_dev = i; //reassigning, now with correct value. + log(pvrDEBUG1, "/dev/video%d = v4l2 dev (analog properties: volume/hue/brightness/inputs..)",v4l2_dev); + log(pvrDEBUG1, "/dev/video%d = mpeg dev (MPEG properties: bitrates/frame rate/filters..)",mpeg_dev); + } + close(mpeg_fd); + mpeg_fd = -1; + } + } + } // end device search loop + switch (driver) { + case ivtv: //ivtv, cx18, pvrusb2 and hdpvr share the same device. + case cx18: + case pvrusb2: + case hdpvr: + mpeg_dev = v4l2_dev; + mpeg_fd = v4l2_fd; + break; + case cx88_blackbird: + //blackbird: reopen mpeg device. //FIXME: WE SHOULD OPEN V4L2 DEV for inputs/picture properties/volume, mpeg for all other stuff + devName = cString::sprintf("/dev/video%d", mpeg_dev); + v4l2_fd = open(devName, O_RDWR); //FIXME: FOR NOW THIS IS A WILD MIXTURE. + break; + default:; + } + QueryAllControls(); //we have to split in mpeg and v4l2 here. + memset(&inputs, -1, sizeof(inputs)); + numInputs = 0; + for (i = 0;; i++) { + memset(&input, 0, sizeof(input)); + input.index = i; + if (IOCTL(v4l2_fd, VIDIOC_ENUMINPUT, &input) == -1) { + break; + } + else { + log(pvrDEBUG1, "input %d = %s", i, input.name); + numInputs++; + } + if (!memcmp(input.name, "Tuner", 5)) { inputs[eTelevision]=inputs[eRadio]=i; continue; } //ivtv: Radio and TV tuner are same input. + else if (!memcmp(input.name, "television", 10)) { inputs[eTelevision]=i; continue; } //pvrusb2 + else if (!memcmp(input.name, "Television", 10)) { inputs[eTelevision]=i; continue; } //cx88_blackbird + else if (!memcmp(input.name, "radio", 5)) { inputs[eRadio] =i; continue; } //pvrusb2 + else if (!memcmp(input.name, "Composite 0", 11)) { inputs[eComposite0]=i; continue; } + else if (!memcmp(input.name, "Composite 1", 11)) { inputs[eComposite1]=i; continue; } + else if (!memcmp(input.name, "Composite 2", 11)) { inputs[eComposite2]=i; continue; } + else if (!memcmp(input.name, "Composite 3", 11)) { inputs[eComposite3]=i; continue; } + else if (!memcmp(input.name, "Composite 4", 11)) { inputs[eComposite4]=i; continue; } + else if (!memcmp(input.name, "Composite1", 10)) { inputs[eComposite1]=i; continue; } //cx88_blackbird + else if (!memcmp(input.name, "composite", 9)) { inputs[eComposite0]=i; continue; } //pvrusb2 + else if (!memcmp(input.name, "Composite", 9)) { inputs[eComposite0]=i; continue; } //hdpvr + else if (!memcmp(input.name, "S-Video 3", 9)) { inputs[eSVideo3] =i; continue; } + else if (!memcmp(input.name, "S-Video 0", 9)) { inputs[eSVideo0] =i; continue; } + else if (!memcmp(input.name, "S-Video 1", 9)) { inputs[eSVideo1] =i; continue; } + else if (!memcmp(input.name, "S-Video 2", 9)) { inputs[eSVideo2] =i; continue; } + else if (!memcmp(input.name, "S-Video", 7)) { inputs[eSVideo0] =i; continue; } //cx88_blackbird & hdpvr + else if (!memcmp(input.name, "s-video", 7)) { inputs[eSVideo0] =i; continue; } //pvrusb2 + else if (!memcmp(input.name, "Component", 9)) { inputs[eComponent] =i; continue; } //hdpvr + else log(pvrERROR, "unknown input %s. PLEASE SEND LOG TO MAINTAINER.", input.name); + } + if (inputs[eTelevision] >= 0) + SetInput(inputs[eTelevision]); + else if ((driver == hdpvr) && (inputs[eComponent] >= 0)) { + hasTuner = false; + SetInput(inputs[eComponent]); + } + else { + hasTuner = false; + log(pvrERROR, "device has no tuner"); + } + tsBuffer = new cRingBufferLinear(MEGABYTE(PvrSetup.TsBufferSizeMB), TS_SIZE, false, "PVRTS"); + tsBuffer->SetTimeouts(100, 100); + ResetBuffering(); + GetStandard(); + if (driver == hdpvr) { + SetControlValue(&PvrSetup.HDPVR_AudioEncoding, PvrSetup.HDPVR_AudioEncoding.value); + SetAudioInput(PvrSetup.HDPVR_AudioInput); + } + else { + SetControlValue(&PvrSetup.AudioEncoding, V4L2_MPEG_AUDIO_ENCODING_LAYER_2); + SetControlValue(&PvrSetup.VideoBitratePeak, 15000000); + SetVideoSize(720, CurrentLinesPerFrame == 525 ? 480 : 576); + /* the driver will later automatically adjust the height depending on standard changes */ + } + ReInit(); + StartSectionHandler(); +} + +cPvrDevice::~cPvrDevice() { +#if VDRVERSNUM >= 10600 + StopSectionHandler(); +#endif + cRingBufferLinear * tsBuffer_tmp = tsBuffer; + log(pvrDEBUG2,"~cPvrDevice()"); + tsBuffer = NULL; + delete tsBuffer_tmp; + close(radio_fd); + close(v4l2_fd); +} + +bool cPvrDevice::Probe(int DeviceNumber) { + char device[256]; + struct v4l2_capability vcap; + struct v4l2_format vfmt; + int v4l2_fd; + bool found = false; + memset(&vcap, 0, sizeof(vcap)); + memset(&vfmt, 0, sizeof(vfmt)); + sprintf(device, "/dev/video%d", DeviceNumber); + v4l2_fd = open(device, O_RDONLY); + if (v4l2_fd >=0) { + IOCTL(v4l2_fd, VIDIOC_QUERYCAP, &vcap); + if (!memcmp(vcap.driver, "ivtv",4) || + !memcmp(vcap.driver, "cx18",4) || + !memcmp(vcap.driver, "pvrusb2", 7) || + !memcmp(vcap.driver, "hdpvr", 5)) + found = true; + if (!memcmp(vcap.driver, "cx88_blackbird", 14)) { + vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (! IOCTL(v4l2_fd, VIDIOC_G_FMT, &vfmt) && + (v4l2_fourcc('M','P','E','G') == vfmt.fmt.pix.pixelformat)) + found=true; + } + close(v4l2_fd); + if (found) log(pvrINFO, "cPvrDevice::Probe():found %s", vcap.card); + return found; + } + return false; +} + +bool cPvrDevice::Initialize(void) { + int found = 0; + cPvrSourceParam_Initialize(); + for (int i = 0; i < kMaxPvrDevices; i++) { + PvrDevices[i] = NULL; + if (Probe(i)) { + PvrDevices[i] = new cPvrDevice(i); + found++; + } + } + if (found) + log(pvrINFO, "cPvrDevice::Initialize(): found %d PVR device%s", found, found > 1 ? "s" : ""); + else + log(pvrINFO, "cPvrDevice::Initialize(): no PVR device found"); + externChannelSwitchScript = AddDirectory(cPlugin::ConfigDirectory(PLUGIN_NAME_I18N), "externchannelswitch.sh"); + return found > 0; +} + +void cPvrDevice::Stop(void) { + /* recursively stop all threads inside pvrinputs devices */ + for (int i = 0; i < kMaxPvrDevices; i++) { + if (PvrDevices[i]) { + if (PvrDevices[i]->readThread) { + log(pvrDEBUG2,"cPvrDevice::Stop() for Device %i", i); + PvrDevices[i]->StopReadThread(); + PvrDevices[i]->SetEncoderState(eStop); + PvrDevices[i]->SetVBImode(PvrDevices[i]->CurrentLinesPerFrame, 0); + } + } + } +} + +void cPvrDevice::GetStandard(void) { + v4l2_std_id std; + if (IOCTL(v4l2_fd, VIDIOC_G_STD, &std) == -1) { + log(pvrERROR, "error VIDIOC_G_STD on /dev/video%d (%s): %d:%s", \ + number, CARDNAME[cardname], errno, strerror(errno)); + } + if (std & V4L2_STD_625_50) { + CurrentLinesPerFrame = 625; + } + else { + CurrentLinesPerFrame = 525; + } + /* the driver will automatically set the height to 576 or 480 on each change */ + CurrentNorm = std; + log(pvrINFO, "cPvrDevice::CurrentNorm=0x%08llx, CurrentLinesPerFrame=%d on /dev/video%d (%s)", \ + std, CurrentLinesPerFrame, number, CARDNAME[cardname]); +} + +void cPvrDevice::StopReadThread(void) { + if (readThreadRunning) { + log(pvrDEBUG2,"cPvrDevice::StopReadThread on /dev/video%d (%s): read thread exists, delete it", \ + number, CARDNAME[cardname]); + cPvrReadThread * readThread_tmp = readThread; + readThread = NULL; + delete readThread_tmp; + } + else { + log(pvrDEBUG2,"cPvrDevice::StopReadThread: no read thread running on /dev/video%d (%s)", \ + number, CARDNAME[cardname]); + } +} + +void cPvrDevice::ReInitAll(void) { + log(pvrDEBUG1,"cPvrDevice::ReInitAll"); + int i; + for (i = 0; i < kMaxPvrDevices; i++) { + if (PvrDevices[i]) { + PvrDevices[i]->ReInit(); + } + } +} + +int cPvrDevice::Count() { + int count = 0; + for (int i = 0; i < kMaxPvrDevices; i++) { + if (PvrDevices[i]) + count++; + } + return count; +} + +cPvrDevice * cPvrDevice::Get(int index) { + int count = 0; + for (int i = 0; i < kMaxPvrDevices; i++) { + if (PvrDevices[i]) { + if (count == index) + return PvrDevices[i]; + count++; + } + } + return NULL; +} + +void cPvrDevice::ReInit(void) { + log(pvrDEBUG1,"cPvrDevice::ReInit /dev/video%d = %s (%s)", number, CARDNAME[cardname], DRIVERNAME[driver]); + SetTunerAudioMode(PvrSetup.TunerAudioMode); + SetInput(CurrentInput); + /* begin new v4l2 ivtv controls */ + if ((driver == cx18) || (driver == hdpvr)) + streamType = V4L2_MPEG_STREAM_TYPE_MPEG2_TS; + else + streamType = (PvrSetup.StreamType.value == 0) ? V4L2_MPEG_STREAM_TYPE_MPEG2_PS : V4L2_MPEG_STREAM_TYPE_MPEG2_DVD; + SetControlValue(&PvrSetup.StreamType, streamType); + SetControlValue(&PvrSetup.AspectRatio, PvrSetup.AspectRatio.value); + SetControlValue(&PvrSetup.AudioBitrate, PvrSetup.AudioBitrate.value); + SetControlValue(&PvrSetup.AudioSampling, PvrSetup.AudioSampling.value); + if (driver == hdpvr) { + SetControlValue(&PvrSetup.HDPVR_AudioEncoding, PvrSetup.HDPVR_AudioEncoding.value); + SetAudioInput(PvrSetup.HDPVR_AudioInput); + } + SetControlValue(&PvrSetup.VideoBitrateTV, PvrSetup.VideoBitrateTV.value); + SetControlValue(&PvrSetup.BitrateMode, PvrSetup.BitrateMode.value); + SetControlValue(&PvrSetup.GopSize, PvrSetup.GopSize.queryctrl.default_value); + SetControlValue(&PvrSetup.GopClosure, PvrSetup.GopClosure.queryctrl.default_value); + SetControlValue(&PvrSetup.BFrames, PvrSetup.BFrames.queryctrl.default_value); + SetControlValue(&PvrSetup.FilterSpatialMode, PvrSetup.FilterSpatialMode.value); + SetControlValue(&PvrSetup.FilterSpatial, PvrSetup.FilterSpatial.value); + SetControlValue(&PvrSetup.FilterLumaSpatialType, PvrSetup.FilterLumaSpatialType.value); + SetControlValue(&PvrSetup.FilterChromaSpatialType, PvrSetup.FilterChromaSpatialType.value); + SetControlValue(&PvrSetup.FilterTemporalMode, PvrSetup.FilterTemporalMode.value); + SetControlValue(&PvrSetup.FilterTemporal, PvrSetup.FilterTemporal.value); + SetControlValue(&PvrSetup.FilterMedianType, PvrSetup.FilterMedianType.value); + SetControlValue(&PvrSetup.FilterLumaMedianBottom, PvrSetup.FilterLumaMedianBottom.value); + SetControlValue(&PvrSetup.FilterLumaMedianTop, PvrSetup.FilterLumaMedianTop.value); + SetControlValue(&PvrSetup.FilterChromaMedianBottom, PvrSetup.FilterChromaMedianBottom.value); + SetControlValue(&PvrSetup.FilterChromaMedianTop, PvrSetup.FilterChromaMedianTop.value); + SetControlValue(&PvrSetup.Brightness, PvrSetup.Brightness.value); + SetControlValue(&PvrSetup.Contrast, PvrSetup.Contrast.value); + SetControlValue(&PvrSetup.Saturation, PvrSetup.Saturation.value); + SetControlValue(&PvrSetup.Hue, PvrSetup.Hue.value); + SetControlValue(&PvrSetup.VideoBitrateTV, PvrSetup.VideoBitrateTV.value); + if ((radio_fd >= 0) || (driver == pvrusb2 && CurrentInput == inputs[eRadio])) { + SetControlValue(&PvrSetup.AudioVolumeFM, PvrSetup.AudioVolumeFM.value); + SetControlValue(&PvrSetup.AudioMute, (int) (PvrSetup.AudioVolumeFM.value == 0)); + } + else { //no radio + SetAudioVolumeTV(); + SetControlValue(&PvrSetup.AudioMute, (int) (PvrSetup.AudioVolumeTVCommon.value == 0)); + } +} + +bool cPvrDevice::Tune(int freq) { + double fac = 16; + int freqaux = freq; + struct v4l2_frequency vf; + if (frequency == freq) return true; + if ((EncoderInput == eRadio) || (EncoderInput == eTelevision)) { + memset(&vf, 0, sizeof(vf)); + struct v4l2_tuner tuner; + memset(&tuner, 0, sizeof(tuner)); + if (IOCTL(v4l2_fd, VIDIOC_G_TUNER,&tuner) == 0) { + fac = (tuner.capability & V4L2_TUNER_CAP_LOW) ? 16000 : 16; + } + vf.tuner = 0; + vf.type = tuner.type; + vf.frequency = (int) ((double) freqaux * fac / 1000.0); + if (IOCTL(v4l2_fd, VIDIOC_S_FREQUENCY, &vf) == 0) { + log(pvrDEBUG1, "cPvrDevice::Tune(): set Frequency on /dev/video%d (%s) to %.2f MHz (%d)", \ + number, CARDNAME[cardname], vf.frequency / fac, vf.frequency); + } + else { + log(pvrERROR, "cPvrDevice::Tune(): error on /dev/video%d (%s) tuning to %.2f MHz (%d): %d:%s", \ + number, CARDNAME[cardname], vf.frequency / fac, vf.frequency, errno, strerror(errno)); + return false; + } + } + frequency = freq; + return true; +} + +bool cPvrDevice::SetInput(int input) { + if (input == CurrentInput && !FirstChannelSwitch) return true; + log(pvrDEBUG1,"cPvrDevice::SetInput on /dev/video%d (%s) to %d", number, CARDNAME[cardname], input); + if (IOCTL(v4l2_fd, VIDIOC_S_INPUT, &input) != 0) { + log(pvrERROR, "VIDIOC_S_INPUT failed on /dev/video%d (%s) for input%d, %d:%s", + number, CARDNAME[cardname], errno, strerror(errno)); + return false; + } +#if 1 /* workaround for pvrusb2 driver bug: no audio after switching from radio to TV */ + if (input == 0 && CurrentInput == 3 && driver == pvrusb2) { + usleep(200000); /* 200msec */ + log(pvrDEBUG2,"cPvrDevice::SetInput on /dev/video%d (%s) again to %d (workaround for driver bug)", + number, CARDNAME[cardname], input); + IOCTL(v4l2_fd, VIDIOC_S_INPUT, &input); //set input television again + } +#endif + CurrentInput=input; + if (driver == pvrusb2) { + usleep(100000); /* 100msec */ + if (CurrentInput == inputs[eRadio]) { + SetControlValue(&PvrSetup.AudioVolumeFM, + PvrSetup.AudioVolumeFM.value); + } + else { //television or extern + SetAudioVolumeTV(); + } + } + return true; +} + +bool cPvrDevice::SetAudioInput(int input) { + struct v4l2_audio a; + a.index = input; + log(pvrDEBUG1,"cPvrDevice::SetAudioInput on /dev/video%d (%s) to %d", number, CARDNAME[cardname], input); + if (IOCTL(v4l2_fd, VIDIOC_S_AUDIO, &a) != 0) { + log(pvrERROR, "VIDIOC_S_AUDIO failed on /dev/video%d (%s) for input%d, %d:%s", + number, CARDNAME[cardname], errno, strerror(errno)); + return false; + } + return true; +} + +bool cPvrDevice::SetVideoNorm(uint64_t norm) { + if (norm == CurrentNorm){ + return true; + } + log(pvrDEBUG1, "SetVideoNorm(0x%08llx) for /dev/video%d (%s)", norm, number, CARDNAME[cardname]); + if (hasDecoder) { + log(pvrDEBUG1, "cPvrDevice::we need to stop the PVR350 decoder"); + struct video_command cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd = VIDEO_CMD_STOP; + cmd.flags = VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY; + if (IOCTL(v4l2_fd, VIDEO_COMMAND, &cmd) < 0) { + log(pvrERROR, "pvrinput: VIDEO_CMD_STOP error on /dev/video%d=%d:%s", number, strerror(errno)); + } + } + if (IOCTL(v4l2_fd, VIDIOC_S_STD, &norm) !=0) { + log(pvrERROR, "cPvrDevice::SetVideoNorm() on /dev/video%d (%s) failed, %d:%s", \ + number, CARDNAME[cardname], errno, strerror(errno)); + return false; + } + CurrentNorm=norm; + return true; +} + +bool cPvrDevice::SetVideoSize(int width, int height) { + log(pvrDEBUG1,"cPvrDevice::SetVideoSize"); + struct v4l2_format vfmt; + + vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (IOCTL(v4l2_fd, VIDIOC_G_FMT, &vfmt) != 0) { + log(pvrERROR, "cPvrDevice::SetVideoSize(): VIDIOC_G_FMT failed on /dev/video%d (%s), %d:%s", \ + number, CARDNAME[cardname], errno, strerror(errno)); + return false; + } + vfmt.fmt.pix.width = width; + vfmt.fmt.pix.height = height; + if (IOCTL(v4l2_fd, VIDIOC_S_FMT, &vfmt) != 0) { + log(pvrERROR, "cPvrDevice::SetVideoSize(): VIDIOC_S_FMT failed on /dev/video%d (%s), %d:%s", \ + number, CARDNAME[cardname], errno, strerror(errno)); + return false; + } + return true; +} + +void cPvrDevice::SetTunerAudioMode(int tuneraudiomode) { + if (!hasTuner) + return; + log(pvrDEBUG1, "cPvrDevice::SetTunerAudioMode(%s) on /dev/video%d (%s)", \ + tuneraudiomode == V4L2_TUNER_MODE_MONO?"mono": + tuneraudiomode == V4L2_TUNER_MODE_STEREO?"stereo": + tuneraudiomode == V4L2_TUNER_MODE_LANG1?"lang1": + tuneraudiomode == V4L2_TUNER_MODE_LANG2?"lang2":"bilingual", \ + number, CARDNAME[cardname]); + struct v4l2_tuner vt; + memset(&vt, 0, sizeof(vt)); + vt.audmode = tuneraudiomode; + if (IOCTL(v4l2_fd, VIDIOC_S_TUNER, &vt)) + log(pvrERROR, "cPvrDevice::SetTunerAudioMode(%d): error %d:%s on /dev/video%d (%s)", \ + tuneraudiomode, errno, strerror(errno), number, CARDNAME[cardname]); +} + +void cPvrDevice::SetAudioVolumeTV() { + log(pvrDEBUG2, "AudioVolumeTVException.value=%d; AudioVolumeTVCommon.value=%d; number=%d, AudioVolumeTVExceptionCard=%d", \ + PvrSetup.AudioVolumeTVException.value, PvrSetup.AudioVolumeTVCommon.value, number, PvrSetup.AudioVolumeTVExceptionCard); + if ((PvrSetup.AudioVolumeTVExceptionCard >= 0 && PvrSetup.AudioVolumeTVExceptionCard <=7) \ + && (number == PvrSetup.AudioVolumeTVExceptionCard)) { //special value selected for a /dev/videoX number + log(pvrDEBUG1, "cPvrDevice::SetAudioVolumeTVException %d for /dev/video%d (%s)", \ + PvrSetup.AudioVolumeTVException.value, number, CARDNAME[cardname]); + SetControlValue(&PvrSetup.AudioVolumeTVException, PvrSetup.AudioVolumeTVException.value); + } + if (PvrSetup.AudioVolumeTVExceptionCard >= 9 && PvrSetup.AudioVolumeTVExceptionCard <=18) { //special value for card(s) with certain name + if ((cardname == PVR150 && PvrSetup.AudioVolumeTVExceptionCard ==9) || + (cardname == PVR250 && PvrSetup.AudioVolumeTVExceptionCard ==10) || + (cardname == PVR350 && PvrSetup.AudioVolumeTVExceptionCard ==11) || + (cardname == PVR500_1 && PvrSetup.AudioVolumeTVExceptionCard ==12) || + (cardname == PVR500_2 && PvrSetup.AudioVolumeTVExceptionCard ==13) || + (cardname == HVR1300 && PvrSetup.AudioVolumeTVExceptionCard ==14) || + (cardname == HVR1600 && PvrSetup.AudioVolumeTVExceptionCard ==15) || + (cardname == HVR1900 && PvrSetup.AudioVolumeTVExceptionCard ==16) || + (cardname == HVR1950 && PvrSetup.AudioVolumeTVExceptionCard ==17) || + (cardname == PVRUSB2 && PvrSetup.AudioVolumeTVExceptionCard ==18) ) { + log(pvrDEBUG1, "cPvrDevice::SetAudioVolumeTVException %d for /dev/video%d (%s)", \ + PvrSetup.AudioVolumeTVException.value, number, CARDNAME[cardname]); + SetControlValue(&PvrSetup.AudioVolumeTVException, PvrSetup.AudioVolumeTVException.value); + } + } + else { //no special value (PvrSetup.AudioVolumeTVExceptionCard ==8) + log(pvrDEBUG1, "cPvrDevice::SetAudioVolumeTVCommon %d for /dev/video%d (%s)", \ + PvrSetup.AudioVolumeTVCommon.value, number, CARDNAME[cardname]); + SetControlValue(&PvrSetup.AudioVolumeTVCommon, PvrSetup.AudioVolumeTVCommon.value); + } +} + +void cPvrDevice::SetEncoderState(eEncState state) { + log(pvrDEBUG1, "cPvrDevice::SetEncoderState (%s) for /dev/video%d (%s)", \ + state==eStop?"Stop":"Start", number, CARDNAME[cardname]); + if (driver == ivtv || driver == cx18 || driver == hdpvr) { + struct v4l2_encoder_cmd encoderCommand; + memset(&encoderCommand, 0, sizeof(encoderCommand)); + switch(state) { + case eStop : encoderCommand.cmd = V4L2_ENC_CMD_STOP; break; + case eStart : encoderCommand.cmd = V4L2_ENC_CMD_START; break; + } + if (IOCTL(v4l2_fd, VIDIOC_ENCODER_CMD, &encoderCommand)) + log(pvrERROR, "cPvrDevice::SetEncoderState(%s): error %d:%s on /dev/video%d (%s)", \ + state==eStop?"Stop":"Start", errno, strerror(errno), number, CARDNAME[cardname]); + } + if (driver == pvrusb2 && state == eStop) { + int retry_count = 5; + retry: + close(v4l2_fd); + v4l2_fd = -1; + log(pvrDEBUG2, "cPvrDevice::SetEncoderState (eStop): /dev/video%d (%s) is closed", \ + number, CARDNAME[cardname]); + pvrusb2_ready = false; + char * devName; + if (asprintf(&devName, "/dev/video%d", number) != -1) { + v4l2_fd = open(devName, O_RDWR); //reopen for tuning + } + if (v4l2_fd < 0) { + log(pvrERROR, "cPvrDevice::SetEncoderState(eStop): error reopening %s (%s): %d:%s", \ + CARDNAME[cardname], devName, errno, strerror(errno)); + free(devName); + retry_count--; + if (retry_count > 0) + goto retry; + } + else { + log(pvrDEBUG2, "cPvrDevice::SetEncoderState (eStop): %s (%s) successfully re-opened", \ + devName, CARDNAME[cardname]); + pvrusb2_ready = true; + free(devName); + } + } +} + +bool cPvrDevice::SetVBImode(int vbiLinesPerFrame, int vbistatus) { + if (v4l2_fd >= 0 && SupportsSlicedVBI ) { + log(pvrDEBUG1, "SetVBImode(%d, %d) on /dev/video%d (%s)", \ + vbiLinesPerFrame, vbistatus, number, CARDNAME[cardname]); + struct v4l2_format vbifmt; + struct v4l2_ext_controls ctrls; + struct v4l2_ext_control ctrl; + memset(&vbifmt, 0, sizeof(vbifmt)); + memset(&ctrls, 0, sizeof(ctrls)); + memset(&ctrl, 0, sizeof(ctrl)); + ctrl.id = V4L2_CID_MPEG_STREAM_VBI_FMT; + ctrl.value = vbistatus; + /* 0 = V4L2_MPEG_STREAM_VBI_FMT_NONE, No VBI in the MPEG stream + 1 = V4L2_MPEG_STREAM_VBI_FMT_IVTV, VBI in private packets, IVTV format */ + ctrls.ctrl_class=V4L2_CTRL_CLASS_MPEG; + ctrls.controls=&ctrl; + ctrls.count=1; + if (IOCTL(v4l2_fd, VIDIOC_S_EXT_CTRLS, &ctrls) != 0) { + log(pvrERROR, "cPvrDevice::SetVBImode(): error setting vbi mode (ctrls) on /dev/video%d (%s), %d:%s", + number, CARDNAME[cardname], errno, strerror(errno)); + return false; + } + if (ctrl.value == V4L2_MPEG_STREAM_VBI_FMT_IVTV && vbiLinesPerFrame == 625) { + vbifmt.fmt.sliced.service_set = V4L2_SLICED_TELETEXT_B; + vbifmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; + vbifmt.fmt.sliced.reserved[0] = 0; + vbifmt.fmt.sliced.reserved[1] = 0; + + if (IOCTL(v4l2_fd, VIDIOC_S_FMT, &vbifmt) < 0) { + log(pvrERROR, "cPvrDevice::SetVBImode():error setting vbi mode (fmt) on /dev/video%d (%s), %d:%s", + number, CARDNAME[cardname], errno, strerror(errno)); + return false; + } + } + } + return true; +} + +bool cPvrDevice::ParseChannel(const cChannel *Channel, int *input, uint64_t *norm, int *LinesPerFrame, + int *card, eVideoInputs *EncoderInput, int *apid, int *vpid, int *tpid) const { + *card = 999; + *norm = CurrentNorm; //make sure we have a value if channels.conf has no optArg for norm + *LinesPerFrame = CurrentLinesPerFrame; //see above + *input = -1; + if (Channel->IsCable() && Channel->Ca() == 0xA1) { + Skins.Message(mtError, tr("pvrinput no longer supports old channel syntax!"), 2); + log(pvrERROR, "cPvrDevice::pvrinput no longer supports old channel syntax!"); + } +#ifdef PVR_SOURCEPARAMS + if (cPvrSourceParam::IsPvr(Channel->Source())) { + const char * str = Channel->Parameters(); + const char * PluginId = "PVRINPUT"; + char * Input = NULL; + char * optArg1 = NULL; + char * optArg2 = NULL; + int NumArgs = 1 + sscanf(str, "%a[^|]|%a[^|]|%a[^:\n]", + &Input, &optArg1, &optArg2); +#else + if (Channel->IsPlug()) { + const char * str = Channel->PluginParam(); + char * PluginId = NULL; + char * Input = NULL; + char * optArg1 = NULL; + char * optArg2 = NULL; + int NumArgs = sscanf(str, "%a[^|]|%a[^|]|%a[^|]|%a[^:\n]", + &PluginId, &Input, &optArg1, &optArg2); +#endif + + if (NumArgs >= 2) { + if (strcasecmp(PluginId, "PVRINPUT")) return false; + log(pvrDEBUG1, "cPvrDevice::ParseChannel: using iptv channel syntax."); + *vpid = Channel->Vpid(); + *apid = Channel->Apid(0); + *tpid = Channel->Tpid(); + if (!strcasecmp (Input, "RADIO")) *EncoderInput=eRadio; + else if (!strcasecmp (Input, "TV")) *EncoderInput=eTelevision; + else if (!strncasecmp(Input, "COMPOSITE",9)) *EncoderInput=eExternalInput; + else if (!strncasecmp(Input, "SVIDEO",6)) *EncoderInput=eExternalInput; + else if (!strncasecmp(Input, "COMPONENT",9)) *EncoderInput=eExternalInput; + else return false; + if (!strcasecmp (Input, "TV")) *input = inputs[eTelevision]; + else if (!strcasecmp (Input, "RADIO")) *input = inputs[eRadio]; + else if (!strcasecmp (Input, "COMPOSITE0")) *input = inputs[eComposite0]; + else if (!strcasecmp (Input, "COMPOSITE1")) *input = inputs[eComposite1]; + else if (!strcasecmp (Input, "COMPOSITE2")) *input = inputs[eComposite2]; + else if (!strcasecmp (Input, "COMPOSITE3")) *input = inputs[eComposite3]; + else if (!strcasecmp (Input, "COMPOSITE4")) *input = inputs[eComposite4]; + else if (!strcasecmp (Input, "COMPOSITE")) *input = inputs[eComposite0]; + else if (!strcasecmp (Input, "SVIDEO0")) *input = inputs[eSVideo0]; + else if (!strcasecmp (Input, "SVIDEO1")) *input = inputs[eSVideo1]; + else if (!strcasecmp (Input, "SVIDEO2")) *input = inputs[eSVideo2]; + else if (!strcasecmp (Input, "SVIDEO3")) *input = inputs[eSVideo3]; + else if (!strcasecmp (Input, "SVIDEO")) *input = inputs[eSVideo0]; + else if (!strcasecmp (Input, "COMPONENT")) *input = inputs[eComponent]; + else return false; //unknown + if (NumArgs > 2) { /* only if optional arg norm or card given */ + if (!strcasecmp (optArg1, "PAL")) { *norm = V4L2_STD_PAL; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg1, "NTSC" )) { *norm = V4L2_STD_NTSC; *LinesPerFrame = 525; } + else if (!strcasecmp (optArg1, "SECAM")) { *norm = V4L2_STD_SECAM; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg1, "NTSC_M")) { *norm = V4L2_STD_NTSC_M; *LinesPerFrame = 525; } + else if (!strcasecmp (optArg1, "NTSC_M_JP")) { *norm = V4L2_STD_NTSC_M_JP; *LinesPerFrame = 525; } + else if (!strcasecmp (optArg1, "NTSC_443")) { *norm = V4L2_STD_NTSC_443; *LinesPerFrame = 525; } + else if (!strcasecmp (optArg1, "NTSC_M_KR")) { *norm = V4L2_STD_NTSC_M_KR; *LinesPerFrame = 525; } + else if (!strcasecmp (optArg1, "PAL_M")) { *norm = V4L2_STD_PAL_M; *LinesPerFrame = 525; } + else if (!strcasecmp (optArg1, "PAL_N")) { *norm = V4L2_STD_PAL_N; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg1, "PAL_NC")) { *norm = V4L2_STD_PAL_Nc; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg1, "PAL_B")) { *norm = V4L2_STD_PAL_B; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg1, "PAL_B1")) { *norm = V4L2_STD_PAL_B1; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg1, "PAL_G")) { *norm = V4L2_STD_PAL_G; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg1, "PAL_BG")) { *norm = V4L2_STD_PAL_BG; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg1, "PAL_D")) { *norm = V4L2_STD_PAL_D; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg1, "PAL_D1")) { *norm = V4L2_STD_PAL_D1; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg1, "PAL_K")) { *norm = V4L2_STD_PAL_K; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg1, "PAL_DK")) { *norm = V4L2_STD_PAL_DK; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg1, "PAL_H")) { *norm = V4L2_STD_PAL_H; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg1, "PAL_I")) { *norm = V4L2_STD_PAL_I; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg1, "PAL_60")) { *norm = V4L2_STD_PAL_60; *LinesPerFrame = 525; } + else if (!strcasecmp (optArg1, "SECAM_B")) { *norm = V4L2_STD_SECAM_B; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg1, "SECAM_D")) { *norm = V4L2_STD_SECAM_D; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg1, "SECAM_G")) { *norm = V4L2_STD_SECAM_G; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg1, "SECAM_H")) { *norm = V4L2_STD_SECAM_H; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg1, "SECAM_K")) { *norm = V4L2_STD_SECAM_K; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg1, "SECAM_K1")) { *norm = V4L2_STD_SECAM_K1; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg1, "SECAM_DK")) { *norm = V4L2_STD_SECAM_DK; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg1, "SECAM_L")) { *norm = V4L2_STD_SECAM_L; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg1, "SECAM_LC")) { *norm = V4L2_STD_SECAM_LC; *LinesPerFrame = 625; } + if (!strcasecmp (optArg1, "CARD0")) *card = 0; + else if (!strcasecmp (optArg1, "CARD1")) *card = 1; + else if (!strcasecmp (optArg1, "CARD2")) *card = 2; + else if (!strcasecmp (optArg1, "CARD3")) *card = 3; + else if (!strcasecmp (optArg1, "CARD4")) *card = 4; + else if (!strcasecmp (optArg1, "CARD5")) *card = 5; + else if (!strcasecmp (optArg1, "CARD6")) *card = 6; + else if (!strcasecmp (optArg1, "CARD7")) *card = 7; + } + if (NumArgs > 3) { /* only if optional arg norm or card given */ + if (!strcasecmp (optArg2, "PAL")) { *norm = V4L2_STD_PAL; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg2, "NTSC" )) { *norm = V4L2_STD_NTSC; *LinesPerFrame = 525; } + else if (!strcasecmp (optArg2, "SECAM")) { *norm = V4L2_STD_SECAM; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg2, "NTSC_M")) { *norm = V4L2_STD_NTSC_M; *LinesPerFrame = 525; } + else if (!strcasecmp (optArg2, "NTSC_M_JP")) { *norm = V4L2_STD_NTSC_M_JP; *LinesPerFrame = 525; } + else if (!strcasecmp (optArg2, "NTSC_443")) { *norm = V4L2_STD_NTSC_443; *LinesPerFrame = 525; } + else if (!strcasecmp (optArg2, "NTSC_M_KR")) { *norm = V4L2_STD_NTSC_M_KR; *LinesPerFrame = 525; } + else if (!strcasecmp (optArg2, "PAL_M")) { *norm = V4L2_STD_PAL_M; *LinesPerFrame = 525; } + else if (!strcasecmp (optArg2, "PAL_N")) { *norm = V4L2_STD_PAL_N; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg2, "PAL_NC")) { *norm = V4L2_STD_PAL_Nc; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg2, "PAL_B")) { *norm = V4L2_STD_PAL_B; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg2, "PAL_B1")) { *norm = V4L2_STD_PAL_B1; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg2, "PAL_G")) { *norm = V4L2_STD_PAL_G; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg2, "PAL_BG")) { *norm = V4L2_STD_PAL_BG; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg2, "PAL_D")) { *norm = V4L2_STD_PAL_D; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg2, "PAL_D1")) { *norm = V4L2_STD_PAL_D1; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg2, "PAL_K")) { *norm = V4L2_STD_PAL_K; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg2, "PAL_DK")) { *norm = V4L2_STD_PAL_DK; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg2, "PAL_H")) { *norm = V4L2_STD_PAL_H; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg2, "PAL_I")) { *norm = V4L2_STD_PAL_I; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg2, "PAL_60")) { *norm = V4L2_STD_PAL_60; *LinesPerFrame = 525; } + else if (!strcasecmp (optArg2, "SECAM_B")) { *norm = V4L2_STD_SECAM_B; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg2, "SECAM_D")) { *norm = V4L2_STD_SECAM_D; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg2, "SECAM_G")) { *norm = V4L2_STD_SECAM_G; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg2, "SECAM_H")) { *norm = V4L2_STD_SECAM_H; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg2, "SECAM_K")) { *norm = V4L2_STD_SECAM_K; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg2, "SECAM_K1")) { *norm = V4L2_STD_SECAM_K1; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg2, "SECAM_DK")) { *norm = V4L2_STD_SECAM_DK; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg2, "SECAM_L")) { *norm = V4L2_STD_SECAM_L; *LinesPerFrame = 625; } + else if (!strcasecmp (optArg2, "SECAM_LC")) { *norm = V4L2_STD_SECAM_LC; *LinesPerFrame = 625; } + if (!strcasecmp (optArg2, "CARD0")) *card = 0; + else if (!strcasecmp (optArg2, "CARD1")) *card = 1; + else if (!strcasecmp (optArg2, "CARD2")) *card = 2; + else if (!strcasecmp (optArg2, "CARD3")) *card = 3; + else if (!strcasecmp (optArg2, "CARD4")) *card = 4; + else if (!strcasecmp (optArg2, "CARD5")) *card = 5; + else if (!strcasecmp (optArg2, "CARD6")) *card = 6; + else if (!strcasecmp (optArg2, "CARD7")) *card = 7; + } + log(pvrDEBUG2, "ParseChannel %s input %d, norm=0x%08llx, card %d", + (*EncoderInput==eRadio)?"Radio":(*EncoderInput==eTelevision)?"TV":"Ext", + *input,*norm,*card); + return true; + } + } + return false; +} + +bool cPvrDevice::SetChannelDevice(const cChannel * Channel, bool LiveView) { + log(pvrDEBUG1, "cPvrDevice::SetChannelDevice %d (%s) %3.2fMHz (/dev/video%d = %s)", \ + Channel->Number(), Channel->Name(), (double) Channel->Frequency()/1000, number, CARDNAME[cardname]); + int input, LinesPerFrame, card; + uint64_t norm; + if (! ParseChannel(Channel,&input,&norm,&LinesPerFrame,&card,&EncoderInput,&apid,&vpid,&tpid)) + return false; + + if ((Channel->Number() == currentChannel.Number()) && (Channel->Frequency() == frequency) && (input == CurrentInput) && (norm == CurrentNorm)) + return true; + newFrequency = Channel->Frequency(); + newInput = input; + newNorm = norm; + newLinesPerFrame = LinesPerFrame; + newEncoderInput = EncoderInput; + ChannelSettingsDone = false; + currentChannel = *Channel; + return true; +} + +bool cPvrDevice::SetPid(cPidHandle * Handle, int Type, bool On) { + log(pvrDEBUG2, "cPvrDevice::SetPid %d = %s", Handle->pid, On?"On":"Off"); + return true; +} + +int cPvrDevice::OpenFilter(u_short Pid, u_char Tid, u_char Mask) +{ + int handle = sectionHandler.AddFilter(Pid, Tid, Mask); + log(pvrDEBUG2, "cPvrDevice::OpenFilter: /dev/video%d (%s) pid = %d, tid = %d, mask = %d, handle = %d", + number, CARDNAME[cardname], Pid, Tid, Mask, handle); + return handle; +} + +#if VDRVERSNUM >= 10600 +void cPvrDevice::CloseFilter(int Handle) +{ + log(pvrDEBUG2, "cPvrDevice::CloseFilter: /dev/video%d (%s) handle = %d", + number, CARDNAME[cardname], Handle); + sectionHandler.RemoveFilter(Handle); +} +#endif + +bool cPvrDevice::OpenDvr(void) { + log(pvrDEBUG1, "entering cPvrDevice::OpenDvr: Dvr of /dev/video%d (%s) is %s", + number, CARDNAME[cardname], (dvrOpen)?"open":"closed"); + delivered = false; + CloseDvr(); + while (dvrOpen) { //wait until CloseDvr has finnished + usleep(40000); + log(pvrDEBUG1, "OpenDvr: wait for CloseDvr on /dev/video%d (%s) to finnish", \ + number, CARDNAME[cardname]); + } + tsBuffer->Clear(); + ResetBuffering(); + if (!dvrOpen) { + if (driver == ivtv || driver == cx18) { + if (PvrSetup.repeat_ReInitAll_after_next_encoderstop) { + ReInitAll(); //some settings require an encoder stop, so we repeat them now + PvrSetup.repeat_ReInitAll_after_next_encoderstop = false; + } + } + if (!ChannelSettingsDone) { + if (driver == pvrusb2) { + while (!pvrusb2_ready) { //re-opening pvrusb2 in SetEncoderState might be in progress + usleep(40000); + log(pvrDEBUG1, "OpenDvr: wait until /dev/video%d (%s) is ready", \ + number, CARDNAME[cardname]); + } + } + switch (newEncoderInput) { + case eComposite0 ... eComposite4: //no break here, continuing at next case item + case eSVideo0 ... eSVideo3: //no break here, continuing at next case item + case eComponent: //no break here, continuing at next case item + case eExternalInput: + { + log(pvrDEBUG2,"channel is external input."); + if (radio_fd >= 0) { + close(radio_fd); + radio_fd = -1; + usleep(100000); /* 100msec */ + SetAudioVolumeTV(); + SetControlValue(&PvrSetup.VideoBitrateTV, \ + PvrSetup.VideoBitrateTV.value); + } + + if (PvrSetup.UseExternChannelSwitchScript) { + cString cmd = cString::sprintf("%s %d %d %d", *externChannelSwitchScript, currentChannel.Sid(), currentChannel.Number(), number); + log(pvrDEBUG1, "OpenDvr: calling %s", *cmd); + if (system(*cmd) < 0) { + log(pvrERROR, "OpenDvr: executing %s failed", *cmd); + } + log(pvrDEBUG1, "OpenDvr: returned from %s", *cmd); + } + + if (! SetInput(newInput)) return false; + if (! SetVideoNorm(newNorm)) return false; + frequency=newFrequency; // since we don't tune: set it here + break; + } + case eRadio : + { + log(pvrDEBUG2,"channel is FM radio."); + switch (driver) { + case ivtv: + case cx18: + if (radio_dev < 0) return false; //no hardware support. + if (radio_fd < 0) { + char * devName; + if (asprintf(&devName, "/dev/radio%d", radio_dev) != -1) { + radio_fd = open(devName, O_RDONLY); + } + if (radio_fd < 0) { + log(pvrERROR, "Error opening FM radio device %s: %s",\ + devName, strerror(errno)); + return false; + } + free(devName); + usleep(100000); /* 100msec */ + SetControlValue(&PvrSetup.AudioVolumeFM, \ + PvrSetup.AudioVolumeFM.value); + } + break; + case pvrusb2: + if (! SetInput(inputs[eRadio])) return false; + case cx88_blackbird: + case hdpvr: + break; + case undef: log(pvrERROR, "driver is unknown!!"); return false; + } + if (! Tune(newFrequency)) return false; + break; + } + case eTelevision : + { + log(pvrDEBUG2,"channel is television."); + if ((driver == ivtv) && (radio_fd >= 0)) { + close(radio_fd); + radio_fd = -1; + usleep(100000); /* 100msec */ + SetAudioVolumeTV(); + SetControlValue(&PvrSetup.VideoBitrateTV, \ + PvrSetup.VideoBitrateTV.value); + } + if (! SetInput(inputs[eTelevision])) return false; + if (! SetVideoNorm(newNorm)) return false; + if (! SetVBImode(newLinesPerFrame, PvrSetup.SliceVBI)) return false; + if (! Tune(newFrequency)) return false; + } + } //end: switch (newEncoderInput) + ChannelSettingsDone = true; + FirstChannelSwitch = false; + } //end: if ((!ChannelSettingsDone) + SetEncoderState(eStart); + if (!readThreadRunning) { + log(pvrDEBUG2, "cPvrDevice::OpenDvr: create new readThread on /dev/video%d (%s)", \ + number, CARDNAME[cardname]); + readThread = new cPvrReadThread(tsBuffer, this); + } + } //end: if (!dvrOpen) + dvrOpen = true; + return true; +} + +void cPvrDevice::CloseDvr(void) { + if (isClosing) return; + isClosing = true; + log(pvrDEBUG2, "entering cPvrDevice::CloseDvr: Dvr of /dev/video%d (%s) is %s", \ + number, CARDNAME[cardname], (dvrOpen)?"open":"closed"); + if (dvrOpen) { + StopReadThread(); + SetEncoderState(eStop); + SetVBImode(CurrentLinesPerFrame, 0); + } + dvrOpen = false; + isClosing = false; +} + +void cPvrDevice::ResetBuffering() { + tsBufferPrefill = (MEGABYTE(PvrSetup.TsBufferSizeMB) * PvrSetup.TsBufferPrefillRatio) / 100; + tsBufferPrefill -= (tsBufferPrefill % TS_SIZE); + log(pvrDEBUG2, "cPvrDevice::ResetBuffering(): tsBuffer prefill = %d for /dev/video%d (%s)", + tsBufferPrefill, number, CARDNAME[cardname]); +} + +bool cPvrDevice::IsBuffering() { + int avail = tsBuffer->Available(); + if (tsBufferPrefill && (avail < tsBufferPrefill)) { + log(pvrDEBUG2, "cPvrDevice::IsBuffering(): available = %d, prefill = %d for /dev/video%d (%s)", + avail, tsBufferPrefill, number, CARDNAME[cardname]); + return true; + } + tsBufferPrefill = 0; + return false; +} + +bool cPvrDevice::GetTSPacket(uchar *&Data) { + int Count = 0; + if (! tsBuffer ) { + log(pvrERROR, "cPvrDevice::GetTSPacket(): no tsBuffer for /dev/video%d (%s)", \ + number, CARDNAME[cardname]); + return false; + } + if (tsBuffer && readThreadRunning) { + if (!IsBuffering()) { + if (delivered) { + tsBuffer->Del(TS_SIZE); + delivered = false; + //log(pvrDEBUG2, "cPvrDevice::GetTSPacket(): packet delivered"); + } + uchar *p = tsBuffer->Get(Count); + if (p && Count >= TS_SIZE) { + if (*p != TS_SYNC_BYTE) { + for (int i = 1; i < Count; i++) { + if (p[i] == TS_SYNC_BYTE) { + Count = i; + break; + } //end: if + } //end: for + tsBuffer->Del(Count); + log(pvrINFO, "ERROR: cPvrDevice::GetTSPacket(): skipped %d bytes to sync on TS packet\n", Count); + return false; + } + sectionHandler.ProcessTSPacket(p); + delivered = true; + Data = p; + return true; + } + } + cCondWait::SleepMs(10); // reduce cpu load while buffering + Data = NULL; + return true; + } + return false; +} + +bool cPvrDevice::ProvidesSource(int Source) const { +#ifdef PVR_SOURCEPARAMS + if (cPvrSourceParam::IsPvr(Source)) { +#else + if (cSource::IsPlug(Source)) { +#endif + log(pvrDEBUG1, "cPvrDevice::ProvidesSource Source=%s -> true", + (const char *) cSource::ToString(Source)); + return true; + } + return false; +} + +bool cPvrDevice::ProvidesTransponder(const cChannel * Channel) const { + return ProvidesSource(Channel->Source()); +} + +int cPvrDevice::NumProvidedSystems(void) const +{ + return 1; +} + +bool cPvrDevice::ProvidesChannel(const cChannel * Channel, int Priority, \ + bool * NeedsDetachReceivers) const { + bool result = false; + bool hasPriority = Priority < 0 || Priority > this->Priority(); + bool needsDetachReceivers = true; + int input, LinesPerFrame, dev; + uint64_t norm; + eVideoInputs encoderInput; //only dummy + int audioPid,videoPid,teletextPid; //only dummy + if (! ParseChannel(Channel, &input, &norm, &LinesPerFrame, &dev, &encoderInput, &audioPid, &videoPid, &teletextPid)) + return false; // non-pvrinput channel or wrong syntax used, silently ignore this one + if ((dev < 999) && (number != dev)) { + log(pvrDEBUG1, "cPvrDevice::ProvidesChannel %s -> false (this is /dev/video%d, but CARD parameter says /dev/video%d only)", + Channel->Name(), number, dev); + return false; + } + if ((PvrSetup.UseOnlyCard >= 0 && PvrSetup.UseOnlyCard <=7) && (number != PvrSetup.UseOnlyCard)) { //select by number + log(pvrDEBUG1, "cPvrDevice::ProvidesChannel %s -> false (this is /dev/video%d, but /dev/video%d only was selected in setup)", + Channel->Name(), number, PvrSetup.UseOnlyCard); + return false; + } + if (PvrSetup.UseOnlyCard >= 9 && PvrSetup.UseOnlyCard <=19) { //select by Name + if ((cardname == PVR150 && PvrSetup.UseOnlyCard !=9) || + (cardname == PVR250 && PvrSetup.UseOnlyCard !=10) || + (cardname == PVR350 && PvrSetup.UseOnlyCard !=11) || + (cardname == PVR500_1 && PvrSetup.UseOnlyCard !=12) || + (cardname == PVR500_2 && PvrSetup.UseOnlyCard !=13) || + (cardname == HVR1300 && PvrSetup.UseOnlyCard !=14) || + (cardname == HVR1600 && PvrSetup.UseOnlyCard !=15) || + (cardname == HVR1900 && PvrSetup.UseOnlyCard !=16) || + (cardname == HVR1950 && PvrSetup.UseOnlyCard !=17) || + (cardname == PVRUSB2 && PvrSetup.UseOnlyCard !=18) || + (cardname == HDPVR && PvrSetup.UseOnlyCard !=19) ) { + log(pvrDEBUG1, "cPvrDevice::ProvidesChannel %s -> false (this is %s, but %s only was selected in setup)", + Channel->Name(),CARDNAME[cardname], + PvrSetup.UseOnlyCard==9?"PVR150": \ + PvrSetup.UseOnlyCard==10?"PVR250": \ + PvrSetup.UseOnlyCard==11?"PVR350": \ + PvrSetup.UseOnlyCard==12?"PVR500#1": \ + PvrSetup.UseOnlyCard==13?"PVR500#2": \ + PvrSetup.UseOnlyCard==14?"HVR1300": \ + PvrSetup.UseOnlyCard==15?"HVR1600": \ + PvrSetup.UseOnlyCard==16?"HVR1900": \ + PvrSetup.UseOnlyCard==17?"HVR1950": \ + PvrSetup.UseOnlyCard==18?"PVRUSB2": \ + PvrSetup.UseOnlyCard==19?"HDPVR":"UNDEF"); + return false; + } + } + if (Channel->Frequency() >= 87500 && Channel->Frequency() <= 108000) { + if (radio_dev < 0) { + log(pvrDEBUG1, "cPvrDevice::ProvidesChannel: /dev/video%d (%s) has no radio", number, CARDNAME[cardname]); + return false; + } + } + if ((input < 0) || (input >= numInputs)) { + log(pvrERROR, "cPvrDevice::ProvidesChannel: input %d unknown on /dev/video%d (%s)", \ + input, number, CARDNAME[cardname]); + return false; + } + if ((Priority >= 0) && Receiving(true)) { + if ((Channel->Number() == currentChannel.Number()) && (frequency == Channel->Frequency()) && (CurrentInput == input) && (CurrentNorm == norm)) { + needsDetachReceivers = false; + result = true; + log(pvrDEBUG1, "cPvrDevice::ProvidesChannel: %s already set -> true.", Channel->Name()); + } + else + result = hasPriority; + } + else + result = hasPriority; + if (NeedsDetachReceivers) + *NeedsDetachReceivers = needsDetachReceivers; + log(pvrDEBUG1, "cPvrDevice::ProvidesChannel: /dev/video%d (%s): Channel %d (%s) %3.2fMHz, -> %s", \ + number, CARDNAME[cardname], Channel->Number(), Channel->Name(), (double) Channel->Frequency()/1000, \ + result?"true":"false"); + log(pvrDEBUG2, "cPvrDevice::ProvidesChannel: /dev/video%d: Receiving()=%s, needsDetachReceivers=%s, Priority=%d, hasPriority=%s", \ + number, Receiving(true)?"true":"false", needsDetachReceivers?"true":"false", Priority, hasPriority?"true":"false"); + return result; +} + +/* +if a card/driver has unsupported controls, it returns false for this +*/ +bool cPvrDevice::ControlIdIsValid(__u32 ctrlid) { + if (driver == hdpvr) { + if (/*(ctrlid != V4L2_CID_BRIGHTNESS) // these controls seem to use other values for the HD PVR + && (ctrlid != V4L2_CID_CONTRAST) // than the other PVRs (ioctl returns "invalid argument") + && (ctrlid != V4L2_CID_SATURATION) // so ignore them for now, since the defaults seem to be ok. + && (ctrlid != V4L2_CID_HUE) + &&*/ (ctrlid != V4L2_CID_SHARPNESS) + && (ctrlid != V4L2_CID_MPEG_AUDIO_ENCODING) + && (ctrlid != V4L2_CID_MPEG_VIDEO_ENCODING) + && (ctrlid != V4L2_CID_MPEG_VIDEO_BITRATE_MODE) + && (ctrlid != V4L2_CID_MPEG_VIDEO_BITRATE) + && (ctrlid != V4L2_CID_MPEG_VIDEO_BITRATE_PEAK)) + return false; + } + return true; +} + +/* +Sets a V4L2 control. +If the value Val is out of range, the ivtv default value will be +used. The value set to V4L2 will be returned, either Val or the +default. +Has to be used *after* calling cPvrDevice::QueryAllControls() +20070506 +If possible, use THIS function for setting VIDIOC_S_EXT_CTRLS. +-wirbel- +*/ + +int cPvrDevice::SetControlValue(struct valSet * vs, __s32 Val) { + if (SetControlValue(vs->ctrl_class, vs->queryctrl.id, Val, vs->queryctrl) == 0) { + vs->value = Val; + return 0; + } + else + return -1; +} + +/* +functionality as above, but no valSet needed. +*/ + +int cPvrDevice::SetControlValue(__u32 control, __s32 Val) { + +__u32 ctrl_class=V4L2_CTRL_CLASS_MPEG; +struct v4l2_queryctrl * query; + +switch (control) { + // picture properties + case V4L2_CID_BRIGHTNESS: ctrl_class=V4L2_CTRL_CLASS_USER; + query=&PvrSetup.Brightness.queryctrl; + break; + case V4L2_CID_CONTRAST: ctrl_class=V4L2_CTRL_CLASS_USER; + query=&PvrSetup.Contrast.queryctrl; + break; + case V4L2_CID_SATURATION: ctrl_class=V4L2_CTRL_CLASS_USER; + query=&PvrSetup.Saturation.queryctrl; + break; + case V4L2_CID_HUE: ctrl_class=V4L2_CTRL_CLASS_USER; + query=&PvrSetup.Hue.queryctrl; + break; + // Audio + case V4L2_CID_AUDIO_VOLUME: ctrl_class=V4L2_CTRL_CLASS_USER; + query=&PvrSetup.AudioVolumeTVCommon.queryctrl; + break; + case V4L2_CID_AUDIO_MUTE: ctrl_class=V4L2_CTRL_CLASS_USER; + query=&PvrSetup.AudioMute.queryctrl; + break; + case V4L2_CID_MPEG_AUDIO_L2_BITRATE: + query=&PvrSetup.AudioBitrate.queryctrl; + break; + case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: + query=&PvrSetup.AudioSampling.queryctrl; + break; + case V4L2_CID_MPEG_AUDIO_ENCODING: + if (driver == hdpvr) + query=&PvrSetup.HDPVR_AudioEncoding.queryctrl; + else + query=&PvrSetup.AudioEncoding.queryctrl; + break; + // Video + case V4L2_CID_MPEG_VIDEO_BITRATE: + query=&PvrSetup.VideoBitrateTV.queryctrl; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: + query=&PvrSetup.VideoBitratePeak.queryctrl; + break; + case V4L2_CID_MPEG_VIDEO_ASPECT: + query=&PvrSetup.AspectRatio.queryctrl; + break; + // MPEG + case V4L2_CID_MPEG_STREAM_TYPE: + query=&PvrSetup.StreamType.queryctrl; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + query=&PvrSetup.BitrateMode.queryctrl; + break; + case V4L2_CID_MPEG_VIDEO_B_FRAMES: + query=&PvrSetup.BFrames.queryctrl; + break; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + query=&PvrSetup.GopSize.queryctrl; + break; + case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: + query=&PvrSetup.GopClosure.queryctrl; + break; + // Video Filters + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: + query=&PvrSetup.FilterSpatialMode.queryctrl; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: + query=&PvrSetup.FilterSpatial.queryctrl; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE: + query=&PvrSetup.FilterLumaSpatialType.queryctrl; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE: + query=&PvrSetup.FilterChromaSpatialType.queryctrl; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE: + query=&PvrSetup.FilterTemporalMode.queryctrl; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: + query=&PvrSetup.FilterTemporal.queryctrl; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE: + query=&PvrSetup.FilterMedianType.queryctrl; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: + query=&PvrSetup.FilterLumaMedianBottom.queryctrl; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: + query=&PvrSetup.FilterLumaMedianTop.queryctrl; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: + query=&PvrSetup.FilterChromaMedianBottom.queryctrl; + break; + case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: + query=&PvrSetup.FilterChromaMedianTop.queryctrl; + break; + if (SupportsSlicedVBI) { + // VBI + case V4L2_CID_MPEG_STREAM_VBI_FMT: + query=&PvrSetup.VBIformat.queryctrl; + break; + } + default: + log(pvrERROR, "SetControlValue(__u32 control, __s32 Val): ERROR: control %d not included in switch(control)",\ + control); + return -1; + } + + return SetControlValue(ctrl_class, control, Val, *query); +} + + +/* +Sets a V4L2 control. +If the value Val is out of range, the ivtv default value will be +used. The value set to V4L2 will be returned, either Val or the +default. +Has to be used *after* calling cPvrDevice::QueryAllControls(). +-wirbel- +*/ + +int cPvrDevice::SetControlValue(__u32 control_class, __u32 control, __s32 Val, \ + struct v4l2_queryctrl queryctrl) { + + struct v4l2_ext_controls ctrls; + struct v4l2_ext_control ctrl; + + if (!ControlIdIsValid(queryctrl.id)) // skip known unsupported controls of the driver with "no error" + return Val; + log (3, "SetControlValue(%s, %d)",queryctrl.name, Val); + if ((queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) == V4L2_CTRL_FLAG_DISABLED){ + log(pvrINFO, " Error: this control is disabled by driver. Skipped."); + return Val; + } + if ((Val>queryctrl.maximum) || (Val<queryctrl.minimum)) { + log(pvrINFO, " Info: setting value to default(%d)",queryctrl.default_value); + Val=queryctrl.default_value; + } + ctrl.id = control; + ctrl.value = Val; + + ctrls.ctrl_class=control_class; + ctrls.controls=&ctrl; + ctrls.count=1; + + if (IOCTL(v4l2_fd, VIDIOC_S_EXT_CTRLS, &ctrls) != 0) { + log(pvrERROR, "cPvrDevice::SetControlValue(): setting control value %s: %d:%s", + queryctrl.name, errno, strerror(errno)); + } + return Val; +} + + +/* +Queries the properties of a given valSet +20070510 +-> Before using function, valSet's queryctrl.id has to be set. +-wirbel- +*/ + +int cPvrDevice::QueryControl(struct valSet * vs) { + if (!ControlIdIsValid(vs->queryctrl.id)) // skip known unsupported controls of the driver with "no error" + return 0; + vs->query_isvalid = false; + if (vs->queryctrl.id == 0) { + log(pvrERROR, "cPvrDevice::QueryControl(): valSet not initialized."); + return -1; + } + if (IOCTL(v4l2_fd, VIDIOC_QUERYCTRL, &vs->queryctrl) != 0) { + log(pvrERROR, "QueryControl(): quering control %d failed.", vs->queryctrl.id); + return -1; + } + vs->query_isvalid = true; + return 0; +} + + +/* +Queries the properties of all valSets used in SetControlValue +20070510 +-wirbel- +*/ + +bool cPvrDevice::QueryAllControls(void) { + int err=0; + log(pvrDEBUG1, "QueryAllControls"); + + PvrSetup.Brightness.ctrl_class = V4L2_CTRL_CLASS_USER; + PvrSetup.Contrast.ctrl_class = V4L2_CTRL_CLASS_USER; + PvrSetup.Saturation.ctrl_class = V4L2_CTRL_CLASS_USER; + PvrSetup.Hue.ctrl_class = V4L2_CTRL_CLASS_USER; + // Audio + PvrSetup.AudioVolumeTVCommon.ctrl_class = V4L2_CTRL_CLASS_USER; + PvrSetup.AudioVolumeTVException.ctrl_class = V4L2_CTRL_CLASS_USER; + PvrSetup.AudioVolumeFM.ctrl_class = V4L2_CTRL_CLASS_USER; + PvrSetup.AudioMute.ctrl_class = V4L2_CTRL_CLASS_USER; + PvrSetup.AudioBitrate.ctrl_class = V4L2_CTRL_CLASS_MPEG; + PvrSetup.AudioSampling.ctrl_class = V4L2_CTRL_CLASS_MPEG; + PvrSetup.AudioEncoding.ctrl_class = V4L2_CTRL_CLASS_MPEG; + PvrSetup.HDPVR_AudioEncoding.ctrl_class = V4L2_CTRL_CLASS_MPEG; + // Video + PvrSetup.VideoBitrateTV.ctrl_class = V4L2_CTRL_CLASS_MPEG; + PvrSetup.VideoBitratePeak.ctrl_class = V4L2_CTRL_CLASS_MPEG; + PvrSetup.AspectRatio.ctrl_class = V4L2_CTRL_CLASS_MPEG; + // MPEG + PvrSetup.StreamType.ctrl_class = V4L2_CTRL_CLASS_MPEG; + PvrSetup.BitrateMode.ctrl_class = V4L2_CTRL_CLASS_MPEG; + PvrSetup.BFrames.ctrl_class = V4L2_CTRL_CLASS_MPEG; + PvrSetup.GopSize.ctrl_class = V4L2_CTRL_CLASS_MPEG; + PvrSetup.GopClosure.ctrl_class = V4L2_CTRL_CLASS_MPEG; + // Video Filters + PvrSetup.FilterSpatialMode.ctrl_class = V4L2_CTRL_CLASS_MPEG; + PvrSetup.FilterSpatial.ctrl_class = V4L2_CTRL_CLASS_MPEG; + PvrSetup.FilterLumaSpatialType.ctrl_class = V4L2_CTRL_CLASS_MPEG; + PvrSetup.FilterChromaSpatialType.ctrl_class = V4L2_CTRL_CLASS_MPEG; + PvrSetup.FilterTemporalMode.ctrl_class = V4L2_CTRL_CLASS_MPEG; + PvrSetup.FilterTemporal.ctrl_class = V4L2_CTRL_CLASS_MPEG; + PvrSetup.FilterMedianType.ctrl_class = V4L2_CTRL_CLASS_MPEG; + PvrSetup.FilterLumaMedianBottom.ctrl_class = V4L2_CTRL_CLASS_MPEG; + PvrSetup.FilterLumaMedianTop.ctrl_class = V4L2_CTRL_CLASS_MPEG; + PvrSetup.FilterChromaMedianBottom.ctrl_class = V4L2_CTRL_CLASS_MPEG; + PvrSetup.FilterChromaMedianTop.ctrl_class = V4L2_CTRL_CLASS_MPEG; + + PvrSetup.Brightness.queryctrl.id = V4L2_CID_BRIGHTNESS; + PvrSetup.Contrast.queryctrl.id = V4L2_CID_CONTRAST; + PvrSetup.Saturation.queryctrl.id = V4L2_CID_SATURATION; + PvrSetup.Hue.queryctrl.id = V4L2_CID_HUE; + // Audio + PvrSetup.AudioVolumeTVCommon.queryctrl.id = V4L2_CID_AUDIO_VOLUME; + PvrSetup.AudioVolumeTVException.queryctrl.id = V4L2_CID_AUDIO_VOLUME; + PvrSetup.AudioVolumeFM.queryctrl.id = V4L2_CID_AUDIO_VOLUME; + PvrSetup.AudioMute.queryctrl.id = V4L2_CID_AUDIO_MUTE; + PvrSetup.AudioBitrate.queryctrl.id = V4L2_CID_MPEG_AUDIO_L2_BITRATE; + PvrSetup.AudioSampling.queryctrl.id = V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ; + PvrSetup.AudioEncoding.queryctrl.id = V4L2_CID_MPEG_AUDIO_ENCODING; + PvrSetup.HDPVR_AudioEncoding.queryctrl.id = V4L2_CID_MPEG_AUDIO_ENCODING; + // Video + PvrSetup.VideoBitrateTV.queryctrl.id = V4L2_CID_MPEG_VIDEO_BITRATE; + PvrSetup.VideoBitratePeak.queryctrl.id = V4L2_CID_MPEG_VIDEO_BITRATE_PEAK; + PvrSetup.AspectRatio.queryctrl.id = V4L2_CID_MPEG_VIDEO_ASPECT; + // MPEG + PvrSetup.StreamType.queryctrl.id = V4L2_CID_MPEG_STREAM_TYPE; + PvrSetup.BitrateMode.queryctrl.id = V4L2_CID_MPEG_VIDEO_BITRATE_MODE; + PvrSetup.BFrames.queryctrl.id = V4L2_CID_MPEG_VIDEO_B_FRAMES; + PvrSetup.GopSize.queryctrl.id = V4L2_CID_MPEG_VIDEO_GOP_SIZE; + PvrSetup.GopClosure.queryctrl.id = V4L2_CID_MPEG_VIDEO_GOP_CLOSURE; + // Video Filters + PvrSetup.FilterSpatialMode.queryctrl.id = V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE; + PvrSetup.FilterSpatial.queryctrl.id = V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER; + PvrSetup.FilterLumaSpatialType.queryctrl.id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE; + PvrSetup.FilterChromaSpatialType.queryctrl.id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE; + PvrSetup.FilterTemporalMode.queryctrl.id = V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE; + PvrSetup.FilterTemporal.queryctrl.id = V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER; + PvrSetup.FilterMedianType.queryctrl.id = V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE; + PvrSetup.FilterLumaMedianBottom.queryctrl.id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM; + PvrSetup.FilterLumaMedianTop.queryctrl.id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP; + PvrSetup.FilterChromaMedianBottom.queryctrl.id= V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM; + PvrSetup.FilterChromaMedianTop.queryctrl.id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP; + if (SupportsSlicedVBI) { + // VBI + PvrSetup.VBIformat.queryctrl.id = V4L2_CID_MPEG_STREAM_VBI_FMT; + } + /* now quering min, max, default */ + // picture properties + err += QueryControl(&PvrSetup.Brightness); + err += QueryControl(&PvrSetup.Contrast); + err += QueryControl(&PvrSetup.Saturation); + err += QueryControl(&PvrSetup.Hue); + // Audio + err += QueryControl(&PvrSetup.AudioVolumeTVCommon); + err += QueryControl(&PvrSetup.AudioVolumeTVException); + err += QueryControl(&PvrSetup.AudioVolumeFM); + err += QueryControl(&PvrSetup.AudioMute); + err += QueryControl(&PvrSetup.AudioBitrate); + err += QueryControl(&PvrSetup.AudioSampling); + if (driver == hdpvr) + err += QueryControl(&PvrSetup.HDPVR_AudioEncoding); + else + err += QueryControl(&PvrSetup.AudioEncoding); + // Video + err += QueryControl(&PvrSetup.VideoBitrateTV); + err += QueryControl(&PvrSetup.VideoBitratePeak); + err += QueryControl(&PvrSetup.AspectRatio); + // MPEG + err += QueryControl(&PvrSetup.StreamType); + err += QueryControl(&PvrSetup.BitrateMode); + err += QueryControl(&PvrSetup.BFrames); + err += QueryControl(&PvrSetup.GopSize); + err += QueryControl(&PvrSetup.GopClosure); + // Video Filters + err += QueryControl(&PvrSetup.FilterSpatialMode); + err += QueryControl(&PvrSetup.FilterSpatial); + err += QueryControl(&PvrSetup.FilterLumaSpatialType); + err += QueryControl(&PvrSetup.FilterChromaSpatialType); + err += QueryControl(&PvrSetup.FilterTemporalMode); + err += QueryControl(&PvrSetup.FilterTemporal); + err += QueryControl(&PvrSetup.FilterMedianType); + err += QueryControl(&PvrSetup.FilterLumaMedianBottom); + err += QueryControl(&PvrSetup.FilterLumaMedianTop); + err += QueryControl(&PvrSetup.FilterChromaMedianBottom); + err += QueryControl(&PvrSetup.FilterChromaMedianTop); + if (SupportsSlicedVBI) { + // VBI + err += QueryControl(&PvrSetup.VBIformat); + } + if (err) + return false; + + // The following code checks wether a valSet.value is + // INVALID_VALUE and if so -> set it to its queryctrl.default_value. + // The macro INIT(v) is a abbreviation for the 'if .. then' comparison. + #define INIT(v) if (v.value == INVALID_VALUE) v.value=v.queryctrl.default_value + // picture properties + INIT(PvrSetup.Brightness); + INIT(PvrSetup.Contrast); + INIT(PvrSetup.Saturation); + INIT(PvrSetup.Hue); + // Audio + if (PvrSetup.AudioVolumeTVCommon.value == INVALID_VALUE) + PvrSetup.AudioVolumeTVCommon.value = \ + (int) (0.95 * PvrSetup.AudioVolumeTVCommon.queryctrl.maximum); + if (PvrSetup.AudioVolumeTVException.value == INVALID_VALUE) + PvrSetup.AudioVolumeTVException.value = \ + (int) (0.95 * PvrSetup.AudioVolumeTVException.queryctrl.maximum); + if (PvrSetup.AudioVolumeFM.value == INVALID_VALUE) + PvrSetup.AudioVolumeFM.value = \ + PvrSetup.AudioVolumeFM.queryctrl.maximum; + INIT(PvrSetup.AudioBitrate); + INIT(PvrSetup.AudioSampling); + if (driver == hdpvr) + INIT(PvrSetup.HDPVR_AudioEncoding); + // Video + INIT(PvrSetup.VideoBitrateTV); + INIT(PvrSetup.AspectRatio); + // MPEG + INIT(PvrSetup.BitrateMode); + INIT(PvrSetup.BFrames); + INIT(PvrSetup.GopSize); + INIT(PvrSetup.GopClosure); + // Video Filters + INIT(PvrSetup.FilterSpatialMode); + INIT(PvrSetup.FilterSpatial); + INIT(PvrSetup.FilterLumaSpatialType); + INIT(PvrSetup.FilterChromaSpatialType); + INIT(PvrSetup.FilterTemporalMode); + INIT(PvrSetup.FilterTemporal); + INIT(PvrSetup.FilterMedianType); + INIT(PvrSetup.FilterLumaMedianBottom); + INIT(PvrSetup.FilterLumaMedianTop); + INIT(PvrSetup.FilterChromaMedianBottom); + INIT(PvrSetup.FilterChromaMedianTop); + return true; +} + |