/** * DVB card access on the OS level (system function ioctl()), to tune. * * This needs to happen in C/C++, because ioctl() is not accessible from Java. * This implements the JNI library libzeipisdvbtune.so, a native C++ library * accessible from Java. It implements the |native| methods from the * org.bucksch.zeipis.recorder.DVBTunerNativeJNILinux Java class. * We do as little as possible in C++. */ #include "DVBTunerNativeJNILinux.h" #include #include #include #include #include void throwEx(JNIEnv* env, const char* message) { jclass eClass = env->FindClass("org/bucksch/zeipis/recorder/CantUseTVCard"); if (eClass == 0) // can't find exception class; unexpected return; env->ThrowNew(eClass, message); } #define CHECK_RV(message) \ if (rv < 0) \ { \ throwEx(env, message); \ close(frontendFD); \ return -1; \ } /** * Called with e.g. "/dev/dvb/adapter0/frontend0", false, 12187000, false, 27500000 (=VOX in Germany) */ JNIEXPORT jint JNICALL Java_org_bucksch_zeipis_recorder_DVBTunerNativeJNILinux_tuneToNative (JNIEnv* env, jobject self, jstring frontendFilename, jboolean dvbs2, jint frequency, jboolean polv, jint symbolrate) { // Open frontend device const char* frontendFilenameC = env->GetStringUTFChars(frontendFilename, 0); int frontendFD = open(frontendFilenameC, O_RDWR | O_NONBLOCK); env->ReleaseStringUTFChars(frontendFilename, frontendFilenameC); if (frontendFD < 0) { throwEx(env, "Can't get info from device"); return -1; } // Get info from device int rv; dvb_frontend_info info; rv = ioctl(frontendFD, FE_GET_INFO, &info); CHECK_RV("Can't get info from device"); #ifdef FE_GET_EXTENDED_INFO dvb_fe_caps_extended info2; if (dvbs2 && info.caps & FE_HAS_EXTENDED_INFO) // TODO { rv = ioctl(frontendFD, FE_GET_EXTENDED_INFO, &info2); CHECK_RV("Can't get extended info from device"); if (info2.modulations & MOD_8PSK) { // set card to DVB-S2 mode rv = ioctl(frontendFD, FE_SET_STANDARD, FE_DVB_S2) CHECK_RV("Failed to set card to DVB-S2 mode"); // get new info rv = ioctl(frontendFD, FE_GET_INFO, &info); CHECK_RV("Can't get info from device after setting to DVB-S2 mode"); } } #endif // Disecq rv = ioctl(frontendFD, FE_SET_VOLTAGE, polv ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18); CHECK_RV("Could not set disecq voltage for polarity"); // TODO diseqc power on // TODO more diseqc // CAM start // TODO // Put tuning data into struct #ifdef FE_GET_EXTENDED_INFO #define dvb_fe_params dvb_frontend_parameters_new #else #define dvb_fe_params dvb_frontend_parameters #endif dvb_fe_params p; // DVB-S p.frequency = frequency; p.u.qpsk.symbol_rate = symbolrate / 1000; // TODO inversion p.inversion = INVERSION_AUTO; if (info.caps & FE_CAN_FEC_AUTO) p.u.qpsk.fec_inner = FEC_AUTO; else { rv = -1; CHECK_RV("Card needs to support FEC INNER AUTO (TODO support setting it)"); // TODO fec inner } // TODO DVB-T // TODO DVB-C // Tuning #ifdef FE_GET_EXTENDED_INFO if (dvbs2) rv = ioctl(frontendFD, FE_SET_FRONTEND2, &p); else #endif rv = ioctl(frontendFD, FE_SET_FRONTEND, &p); CHECK_RV("Setting tuning params failed"); // Wait for lock bool haveLock = false; int loops = 0; while (!haveLock && loops++ < 50) // 5s { fe_status_t status; rv = ioctl(frontendFD, FE_READ_STATUS, &status); CHECK_RV("Reading status failed after setting tuning param"); haveLock = status & FE_HAS_LOCK; usleep(100000); // 0.1s } if (!haveLock) { uint16_t snr, sig; rv = ioctl(frontendFD, FE_READ_SIGNAL_STRENGTH, &sig); rv = ioctl(frontendFD, FE_READ_SNR, &snr); std::cout << "Tuning failed. Signal strength " << sig << ", Signal/noise ratio " << snr << "%" << std::endl; // TODO rv = -1; CHECK_RV("Tuning failed"); } return frontendFD; } JNIEXPORT jint JNICALL Java_org_bucksch_zeipis_recorder_DVBTunerNativeJNILinux_openDemuxNative (JNIEnv* env, jobject self, jstring demuxFilename, jint pid) { // Open demux device const char* demuxFilenameC = env->GetStringUTFChars(demuxFilename, 0); int demuxFD = open(demuxFilenameC, O_RDWR | O_NONBLOCK); env->ReleaseStringUTFChars(demuxFilename, demuxFilenameC); if (demuxFD < 0) { throwEx(env, "Could not open de-multiplexer device"); return -1; } // Set PID struct dmx_pes_filter_params pesfilter; pesfilter.pid = pid; pesfilter.input = DMX_IN_FRONTEND; pesfilter.output = DMX_OUT_TS_TAP; pesfilter.pes_type = DMX_PES_OTHER; // TODO DMX_PES_VIDEO, DMX_PES_AUDIO? pesfilter.flags = DMX_IMMEDIATE_START; int rv = ioctl(demuxFD, DMX_SET_PES_FILTER, &pesfilter); if (rv < 0) { throwEx(env, "Could not set PID"); close(demuxFD); return -1; } return demuxFD; } JNIEXPORT void JNICALL Java_org_bucksch_zeipis_recorder_DVBTunerNativeJNILinux_closeNative (JNIEnv* env, jobject self, jint fd) { close(fd); }