/* This file is part of JFLICKS. JFLICKS is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. JFLICKS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with JFLICKS. If not, see <http://www.gnu.org/licenses/>. */ package org.jflicks.tv.recorder.jhdhr; import java.io.File; import org.jflicks.configure.BaseConfiguration; import org.jflicks.configure.Configuration; import org.jflicks.configure.NameValue; import org.jflicks.job.JobContainer; import org.jflicks.job.JobManager; import org.jflicks.nms.NMSConstants; import org.jflicks.tv.Channel; import org.jflicks.tv.recorder.BaseRecorder; import org.jflicks.util.LogUtil; import org.jflicks.util.Util; /** * Class that can record from an HDHR. * * @author Doug Barnum * @version 1.0 */ public class HDHRRecorder extends BaseRecorder { private static final String VIDEO_TRANSCODE_OPTIONS = "Video Transcode Options"; private static final String TRANSCODE_MODE = "Transcode Mode"; private static final String FREQUENCY_TYPE = "Frequency Type"; private static final String AUTO = "auto"; private static final String US_BCAST = "us-bcast"; private static final String US_CABLE = "us-cable"; private static final String US_HRC = "us-hrc"; private static final String US_IRC = "us-irc"; private JobContainer jobContainer; private boolean useScanFile; private ScanFile scanFile; private String ipAddress; private String model; /** * Simple default constructor. */ public HDHRRecorder() { setTitle("HDHomerun"); setExtension("ts"); setQuickTunable(false); } /** * Modern HDHR devices can be streamed using their I{P address rather * than using hdhomerun_config and the device Id. This new way is * preferred if the hardware is capable or HLS recording is turned on. * * @return An IP address as a String. */ public String getIpAddress() { return (ipAddress); } /** * Modern HDHR devices can be streamed using their I{P address rather * than using hdhomerun_config and the device Id. This new way is * preferred if the hardware is capable or HLS recording is turned on. * * @param s An IP address as a String. */ public void setIpAddress(String s) { ipAddress = s; } public String getModel() { return (model); } public void setModel(String s) { model = s; } /** * {@inheritDoc} */ public void startRecording(Channel c, long duration, File destination, boolean live) { if (!isRecording()) { setStartedAt(System.currentTimeMillis()); setChannel(c); setDuration(duration); setDestination(destination); setRecording(true); setRecordingLiveTV(live); if (isHlsMode()) { if (isConfiguredTranscodeMode()) { HDHRRecorderTranscodeHlsJob job = new HDHRRecorderTranscodeHlsJob(this); JobContainer jc = JobManager.getJobContainer(job); setJobContainer(jc); jc.start(); } else { HDHRRecorderHlsJob job = new HDHRRecorderHlsJob(this); JobContainer jc = JobManager.getJobContainer(job); setJobContainer(jc); jc.start(); } } else { if (isConfiguredTranscodeMode()) { HDHRRecorderTranscodeJob job = new HDHRRecorderTranscodeJob(this); JobContainer jc = JobManager.getJobContainer(job); setJobContainer(jc); jc.start(); } else { HDHRRecorderJob job = new HDHRRecorderJob(this); JobContainer jc = JobManager.getJobContainer(job); setJobContainer(jc); jc.start(); } } } } /** * {@inheritDoc} */ public void stopRecording() { JobContainer jc = getJobContainer(); if (jc != null) { jc.stop(); setJobContainer(null); setRecording(false); setRecordingLiveTV(false); } } /** * {@inheritDoc} */ public void startStreaming(Channel c, String host, int port) { if (!isRecording()) { setChannel(c); setHost(host); setPort(port); setRecording(true); setRecordingLiveTV(true); HDHRStreamJob job = new HDHRStreamJob(this); JobContainer jc = JobManager.getJobContainer(job); setJobContainer(jc); jc.start(); } } /** * {@inheritDoc} */ public void stopStreaming() { JobContainer jc = getJobContainer(); if (jc != null) { jc.stop(); setJobContainer(null); setRecording(false); setRecordingLiveTV(false); } } /** * {@inheritDoc} */ public void quickTune(Channel c) { if (isRecordingLiveTV()) { boolean doFrequency = true; Channel old = getChannel(); if ((old != null) && (c != null)) { if (old.getFrequency() == c.getFrequency()) { doFrequency = false; } } setChannel(c); HDHRQuickTuneJob job = new HDHRQuickTuneJob(this, doFrequency); JobContainer jc = JobManager.getJobContainer(job); jc.start(); } } private JobContainer getJobContainer() { return (jobContainer); } private void setJobContainer(JobContainer jc) { jobContainer = jc; } private boolean isUseScanFile() { if (!useScanFile) { // It's set NOT to use, so let's check the existence of the // scan file. File conf = new File("conf"); if ((conf.exists()) && (conf.isDirectory())) { File scan = new File(conf, getDevice() + "-scan.conf"); if ((scan.exists()) && (scan.isFile())) { useScanFile = true; setScanFile(new ScanFile(getDevice())); } else { scan = new File(conf, "hdhr-scan.conf"); if ((scan.exists()) && (scan.isFile())) { useScanFile = true; setScanFile(new ScanFile(getDevice())); } } } } return (useScanFile); } private ScanFile getScanFile() { return (scanFile); } private void setScanFile(ScanFile sf) { scanFile = sf; } /** * We need to update the "Source" property of the Default Configuration * instance because there may be more than one HDHR and this will make * this instance unique. We will use the Device property to help us. */ public void updateDefault() { BaseConfiguration c = (BaseConfiguration) getDefaultConfiguration(); if (c != null) { c.setSource(c.getSource() + " " + getDevice()); } } private int getFrequencyFromScanFile(String s) { int result = -1; ScanFile sf = getScanFile(); if ((s != null) && (sf != null)) { result = sf.getFrequency(s); } return (result); } private String getProgramFromScanFile(String s) { String result = null; ScanFile sf = getScanFile(); if ((s != null) && (sf != null)) { result = sf.getProgram(s); } return (result); } /** * Convenience method to get the proper frequency. * * @return A frequency as an int. */ public int getFrequency() { int result = -1; Channel c = getChannel(); if (c != null) { if (isUseScanFile()) { result = getFrequencyFromScanFile(c.getNumber()); } else { result = c.getFrequency(); } } return (result); } /** * Convenience method to get the proper program. * * @return A program as a String. */ public String getProgram() { String result = null; Channel c = getChannel(); if (c != null) { if (isUseScanFile()) { result = getProgramFromScanFile(c.getNumber()); } else { result = c.getNumber(); } } return (result); } /** * {@inheritDoc} */ public void performScan(Channel[] array, String type) { LogUtil.log(LogUtil.DEBUG, "performScan hdhr called: " + array); for (int i = 0; i < array.length; i++) { LogUtil.log(LogUtil.DEBUG, "number: " + array[i].getNumber()); LogUtil.log(LogUtil.DEBUG, "refnumber: " + array[i].getReferenceNumber()); LogUtil.log(LogUtil.DEBUG, "------------------"); } String ftype = null; if (NMSConstants.OTA.equals(type)) { ftype = US_BCAST; } else if (NMSConstants.CABLE.equals(type)) { ftype = US_CABLE; } LogUtil.log(LogUtil.DEBUG, "type given: <" + type + ">"); LogUtil.log(LogUtil.DEBUG, "ftype using: <" + ftype + ">"); HDHRScanJob scanner = new HDHRScanJob(this, array, ftype); JobContainer jc = JobManager.getJobContainer(scanner); jc.start(); } /** * {@inheritDoc} */ public boolean supportsScan() { return (true); } /** * Convenience method to see the configured frequency type. * * @return The setting as a String. */ public String getConfiguredFrequencyType() { String result = "auto"; Configuration c = getConfiguration(); if (c != null) { NameValue[] array = c.getNameValues(); if (array != null) { for (int i = 0; i < array.length; i++) { String name = array[i].getName(); if ((name != null) && (name.equals(FREQUENCY_TYPE))) { result = array[i].getValue(); if (result != null) { result = result.trim(); } if ((result != null) && (result.length() == 0)) { result = null; } break; } } } } return (result); } /** * If the HDHR hardware is a new type it can transcode to h264. Of * course its optional but desirable if the hardware can do it. * * @return True when transcoding is on. */ public boolean isConfiguredTranscodeMode() { boolean result = false; Configuration c = getConfiguration(); if (c != null) { NameValue nv = c.findNameValueByName(TRANSCODE_MODE); if (nv != null) { result = Util.str2boolean(nv.getValue(), result); } } return (result); } /** * Convenience method to see the configured video transcode options. * * @return The setting as a String. */ public String getConfiguredVideoTranscodeOptions() { String result = "copy"; Configuration c = getConfiguration(); if (c != null) { NameValue nv = c.findNameValueByName(VIDEO_TRANSCODE_OPTIONS); if (nv != null) { result = nv.getValue(); } } return (result); } }