/** * 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; } /* from szap // Remove stale events struct dvb_frontend_event ev; while (ioctl(frontendFD, FE_GET_EVENT, &ev) >= 0) ; */ // 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 int sat_no = 0; bool hiband = false; // LNB config from szap // Universal LNB const int lnb_low_val = 9750; const int lnb_high_val = 10600; const int lnb_switch_val = 11700; if (lnb_switch_val && lnb_high_val && frequency >= lnb_switch_val) hiband = true; if (hiband) frequency = frequency - lnb_high_val; else { if (frequency < lnb_low_val) frequency = lnb_low_val - frequency; else frequency = frequency - lnb_low_val; } rv = ioctl(frontendFD, FE_SET_TONE, SEC_TONE_OFF); CHECK_RV("Cound not turn disecq tone"); rv = ioctl(frontendFD, FE_SET_VOLTAGE, polv ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18); CHECK_RV("Could not set disecq voltage for polarity"); usleep(15 * 1000); rv = ioctl(frontendFD, FE_SET_TONE, SEC_TONE_OFF); CHECK_RV("Cound not turn off disecq tone"); usleep(15 * 1000); struct dvb_diseqc_master_cmd disecq_cmd = {{0xe0, 0x10, 0x38, 0xf0, 0x00, 0x00}, 4}; disecq_cmd.msg[3] = 0xf0 | (((sat_no * 4) & 0x0f) | (hiband ? 1 : 0) | (polv ? 0 : 2)); rv = ioctl(frontendFD, FE_DISEQC_SEND_MASTER_CMD, &disecq_cmd); CHECK_RV("Cound send disecq master command"); usleep(15 * 1000); rv = ioctl(frontendFD, FE_DISEQC_SEND_BURST, (sat_no / 4) % 2 ? SEC_MINI_B : SEC_MINI_A); CHECK_RV("Cound set satellite on disecq"); usleep(15 * 1000); rv = ioctl(frontendFD, FE_SET_TONE, hiband ? SEC_TONE_ON : SEC_TONE_OFF); CHECK_RV("Cound set disecq tone"); // 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 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); env->ReleaseStringUTFChars(demuxFilename, demuxFilenameC); if (demuxFD < 0) { throwEx(env, "Could not open de-multiplexer device"); return -1; } // Buffer size From szap TODO needed? int rv = ioctl(demuxFD, DMX_SET_BUFFER_SIZE, 64 * 1024); if (rv < 0) { throwEx(env, "Setting buffer size for PID filter failed"); close(demuxFD); 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; 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); }