Go to:
Gentoo Home
Documentation
Forums
Lists
Bugs
Planet
Store
Wiki
Get Gentoo!
Gentoo's Bugzilla – Attachment 182056 Details for
Bug 259009
New ebuild: MythTV-0.21 with backported support for HDPVR and VPDAU
Home
|
New
–
[Ex]
|
Browse
|
Search
|
Privacy Policy
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
HDPVR support in MythTV-0.21
mythtv-0.21-hdpvr-unified.patch (text/plain), 191.82 KB, created by
Marshall McMullen
on 2009-02-14 21:17:05 UTC
(
hide
)
Description:
HDPVR support in MythTV-0.21
Filename:
MIME Type:
Creator:
Marshall McMullen
Created:
2009-02-14 21:17:05 UTC
Size:
191.82 KB
patch
obsolete
>Index: mythtv/configure >=================================================================== >--- mythtv/configure (revision 18528) >+++ mythtv/configure (working copy) >@@ -157,6 +157,7 @@ > echo " --disable-hdhomerun disable support for HDHomeRun boxes" > echo " --disable-v4l disable Video4Linux support" > echo " --disable-ivtv disable ivtv support (PVR-x50) req. v4l support" >+ echo " --disable-hdpvr disable HD-PVR support" > echo " --disable-dvb disable DVB support" > echo " --dvb-path=HDRLOC location of directory containing" > echo " 'linux/dvb/frontend.h', not the" >@@ -891,6 +892,7 @@ > hdhomerun > iptv > ivtv >+ hdpvr > joystick_menu > libfftw3 > lirc >@@ -1048,6 +1050,7 @@ > firewire_deps="backend" > iptv_deps="backend" > ivtv_deps="backend v4l" >+hdpvr_deps="backend v4l" > hdhomerun_deps="backend" > opengl_deps="GL_gl_h" > opengl_deps_any="windows x11" >@@ -1179,6 +1182,7 @@ > hdhomerun="yes" > iptv="yes" > ivtv="yes" >+hdpvr="yes" > joystick_menu="default" > lamemp3="yes" > lirc="yes" >@@ -1628,6 +1632,7 @@ > SHFLAGS='-dynamiclib -Wl,-single_module -Wl,-install_name,$(SHLIBDIR)/$(SLIBNAME),-current_version,$(SPPVERSION),-compatibility_version,$(SPPVERSION) -Wl,-read_only_relocs,suppress' > VHOOKSHFLAGS='-dynamiclib -Wl,-single_module -flat_namespace -undefined suppress -Wl,-install_name,$(SHLIBDIR)/vhook/$@' > strip="strip -x" >+ disable hdpvr > FFLDFLAGS="-Wl,-dynamic,-search_paths_first" > SLIBSUF=".dylib" > SLIBNAME_WITH_VERSION='$(SLIBPREF)$(FULLNAME).$(LIBVERSION)$(SLIBSUF)' >@@ -1662,6 +1667,7 @@ > disable ffserver > SLIBPREF="lib" > SLIBSUF=".dll" >+ disable hdpvr > EXESUF=".exe" > SLIBNAME_WITH_VERSION='$(SLIBPREF)$(NAME)-$(LIBVERSION)$(SLIBSUF)' > SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(NAME)-$(LIBMAJOR)$(SLIBSUF)' >@@ -2999,6 +3005,7 @@ > if enabled backend; then > echo "Video4Linux sup. ${v4l-no}" > echo "ivtv support ${ivtv-no}" >+ echo "HR-PVR support ${hdpvr-no}" > echo "FireWire support ${firewire-no}" > echo "DVB support ${dvb-no} [$dvb_path]" > echo "DBox2 support ${dbox2-no}" >Index: mythtv/libs/libmythtv/cardutil.h >=================================================================== >--- mythtv/libs/libmythtv/cardutil.h (revision 18528) >+++ mythtv/libs/libmythtv/cardutil.h (working copy) >@@ -53,6 +53,7 @@ > FIREWIRE, > HDHOMERUN, > FREEBOX, >+ HDPVR = 12, > }; > > static enum CARD_TYPES toCardType(const QString &name) >@@ -81,6 +82,8 @@ > return HDHOMERUN; > if ("FREEBOX" == name) > return FREEBOX; >+ if ("HDPVR" == name) >+ return HDPVR; > return ERROR_UNKNOWN; > } > >@@ -95,7 +98,8 @@ > static bool IsUnscanable(const QString &rawtype) > { > return >- (rawtype == "FIREWIRE") || (rawtype == "DBOX2"); >+ (rawtype == "FIREWIRE") || (rawtype == "DBOX2") || >+ (rawtype == "HDPVR"); > } > > static bool IsEITCapable(const QString &rawtype) >@@ -119,7 +123,9 @@ > > static bool IsTuningAnalog(const QString &rawtype) > { >- return (rawtype == "V4L"); >+ return >+ (rawtype == "V4L") || (rawtype == "MPEG") || >+ (rawtype == "HDPVR"); > } > > /// Convenience function for GetCardIDs(const QString&, QString, QString) >@@ -137,6 +143,8 @@ > > static bool IsCardTypePresent(const QString &rawtype, > QString hostname = QString::null); >+ static QStringList GetCardTypes(void); // card types on ALL hosts >+ > static QStringVec GetVideoDevices(const QString &rawtype, > QString hostname = QString::null); > >@@ -144,6 +152,8 @@ > { return get_on_cardid("cardtype", cardid).upper(); } > static QString GetVideoDevice(uint cardid) > { return get_on_cardid("videodevice", cardid); } >+ static QString GetAudioDevice(uint cardid) >+ { return get_on_cardid("audiodevice", cardid); } > static QString GetVBIDevice(uint cardid) > { return get_on_cardid("vbidevice", cardid); } > static uint GetHDHRTuner(uint cardid) >@@ -188,8 +198,10 @@ > > static QString ProbeSubTypeName(uint cardid); > >- static QStringList probeInputs(QString device, >- QString cardtype = QString::null); >+ static QStringList ProbeVideoInputs(QString device, >+ QString cardtype = QString::null); >+ static QStringList ProbeAudioInputs(QString device, >+ QString cardtype = QString::null); > static void GetCardInputs(uint cardid, > const QString &device, > const QString &cardtype, >@@ -234,11 +246,13 @@ > uint32_t &version); > static bool GetV4LInfo(int videofd, QString &card, QString &driver) > { uint32_t dummy; return GetV4LInfo(videofd, card, driver, dummy); } >- static InputNames probeV4LInputs(int videofd, bool &ok); >+ static InputNames ProbeV4LVideoInputs(int videofd, bool &ok); >+ static InputNames ProbeV4LAudioInputs(int videofd, bool &ok); > > private: >- static QStringList probeV4LInputs(QString device); >- static QStringList probeDVBInputs(QString device); >+ static QStringList ProbeV4LVideoInputs(QString device); >+ static QStringList ProbeV4LAudioInputs(QString device); >+ static QStringList ProbeDVBInputs(QString device); > }; > > #endif //_CARDUTIL_H_ >Index: mythtv/libs/libmythtv/videosource.h >=================================================================== >--- mythtv/libs/libmythtv/videosource.h (revision 18528) >+++ mythtv/libs/libmythtv/videosource.h (working copy) >@@ -299,6 +299,22 @@ > } > }; > >+class TunerCardAudioInput : public ComboBoxSetting, public CaptureCardDBStorage >+{ >+ Q_OBJECT >+ public: >+ TunerCardAudioInput(const CaptureCard &parent, >+ QString dev = QString::null, >+ QString type = QString::null); >+ >+ public slots: >+ void fillSelections(const QString &device); >+ >+ private: >+ QString last_device; >+ QString last_cardtype; >+}; >+ > class DVBAudioDevice : public LineEditSetting, public CaptureCardDBStorage > { > Q_OBJECT >@@ -385,6 +401,23 @@ > TunerCardInput *input; > }; > >+class HDPVRConfigurationGroup: public VerticalConfigurationGroup >+{ >+ Q_OBJECT >+ >+ public: >+ HDPVRConfigurationGroup(CaptureCard &parent); >+ >+ public slots: >+ void probeCard(const QString &device); >+ >+ private: >+ CaptureCard &parent; >+ TransLabelSetting *cardinfo; >+ TunerCardInput *videoinput; >+ TunerCardAudioInput *audioinput; >+}; >+ > class DVBCardNum; > class DVBInput; > class DVBCardName; >Index: mythtv/libs/libmythtv/libmythtv.pro >=================================================================== >--- mythtv/libs/libmythtv/libmythtv.pro (revision 18528) >+++ mythtv/libs/libmythtv/libmythtv.pro (working copy) >@@ -386,8 +386,8 @@ > > # Support for Video4Linux devices > using_v4l { >- HEADERS += channel.h analogsignalmonitor.h >- SOURCES += channel.cpp analogsignalmonitor.h >+ HEADERS += v4lchannel.h analogsignalmonitor.h >+ SOURCES += v4lchannel.cpp analogsignalmonitor.h > > DEFINES += USING_V4L > } >@@ -472,6 +472,11 @@ > using_ivtv:SOURCES += mpegrecorder.cpp > using_ivtv:DEFINES += USING_IVTV > >+ # Support for HD-PVR on Linux >+ using_hdpvr:HEADERS *= mpegrecorder.h >+ using_hdpvr:SOURCES *= mpegrecorder.cpp >+ using_hdpvr:DEFINES += USING_HDPVR >+ > # Support for Linux DVB drivers > using_dvb { > # Basic DVB types >Index: mythtv/libs/libmythtv/v4lchannel.h >=================================================================== >--- mythtv/libs/libmythtv/v4lchannel.h (revision 0) >+++ mythtv/libs/libmythtv/v4lchannel.h (revision 0) >@@ -0,0 +1,108 @@ >+// -*- Mode: c++ -*- >+ >+#ifndef V4LCHANNEL_H >+#define V4LCHANNEL_H >+ >+#include "dtvchannel.h" >+#include "videodev_myth.h" // needed for v4l2_std_id type >+ >+using namespace std; >+ >+#define FAKE_VIDEO 0 >+ >+class TVRec; >+ >+typedef QMap<int,int> VidModV4L1; >+typedef QMap<int,v4l2_std_id> VidModV4L2; >+ >+/** \class V4LChannel >+ * \brief Implements tuning for TV cards using the V4L driver API, >+ * both versions 1 and 2. >+ * >+ * This class supports a wide range of tuning hardware including >+ * frame grabbers (whose output requires encoding), hardware encoders, >+ * digital cameras, and non-encoding hardware which simply records >+ * pre-encoded broadcast streams. >+ * >+ */ >+class V4LChannel : public DTVChannel >+{ >+ public: >+ V4LChannel(TVRec *parent, const QString &videodevice); >+ virtual ~V4LChannel(void); >+ >+ bool Init(QString &inputname, QString &startchannel, bool setchan); >+ >+ bool Open(void); >+ void Close(void); >+ >+ // Sets >+ void SetFd(int fd); >+ void SetFormat(const QString &format); >+ int SetDefaultFreqTable(const QString &name); >+ bool SetChannelByString(const QString &chan); >+ >+ // Gets >+ bool IsOpen(void) const { return GetFd() >= 0; } >+ int GetFd(void) const { return videofd; } >+ QString GetDevice(void) const { return device; } >+ QString GetSIStandard(void) const { return "atsc"; } >+ >+ // Commands >+ bool SwitchToInput(int newcapchannel, bool setstarting); >+ bool Retune(void); >+ >+ // Picture attributes. >+ bool InitPictureAttributes(void); >+ int GetPictureAttribute(PictureAttribute) const; >+ int ChangePictureAttribute(PictureAdjustType, PictureAttribute, bool up); >+ >+ // PID caching >+ void SaveCachedPids(const pid_cache_t&) const; >+ void GetCachedPids(pid_cache_t&) const; >+ >+ // Digital scanning stuff >+ bool TuneMultiplex(uint mplexid, QString inputname); >+ bool Tune(const DTVMultiplex &tuning, QString inputname); >+ >+ // Analog scanning stuff >+ bool Tune(uint frequency, QString inputname, >+ QString modulation, QString si_std); >+ >+ private: >+ // Helper Sets >+ void SetFreqTable(const int index); >+ int SetFreqTable(const QString &name); >+ bool SetInputAndFormat(int newcapchannel, QString newFmt); >+ >+ // Helper Gets >+ int GetCurrentChannelNum(const QString &channame); >+ QString GetFormatForChannel(QString channum, >+ QString inputname); >+ >+ // Helper Commands >+ bool InitPictureAttribute(const QString db_col_name); >+ bool TuneTo(const QString &chan, int finetune); >+ bool InitializeInputs(void); >+ >+ private: >+ // Data >+ QString device; >+ int videofd; >+ QString device_name; >+ QString driver_name; >+ QMap<QString,int> pict_attr_default; >+ >+ struct CHANLIST *curList; >+ int totalChannels; >+ >+ QString currentFormat; >+ bool is_dtv; ///< Set if 'currentFormat' is a DTV format >+ bool usingv4l2; ///< Set to true if tuner accepts v4l2 commands >+ VidModV4L1 videomode_v4l1; ///< Current video mode if 'usingv4l2' is false >+ VidModV4L2 videomode_v4l2; ///< Current video mode if 'usingv4l2' is true >+ >+ int defaultFreqTable; >+}; >+ >+#endif >Index: mythtv/libs/libmythtv/channel.h >=================================================================== >--- mythtv/libs/libmythtv/channel.h (revision 18528) >+++ mythtv/libs/libmythtv/channel.h (working copy) >@@ -1,108 +0,0 @@ >-// -*- Mode: c++ -*- >- >-#ifndef CHANNEL_H >-#define CHANNEL_H >- >-#include "dtvchannel.h" >-#include "videodev_myth.h" // needed for v4l2_std_id type >- >-using namespace std; >- >-#define FAKE_VIDEO 0 >- >-class TVRec; >- >-typedef QMap<int,int> VidModV4L1; >-typedef QMap<int,v4l2_std_id> VidModV4L2; >- >-/** \class Channel >- * \brief Implements tuning for TV cards using the V4L driver API, >- * both versions 1 and 2. >- * >- * This class supports a wide range of tuning hardware including >- * frame grabbers (whose output requires encoding), hardware encoders, >- * digital cameras, and non-encoding hardware which simply records >- * pre-encoded broadcast streams. >- * >- */ >-class Channel : public DTVChannel >-{ >- public: >- Channel(TVRec *parent, const QString &videodevice); >- virtual ~Channel(void); >- >- bool Init(QString &inputname, QString &startchannel, bool setchan); >- >- bool Open(void); >- void Close(void); >- >- // Sets >- void SetFd(int fd); >- void SetFormat(const QString &format); >- int SetDefaultFreqTable(const QString &name); >- bool SetChannelByString(const QString &chan); >- >- // Gets >- bool IsOpen(void) const { return GetFd() >= 0; } >- int GetFd(void) const { return videofd; } >- QString GetDevice(void) const { return device; } >- QString GetSIStandard(void) const { return "atsc"; } >- >- // Commands >- bool SwitchToInput(int newcapchannel, bool setstarting); >- bool Retune(void); >- >- // Picture attributes. >- bool InitPictureAttributes(void); >- int GetPictureAttribute(PictureAttribute) const; >- int ChangePictureAttribute(PictureAdjustType, PictureAttribute, bool up); >- >- // PID caching >- void SaveCachedPids(const pid_cache_t&) const; >- void GetCachedPids(pid_cache_t&) const; >- >- // Digital scanning stuff >- bool TuneMultiplex(uint mplexid, QString inputname); >- bool Tune(const DTVMultiplex &tuning, QString inputname); >- >- // Analog scanning stuff >- bool Tune(uint frequency, QString inputname, >- QString modulation, QString si_std); >- >- private: >- // Helper Sets >- void SetFreqTable(const int index); >- int SetFreqTable(const QString &name); >- bool SetInputAndFormat(int newcapchannel, QString newFmt); >- >- // Helper Gets >- int GetCurrentChannelNum(const QString &channame); >- QString GetFormatForChannel(QString channum, >- QString inputname); >- >- // Helper Commands >- bool InitPictureAttribute(const QString db_col_name); >- bool TuneTo(const QString &chan, int finetune); >- bool InitializeInputs(void); >- >- private: >- // Data >- QString device; >- int videofd; >- QString device_name; >- QString driver_name; >- QMap<QString,int> pict_attr_default; >- >- struct CHANLIST *curList; >- int totalChannels; >- >- QString currentFormat; >- bool is_dtv; ///< Set if 'currentFormat' is a DTV format >- bool usingv4l2; ///< Set to true if tuner accepts v4l2 commands >- VidModV4L1 videomode_v4l1; ///< Current video mode if 'usingv4l2' is false >- VidModV4L2 videomode_v4l2; ///< Current video mode if 'usingv4l2' is true >- >- int defaultFreqTable; >-}; >- >-#endif >Index: mythtv/libs/libmythtv/dbcheck.cpp >=================================================================== >--- mythtv/libs/libmythtv/dbcheck.cpp (revision 18528) >+++ mythtv/libs/libmythtv/dbcheck.cpp (working copy) >@@ -3640,8 +3640,6 @@ > return true; > } > >- >- > bool InitializeDatabase(void) > { > MSqlQuery query(MSqlQuery::InitCon()); >Index: mythtv/libs/libmythtv/scanwizardscanner.cpp >=================================================================== >--- mythtv/libs/libmythtv/scanwizardscanner.cpp (revision 18528) >+++ mythtv/libs/libmythtv/scanwizardscanner.cpp (working copy) >@@ -46,7 +46,7 @@ > #include "dvbconfparser.h" > > #ifdef USING_V4L >-#include "channel.h" >+#include "v4lchannel.h" > #include "analogsignalmonitor.h" > #endif > >@@ -511,8 +511,11 @@ > #endif > > #ifdef USING_V4L >- if (("V4L" == card_type) || ("MPEG" == card_type)) >- channel = new Channel(NULL, device); >+ if (("V4L" == card_type) || ("MPEG" == card_type) || >+ ("HDPVR" == card_type)) >+ { >+ channel = new V4LChannel(NULL, device); >+ } > #endif > > #ifdef USING_HDHOMERUN >Index: mythtv/libs/libmythtv/dtvrecorder.h >=================================================================== >--- mythtv/libs/libmythtv/dtvrecorder.h (revision 18528) >+++ mythtv/libs/libmythtv/dtvrecorder.h (working copy) >@@ -55,16 +55,20 @@ > > void BufferedWrite(const TSPacket &tspacket); > >- // MPEG "audio only" support >+ // MPEG TS "audio only" support > bool FindAudioKeyframes(const TSPacket *tspacket); > >- // MPEG2 support >+ // MPEG2 TS support > bool FindMPEG2Keyframes(const TSPacket* tspacket); > >- // MPEG4 AVC / H.264 support >+ // MPEG4 AVC / H.264 TS support > bool FindH264Keyframes(const TSPacket* tspacket); > void HandleH264Keyframe(void); > >+ // MPEG2 PS support (Hauppauge PVR-x50/PVR-500) >+ void HandlePSKeyframe(void); >+ bool FindPSKeyFrames(unsigned char *buffer, int len); >+ > // For handling other (non audio/video) packets > bool FindOtherKeyframes(const TSPacket *tspacket); > >@@ -93,6 +97,9 @@ > > bool _has_written_other_keyframe; > >+ /// Used for PVR-150/250/500 which have a fixed keyframe distance of 12 or 15 >+ int _keyframedist; >+ > // state tracking variables > /// True iff recording is actually being performed > bool _recording; >Index: mythtv/libs/libmythtv/analogsignalmonitor.cpp >=================================================================== >--- mythtv/libs/libmythtv/analogsignalmonitor.cpp (revision 18528) >+++ mythtv/libs/libmythtv/analogsignalmonitor.cpp (working copy) >@@ -8,12 +8,12 @@ > #include "videodev_myth.h" > #include "mythcontext.h" > #include "analogsignalmonitor.h" >-#include "channel.h" >+#include "v4lchannel.h" > > #define LOC QString("AnalogSM: ").arg(channel->GetDevice()) > #define LOC_ERR QString("AnalogSM, Error: ").arg(channel->GetDevice()) > >-AnalogSignalMonitor::AnalogSignalMonitor(int db_cardnum, Channel *_channel, >+AnalogSignalMonitor::AnalogSignalMonitor(int db_cardnum, V4LChannel *_channel, > uint64_t _flags, const char *_name) : > SignalMonitor(db_cardnum, _channel, _flags, _name), > usingv4l2(false) >Index: mythtv/libs/libmythtv/siscan.cpp >=================================================================== >--- mythtv/libs/libmythtv/siscan.cpp (revision 18528) >+++ mythtv/libs/libmythtv/siscan.cpp (working copy) >@@ -29,7 +29,7 @@ > > #include "dvbchannel.h" > #include "hdhrchannel.h" >-#include "channel.h" >+#include "v4lchannel.h" > #include "compat.h" > > QString SIScan::loc(const SIScan *siscan) >@@ -555,10 +555,10 @@ > #endif > } > >-Channel *SIScan::GetChannel(void) >+V4LChannel *SIScan::GetV4LChannel(void) > { > #ifdef USING_V4L >- return dynamic_cast<Channel*>(channel); >+ return dynamic_cast<V4LChannel*>(channel); > #else > return NULL; > #endif >@@ -1619,7 +1619,7 @@ > #endif // USING_DVB > > #ifdef USING_V4L >- if (GetChannel()) >+ if (GetV4LChannel()) > { > // convert to visual carrier > tuning.frequency = tuning.frequency - 1750000; >Index: mythtv/libs/libmythtv/dtvrecorder.cpp >=================================================================== >--- mythtv/libs/libmythtv/dtvrecorder.cpp (revision 18528) >+++ mythtv/libs/libmythtv/dtvrecorder.cpp (working copy) >@@ -46,6 +46,7 @@ > _request_recording(false), > _wait_for_keyframe_option(true), > _has_written_other_keyframe(false), >+ _keyframedist(15), > // state > _recording(false), > _error(false), >@@ -57,6 +58,7 @@ > _frames_seen_count(0), _frames_written_count(0) > { > SetPositionMapType(MARK_GOP_BYFRAME); >+ _payload_buffer.reserve(TSPacket::SIZE * (50 + 1)); > } > > DTVRecorder::~DTVRecorder() >@@ -431,7 +433,10 @@ > bool DTVRecorder::FindH264Keyframes(const TSPacket *tspacket) > { > if (!ringBuffer) >+ { >+ VERBOSE(VB_IMPORTANT, LOC_ERR + "FindH264Keyframes: No ringbuffer"); > return false; >+ } > > bool haveBufferedData = !_payload_buffer.empty(); > if (!tspacket->HasPayload()) // no payload to scan >@@ -528,7 +533,7 @@ > hasFrame = true; > } > >- if (_h264_kf_seq.IsOnFrame()) >+ else if (_h264_kf_seq.IsOnFrame()) > hasFrame = true; > } > } // for (; i < TSPacket::SIZE; i++) >@@ -572,4 +577,108 @@ > CheckForRingBufferSwitch(); > } > >+/** \fn DTVRecorder::HandlePSKeyframe(void) >+ * \brief This save the current frame to the position maps >+ * and handles ringbuffer switching. >+ */ >+void DTVRecorder::HandlePSKeyframe(void) >+{ >+ if (!ringBuffer) >+ return; >+ >+ unsigned long long frameNum = _last_gop_seen; >+ >+ _first_keyframe = (_first_keyframe < 0) ? frameNum : _first_keyframe; >+ >+ // Add key frame to position map >+ positionMapLock.lock(); >+ if (!positionMap.contains(frameNum)) >+ { >+ long long startpos = ringBuffer->GetWritePosition(); >+ // FIXME: handle keyframes with start code spanning over two ts packets >+ startpos += _payload_buffer.size(); >+ positionMapDelta[frameNum] = startpos; >+ positionMap[frameNum] = startpos; >+ } >+ positionMapLock.unlock(); >+ >+ // Perform ringbuffer switch if needed. >+ CheckForRingBufferSwitch(); >+} >+ >+ >+bool DTVRecorder::FindPSKeyFrames(unsigned char *buffer, int len) >+{ >+ unsigned char *bufptr = buffer, *bufstart = buffer; >+ uint v = 0; >+ int leftlen = len; >+ bool hasKeyFrame = false; >+ >+ while (bufptr < buffer + len) >+ { >+ v = *bufptr++; >+ if (_start_code == 0x000001) >+ { >+ _start_code = ((_start_code << 8) | v) & 0xFFFFFF; >+ const int stream_id = _start_code & 0x000000ff; >+ >+ if (stream_id == PESStreamID::PackHeader) >+ { >+ _last_keyframe_seen = ringBuffer->GetWritePosition() + >+ _payload_buffer.size(); >+ >+ int curpos = bufptr - bufstart - 4; >+ if (curpos < 0) >+ { >+ // header was split >+ if (_payload_buffer.size() + curpos > 0) >+ ringBuffer->Write(&_payload_buffer[0], >+ _payload_buffer.size() + curpos); >+ >+ _payload_buffer.resize(4); >+ memcpy(&_payload_buffer[0], &_start_code, 4); >+ >+ leftlen = leftlen - curpos + 4; >+ bufstart = bufptr; >+ } >+ else >+ { >+ // header was entirely in this packet >+ int idx = _payload_buffer.size(); >+ _payload_buffer.resize(idx + curpos); >+ memcpy(&_payload_buffer[idx], bufstart, curpos); >+ >+ bufstart += curpos; >+ leftlen -= curpos; >+ >+ if (_payload_buffer.size() > 0) >+ ringBuffer->Write(&_payload_buffer[0], >+ _payload_buffer.size()); >+ _payload_buffer.clear(); >+ } >+ _frames_seen_count++; >+ } >+ else if (stream_id == PESStreamID::SequenceStartCode) >+ { >+ _last_seq_seen = _last_keyframe_seen; >+ } >+ else if (stream_id == PESStreamID::GOPStartCode && >+ _last_seq_seen == _last_keyframe_seen) >+ { >+ _frames_written_count = _last_gop_seen * _keyframedist; >+ _last_gop_seen++; >+ HandlePSKeyframe(); >+ } >+ } >+ else >+ _start_code = ((_start_code << 8) | v) & 0xFFFFFF; >+ } >+ >+ int idx = _payload_buffer.size(); >+ _payload_buffer.resize(idx + leftlen); >+ memcpy(&_payload_buffer[idx], bufstart, leftlen); >+ >+ return hasKeyFrame; >+} >+ > /* vim: set expandtab tabstop=4 shiftwidth=4: */ >Index: mythtv/libs/libmythtv/mpegrecorder.h >=================================================================== >--- mythtv/libs/libmythtv/mpegrecorder.h (revision 18528) >+++ mythtv/libs/libmythtv/mpegrecorder.h (working copy) >@@ -3,12 +3,19 @@ > #ifndef MPEGRECORDER_H_ > #define MPEGRECORDER_H_ > >-#include "recorderbase.h" >+#include "dtvrecorder.h" >+#include "tspacket.h" >+#include "mpegstreamdata.h" >+#include "DeviceReadBuffer.h" > > struct AVFormatContext; > struct AVPacket; > >-class MpegRecorder : public RecorderBase >+class MpegRecorder : public DTVRecorder, >+ public MPEGSingleProgramStreamListener, >+ public TSPacketListener, >+ public TSPacketListenerAV, >+ public ReaderPausedCB > { > public: > MpegRecorder(TVRec*); >@@ -33,36 +40,55 @@ > bool PauseAndWait(int timeout = 100); > > bool IsRecording(void) { return recording; } >- bool IsErrored(void) { return errored; } > >- long long GetFramesWritten(void) { return framesWritten; } >- > bool Open(void); > int GetVideoFd(void) { return chanfd; } > >- long long GetKeyframePosition(long long desired); >+ // TS >+ virtual void SetStreamData(MPEGStreamData*); >+ virtual MPEGStreamData *GetStreamData(void) { return _stream_data; } > >- void SetNextRecording(const ProgramInfo*, RingBuffer*); >+ // TSPacketListener >+ bool ProcessTSPacket(const TSPacket &tspacket); > >+ // TSPacketListenerAV >+ bool ProcessVideoTSPacket(const TSPacket &tspacket); >+ bool ProcessAudioTSPacket(const TSPacket &tspacket); >+ bool ProcessAVTSPacket(const TSPacket &tspacket); >+ >+ // Implements MPEGSingleProgramStreamListener >+ void HandleSingleProgramPAT(ProgramAssociationTable *pat); >+ void HandleSingleProgramPMT(ProgramMapTable *pmt); >+ >+ // ReaderPausedCB >+ virtual void ReaderPaused(int fd) { paused = true; pauseWait.wakeAll(); } >+ > private: >- bool SetupRecording(void); >- void FinishRecording(void); >- void HandleKeyframe(void); >+ void SetIntOption(RecordingProfile *profile, const QString &name); >+ void SetStrOption(RecordingProfile *profile, const QString &name); > >- void ProcessData(unsigned char *buffer, int len); >- > bool OpenMpegFileAsInput(void); > bool OpenV4L2DeviceAsInput(void); > bool SetIVTVDeviceOptions(int chanfd); > bool SetV4L2DeviceOptions(int chanfd); >+ bool SetFormat(int chanfd); >+ bool SetLanguageMode(int chanfd); >+ bool SetRecordingVolume(int chanfd); > bool SetVBIOptions(int chanfd); > uint GetFilteredStreamType(void) const; > uint GetFilteredAudioSampleRate(void) const; > uint GetFilteredAudioLayer(void) const; > uint GetFilteredAudioBitRate(uint audio_layer) const; > >+ bool StartEncoding(int fd); >+ bool StopEncoding(int fd); >+ > void ResetForNewFile(void); > >+ void HandleResolutionChanges(void); >+ >+ inline bool CheckCC(uint pid, uint cc); >+ > bool deviceIsMpegFile; > int bufferSize; > >@@ -78,42 +104,62 @@ > // State > bool recording; > bool encoding; >- bool errored; >+ bool needs_resolution; >+ mutable QMutex start_stop_encoding_lock; >+ QMutex recording_wait_lock; >+ QWaitCondition recording_wait; > > // Pausing state > bool cleartimeonpause; > >- // Number of frames written >- long long framesWritten; >- > // Encoding info > int width, height; > int bitrate, maxbitrate, streamtype, aspectratio; > int audtype, audsamplerate, audbitratel1, audbitratel2, audbitratel3; > int audvolume; > unsigned int language; ///< 0 is Main Lang; 1 is SAP Lang; 2 is Dual >+ unsigned int low_mpeg4avgbitrate; >+ unsigned int low_mpeg4peakbitrate; >+ unsigned int medium_mpeg4avgbitrate; >+ unsigned int medium_mpeg4peakbitrate; >+ unsigned int high_mpeg4avgbitrate; >+ unsigned int high_mpeg4peakbitrate; > > // Input file descriptors > int chanfd; > int readfd; > >- // Keyframe tracking inforamtion >- int keyframedist; >- bool gopset; >- unsigned int leftovers; >- long long lastpackheaderpos; >- long long lastseqstart; >- long long numgops; >- >- // buffer used for ... >- unsigned char *buildbuffer; >- unsigned int buildbuffersize; >- > static const int audRateL1[]; > static const int audRateL2[]; > static const int audRateL3[]; > static const char *streamType[]; > static const char *aspectRatio[]; > static const unsigned int kBuildBufferMaxSize; >+ >+ // Buffer device reads >+ DeviceReadBuffer *_device_read_buffer; >+ >+ // TS >+ MPEGStreamData *_stream_data; >+ unsigned char _stream_id[0x1fff]; >+ unsigned char _pid_status[0x1fff]; >+ unsigned char _continuity_counter[0x1fff]; >+ static const unsigned char kPayloadStartSeen = 0x2; >+ >+ // Statistics >+ mutable uint _continuity_error_count; >+ mutable uint _stream_overflow_count; >+ mutable uint _bad_packet_count; > }; >+ >+inline bool MpegRecorder::CheckCC(uint pid, uint new_cnt) >+{ >+ bool ok = ((((_continuity_counter[pid] + 1) & 0xf) == new_cnt) || >+ (_continuity_counter[pid] == 0xFF)); >+ >+ _continuity_counter[pid] = new_cnt & 0xf; >+ >+ return ok; >+} >+ > #endif >Index: mythtv/libs/libmythtv/channelbase.cpp >=================================================================== >--- mythtv/libs/libmythtv/channelbase.cpp (revision 18528) >+++ mythtv/libs/libmythtv/channelbase.cpp (working copy) >@@ -17,7 +17,7 @@ > > // MythTV headers > #include "videodev_myth.h" >-#include "channel.h" >+#include "channelbase.h" > #include "frequencies.h" > #include "tv_rec.h" > #include "mythcontext.h" >Index: mythtv/libs/libmythtv/mpeg/h264utils.h >=================================================================== >--- mythtv/libs/libmythtv/mpeg/h264utils.h (revision 18528) >+++ mythtv/libs/libmythtv/mpeg/h264utils.h (working copy) >@@ -35,6 +35,11 @@ > > #include <stdint.h> > >+extern "C" { >+#include <limits.h> // golomb.h should include this... >+#include "golomb.h" >+} >+ > namespace H264 > { > >@@ -174,6 +179,8 @@ > > private: > void KeyframePredicate(const uint8_t new_first_NAL_byte); /* throw() */ >+ void decode_Header(GetBitContext *gb); >+ void decode_SPS(GetBitContext *gb); > > bool errored; > bool state_changed; >@@ -182,14 +189,27 @@ > int64_t sync_stream_offset; > > uint8_t first_NAL_byte; >+ int log2_max_frame_num; >+ int frame_num; >+ int prev_frame_num; > > bool saw_AU_delimiter; > bool saw_first_VCL_NAL_unit; > bool saw_sps; >+ bool new_VLC_NAL; > >+ bool separate_colour_plane_flag; >+ bool frame_mbs_only_flag; >+ bool prev_field_pic_flag; >+ bool prev_bottom_field_flag; >+ uint prev_pic_parameter_set_id; >+ >+ > bool did_evaluate_once; > bool keyframe; > int64_t keyframe_sync_stream_offset; >+ >+ GetBitContext gb; > }; > > } // namespace H264 >Index: mythtv/libs/libmythtv/mpeg/mpegdescriptors.cpp >=================================================================== >--- mythtv/libs/libmythtv/mpeg/mpegdescriptors.cpp (revision 18528) >+++ mythtv/libs/libmythtv/mpeg/mpegdescriptors.cpp (working copy) >@@ -3,9 +3,15 @@ > > #include <limits.h> > >+#include <qdeepcopy.h> >+ > #include "atscdescriptors.h" > #include "dvbdescriptors.h" > >+QMutex RegistrationDescriptor::description_map_lock; >+bool RegistrationDescriptor::description_map_initialized = false; >+QMap<QString,QString> RegistrationDescriptor::description_map; >+ > desc_list_t MPEGDescriptor::Parse( > const unsigned char* data, uint len) > { >@@ -448,19 +454,86 @@ > return str; > } > >+void RegistrationDescriptor::InitializeDescriptionMap(void) >+{ >+ QMutexLocker locker(&description_map_lock); >+ if (description_map_initialized) >+ return; >+ >+ description_map["AC-3"] = "ATSC audio stream A/52"; >+ description_map["AVSV"] = "China A/V Working Group"; >+ description_map["BDC0"] = "Broadcast Data Corporation Software Data Service"; >+ description_map["BSSD"] = "SMPTE 302M-1998 Audio data as specified in (AES3)"; >+ description_map["CAPO"] = "SMPTE 315M-1999 Camera Positioning Information"; >+ description_map["CUEI"] = "SCTE 35 2003, Cable Digital Program Insertion Cueing Message"; >+ description_map["DDED"] = "LGEUS Digital Delivery with encryption and decryption"; >+ description_map["DISH"] = "EchoStar MPEG streams"; >+ description_map["DRA1"] = "Chinese EIS SJ/T11368-2006 DRA digital audio"; >+ description_map["DTS1"] = "DTS Frame size of 512 bytes"; >+ description_map["DTS2"] = "DTS Frame size of 1024 bytes"; >+ description_map["DTS3"] = "DTS Frame size of 2048"; >+ description_map["DVDF"] = "DVD compatible MPEG2-TS"; >+ description_map["ETV1"] = "CableLabs ETV info is present"; >+ description_map["GA94"] = "ATSC program ID A/53"; >+ description_map["GWKS"] = "GuideWorks EPG info"; >+ description_map["HDMV"] = "Blu-Ray A/V for read-only media (H.264 TS)"; >+ description_map["HDMX"] = "Matsushita-TS"; >+ description_map["KLVA"] = "SMPTE RP 217-2001 MXF KLV packets present"; >+ description_map["LU-A"] = "SMPTE RDD-11 HDSDI HD serial/video data"; >+ description_map["MTRM"] = "D-VHS compatible MPEG2-TS"; >+ description_map["NMR1"] = "Nielsen content identifier"; >+ description_map["PAUX"] = "Philips ES containing low-speed data"; >+ description_map["PMSF"] = "MPEG-TS stream modified by STB"; >+ description_map["PRMC"] = "Philips ES containing remote control data"; >+ description_map["SCTE"] = "SCTE 54 2003 Digital Video Service Multiplex and TS for Cable"; >+ description_map["SEN1"] = "ATSC Private Information identifies source of stream"; >+ description_map["SESF"] = "Blu-Ray A/V for ReWritable media (H.264 TS)"; >+ description_map["SPLC"] = "SMPTE Proposed 312M Splice Point compatible TS"; >+ description_map["SVMD"] = "SMPTE Proposed Video Metatdata Dictionary for MPEG2-TS"; >+ description_map["SZMI"] = "ATSC Private Info from Building B"; >+ description_map["TRIV"] = "ATSC Private Info from Triveni Digital"; >+ description_map["TSBV"] = "Toshiba self-encoded H.264 TS"; >+ description_map["TSHV"] = "Sony self-encoded MPEG-TS and private data"; >+ description_map["TSMV"] = "Sony self-encoded MPEG-TS and private data"; >+ description_map["TVG1"] = "TV Guide EPG Data"; >+ description_map["TVG2"] = "TV Guide EPG Data"; >+ description_map["TVG3"] = "TV Guide EPG Data"; >+ description_map["ULE1"] = "IETF RFC4326 compatible MPEG2-TS"; >+ description_map["VC-1"] = "SMPTE Draft RP 227 VC-1 Bitstream Transport Encodings"; >+ >+ for (uint i = 0; i <= 99; i++) >+ { >+ description_map[QString("US%1").arg(i, 2, 0)] = >+ "NIMA, Unspecified military application"; >+ } >+ >+ description_map_initialized = true; >+} >+ >+QString RegistrationDescriptor::GetDescription(const QString &fmt) >+{ >+ InitializeDescriptionMap(); >+ >+ QString ret = QString::null; >+ { >+ QMutexLocker locker(&description_map_lock); >+ QMap<QString,QString>::const_iterator it = description_map.find(fmt); >+ if (it != description_map.end()) >+ ret = QDeepCopy<QString>(*it); >+ } >+ >+ return ret; >+} >+ > QString RegistrationDescriptor::toString() const > { > QString fmt = FormatIdentifierString(); > QString msg = QString("Registration Descriptor: '%1' ").arg(fmt); >- QString msg2 = "Unknown"; >- if (fmt == "CUEI") >- msg2 = "SCTE 35 2003, Cable Digital Program Insertion Cueing Message"; >- else if (fmt == "AC-3") >- msg2 = "ATSC audio stream A/52"; >- else if (fmt == "GA94") >- msg2 = "ATSC program ID A/53"; >- else >- msg2 = "Unknown, see http://www.smpte-ra.org/mpegreg.html"; >+ >+ QString msg2 = GetDescription(fmt); >+ if (msg2.isEmpty()) >+ msg2 = "Unknown, see http://www.smpte-ra.org/mpegreg/mpegreg.html"; >+ > return msg + msg2; > } > >Index: mythtv/libs/libmythtv/mpeg/h264utils.cpp >=================================================================== >--- mythtv/libs/libmythtv/mpeg/h264utils.cpp (revision 18528) >+++ mythtv/libs/libmythtv/mpeg/h264utils.cpp (working copy) >@@ -33,15 +33,14 @@ > // C headers > #include <cstring> > >-// C++ headers >-#include <iostream> >- > // MythTV headers >+#include "mythverbose.h" > #include "h264utils.h" > > extern "C" { > // from libavcodec > extern const uint8_t *ff_find_start_code(const uint8_t * p, const uint8_t *end, uint32_t * state); >+#include "avcodec.h" > } > > namespace H264 >@@ -62,9 +61,16 @@ > > first_NAL_byte = H264::NALUnitType::UNKNOWN; > >+ log2_max_frame_num = -1; >+ frame_num = 0; >+ prev_frame_num = -1; >+ > saw_AU_delimiter = false; > saw_first_VCL_NAL_unit = false; > saw_sps = false; >+ separate_colour_plane_flag = false; >+ frame_mbs_only_flag = true; >+ new_VLC_NAL = false; > > did_evaluate_once = false; > keyframe = false; >@@ -127,7 +133,7 @@ > // stage 3: did we see the AU's first VCL NAL unit yet? > if (!saw_first_VCL_NAL_unit && NALUnitType::IsVCLType(new_NAL_type)) > { >- saw_first_VCL_NAL_unit = true; >+ saw_first_VCL_NAL_unit = new_VLC_NAL; > saw_AU_delimiter = false; > state_changed = true; > if (saw_sps) >@@ -154,9 +160,35 @@ > if ((sync_accumulator & 0xffffff00) == 0x00000100) > { > uint8_t k = *(local_bytes-1); >+ uint8_t NAL_type = k & 0x1f; > sync_stream_offset = stream_offset; > keyframe = false; > >+ if (NAL_type == NALUnitType::SPS || >+ NAL_type == NALUnitType::SLICE || >+ NAL_type == NALUnitType::SLICE_DPA) >+ { >+ /* >+ bitstream buffer, must be FF_INPUT_BUFFER_PADDING_SIZE >+ bytes larger then the actual read bits >+ */ >+ if (local_bytes + 20 + FF_INPUT_BUFFER_PADDING_SIZE < >+ local_bytes_end) >+ { >+ init_get_bits(&gb, local_bytes, >+ 8 * (local_bytes_end - local_bytes)); >+ >+ if (NAL_type == NALUnitType::SPS) >+ decode_SPS(&gb); >+ else >+ decode_Header(&gb); >+ } >+ } >+ else if (NAL_type == NALUnitType::SLICE_IDR) >+ { >+ frame_num = 0; >+ } >+ > KeyframePredicate(k); > first_NAL_byte = k; > >@@ -175,4 +207,141 @@ > return saw_first_VCL_NAL_unit; > } > >+void KeyframeSequencer::decode_Header(GetBitContext *gb) >+{ >+ uint first_mb_in_slice; >+ uint slice_type; >+ uint pic_parameter_set_id; >+ bool field_pic_flag; >+ bool bottom_field_flag; >+ >+ if (log2_max_frame_num < 1) >+ { >+ VERBOSE(VB_IMPORTANT, "KeyframeSequencer::decode_Header: " >+ "SPS has not been parsed!"); >+ return; >+ } >+ >+ new_VLC_NAL = false; >+ >+ prev_frame_num = frame_num; >+ >+ first_mb_in_slice = get_ue_golomb(gb); >+ slice_type = get_ue_golomb(gb); >+ >+ pic_parameter_set_id = get_ue_golomb(gb); >+ if (pic_parameter_set_id != prev_pic_parameter_set_id) >+ { >+ new_VLC_NAL = true; >+ prev_pic_parameter_set_id = pic_parameter_set_id; >+ } >+ >+ if (separate_colour_plane_flag) >+ get_bits(gb, 2); // colour_plane_id >+ >+ frame_num = get_bits(gb, log2_max_frame_num); >+ >+ if (frame_mbs_only_flag) >+ { >+ new_VLC_NAL = true; >+ } >+ else >+ { >+ /* From section 7.3.3 Slice header syntax >+ if (!frame_mbs_only_flag) >+ { >+ field_pic_flag = get_bits1(gb); >+ if (field_pic_flag) >+ bottom_field_flag = get_bits1(gb); >+ } >+ */ >+ >+ field_pic_flag = get_bits1(gb); >+ if (field_pic_flag != prev_field_pic_flag) >+ { >+ new_VLC_NAL = true; >+ prev_field_pic_flag = field_pic_flag; >+ } >+ >+ if (field_pic_flag) >+ { >+ bottom_field_flag = get_bits1(gb); >+ if (bottom_field_flag != prev_bottom_field_flag) >+ { >+ new_VLC_NAL = !bottom_field_flag; >+ prev_bottom_field_flag = bottom_field_flag; >+ } >+ } >+ } >+} >+ >+/* >+ * libavcodec used for example >+ */ >+void KeyframeSequencer::decode_SPS(GetBitContext * gb) >+{ >+ int profile_idc, chroma_format_idc; >+ >+ profile_idc = get_bits(gb, 8); // profile_idc >+ get_bits1(gb); // constraint_set0_flag >+ get_bits1(gb); // constraint_set1_flag >+ get_bits1(gb); // constraint_set2_flag >+ get_bits1(gb); // constraint_set3_flag >+ get_bits(gb, 4); // reserved >+ get_bits(gb, 8); // level_idc >+ get_ue_golomb(gb); // sps_id >+ >+ if (profile_idc >= 100) >+ { // high profile >+ if ((chroma_format_idc = get_ue_golomb(gb)) == 3) // chroma_format_idc >+ separate_colour_plane_flag = (get_bits1(gb) == 1); >+ >+ get_ue_golomb(gb); // bit_depth_luma_minus8 >+ get_ue_golomb(gb); // bit_depth_chroma_minus8 >+ get_bits1(gb); // qpprime_y_zero_transform_bypass_flag >+ >+ if (get_bits1(gb)) // seq_scaling_matrix_present_flag >+ { >+ for (int idx = 0; idx < ((chroma_format_idc != 3) ? 8 : 12); ++idx) >+ { >+ get_bits1(gb); // scaling_list >+ } >+ } >+ } >+ >+ log2_max_frame_num = get_ue_golomb(gb) + 4; >+ >+ uint pic_order_cnt_type; >+ uint log2_max_pic_order_cnt_lsb; >+ bool delta_pic_order_always_zero_flag; >+ int offset_for_non_ref_pic; >+ int offset_for_top_to_bottom_field; >+ uint tmp; >+ uint num_ref_frames; >+ bool gaps_in_frame_num_allowed_flag; >+ uint pic_width_in_mbs; >+ uint pic_height_in_map_units; >+ >+ pic_order_cnt_type = get_ue_golomb(gb); >+ if (pic_order_cnt_type == 0) >+ { >+ log2_max_pic_order_cnt_lsb = get_ue_golomb(gb) + 4; >+ } >+ else if (pic_order_cnt_type == 1) >+ { >+ delta_pic_order_always_zero_flag = get_bits1(gb); >+ offset_for_non_ref_pic = get_se_golomb(gb); >+ offset_for_top_to_bottom_field = get_se_golomb(gb); >+ tmp = get_ue_golomb(gb); >+ for (uint idx = 0; idx < tmp; ++idx) >+ get_se_golomb(gb); // offset_for_ref_frame[i] >+ } >+ >+ num_ref_frames = get_ue_golomb(gb); >+ gaps_in_frame_num_allowed_flag = get_bits1(gb); >+ pic_width_in_mbs = get_ue_golomb(gb) + 1; >+ pic_height_in_map_units = get_ue_golomb(gb) + 1; >+ frame_mbs_only_flag = get_bits1(gb); >+} >+ > } // namespace H264 >Index: mythtv/libs/libmythtv/mpeg/mpegdescriptors.h >=================================================================== >--- mythtv/libs/libmythtv/mpeg/mpegdescriptors.h (revision 18528) >+++ mythtv/libs/libmythtv/mpeg/mpegdescriptors.h (working copy) >@@ -3,7 +3,18 @@ > #ifndef _MPEG_DESCRIPTORS_H_ > #define _MPEG_DESCRIPTORS_H_ > >+// ANSI C headers > #include <cassert> >+ >+// C++ headers >+#include <vector> >+using namespace std; >+ >+// Qt headers >+#include <qmutex.h> >+#include <qstring.h> >+ >+// MythTV > #include "iso639.h" > > typedef vector<const unsigned char*> desc_list_t; >@@ -188,6 +199,15 @@ > QChar(_data[4]) + QChar(_data[5]); > } > QString toString() const; >+ >+ private: >+ static void InitializeDescriptionMap(void); >+ static QString GetDescription(const QString &fmt); >+ >+ private: >+ static QMutex description_map_lock; >+ static bool description_map_initialized; >+ static QMap<QString,QString> description_map; > }; > > class ConditionalAccessDescriptor : public MPEGDescriptor >Index: mythtv/libs/libmythtv/signalmonitor.cpp >=================================================================== >--- mythtv/libs/libmythtv/signalmonitor.cpp (revision 18528) >+++ mythtv/libs/libmythtv/signalmonitor.cpp (working copy) >@@ -23,7 +23,7 @@ > > #ifdef USING_V4L > # include "analogsignalmonitor.h" >-# include "channel.h" >+# include "v4lchannel.h" > #endif > > #ifdef USING_HDHOMERUN >@@ -93,9 +93,10 @@ > > #ifdef USING_V4L > if ((cardtype.upper() == "V4L") || >- (cardtype.upper() == "MPEG")) >+ (cardtype.upper() == "MPEG") || >+ (cardtype.upper() == "HDPVR")) > { >- Channel *chan = dynamic_cast<Channel*>(channel); >+ V4LChannel *chan = dynamic_cast<V4LChannel*>(channel); > if (chan) > signalMonitor = new AnalogSignalMonitor(db_cardnum, chan); > } >Index: mythtv/libs/libmythtv/v4lchannel.cpp >=================================================================== >--- mythtv/libs/libmythtv/v4lchannel.cpp (revision 0) >+++ mythtv/libs/libmythtv/v4lchannel.cpp (revision 0) >@@ -0,0 +1,1320 @@ >+// Std C headers >+#include <cstdio> >+#include <cstdlib> >+#include <cerrno> >+ >+// POSIX headers >+#include <unistd.h> >+#include <fcntl.h> >+#include <sys/ioctl.h> >+#include <sys/types.h> >+#include <sys/stat.h> >+#include <sys/wait.h> >+ >+// C++ headers >+#include <algorithm> >+#include <iostream> >+using namespace std; >+ >+// Qt headers >+#include <qsqldatabase.h> >+ >+// MythTV headers >+#include "videodev_myth.h" >+#include "v4lchannel.h" >+#include "frequencies.h" >+#include "tv_rec.h" >+#include "mythcontext.h" >+#include "mythdbcon.h" >+#include "channelutil.h" >+#include "cardutil.h" >+ >+#define DEBUG_ATTRIB 1 >+ >+#define LOC QString("Channel(%1): ").arg(device) >+#define LOC_WARN QString("Channel(%1) Warning: ").arg(device) >+#define LOC_ERR QString("Channel(%1) Error: ").arg(device) >+ >+static int format_to_mode(const QString& fmt, int v4l_version); >+static QString mode_to_format(int mode, int v4l_version); >+ >+/** \class Channel >+ * \brief Class implementing ChannelBase interface to tuning hardware >+ * when using Video4Linux based drivers. >+ */ >+ >+V4LChannel::V4LChannel(TVRec *parent, const QString &videodevice) >+ : DTVChannel(parent), >+ device(videodevice), videofd(-1), >+ device_name(QString::null), driver_name(QString::null), >+ curList(NULL), totalChannels(0), >+ currentFormat(""), is_dtv(false), >+ usingv4l2(false), defaultFreqTable(1) >+{ >+} >+ >+V4LChannel::~V4LChannel(void) >+{ >+ Close(); >+} >+ >+bool V4LChannel::Init(QString &inputname, QString &startchannel, bool setchan) >+{ >+ if (setchan) >+ { >+ SetFormat(gContext->GetSetting("TVFormat")); >+ SetDefaultFreqTable(gContext->GetSetting("FreqTable")); >+ } >+ return ChannelBase::Init(inputname, startchannel, setchan); >+} >+ >+bool V4LChannel::Open(void) >+{ >+#if FAKE_VIDEO >+ return true; >+#endif >+ if (videofd >= 0) >+ return true; >+ >+ videofd = open(device.ascii(), O_RDWR); >+ if (videofd < 0) >+ { >+ VERBOSE(VB_IMPORTANT, >+ QString("Channel(%1)::Open(): Can't open video device, " >+ "error \"%2\"").arg(device).arg(strerror(errno))); >+ return false; >+ } >+ >+ usingv4l2 = CardUtil::hasV4L2(videofd); >+ CardUtil::GetV4LInfo(videofd, device_name, driver_name); >+ VERBOSE(VB_CHANNEL, LOC + QString("Device name '%1' driver '%2'.") >+ .arg(device_name).arg(driver_name)); >+ >+ if (!InitializeInputs()) >+ { >+ Close(); >+ return false; >+ } >+ >+ SetFormat("Default"); >+ >+ return true; >+} >+ >+void V4LChannel::Close(void) >+{ >+ if (videofd >= 0) >+ close(videofd); >+ videofd = -1; >+} >+ >+void V4LChannel::SetFd(int fd) >+{ >+ if (fd != videofd) >+ Close(); >+ videofd = (fd >= 0) ? fd : -1; >+} >+ >+static int format_to_mode(const QString &fmt, int v4l_version) >+{ >+ if (2 == v4l_version) >+ { >+ if (fmt == "PAL-BG") >+ return V4L2_STD_PAL_BG; >+ else if (fmt == "PAL-D") >+ return V4L2_STD_PAL_D; >+ else if (fmt == "PAL-DK") >+ return V4L2_STD_PAL_DK; >+ else if (fmt == "PAL-I") >+ return V4L2_STD_PAL_I; >+ else if (fmt == "PAL-60") >+ return V4L2_STD_PAL_60; >+ else if (fmt == "SECAM") >+ return V4L2_STD_SECAM; >+ else if (fmt == "SECAM-D") >+ return V4L2_STD_SECAM_D; >+ else if (fmt == "PAL-NC") >+ return V4L2_STD_PAL_Nc; >+ else if (fmt == "PAL-M") >+ return V4L2_STD_PAL_M; >+ else if (fmt == "PAL-N") >+ return V4L2_STD_PAL_N; >+ else if (fmt == "NTSC-JP") >+ return V4L2_STD_NTSC_M_JP; >+ // generics... >+ else if (fmt.left(4) == "NTSC") >+ return V4L2_STD_NTSC; >+ else if (fmt.left(4) == "ATSC") >+ return V4L2_STD_NTSC; // We've dropped V4L ATSC support... >+ else if (fmt.left(3) == "PAL") >+ return V4L2_STD_PAL; >+ return V4L2_STD_NTSC; >+ } >+ else if (1 == v4l_version) >+ { >+ if (fmt == "NTSC-JP") >+ return 6; >+ else if (fmt.left(5) == "SECAM") >+ return VIDEO_MODE_SECAM; >+ else if (fmt == "PAL-NC") >+ return 3; >+ else if (fmt == "PAL-M") >+ return 4; >+ else if (fmt == "PAL-N") >+ return 5; >+ // generics... >+ else if (fmt.left(3) == "PAL") >+ return VIDEO_MODE_PAL; >+ else if (fmt.left(4) == "NTSC") >+ return VIDEO_MODE_NTSC; >+ else if (fmt.left(4) == "ATSC") >+ return VIDEO_MODE_NTSC; // We've dropped V4L ATSC support... >+ return VIDEO_MODE_NTSC; >+ } >+ >+ VERBOSE(VB_IMPORTANT, >+ "format_to_mode() does not recognize V4L" << v4l_version); >+ >+ return V4L2_STD_NTSC; // assume V4L version 2 >+} >+ >+static QString mode_to_format(int mode, int v4l_version) >+{ >+ if (2 == v4l_version) >+ { >+ if (mode == V4L2_STD_NTSC) >+ return "NTSC"; >+ else if (mode == V4L2_STD_NTSC_M_JP) >+ return "NTSC-JP"; >+ else if (mode == V4L2_STD_PAL) >+ return "PAL"; >+ else if (mode == V4L2_STD_PAL_60) >+ return "PAL-60"; >+ else if (mode == V4L2_STD_PAL_BG) >+ return "PAL-BG"; >+ else if (mode == V4L2_STD_PAL_D) >+ return "PAL-D"; >+ else if (mode == V4L2_STD_PAL_DK) >+ return "PAL-DK"; >+ else if (mode == V4L2_STD_PAL_I) >+ return "PAL-I"; >+ else if (mode == V4L2_STD_PAL_M) >+ return "PAL-M"; >+ else if (mode == V4L2_STD_PAL_N) >+ return "PAL-N"; >+ else if (mode == V4L2_STD_PAL_Nc) >+ return "PAL-NC"; >+ else if (mode == V4L2_STD_SECAM) >+ return "SECAM"; >+ else if (mode == V4L2_STD_SECAM_D) >+ return "SECAM-D"; >+ // generic.. >+ else if ((V4L2_STD_NTSC_M == mode) || >+ (V4L2_STD_NTSC_443 == mode) || >+ (V4L2_STD_NTSC_M_KR == mode)) >+ return "NTSC"; >+ else if ((V4L2_STD_PAL_B == mode) || >+ (V4L2_STD_PAL_B1 == mode) || >+ (V4L2_STD_PAL_G == mode) || >+ (V4L2_STD_PAL_H == mode) || >+ (V4L2_STD_PAL_D1 == mode) || >+ (V4L2_STD_PAL_K == mode)) >+ return "PAL"; >+ else if ((V4L2_STD_SECAM_B == mode) || >+ (V4L2_STD_SECAM_DK == mode) || >+ (V4L2_STD_SECAM_G == mode) || >+ (V4L2_STD_SECAM_H == mode) || >+ (V4L2_STD_SECAM_K == mode) || >+ (V4L2_STD_SECAM_K1 == mode) || >+ (V4L2_STD_SECAM_L == mode) || >+ (V4L2_STD_SECAM_LC == mode)) >+ return "SECAM"; >+ else if ((V4L2_STD_ATSC == mode) || >+ (V4L2_STD_ATSC_8_VSB == mode) || >+ (V4L2_STD_ATSC_16_VSB == mode)) >+ { >+ // We've dropped V4L ATSC support, but this still needs to be >+ // returned here so we will change the mode if the device is >+ // in ATSC mode already. >+ return "ATSC"; >+ } >+ } >+ else if (1 == v4l_version) >+ { >+ if (mode == VIDEO_MODE_NTSC) >+ return "NTSC"; >+ else if (mode == VIDEO_MODE_ATSC) >+ return "ATSC"; >+ else if (mode == VIDEO_MODE_PAL) >+ return "PAL"; >+ else if (mode == VIDEO_MODE_SECAM) >+ return "SECAM"; >+ else if (mode == 3) >+ return "PAL-NC"; >+ else if (mode == 4) >+ return "PAL-M"; >+ else if (mode == 5) >+ return "PAL-N"; >+ else if (mode == 6) >+ return "NTSC-JP"; >+ } >+ else >+ { >+ VERBOSE(VB_IMPORTANT, >+ "mode_to_format() does not recognize V4L" << v4l_version); >+ } >+ >+ return "Unknown"; >+} >+ >+/** \fn V4LChannel::InitializeInputs(void) >+ * This enumerates the inputs, converts the format >+ * string to something the hardware understands, and >+ * if the parent pointer is valid retrieves the >+ * channels from the database. >+ */ >+bool V4LChannel::InitializeInputs(void) >+{ >+ // Get Inputs from DB >+ if (!ChannelBase::InitializeInputs()) >+ return false; >+ >+ // Get global TVFormat setting >+ QString fmt = gContext->GetSetting("TVFormat"); >+ VERBOSE(VB_CHANNEL, QString("Global TVFormat Setting '%1'").arg(fmt)); >+ int videomode_v4l1 = format_to_mode(fmt.upper(), 1); >+ int videomode_v4l2 = format_to_mode(fmt.upper(), 2); >+ >+ bool ok = false; >+ InputNames v4l_inputs = CardUtil::ProbeV4LVideoInputs(videofd, ok); >+ >+ // Insert info from hardware >+ uint valid_cnt = 0; >+ InputMap::const_iterator it; >+ for (it = inputs.begin(); it != inputs.end(); ++it) >+ { >+ InputNames::const_iterator v4l_it = v4l_inputs.begin(); >+ for (; v4l_it != v4l_inputs.end(); ++v4l_it) >+ { >+ if (*v4l_it == (*it)->name) >+ { >+ (*it)->inputNumV4L = v4l_it.key(); >+ (*it)->videoModeV4L1 = videomode_v4l1; >+ (*it)->videoModeV4L2 = videomode_v4l2; >+ valid_cnt++; >+ } >+ } >+ } >+ >+ // print em >+ for (it = inputs.begin(); it != inputs.end(); ++it) >+ { >+ VERBOSE(VB_CHANNEL, LOC + QString("Input #%1: '%2' schan(%3) " >+ "tun(%4) v4l1(%5) v4l2(%6)") >+ .arg(it.key()).arg((*it)->name).arg((*it)->startChanNum) >+ .arg((*it)->tuneToChannel) >+ .arg(mode_to_format((*it)->videoModeV4L1,1)) >+ .arg(mode_to_format((*it)->videoModeV4L2,2))); >+ } >+ >+ return valid_cnt; >+} >+ >+/** \fn V4LChannel::SetFormat(const QString &format) >+ * \brief Initializes tuner and modulator variables >+ * >+ * \param format One of twelve formats: >+ * "NTSC", "NTSC-JP", "ATSC", >+ * "SECAM", >+ * "PAL", "PAL-BG", "PAL-DK", "PAL-I", >+ * "PAL-60", "PAL-NC", "PAL-M", or "PAL-N" >+ */ >+void V4LChannel::SetFormat(const QString &format) >+{ >+ if (!Open()) >+ return; >+ >+ int inputNum = currentInputID; >+ if (currentInputID < 0) >+ inputNum = GetNextInputNum(); >+ >+ QString fmt = format; >+ if ((fmt == "Default") || format.isEmpty()) >+ { >+ InputMap::const_iterator it = inputs.find(inputNum); >+ if (it != inputs.end()) >+ fmt = mode_to_format((*it)->videoModeV4L2, 2); >+ } >+ >+ VERBOSE(VB_CHANNEL, LOC + QString("SetFormat(%1) fmt(%2) input(%3)") >+ .arg(format).arg(fmt).arg(inputNum)); >+ >+ if ((fmt == currentFormat) || SetInputAndFormat(inputNum, fmt)) >+ { >+ currentFormat = fmt; >+ is_dtv = (fmt == "ATSC"); >+ } >+} >+ >+int V4LChannel::SetDefaultFreqTable(const QString &name) >+{ >+ defaultFreqTable = SetFreqTable(name); >+ return defaultFreqTable; >+} >+ >+void V4LChannel::SetFreqTable(const int index) >+{ >+ curList = chanlists[index].list; >+ totalChannels = chanlists[index].count; >+} >+ >+int V4LChannel::SetFreqTable(const QString &name) >+{ >+ int i = 0; >+ char *listname = (char *)chanlists[i].name; >+ >+ curList = NULL; >+ while (listname != NULL) >+ { >+ if (name == listname) >+ { >+ SetFreqTable(i); >+ return i; >+ } >+ i++; >+ listname = (char *)chanlists[i].name; >+ } >+ >+ VERBOSE(VB_CHANNEL, QString("Channel(%1)::SetFreqTable(): Invalid " >+ "frequency table name %2, using %3."). >+ arg(device).arg(name).arg((char *)chanlists[1].name)); >+ SetFreqTable(1); >+ return 1; >+} >+ >+int V4LChannel::GetCurrentChannelNum(const QString &channame) >+{ >+ for (int i = 0; i < totalChannels; i++) >+ { >+ if (channame == curList[i].name) >+ return i; >+ } >+ >+ VERBOSE(VB_IMPORTANT, LOC_ERR + >+ QString("GetCurrentChannelNum(%1): " >+ "Failed to find Channel").arg(channame)); >+ >+ return -1; >+} >+ >+void V4LChannel::SaveCachedPids(const pid_cache_t &pid_cache) const >+{ >+ int chanid = GetChanID(); >+ if (chanid > 0) >+ DTVChannel::SaveCachedPids(chanid, pid_cache); >+} >+ >+void V4LChannel::GetCachedPids(pid_cache_t &pid_cache) const >+{ >+ int chanid = GetChanID(); >+ if (chanid > 0) >+ DTVChannel::GetCachedPids(chanid, pid_cache); >+} >+ >+bool V4LChannel::SetChannelByString(const QString &channum) >+{ >+ QString loc = LOC + QString("SetChannelByString(%1)").arg(channum); >+ QString loc_err = loc + ", Error: "; >+ VERBOSE(VB_CHANNEL, loc); >+ >+ if (!Open()) >+ { >+ VERBOSE(VB_IMPORTANT, loc_err + "Channel object " >+ "will not open, can not change channels."); >+ >+ return false; >+ } >+ >+ QString inputName; >+ if (!CheckChannel(channum, inputName)) >+ { >+ VERBOSE(VB_IMPORTANT, loc_err + >+ "CheckChannel failed.\n\t\t\tPlease verify the channel " >+ "in the 'mythtv-setup' Channel Editor."); >+ >+ return false; >+ } >+ >+ // If CheckChannel filled in the inputName then we need to >+ // change inputs and return, since the act of changing >+ // inputs will change the channel as well. >+ if (!inputName.isEmpty()) >+ return ChannelBase::SwitchToInput(inputName, channum); >+ >+ ClearDTVInfo(); >+ >+ InputMap::const_iterator it = inputs.find(currentInputID); >+ if (it == inputs.end()) >+ return false; >+ >+ uint mplexid_restriction; >+ if (!IsInputAvailable(currentInputID, mplexid_restriction)) >+ return false; >+ >+ // Fetch tuning data from the database. >+ QString tvformat, modulation, freqtable, freqid, dtv_si_std; >+ int finetune; >+ uint64_t frequency; >+ int mpeg_prog_num; >+ uint atsc_major, atsc_minor, mplexid, tsid, netid; >+ >+ if (!ChannelUtil::GetChannelData( >+ (*it)->sourceid, channum, >+ tvformat, modulation, freqtable, freqid, >+ finetune, frequency, >+ dtv_si_std, mpeg_prog_num, atsc_major, atsc_minor, tsid, netid, >+ mplexid, commfree)) >+ { >+ return false; >+ } >+ >+ if (mplexid_restriction && (mplexid != mplexid_restriction)) >+ return false; >+ >+ // If the frequency is zeroed out, don't use it directly. >+ bool ok = (frequency > 0); >+ >+ if (!ok) >+ { >+ frequency = (freqid.toInt(&ok) + finetune) * 1000; >+ mplexid = 0; >+ } >+ bool isFrequency = ok && (frequency > 10000000); >+ >+ // If we are tuning to a freqid, rather than an actual frequency, >+ // we need to set the frequency table to use. >+ if (!isFrequency) >+ { >+ if (freqtable == "default" || freqtable.isEmpty()) >+ SetFreqTable(defaultFreqTable); >+ else >+ SetFreqTable(freqtable); >+ } >+ >+ // Set NTSC, PAL, ATSC, etc. >+ SetFormat(tvformat); >+ >+ // If a tuneToChannel is set make sure we're still on it >+ if (!(*it)->tuneToChannel.isEmpty() && (*it)->tuneToChannel != "Undefined") >+ TuneTo((*it)->tuneToChannel, 0); >+ >+ // Tune to proper frequency >+ if ((*it)->externalChanger.isEmpty()) >+ { >+ if ((*it)->name.contains("composite", false) || >+ (*it)->name.contains("s-video", false)) >+ { >+ VERBOSE(VB_GENERAL, LOC_WARN + "You have not set " >+ "an external channel changing" >+ "\n\t\t\tscript for a composite or s-video " >+ "input. Channel changing will do nothing."); >+ } >+ else if (isFrequency) >+ { >+ if (!Tune(frequency, "", (is_dtv) ? "8vsb" : "analog", dtv_si_std)) >+ { >+ return false; >+ } >+ } >+ else >+ { >+ if (!TuneTo(freqid, finetune)) >+ return false; >+ } >+ } >+ else if (!ChangeExternalChannel(freqid)) >+ return false; >+ >+ // Set the current channum to the new channel's channum >+ curchannelname = QDeepCopy<QString>(channum); >+ >+ // Setup filters & recording picture attributes for framegrabing recorders >+ // now that the new curchannelname has been established. >+ if (pParent) >+ pParent->SetVideoFiltersForChannel(GetCurrentSourceID(), channum); >+ InitPictureAttributes(); >+ >+ // Set the major and minor channel for any additional multiplex tuning >+ SetDTVInfo(atsc_major, atsc_minor, netid, tsid, mpeg_prog_num); >+ >+ // Set this as the future start channel for this source >+ inputs[currentInputID]->startChanNum = QDeepCopy<QString>(curchannelname); >+ >+ return true; >+} >+ >+bool V4LChannel::TuneTo(const QString &channum, int finetune) >+{ >+ int i = GetCurrentChannelNum(channum); >+ VERBOSE(VB_CHANNEL, QString("Channel(%1)::TuneTo(%2): " >+ "curList[%3].freq(%4)") >+ .arg(device).arg(channum).arg(i) >+ .arg((i != -1) ? curList[i].freq : -1)); >+ >+ if (i == -1) >+ { >+ VERBOSE(VB_IMPORTANT, QString("Channel(%1)::TuneTo(%2): Error, " >+ "failed to find channel.") >+ .arg(device).arg(channum)); >+ return false; >+ } >+ >+ int frequency = (curList[i].freq + finetune) * 1000; >+ >+ return Tune(frequency, "", "analog", "analog"); >+} >+ >+bool V4LChannel::Tune(const DTVMultiplex &tuning, QString inputname) >+{ >+ return Tune(tuning.frequency - 1750000, // to visual carrier >+ inputname, tuning.modulation.toString(), tuning.sistandard); >+} >+ >+/** \fn V4LChannel::Tune(uint,QString,QString,QString) >+ * \brief Tunes to a specific frequency (Hz) on a particular input, using >+ * the specified modulation. >+ * >+ * Note: This function always uses modulator zero. >+ * >+ * \param frequency Frequency in Hz, this is divided by 62.5 kHz or 62.5 Hz >+ * depending on the modulator and sent to the hardware. >+ * \param inputname Name of the input (Television, Antenna 1, etc.) >+ * \param modulation "radio", "analog", or "digital" >+ */ >+bool V4LChannel::Tune(uint frequency, QString inputname, >+ QString modulation, QString si_std) >+{ >+ VERBOSE(VB_CHANNEL, LOC + QString("Tune(%1, %2, %3, %4)") >+ .arg(frequency).arg(inputname).arg(modulation).arg(si_std)); >+ >+ int ioctlval = 0; >+ >+ if (modulation == "8vsb") >+ SetFormat("ATSC"); >+ modulation = (is_dtv) ? "digital" : modulation; >+ >+ int inputnum = GetInputByName(inputname); >+ >+ bool ok = true; >+ if ((inputnum >= 0) && (GetCurrentInputNum() != inputnum)) >+ ok = SwitchToInput(inputnum, false); >+ else if (GetCurrentInputNum() < 0) >+ ok = SwitchToInput(0, false); >+ >+ if (!ok) >+ return false; >+ >+ // If the frequency is a center frequency and not >+ // a visual carrier frequency, convert it. >+ int offset = frequency % 1000000; >+ offset = (offset > 500000) ? 1000000 - offset : offset; >+ bool is_visual_carrier = (offset > 150000) && (offset < 350000); >+ if (!is_visual_carrier && currentFormat == "ATSC") >+ { >+ VERBOSE(VB_CHANNEL, QString("Channel(%1): ").arg(device) + >+ QString("Converting frequency from center frequency " >+ "(%1 Hz) to visual carrier frequency (%2 Hz).") >+ .arg(frequency).arg(frequency - 1750000)); >+ frequency -= 1750000; // convert to visual carrier >+ } >+ >+ // Video4Linux version 2 tuning >+ if (usingv4l2) >+ { >+ bool isTunerCapLow = false; >+ struct v4l2_modulator mod; >+ bzero(&mod, sizeof(mod)); >+ mod.index = 0; >+ ioctlval = ioctl(videofd, VIDIOC_G_MODULATOR, &mod); >+ if (ioctlval >= 0) >+ { >+ isTunerCapLow = (mod.capability & V4L2_TUNER_CAP_LOW); >+ VERBOSE(VB_CHANNEL, " name: "<<mod.name); >+ VERBOSE(VB_CHANNEL, "CapLow: "<<isTunerCapLow); >+ } >+ >+ struct v4l2_frequency vf; >+ bzero(&vf, sizeof(vf)); >+ >+ vf.tuner = 0; // use first tuner >+ vf.frequency = (isTunerCapLow) ? >+ ((int)(frequency / 62.5)) : (frequency / 62500); >+ >+ if (modulation.lower() == "digital") >+ { >+ VERBOSE(VB_CHANNEL, "using digital modulation"); >+ vf.type = V4L2_TUNER_DIGITAL_TV; >+ if (ioctl(videofd, VIDIOC_S_FREQUENCY, &vf)>=0) >+ return true; >+ VERBOSE(VB_CHANNEL, "digital modulation failed"); >+ } >+ >+ vf.type = V4L2_TUNER_ANALOG_TV; >+ >+ ioctlval = ioctl(videofd, VIDIOC_S_FREQUENCY, &vf); >+ if (ioctlval < 0) >+ { >+ VERBOSE(VB_IMPORTANT, >+ QString("Channel(%1)::Tune(): Error %2 " >+ "while setting frequency (v2): %3") >+ .arg(device).arg(ioctlval).arg(strerror(errno))); >+ return false; >+ } >+ ioctlval = ioctl(videofd, VIDIOC_G_FREQUENCY, &vf); >+ >+ if (ioctlval >= 0) >+ { >+ VERBOSE(VB_CHANNEL, QString( >+ "Channel(%1)::Tune(): Frequency is now %2") >+ .arg(device).arg(vf.frequency * 62500)); >+ } >+ >+ return true; >+ } >+ >+ // Video4Linux version 1 tuning >+ uint freq = frequency / 62500; >+ ioctlval = ioctl(videofd, VIDIOCSFREQ, &freq); >+ if (ioctlval < 0) >+ { >+ VERBOSE(VB_IMPORTANT, >+ QString("Channel(%1)::Tune(): Error %2 " >+ "while setting frequency (v1): %3") >+ .arg(device).arg(ioctlval).arg(strerror(errno))); >+ return false; >+ } >+ >+ SetSIStandard(si_std); >+ >+ return true; >+} >+ >+/** \fn V4LChannel::Retune(void) >+ * \brief Retunes to last tuned frequency. >+ * >+ * NOTE: This only works for V4L2 and only for analog tuning. >+ */ >+bool V4LChannel::Retune(void) >+{ >+ if (usingv4l2) >+ { >+ struct v4l2_frequency vf; >+ bzero(&vf, sizeof(vf)); >+ >+ vf.tuner = 0; // use first tuner >+ vf.type = V4L2_TUNER_ANALOG_TV; >+ >+ // Get the last tuned frequency >+ int ioctlval = ioctl(videofd, VIDIOC_G_FREQUENCY, &vf); >+ if (ioctlval < 0) >+ { >+ VERBOSE(VB_IMPORTANT, LOC_ERR + "Retune failed (1)" + ENO); >+ return false; >+ } >+ >+ // Set the last tuned frequency again... >+ ioctlval = ioctl(videofd, VIDIOC_S_FREQUENCY, &vf); >+ if (ioctlval < 0) >+ { >+ VERBOSE(VB_IMPORTANT, LOC_ERR + "Retune failed (2)" + ENO); >+ return false; >+ } >+ >+ return true; >+ } >+ >+ return false; >+} >+ >+// documented in dtvchannel.h >+bool V4LChannel::TuneMultiplex(uint mplexid, QString inputname) >+{ >+ VERBOSE(VB_CHANNEL, LOC + QString("TuneMultiplex(%1)").arg(mplexid)); >+ >+ QString modulation; >+ QString si_std; >+ uint64_t frequency; >+ uint transportid; >+ uint dvb_networkid; >+ >+ if (!ChannelUtil::GetTuningParams( >+ mplexid, modulation, frequency, >+ transportid, dvb_networkid, si_std)) >+ { >+ VERBOSE(VB_IMPORTANT, LOC_ERR + "TuneMultiplex(): " + >+ QString("Could not find tuning parameters for multiplex %1.") >+ .arg(mplexid)); >+ >+ return false; >+ } >+ >+ if (!Tune(frequency, inputname, modulation, si_std)) >+ return false; >+ >+ return true; >+} >+ >+QString V4LChannel::GetFormatForChannel(QString channum, QString inputname) >+{ >+ MSqlQuery query(MSqlQuery::InitCon()); >+ query.prepare( >+ "SELECT tvformat " >+ "FROM channel, cardinput " >+ "WHERE channum = :CHANNUM AND " >+ " inputname = :INPUTNAME AND " >+ " cardinput.cardid = :CARDID AND " >+ " cardinput.sourceid = channel.sourceid"); >+ query.bindValue(":CHANNUM", channum); >+ query.bindValue(":INPUTNAME", inputname); >+ query.bindValue(":CARDID", GetCardID()); >+ >+ QString fmt = QString::null; >+ if (!query.exec() || !query.isActive()) >+ MythContext::DBError("SwitchToInput:find format", query); >+ else if (query.next()) >+ fmt = query.value(0).toString(); >+ return fmt; >+} >+ >+bool V4LChannel::SetInputAndFormat(int inputNum, QString newFmt) >+{ >+ InputMap::const_iterator it = inputs.find(inputNum); >+ if (it == inputs.end() || (*it)->inputNumV4L < 0) >+ return false; >+ >+ int inputNumV4L = (*it)->inputNumV4L; >+ bool usingv4l1 = !usingv4l2; >+ bool ok = true; >+ >+ QString msg = >+ QString("SetInputAndFormat(%1, %2) ").arg(inputNum).arg(newFmt); >+ >+ if (usingv4l2) >+ { >+ VERBOSE(VB_CHANNEL, LOC + msg + "(v4l v2)"); >+ >+ int ioctlval = ioctl(videofd, VIDIOC_S_INPUT, &inputNumV4L); >+ >+ // ConvertX (wis-go7007) requires streaming to be disabled >+ // before an input switch, do this if initial switch failed. >+ bool streamingDisabled = false; >+ int streamType = V4L2_BUF_TYPE_VIDEO_CAPTURE; >+ if ((ioctlval < 0) && (errno == EBUSY)) >+ { >+ ioctlval = ioctl(videofd, VIDIOC_STREAMOFF, &streamType); >+ if (ioctlval < 0) >+ { >+ VERBOSE(VB_IMPORTANT, LOC_ERR + msg + >+ "\n\t\t\twhile disabling streaming (v4l v2)" + ENO); >+ >+ ok = false; >+ ioctlval = 0; >+ } >+ else >+ { >+ streamingDisabled = true; >+ >+ // Resend the input switch ioctl. >+ ioctlval = ioctl(videofd, VIDIOC_S_INPUT, &inputNumV4L); >+ } >+ } >+ >+ if (ioctlval < 0) >+ { >+ VERBOSE(VB_IMPORTANT, LOC_ERR + msg + >+ "\n\t\t\twhile setting input (v4l v2)" + ENO); >+ >+ ok = false; >+ } >+ >+ v4l2_std_id vid_mode = format_to_mode(newFmt, 2); >+ ioctlval = ioctl(videofd, VIDIOC_S_STD, &vid_mode); >+ if (ioctlval < 0) >+ { >+ VERBOSE(VB_IMPORTANT, LOC_ERR + msg + >+ "\n\t\t\twhile setting format (v4l v2)" + ENO); >+ >+ ok = false; >+ } >+ >+ // ConvertX (wis-go7007) requires streaming to be disabled >+ // before an input switch, here we try to re-enable streaming. >+ if (streamingDisabled) >+ { >+ ioctlval = ioctl(videofd, VIDIOC_STREAMON, &streamType); >+ if (ioctlval < 0) >+ { >+ VERBOSE(VB_IMPORTANT, LOC_ERR + msg + >+ "\n\t\t\twhile reenabling streaming (v4l v2)" + ENO); >+ >+ ok = false; >+ } >+ } >+ } >+ >+ if (usingv4l1) >+ { >+ VERBOSE(VB_CHANNEL, LOC + msg + "(v4l v1)"); >+ >+ // read in old settings >+ struct video_channel set; >+ bzero(&set, sizeof(set)); >+ ioctl(videofd, VIDIOCGCHAN, &set); >+ >+ // set new settings >+ set.channel = inputNumV4L; >+ set.norm = format_to_mode(newFmt, 1); >+ int ioctlval = ioctl(videofd, VIDIOCSCHAN, &set); >+ >+ ok = (ioctlval >= 0); >+ if (!ok) >+ { >+ VERBOSE(VB_IMPORTANT, LOC_ERR + msg + >+ "\n\t\t\twhile setting format (v4l v1)" + ENO); >+ } >+ else if (usingv4l2) >+ { >+ VERBOSE(VB_IMPORTANT, LOC + msg + >+ "\n\t\t\tSetting video mode with v4l version 1 worked"); >+ } >+ } >+ return ok; >+} >+ >+bool V4LChannel::SwitchToInput(int inputnum, bool setstarting) >+{ >+ InputMap::const_iterator it = inputs.find(inputnum); >+ if (it == inputs.end()) >+ return false; >+ >+ QString tuneFreqId = (*it)->tuneToChannel; >+ QString channum = (*it)->startChanNum; >+ QString inputname = (*it)->name; >+ >+ VERBOSE(VB_CHANNEL, QString("Channel(%1)::SwitchToInput(in %2, '%3')") >+ .arg(device).arg(inputnum) >+ .arg(setstarting ? channum : QString(""))); >+ >+ uint mplexid_restriction; >+ if (!IsInputAvailable(inputnum, mplexid_restriction)) >+ return false; >+ >+ QString newFmt = mode_to_format((*it)->videoModeV4L2, 2); >+ >+ // If we are setting a channel, get its video mode... >+ bool chanValid = (channum != "Undefined") && !channum.isEmpty(); >+ if (setstarting && chanValid) >+ { >+ QString tmp = GetFormatForChannel(channum, inputname); >+ if (tmp != "Default" && !tmp.isEmpty()) >+ newFmt = tmp; >+ } >+ >+ bool ok = SetInputAndFormat(inputnum, newFmt); >+ >+ // Try to set ATSC mode if NTSC fails >+ if (!ok && newFmt == "NTSC") >+ ok = SetInputAndFormat(inputnum, "ATSC"); >+ >+ if (!ok) >+ { >+ VERBOSE(VB_IMPORTANT, LOC + "SetInputAndFormat() failed"); >+ return false; >+ } >+ >+ currentFormat = newFmt; >+ is_dtv = newFmt == "ATSC"; >+ currentInputID = inputnum; >+ curchannelname = ""; // this will be set by SetChannelByString >+ >+ if (!tuneFreqId.isEmpty() && tuneFreqId != "Undefined") >+ ok = TuneTo(tuneFreqId, 0); >+ >+ if (!ok) >+ return false; >+ >+ if (setstarting && chanValid) >+ ok = SetChannelByString(channum); >+ else if (setstarting && !chanValid) >+ { >+ VERBOSE(VB_IMPORTANT, LOC + >+ QString("SwitchToInput(in %2, set ch): ").arg(inputnum) + >+ QString("\n\t\t\tDefault channel '%1' is not valid.") >+ .arg(channum)); >+ ok = false; >+ } >+ >+ return ok; >+} >+ >+static unsigned short *get_v4l1_field( >+ int v4l2_attrib, struct video_picture &vid_pic) >+{ >+ switch (v4l2_attrib) >+ { >+ case V4L2_CID_CONTRAST: >+ return &vid_pic.contrast; >+ case V4L2_CID_BRIGHTNESS: >+ return &vid_pic.brightness; >+ case V4L2_CID_SATURATION: >+ return &vid_pic.colour; >+ case V4L2_CID_HUE: >+ return &vid_pic.hue; >+ default: >+ VERBOSE(VB_IMPORTANT, "get_v4l1_field: " >+ "invalid attribute argument "<<v4l2_attrib); >+ } >+ return NULL; >+} >+ >+static int get_v4l2_attribute(const QString &db_col_name) >+{ >+ if ("brightness" == db_col_name) >+ return V4L2_CID_BRIGHTNESS; >+ else if ("contrast" == db_col_name) >+ return V4L2_CID_CONTRAST; >+ else if ("colour" == db_col_name) >+ return V4L2_CID_SATURATION; >+ else if ("hue" == db_col_name) >+ return V4L2_CID_HUE; >+ return -1; >+} >+ >+bool V4LChannel::InitPictureAttribute(const QString db_col_name) >+{ >+ if (!pParent || is_dtv) >+ return false; >+ >+ int v4l2_attrib = get_v4l2_attribute(db_col_name); >+ if (v4l2_attrib == -1) >+ return false; >+ >+ int cfield = ChannelUtil::GetChannelValueInt( >+ db_col_name, GetCurrentSourceID(), curchannelname); >+ int sfield = CardUtil::GetValueInt( >+ db_col_name, GetCardID()); >+ >+ if ((cfield == -1) || (sfield == -1)) >+ return false; >+ >+ int field = (cfield + sfield) & 0xFFFF; >+ >+ QString loc = LOC + >+ QString("InitPictureAttribute(%1): ").arg(db_col_name, 10); >+ QString loc_err = LOC_ERR + >+ QString("InitPictureAttribute(%1): ").arg(db_col_name, 10); >+ >+ if (usingv4l2) >+ { >+ struct v4l2_control ctrl; >+ struct v4l2_queryctrl qctrl; >+ bzero(&ctrl, sizeof(ctrl)); >+ bzero(&qctrl, sizeof(qctrl)); >+ >+ ctrl.id = qctrl.id = v4l2_attrib; >+ if (ioctl(videofd, VIDIOC_QUERYCTRL, &qctrl) < 0) >+ { >+ VERBOSE(VB_IMPORTANT, loc_err + "failed to query controls." + ENO); >+ return false; >+ } >+ >+ float new_range = qctrl.maximum - qctrl.minimum; >+ float old_range = 65535 - 0; >+ float scl_range = new_range / old_range; >+ float dfl = (qctrl.default_value - qctrl.minimum) / new_range; >+ int norm_dfl = (0x10000 + (int)(dfl * old_range) - 32768) & 0xFFFF; >+ >+ if (pict_attr_default.find(db_col_name) == pict_attr_default.end()) >+ { >+ if (device_name == "pcHDTV HD3000 HDTV") >+ { >+ pict_attr_default["brightness"] = 9830; >+ pict_attr_default["contrast"] = 39322; >+ pict_attr_default["colour"] = 45875; >+ pict_attr_default["hue"] = 0; >+ } >+ else >+ { >+ pict_attr_default[db_col_name] = norm_dfl; >+ } >+ } >+ >+ int dfield = pict_attr_default[db_col_name]; >+ field = (cfield + sfield + dfield) & 0xFFFF; >+ int value0 = (int) ((scl_range * field) + qctrl.minimum); >+ int value1 = min(value0, (int)qctrl.maximum); >+ ctrl.value = max(value1, (int)qctrl.minimum); >+ >+#if DEBUG_ATTRIB >+ VERBOSE(VB_CHANNEL, loc + QString(" %1\n\t\t\t" >+ "[%2,%3] dflt(%4, %5, %6)") >+ .arg(value0).arg(qctrl.minimum, 5).arg(qctrl.maximum, 5) >+ .arg(qctrl.default_value, 5).arg(dfl, 4, 'f', 2) >+ .arg(norm_dfl)); >+#endif >+ >+ if (ioctl(videofd, VIDIOC_S_CTRL, &ctrl) < 0) >+ { >+ VERBOSE(VB_IMPORTANT, loc_err + "failed to set controls" + ENO); >+ return false; >+ } >+ >+ return true; >+ } >+ >+ // V4L1 >+ unsigned short *setfield; >+ struct video_picture vid_pic; >+ bzero(&vid_pic, sizeof(vid_pic)); >+ >+ if (ioctl(videofd, VIDIOCGPICT, &vid_pic) < 0) >+ { >+ VERBOSE(VB_IMPORTANT, loc_err + "failed to query controls." + ENO); >+ return false; >+ } >+ setfield = get_v4l1_field(v4l2_attrib, vid_pic); >+ >+ if (!setfield) >+ return false; >+ >+ *setfield = field; >+ if (ioctl(videofd, VIDIOCSPICT, &vid_pic) < 0) >+ { >+ VERBOSE(VB_IMPORTANT, loc_err + "failed to set controls." + ENO); >+ return false; >+ } >+ >+ return true; >+} >+ >+bool V4LChannel::InitPictureAttributes(void) >+{ >+ return (InitPictureAttribute("brightness") && >+ InitPictureAttribute("contrast") && >+ InitPictureAttribute("colour") && >+ InitPictureAttribute("hue")); >+} >+ >+int V4LChannel::GetPictureAttribute(PictureAttribute attr) const >+{ >+ QString db_col_name = toDBString(attr); >+ if (db_col_name.isEmpty()) >+ return -1; >+ >+ int cfield = ChannelUtil::GetChannelValueInt( >+ db_col_name, GetCurrentSourceID(), curchannelname); >+ int sfield = CardUtil::GetValueInt( >+ db_col_name, GetCardID()); >+ int dfield = 0; >+ >+ if (pict_attr_default.find(db_col_name) != pict_attr_default.end()) >+ dfield = pict_attr_default[db_col_name]; >+ >+ int val = (cfield + sfield + dfield) & 0xFFFF; >+ >+#if DEBUG_ATTRIB >+ VERBOSE(VB_CHANNEL, QString( >+ "GetPictureAttribute(%1) -> cdb %2 rdb %3 d %4 -> %5") >+ .arg(db_col_name).arg(cfield).arg(sfield) >+ .arg(dfield).arg(val)); >+#endif >+ >+ return val; >+} >+ >+static int get_v4l2_attribute_value(int videofd, int v4l2_attrib) >+{ >+ struct v4l2_control ctrl; >+ struct v4l2_queryctrl qctrl; >+ bzero(&ctrl, sizeof(ctrl)); >+ bzero(&qctrl, sizeof(qctrl)); >+ >+ ctrl.id = qctrl.id = v4l2_attrib; >+ if (ioctl(videofd, VIDIOC_QUERYCTRL, &qctrl) < 0) >+ { >+ VERBOSE(VB_IMPORTANT, "get_v4l2_attribute_value: " >+ "failed to query controls (1)" + ENO); >+ return -1; >+ } >+ >+ if (ioctl(videofd, VIDIOC_G_CTRL, &ctrl) < 0) >+ { >+ VERBOSE(VB_IMPORTANT, "get_v4l2_attribute_value: " >+ "failed to get controls (2)" + ENO); >+ return -1; >+ } >+ >+ float mult = 65535.0 / (qctrl.maximum - qctrl.minimum); >+ return min(max((int)(mult * (ctrl.value - qctrl.minimum)), 0), 65525); >+} >+ >+static int get_v4l1_attribute_value(int videofd, int v4l2_attrib) >+{ >+ struct video_picture vid_pic; >+ bzero(&vid_pic, sizeof(vid_pic)); >+ >+ if (ioctl(videofd, VIDIOCGPICT, &vid_pic) < 0) >+ { >+ VERBOSE(VB_IMPORTANT, "get_v4l1_attribute_value: " >+ "failed to get picture control (1)" + ENO); >+ return -1; >+ } >+ >+ unsigned short *setfield = get_v4l1_field(v4l2_attrib, vid_pic); >+ if (setfield) >+ return *setfield; >+ >+ return -1; >+} >+ >+static int get_attribute_value(bool usingv4l2, int videofd, int v4l2_attrib) >+{ >+ if (usingv4l2) >+ return get_v4l2_attribute_value(videofd, v4l2_attrib); >+ return get_v4l1_attribute_value(videofd, v4l2_attrib); >+} >+ >+static int set_v4l2_attribute_value(int videofd, int v4l2_attrib, int newvalue) >+{ >+ struct v4l2_control ctrl; >+ struct v4l2_queryctrl qctrl; >+ bzero(&ctrl, sizeof(ctrl)); >+ bzero(&qctrl, sizeof(qctrl)); >+ >+ ctrl.id = qctrl.id = v4l2_attrib; >+ if (ioctl(videofd, VIDIOC_QUERYCTRL, &qctrl) < 0) >+ { >+ VERBOSE(VB_IMPORTANT, "set_v4l2_attribute_value: " >+ "failed to query control" + ENO); >+ return -1; >+ } >+ >+ float mult = (qctrl.maximum - qctrl.minimum) / 65535.0; >+ ctrl.value = (int)(mult * newvalue + qctrl.minimum); >+ ctrl.value = min(ctrl.value, qctrl.maximum); >+ ctrl.value = max(ctrl.value, qctrl.minimum); >+ >+ if (ioctl(videofd, VIDIOC_S_CTRL, &ctrl) < 0) >+ { >+ VERBOSE(VB_IMPORTANT, "set_v4l2_attribute_value: " >+ "failed to set control" + ENO); >+ return -1; >+ } >+ >+ return 0; >+} >+ >+static int set_v4l1_attribute_value(int videofd, int v4l2_attrib, int newvalue) >+{ >+ unsigned short *setfield; >+ struct video_picture vid_pic; >+ bzero(&vid_pic, sizeof(vid_pic)); >+ >+ if (ioctl(videofd, VIDIOCGPICT, &vid_pic) < 0) >+ { >+ VERBOSE(VB_IMPORTANT, "set_v4l1_attribute_value: " >+ "failed to get picture control." + ENO); >+ return -1; >+ } >+ setfield = get_v4l1_field(v4l2_attrib, vid_pic); >+ if (newvalue != -1 && setfield) >+ { >+ *setfield = newvalue; >+ if (ioctl(videofd, VIDIOCSPICT, &vid_pic) < 0) >+ { >+ VERBOSE(VB_IMPORTANT, "set_v4l1_attribute_value: " >+ "failed to set picture control." + ENO); >+ return -1; >+ } >+ } >+ else >+ { >+ // ??? >+ return -1; >+ } >+ >+ return 0; >+} >+ >+static int set_attribute_value(bool usingv4l2, int videofd, >+ int v4l2_attrib, int newvalue) >+{ >+ if (usingv4l2) >+ return set_v4l2_attribute_value(videofd, v4l2_attrib, newvalue); >+ return set_v4l1_attribute_value(videofd, v4l2_attrib, newvalue); >+} >+ >+int V4LChannel::ChangePictureAttribute( >+ PictureAdjustType type, PictureAttribute attr, bool up) >+{ >+ if (!pParent || is_dtv) >+ return -1; >+ >+ QString db_col_name = toDBString(attr); >+ if (db_col_name.isEmpty()) >+ return -1; >+ >+ int v4l2_attrib = get_v4l2_attribute(db_col_name); >+ if (v4l2_attrib == -1) >+ return -1; >+ >+ // get the old attribute value from the hardware, this is >+ // just a sanity check on whether this attribute exists >+ if (get_attribute_value(usingv4l2, videofd, v4l2_attrib) < 0) >+ return -1; >+ >+ int old_value = GetPictureAttribute(attr); >+ int new_value = old_value + ((up) ? 655 : -655); >+ >+ // make sure we are within bounds (wrap around for hue) >+ if (V4L2_CID_HUE == v4l2_attrib) >+ new_value &= 0xffff; >+ new_value = min(max(new_value, 0), 65535); >+ >+#if DEBUG_ATTRIB >+ VERBOSE(VB_CHANNEL, QString( >+ "ChangePictureAttribute(%1,%2,%3) cur %4 -> new %5") >+ .arg(type).arg(db_col_name).arg(up) >+ .arg(old_value).arg(new_value)); >+#endif >+ >+ // actually set the new attribute value on the hardware >+ if (set_attribute_value(usingv4l2, videofd, v4l2_attrib, new_value) < 0) >+ return -1; >+ >+ // tell the DB about the new attribute value >+ if (kAdjustingPicture_Channel == type) >+ { >+ int adj_value = ChannelUtil::GetChannelValueInt( >+ db_col_name, GetCurrentSourceID(), curchannelname); >+ >+ int tmp = new_value - old_value + adj_value; >+ tmp = (tmp < 0) ? tmp + 0x10000 : tmp; >+ tmp = (tmp > 0xffff) ? tmp - 0x10000 : tmp; >+ ChannelUtil::SetChannelValue(db_col_name, QString::number(tmp), >+ GetCurrentSourceID(), curchannelname); >+ } >+ else if (kAdjustingPicture_Recording == type) >+ { >+ int adj_value = CardUtil::GetValueInt( >+ db_col_name, GetCardID()); >+ >+ int tmp = new_value - old_value + adj_value; >+ tmp = (tmp < 0) ? tmp + 0x10000 : tmp; >+ tmp = (tmp > 0xffff) ? tmp - 0x10000 : tmp; >+ CardUtil::SetValue(db_col_name, GetCardID(), >+ GetCurrentSourceID(), tmp); >+ } >+ >+ return new_value; >+} >Index: mythtv/libs/libmythtv/remoteencoder.h >=================================================================== >--- mythtv/libs/libmythtv/remoteencoder.h (revision 18528) >+++ mythtv/libs/libmythtv/remoteencoder.h (working copy) >@@ -23,7 +23,7 @@ > int GetRecorderNumber(void); > > ProgramInfo *GetRecording(void); >- bool IsRecording(void); >+ bool IsRecording(bool *ok = NULL); > float GetFrameRate(void); > long long GetFramesWritten(void); > /// \brief Return value last returned by GetFramesWritten(). >Index: mythtv/libs/libmythtv/cardutil.cpp >=================================================================== >--- mythtv/libs/libmythtv/cardutil.cpp (revision 18528) >+++ mythtv/libs/libmythtv/cardutil.cpp (working copy) >@@ -114,6 +114,28 @@ > return count > 0; > } > >+QStringList CardUtil::GetCardTypes(void) >+{ >+ QStringList cardtypes; >+ >+ MSqlQuery query(MSqlQuery::InitCon()); >+ query.prepare("SELECT DISTINCT cardtype " >+ "FROM capturecard " >+ "ORDER BY cardtype"); >+ >+ if (!query.exec()) >+ { >+ MythContext::DBError("CardUtil::GetCardTypes()", query); >+ } >+ else >+ { >+ while (query.next()) >+ cardtypes.push_back(query.value(0).toString()); >+ } >+ >+ return cardtypes; >+} >+ > /** \fn CardUtil::GetVideoDevices(const QString&, QString) > * \brief Returns the videodevices of the matching cards, duplicates removed > * \param rawtype Card type as used in DB or empty string for all cardids >@@ -1365,7 +1387,7 @@ > return !card.isEmpty(); > } > >-InputNames CardUtil::probeV4LInputs(int videofd, bool &ok) >+InputNames CardUtil::ProbeV4LVideoInputs(int videofd, bool &ok) > { > (void) videofd; > >@@ -1430,6 +1452,39 @@ > return list; > } > >+InputNames CardUtil::ProbeV4LAudioInputs(int videofd, bool &ok) >+{ >+ (void) videofd; >+ >+ InputNames list; >+ ok = false; >+ >+#ifdef USING_V4L >+ bool usingv4l2 = hasV4L2(videofd); >+ >+ // V4L v2 query >+ struct v4l2_audio ain; >+ bzero(&ain, sizeof(ain)); >+ while (usingv4l2 && (ioctl(videofd, VIDIOC_ENUMAUDIO, &ain) >= 0)) >+ { >+ QString input((char *)ain.name); >+ list[ain.index] = input; >+ ain.index++; >+ } >+ if (ain.index) >+ { >+ ok = true; >+ return list; >+ } >+ >+ ok = true; >+#else // if !USING_V4L >+ list[-1] += QObject::tr( >+ "ERROR, Compile with V4L support to query audio inputs"); >+#endif // !USING_V4L >+ return list; >+} >+ > InputNames CardUtil::GetConfiguredDVBInputs(uint cardid) > { > InputNames list; >@@ -1450,7 +1505,7 @@ > return list; > } > >-QStringList CardUtil::probeInputs(QString device, QString cardtype) >+QStringList CardUtil::ProbeVideoInputs(QString device, QString cardtype) > { > QStringList ret; > >@@ -1462,15 +1517,26 @@ > ret += "MPEG2TS"; > } > else if ("DVB" == cardtype) >- ret += probeDVBInputs(device); >+ ret += ProbeDVBInputs(device); > else >- ret += probeV4LInputs(device); >+ ret += ProbeV4LVideoInputs(device); > > return ret; > } > >-QStringList CardUtil::probeV4LInputs(QString device) >+QStringList CardUtil::ProbeAudioInputs(QString device, QString cardtype) > { >+ VERBOSE(VB_IMPORTANT, QString("ProbeAudioInputs(%1,%2)").arg(device).arg(cardtype)); >+ QStringList ret; >+ >+ if ("HDPVR" == cardtype) >+ ret += ProbeV4LAudioInputs(device); >+ >+ return ret; >+} >+ >+QStringList CardUtil::ProbeV4LVideoInputs(QString device) >+{ > bool ok; > QStringList ret; > int videofd = open(device.ascii(), O_RDWR); >@@ -1480,7 +1546,7 @@ > "to probe its inputs.").arg(device); > return ret; > } >- InputNames list = CardUtil::probeV4LInputs(videofd, ok); >+ InputNames list = CardUtil::ProbeV4LVideoInputs(videofd, ok); > close(videofd); > > if (!ok) >@@ -1499,10 +1565,43 @@ > return ret; > } > >-QStringList CardUtil::probeDVBInputs(QString device) >+QStringList CardUtil::ProbeV4LAudioInputs(QString device) > { >+ VERBOSE(VB_IMPORTANT, QString("ProbeV4LAudioInputs(%1)").arg(device)); >+ >+ bool ok; > QStringList ret; >+ int videofd = open(device.ascii(), O_RDWR); >+ if (videofd < 0) >+ { >+ VERBOSE(VB_IMPORTANT, QString("ProbeAudioInputs() -> couldn't open device")); >+ ret += QObject::tr("Could not open '%1' " >+ "to probe its inputs.").arg(device); >+ return ret; >+ } >+ InputNames list = CardUtil::ProbeV4LAudioInputs(videofd, ok); >+ close(videofd); > >+ if (!ok) >+ { >+ ret += list[-1]; >+ return ret; >+ } >+ >+ InputNames::iterator it; >+ for (it = list.begin(); it != list.end(); ++it) >+ { >+ if (it.key() >= 0) >+ ret += *it; >+ } >+ >+ return ret; >+} >+ >+QStringList CardUtil::ProbeDVBInputs(QString device) >+{ >+ QStringList ret; >+ > #ifdef USING_DVB > uint cardid = CardUtil::GetFirstCardID(device); > if (!cardid) >@@ -1587,7 +1686,7 @@ > inputs += "MPEG2TS"; > } > else if ("DVB" != cardtype) >- inputs += probeV4LInputs(device); >+ inputs += ProbeV4LVideoInputs(device); > > QString dev_label = GetDeviceLabel(cardid, cardtype, device); > >Index: mythtv/libs/libmythtv/siscan.h >=================================================================== >--- mythtv/libs/libmythtv/siscan.h (revision 18528) >+++ mythtv/libs/libmythtv/siscan.h (working copy) >@@ -20,7 +20,7 @@ > > class ChannelBase; > class DTVChannel; >-class Channel; >+class V4LChannel; > class DVBChannel; > class HDHRChannel; > >@@ -116,7 +116,7 @@ > private: > // some useful gets > DTVChannel *GetDTVChannel(void); >- Channel *GetChannel(void); >+ V4LChannel *GetV4LChannel(void); > DVBChannel *GetDVBChannel(void); > > /// \brief Called by SpawnScanner to run scanning thread >Index: mythtv/libs/libmythtv/videosource.cpp >=================================================================== >--- mythtv/libs/libmythtv/videosource.cpp (revision 18528) >+++ mythtv/libs/libmythtv/videosource.cpp (working copy) >@@ -1446,6 +1446,47 @@ > input->fillSelections(device); > } > >+HDPVRConfigurationGroup::HDPVRConfigurationGroup(CaptureCard &a_parent) : >+ VerticalConfigurationGroup(false, true, false, false), >+ parent(a_parent), cardinfo(new TransLabelSetting()), >+ videoinput(new TunerCardInput(parent)), >+ audioinput(new TunerCardAudioInput(parent, QString::null, "HDPVR")) >+{ >+ VideoDevice *device = >+ new VideoDevice(parent, 0, 15, QString::null, "hdpvr"); >+ >+ cardinfo->setLabel(tr("Probed info")); >+ >+ addChild(device); >+ addChild(cardinfo); >+ addChild(videoinput); >+ addChild(audioinput); >+ >+ connect(device, SIGNAL(valueChanged(const QString&)), >+ this, SLOT( probeCard( const QString&))); >+ >+ probeCard(device->getValue()); >+} >+ >+void HDPVRConfigurationGroup::probeCard(const QString &device) >+{ >+ QString cn = tr("Failed to open"), ci = cn, dn = QString::null; >+ >+ int videofd = open(device.ascii(), O_RDWR); >+ if (videofd >= 0) >+ { >+ if (!CardUtil::GetV4LInfo(videofd, cn, dn)) >+ ci = cn = tr("Failed to probe"); >+ else if (!dn.isEmpty()) >+ ci = cn + " [" + dn + "]"; >+ close(videofd); >+ } >+ >+ cardinfo->setValue(ci); >+ videoinput->fillSelections(device); >+ audioinput->fillSelections(device); >+} >+ > CaptureCardGroup::CaptureCardGroup(CaptureCard &parent) : > TriggeredConfigurationGroup(true, true, false, false) > { >@@ -1462,6 +1503,9 @@ > # ifdef USING_IVTV > addTarget("MPEG", new MPEGConfigurationGroup(parent)); > # endif // USING_IVTV >+# ifdef USING_HDPVR >+ addTarget("HDPVR", new HDPVRConfigurationGroup(parent)); >+# endif // USING_HDPVR > #endif // USING_V4L > > #ifdef USING_DVB >@@ -1639,6 +1683,10 @@ > setting->addSelection( > QObject::tr("MPEG-2 encoder card (PVR-x50, PVR-500)"), "MPEG"); > # endif // USING_IVTV >+# ifdef USING_HDPVR >+ setting->addSelection( >+ QObject::tr("H.264 encoder card (HD-PVR)"), "HDPVR"); >+# endif // USING_HDPVR > #endif // USING_V4L > > #ifdef USING_DVB >@@ -2850,12 +2898,44 @@ > > last_device = device; > QStringList inputs = >- CardUtil::probeInputs(device, last_cardtype); >+ CardUtil::ProbeVideoInputs(device, last_cardtype); > > for (QStringList::iterator i = inputs.begin(); i != inputs.end(); ++i) > addSelection(*i); > } > >+TunerCardAudioInput::TunerCardAudioInput(const CaptureCard &parent, >+ QString dev, QString type) : >+ ComboBoxSetting(this), CaptureCardDBStorage(this, parent, "audiodevice"), >+ last_device(dev), last_cardtype(type) >+{ >+ setLabel(QObject::tr("Audio input")); >+ int cardid = parent.getCardID(); >+ if (cardid <= 0) >+ return; >+ >+ last_cardtype = CardUtil::GetRawCardType(cardid); >+ last_device = CardUtil::GetAudioDevice(cardid); >+} >+ >+void TunerCardAudioInput::fillSelections(const QString &device) >+{ >+ clearSelections(); >+ >+ if (device.isEmpty()) >+ return; >+ >+ last_device = device; >+ QStringList inputs = >+ CardUtil::ProbeAudioInputs(device, last_cardtype); >+ >+ for (uint i = 0; i < inputs.size(); i++) >+ { >+ addSelection(inputs[i], QString::number(i), >+ last_device == QString::number(i)); >+ } >+} >+ > DVBConfigurationGroup::DVBConfigurationGroup(CaptureCard& a_parent) : > VerticalConfigurationGroup(false, true, false, false), > parent(a_parent), >Index: mythtv/libs/libmythtv/remoteencoder.cpp >=================================================================== >--- mythtv/libs/libmythtv/remoteencoder.cpp (revision 18528) >+++ mythtv/libs/libmythtv/remoteencoder.cpp (working copy) >@@ -89,14 +89,28 @@ > return sock; > } > >-bool RemoteEncoder::IsRecording(void) >+bool RemoteEncoder::IsRecording(bool *ok) > { > QStringList strlist = QString("QUERY_RECORDER %1").arg(recordernum); > strlist << "IS_RECORDING"; > > SendReceiveStringList(strlist); > >+ if (strlist.isEmpty()) >+ { >+ VERBOSE(VB_IMPORTANT, >+ "RemoteEncoder::IsRecording(), Error: No Reply."); >+ if (ok) >+ *ok = false; >+ >+ return false; >+ } >+ > bool retval = strlist[0].toInt(); >+ >+ if (ok) >+ *ok = true; >+ > return retval; > } > >Index: mythtv/libs/libmythtv/profilegroup.cpp >=================================================================== >--- mythtv/libs/libmythtv/profilegroup.cpp (revision 18528) >+++ mythtv/libs/libmythtv/profilegroup.cpp (working copy) >@@ -4,6 +4,7 @@ > #include "libmyth/mythcontext.h" > #include "libmyth/mythdbcon.h" > #include <qsqldatabase.h> >+#include "cardutil.h" > #include <qheader.h> > #include <qcursor.h> > #include <qlayout.h> >@@ -66,52 +67,47 @@ > load(); > } > >-void ProfileGroup::fillSelections(SelectSetting* setting) { >- QStringList cardtypes; >- QString transcodeID; >+void ProfileGroup::fillSelections(SelectSetting* setting) >+{ >+ QStringList cardtypes = CardUtil::GetCardTypes(); >+ QString tid = QString::null; > > MSqlQuery result(MSqlQuery::InitCon()); >+ result.prepare( >+ "SELECT name, id, hostname, is_default, cardtype " >+ "FROM profilegroups"); > >- result.prepare("SELECT DISTINCT cardtype FROM capturecard;"); >- >- if (result.exec() && result.isActive() && result.size() > 0) >+ if (!result.exec()) > { >- while (result.next()) >- { >- cardtypes.append(result.value(0).toString()); >- } >+ MythContext::DBError("ProfileGroup::fillSelections", result); >+ return; > } > >- result.prepare("SELECT name,id,hostname,is_default,cardtype " >- "FROM profilegroups;"); >+ while (result.next()) >+ { >+ QString name = result.value(0).toString(); >+ QString id = result.value(1).toString(); >+ QString hostname = result.value(2).toString(); >+ bool is_default = (bool) result.value(3).toInt(); >+ QString cardtype = result.value(4).toString(); > >- if (result.exec() && result.isActive() && result.size() > 0) >- while (result.next()) >+ // Only show default profiles that match installed cards >+ bool have_cardtype = cardtypes.contains(cardtype); >+ if (is_default && (cardtype == "TRANSCODE") && !have_cardtype) > { >- // Only show default profiles that match installed cards >- if (result.value(3).toInt()) >- { >- bool match = false; >- for(QStringList::Iterator it = cardtypes.begin(); >- it != cardtypes.end(); it++) >- if (result.value(4).toString() == *it) >- match = true; >+ tid = id; >+ } >+ else if (have_cardtype) >+ { >+ if (!hostname.isEmpty()) >+ name += QString(" (%1)").arg(result.value(2).toString()); > >- if (! match) >- { >- if (result.value(4).toString() == "TRANSCODE") >- transcodeID = result.value(1).toString(); >- continue; >- } >- } >- QString value = QString::fromUtf8(result.value(0).toString()); >- if (result.value(2).toString() != NULL && >- result.value(2).toString() != "") >- value += QString(" (%1)").arg(result.value(2).toString()); >- setting->addSelection(value, result.value(1).toString()); >+ setting->addSelection(name, id); > } >- if (! transcodeID.isNull()) >- setting->addSelection(QObject::tr("Transcoders"), transcodeID); >+ } >+ >+ if (!tid.isEmpty()) >+ setting->addSelection(QObject::tr("Transcoders"), tid); > } > > QString ProfileGroup::getName(int group) >Index: mythtv/libs/libmythtv/scanwizardscanner.h >=================================================================== >--- mythtv/libs/libmythtv/scanwizardscanner.h (revision 18528) >+++ mythtv/libs/libmythtv/scanwizardscanner.h (working copy) >@@ -50,7 +50,7 @@ > class ScanProgressPopup; > > class ChannelBase; >-class Channel; >+class V4LChannel; > class DVBChannel; > class SignalMonitorValue; > >Index: mythtv/libs/libmythtv/analogsignalmonitor.h >=================================================================== >--- mythtv/libs/libmythtv/analogsignalmonitor.h (revision 18528) >+++ mythtv/libs/libmythtv/analogsignalmonitor.h (working copy) >@@ -7,13 +7,13 @@ > // MythTV headers > #include "signalmonitor.h" > >-class Channel; >+class V4LChannel; > > class AnalogSignalMonitor : public SignalMonitor > { > public: > AnalogSignalMonitor( >- int db_cardnum, Channel *_channel, >+ int db_cardnum, V4LChannel *_channel, > uint64_t _flags = kDTVSigMon_WaitForSig, > const char *_name = "AnalogSignalMonitor"); > >Index: mythtv/libs/libmythtv/mpegrecorder.cpp >=================================================================== >--- mythtv/libs/libmythtv/mpegrecorder.cpp (revision 18528) >+++ mythtv/libs/libmythtv/mpegrecorder.cpp (working copy) >@@ -3,6 +3,11 @@ > // C headers > #include <ctime> > >+// C++ headers >+#include <algorithm> >+#include <vector> >+using namespace std; >+ > // POSIX headers > #include <pthread.h> > #include <fcntl.h> >@@ -15,9 +20,6 @@ > #include <sys/ioctl.h> > #include <sys/time.h> > >-#include <algorithm> >-using namespace std; >- > // avlib headers > extern "C" { > #include "../libavcodec/avcodec.h" >@@ -73,13 +75,10 @@ > "Square", "4:3", "16:9", "2.21:1", 0 > }; > >-const unsigned int MpegRecorder::kBuildBufferMaxSize = 1024 * 1024; >- > MpegRecorder::MpegRecorder(TVRec *rec) : >- RecorderBase(rec), >+ DTVRecorder(rec), > // Debugging variables > deviceIsMpegFile(false), >- bufferSize(4096), > // Driver info > card(QString::null), driver(QString::null), > version(0), usingv4l2(false), >@@ -87,11 +86,10 @@ > requires_special_pause(false), > // State > recording(false), encoding(false), >- errored(false), >+ needs_resolution(false), start_stop_encoding_lock(true), >+ recording_wait_lock(), recording_wait(), > // Pausing state > cleartimeonpause(false), >- // Number of frames written >- framesWritten(0), > // Encoding info > width(720), height(480), > bitrate(4500), maxbitrate(6000), >@@ -100,15 +98,14 @@ > audbitratel1(14), audbitratel2(14), > audbitratel3(10), > audvolume(80), language(0), >+ low_mpeg4avgbitrate(4500), low_mpeg4peakbitrate(6000), >+ medium_mpeg4avgbitrate(9000), medium_mpeg4peakbitrate(13500), >+ high_mpeg4avgbitrate(13500), high_mpeg4peakbitrate(20200), > // Input file descriptors > chanfd(-1), readfd(-1), >- // Keyframe tracking inforamtion >- keyframedist(15), gopset(false), >- leftovers(0), lastpackheaderpos(0), >- lastseqstart(0), numgops(0), >- // buffer used for ... >- buildbuffer(new unsigned char[kBuildBufferMaxSize + 1]), >- buildbuffersize(0) >+ _device_read_buffer(NULL), >+ // TS packet handling >+ _stream_data(NULL) > { > SetPositionMapType(MARK_GOP_START); > } >@@ -116,11 +113,12 @@ > MpegRecorder::~MpegRecorder() > { > TeardownAll(); >- delete [] buildbuffer; > } > > void MpegRecorder::TeardownAll(void) > { >+ StopRecording(); >+ > if (chanfd >= 0) > { > close(chanfd); >@@ -191,6 +189,28 @@ > } > else if (opt == "mpeg2audvolume") > audvolume = value; >+ else if (opt.right(16) == "_mpeg4avgbitrate") >+ { >+ if (opt.left(3) == "low") >+ low_mpeg4avgbitrate = value; >+ else if (opt.left(6) == "medium") >+ medium_mpeg4avgbitrate = value; >+ else if (opt.left(4) == "high") >+ high_mpeg4avgbitrate = value; >+ else >+ RecorderBase::SetOption(opt, value); >+ } >+ else if (opt.right(17) == "_mpeg4peakbitrate") >+ { >+ if (opt.left(3) == "low") >+ low_mpeg4peakbitrate = value; >+ else if (opt.left(6) == "medium") >+ medium_mpeg4peakbitrate = value; >+ else if (opt.left(4) == "high") >+ high_mpeg4peakbitrate = value; >+ else >+ RecorderBase::SetOption(opt, value); >+ } > else > RecorderBase::SetOption(opt, value); > } >@@ -287,6 +307,8 @@ > SetOption("videodevice", videodev); > } > >+ SetOption("audiodevice", audiodev); >+ > SetOption("tvformat", gContext->GetSetting("TVFormat")); > SetOption("vbiformat", gContext->GetSetting("VbiFormat")); > >@@ -305,8 +327,31 @@ > > SetIntOption(profile, "width"); > SetIntOption(profile, "height"); >+ >+ SetIntOption(profile, "low_mpeg4avgbitrate"); >+ SetIntOption(profile, "low_mpeg4peakbitrate"); >+ SetIntOption(profile, "medium_mpeg4avgbitrate"); >+ SetIntOption(profile, "medium_mpeg4peakbitrate"); >+ SetIntOption(profile, "high_mpeg4avgbitrate"); >+ SetIntOption(profile, "high_mpeg4peakbitrate"); > } > >+// same as the base class, it just doesn't complain if an option is missing >+void MpegRecorder::SetIntOption(RecordingProfile *profile, const QString &name) >+{ >+ const Setting *setting = profile->byName(name); >+ if (setting) >+ SetOption(name, setting->getValue().toInt()); >+} >+ >+// same as the base class, it just doesn't complain if an option is missing >+void MpegRecorder::SetStrOption(RecordingProfile *profile, const QString &name) >+{ >+ const Setting *setting = profile->byName(name); >+ if (setting) >+ SetOption(name, setting->getValue()); >+} >+ > bool MpegRecorder::OpenMpegFileAsInput(void) > { > chanfd = readfd = open(videodevice.ascii(), O_RDONLY); >@@ -324,6 +369,9 @@ > bool MpegRecorder::OpenV4L2DeviceAsInput(void) > { > chanfd = open(videodevice.ascii(), O_RDWR); >+ // open implicitly starts encoding, so we need the lock.. >+ QMutexLocker locker(&start_stop_encoding_lock); >+ > if (chanfd < 0) > { > VERBOSE(VB_IMPORTANT, LOC_ERR + "Can't open video device. " + ENO); >@@ -334,15 +382,26 @@ > { > if (driver == "ivtv") > { >+ bufferSize = 4096; > usingv4l2 = (version >= IVTV_KERNEL_VERSION(0, 8, 0)); > has_v4l2_vbi = (version >= IVTV_KERNEL_VERSION(0, 3, 8)); > has_buggy_vbi = true; > requires_special_pause = > (version >= IVTV_KERNEL_VERSION(0, 10, 0)); > } >+ else if (driver == "hdpvr") >+ { >+ bufferSize = 1500 * TSPacket::SIZE; >+ usingv4l2 = true; >+ requires_special_pause = true; >+ >+ bzero(_stream_id, sizeof(_stream_id)); >+ bzero(_pid_status, sizeof(_pid_status)); >+ memset(_continuity_counter, 0xff, sizeof(_continuity_counter)); >+ } > else > { >- VERBOSE(VB_IMPORTANT, "\n\nNot ivtv driver??\n\n"); >+ VERBOSE(VB_IMPORTANT, "\n\nNot ivtv or hdpvr driver??\n\n"); > usingv4l2 = has_v4l2_vbi = true; > has_buggy_vbi = requires_special_pause = false; > } >@@ -352,6 +411,72 @@ > "has_buggy_vbi(%3)") > .arg(usingv4l2).arg(has_v4l2_vbi).arg(has_buggy_vbi)); > >+ >+ if ((driver != "hdpvr") && !SetFormat(chanfd)) >+ return false; >+ >+ if (driver != "hdpvr") >+ { >+ SetLanguageMode(chanfd); // we don't care if this fails... >+ SetRecordingVolume(chanfd); // we don't care if this fails... >+ } >+ >+ bool ok = true; >+ if (usingv4l2) >+ ok = SetV4L2DeviceOptions(chanfd); >+ else >+ { >+ ok = SetIVTVDeviceOptions(chanfd); >+ if (!ok) >+ usingv4l2 = ok = SetV4L2DeviceOptions(chanfd); >+ } >+ >+ if (!ok) >+ return false; >+ >+ SetVBIOptions(chanfd); >+ >+ readfd = open(videodevice.ascii(), O_RDWR | O_NONBLOCK); >+ >+ if (readfd < 0) >+ { >+ VERBOSE(VB_IMPORTANT, LOC_ERR + "Can't open video device." + ENO); >+ return false; >+ } >+ >+ if (_device_read_buffer) >+ { >+ if (_device_read_buffer->IsRunning()) >+ _device_read_buffer->Stop(); >+ >+ delete _device_read_buffer; >+ _device_read_buffer = NULL; >+ } >+ >+ _device_read_buffer = new DeviceReadBuffer(this); >+ >+ if (!_device_read_buffer) >+ { >+ VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to allocate DRB buffer"); >+ _error = true; >+ return false; >+ } >+ >+ if (!_device_read_buffer->Setup(videodevice.ascii(), readfd)) >+ { >+ VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to allocate DRB buffer"); >+ _error = true; >+ return false; >+ } >+ >+ VERBOSE(VB_RECORD, LOC + "DRB ready"); >+ >+ return true; >+} >+ >+ >+bool MpegRecorder::SetFormat(int chanfd) >+{ > struct v4l2_format vfmt; > bzero(&vfmt, sizeof(vfmt)); > >@@ -372,14 +497,18 @@ > return false; > } > >- // Set audio language mode >- bool do_audmode_set = true; >+ return true; >+} >+ >+/// Set audio language mode >+bool MpegRecorder::SetLanguageMode(int chanfd) >+{ > struct v4l2_tuner vt; > bzero(&vt, sizeof(struct v4l2_tuner)); > if (ioctl(chanfd, VIDIOC_G_TUNER, &vt) < 0) > { > VERBOSE(VB_IMPORTANT, LOC_WARN + "Unable to get audio mode" + ENO); >- do_audmode_set = false; >+ return false; > } > > switch (language) >@@ -401,18 +530,26 @@ > } > > int audio_layer = GetFilteredAudioLayer(); >- if (do_audmode_set && (2 == language) && (1 == audio_layer)) >+ bool success = true; >+ if ((2 == language) && (1 == audio_layer)) > { > VERBOSE(VB_GENERAL, "Dual audio mode incompatible with Layer I audio." > "\n\t\t\tFalling back to Main Language"); > vt.audmode = V4L2_TUNER_MODE_LANG1; >+ success = false; > } > >- if (do_audmode_set && ioctl(chanfd, VIDIOC_S_TUNER, &vt) < 0) >+ if (ioctl(chanfd, VIDIOC_S_TUNER, &vt) < 0) > { > VERBOSE(VB_IMPORTANT, LOC_WARN + "Unable to set audio mode" + ENO); >+ success = false; > } > >+ return success; >+} >+ >+bool MpegRecorder::SetRecordingVolume(int chanfd) >+{ > // Get volume min/max values > struct v4l2_queryctrl qctrl; > qctrl.id = V4L2_CID_AUDIO_VOLUME; >@@ -440,28 +577,7 @@ > VERBOSE(VB_IMPORTANT, LOC_WARN + > "Unable to set recording volume" + ENO + "\n\t\t\t" + > "If you are using an AverMedia M179 card this is normal."); >- } >- >- bool ok = true; >- if (usingv4l2) >- ok = SetV4L2DeviceOptions(chanfd); >- else >- { >- ok = SetIVTVDeviceOptions(chanfd); >- if (!ok) >- usingv4l2 = ok = SetV4L2DeviceOptions(chanfd); >- } >- >- if (!ok) > return false; >- >- SetVBIOptions(chanfd); >- >- readfd = open(videodevice.ascii(), O_RDWR | O_NONBLOCK); >- if (readfd < 0) >- { >- VERBOSE(VB_IMPORTANT, LOC_ERR + "Can't open video device." + ENO); >- return false; > } > > return true; >@@ -578,7 +694,7 @@ > return false; > } > >- keyframedist = (ivtvcodec.framerate) ? 12 : keyframedist; >+ _keyframedist = (ivtvcodec.framerate) ? 12 : _keyframedist; > > return true; > } >@@ -602,83 +718,162 @@ > } > } > >-bool MpegRecorder::SetV4L2DeviceOptions(int chanfd) >+static void add_ext_ctrl(vector<struct v4l2_ext_control> &ctrl_list, >+ uint32_t id, int32_t value) > { >- static const uint kNumControls = 7; >- struct v4l2_ext_controls ctrls; >- struct v4l2_ext_control ext_ctrl[kNumControls]; >- QString control_description[kNumControls] = >+ struct v4l2_ext_control tmp_ctrl; >+ bzero(&tmp_ctrl, sizeof(struct v4l2_ext_control)); >+ tmp_ctrl.id = id; >+ tmp_ctrl.value = value; >+ ctrl_list.push_back(tmp_ctrl); >+} >+ >+static void set_ctrls(int fd, vector<struct v4l2_ext_control> &ext_ctrls) >+{ >+ static QMutex control_description_lock; >+ static QMap<uint32_t,QString> control_description; >+ >+ control_description_lock.lock(); >+ if (control_description.isEmpty()) > { >- "Audio Sampling Frequency", >- "Video Aspect ratio", >- "Audio Encoding", >- "Audio L2 Bitrate", >- "Video Peak Bitrate", >- "Video Average Bitrate", >- "MPEG Stream type", >- }; >+ control_description[V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ] = >+ "Audio Sampling Frequency"; >+ control_description[V4L2_CID_MPEG_VIDEO_ASPECT] = >+ "Video Aspect ratio"; >+ control_description[V4L2_CID_MPEG_AUDIO_ENCODING] = >+ "Audio Encoding"; >+ control_description[V4L2_CID_MPEG_AUDIO_L2_BITRATE] = >+ "Audio L2 Bitrate"; >+ control_description[V4L2_CID_MPEG_VIDEO_BITRATE_PEAK] = >+ "Video Peak Bitrate"; >+ control_description[V4L2_CID_MPEG_VIDEO_BITRATE] = >+ "Video Average Bitrate"; >+ control_description[V4L2_CID_MPEG_STREAM_TYPE] = >+ "MPEG Stream type"; >+ control_description[V4L2_CID_MPEG_VIDEO_BITRATE_MODE] = >+ "MPEG Bitrate mode"; >+ } >+ control_description_lock.unlock(); > >+ for (uint i = 0; i < ext_ctrls.size(); i++) >+ { >+ struct v4l2_ext_controls ctrls; >+ bzero(&ctrls, sizeof(struct v4l2_ext_controls)); >+ >+ int value = ext_ctrls[i].value; >+ >+ ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG; >+ ctrls.count = 1; >+ ctrls.controls = &ext_ctrls[i]; >+ >+ if (ioctl(fd, VIDIOC_S_EXT_CTRLS, &ctrls) < 0) >+ { >+ QMutexLocker locker(&control_description_lock); >+ VERBOSE(VB_IMPORTANT, QString("mpegrecorder.cpp:set_ctrls(): ") + >+ QString("Could not set %1 to %2") >+ .arg(control_description[ext_ctrls[i].id]).arg(value) + >+ ENO); >+ } >+ } >+} >+ >+bool MpegRecorder::SetV4L2DeviceOptions(int chanfd) >+{ >+ vector<struct v4l2_ext_control> ext_ctrls; >+ > // Set controls >- bzero(&ctrls, sizeof(struct v4l2_ext_controls)); >- bzero(&ext_ctrl, sizeof(struct v4l2_ext_control) * kNumControls); >+ if (driver != "hdpvr") >+ { >+ add_ext_ctrl(ext_ctrls, V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, >+ GetFilteredAudioSampleRate()); >+ >+ add_ext_ctrl(ext_ctrls, V4L2_CID_MPEG_VIDEO_ASPECT, >+ aspectratio - 1); > >- uint audio_layer = GetFilteredAudioLayer(); >- uint audbitrate = GetFilteredAudioBitRate(audio_layer); >+ uint audio_layer = GetFilteredAudioLayer(); >+ add_ext_ctrl(ext_ctrls, V4L2_CID_MPEG_AUDIO_ENCODING, >+ audio_layer - 1); >+ >+ uint audbitrate = GetFilteredAudioBitRate(audio_layer); >+ add_ext_ctrl(ext_ctrls, V4L2_CID_MPEG_AUDIO_L2_BITRATE, >+ audbitrate - 1); > >- ext_ctrl[0].id = V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ; >- ext_ctrl[0].value = GetFilteredAudioSampleRate(); >+ add_ext_ctrl(ext_ctrls, V4L2_CID_MPEG_STREAM_TYPE, >+ streamtype_ivtv_to_v4l2(GetFilteredStreamType())); > >- ext_ctrl[1].id = V4L2_CID_MPEG_VIDEO_ASPECT; >- ext_ctrl[1].value = aspectratio - 1; >+ } >+ else >+ { >+ maxbitrate = high_mpeg4peakbitrate; >+ bitrate = high_mpeg4avgbitrate; >+ } >+ maxbitrate = std::max(maxbitrate, bitrate); > >- ext_ctrl[2].id = V4L2_CID_MPEG_AUDIO_ENCODING; >- ext_ctrl[2].value = audio_layer - 1; >+ if (driver == "hdpvr") >+ { >+ add_ext_ctrl(ext_ctrls, V4L2_CID_MPEG_VIDEO_BITRATE_MODE, >+ (maxbitrate == bitrate) ? >+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR : >+ V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); >+ } > >- ext_ctrl[3].id = V4L2_CID_MPEG_AUDIO_L2_BITRATE; >- ext_ctrl[3].value = audbitrate - 1; >+ add_ext_ctrl(ext_ctrls, V4L2_CID_MPEG_VIDEO_BITRATE, >+ bitrate * 1000); > >- ext_ctrl[4].id = V4L2_CID_MPEG_VIDEO_BITRATE_PEAK; >- ext_ctrl[4].value = maxbitrate * 1000; >+ add_ext_ctrl(ext_ctrls, V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, >+ maxbitrate * 1000); > >- ext_ctrl[5].id = V4L2_CID_MPEG_VIDEO_BITRATE; >- ext_ctrl[5].value = (bitrate = min(bitrate, maxbitrate)) * 1000; >+ set_ctrls(chanfd, ext_ctrls); > >- ext_ctrl[6].id = V4L2_CID_MPEG_STREAM_TYPE; >- ext_ctrl[6].value = streamtype_ivtv_to_v4l2(GetFilteredStreamType()); >+ bool ok; >+ int audioinput = audiodevice.toUInt(&ok); >+ if (ok) >+ { >+ struct v4l2_audio ain; >+ bzero(&ain, sizeof(ain)); >+ ain.index = audioinput; >+ if (ioctl(chanfd, VIDIOC_ENUMAUDIO, &ain) < 0) >+ { >+ VERBOSE(VB_IMPORTANT, LOC_WARN + >+ "Unable to get audio input."); >+ } >+ else >+ { >+ ain.index = audioinput; >+ if (ioctl(chanfd, VIDIOC_S_AUDIO, &ain) < 0) >+ { >+ VERBOSE(VB_IMPORTANT, LOC_WARN + >+ "Unable to set audio input."); >+ } >+ } >+ } > >- for (uint i = 0; i < kNumControls; i++) >+ if (driver != "hdpvr") > { >- int value = ext_ctrl[i].value; >+ // Get GOP size in frames >+ struct v4l2_ext_control ext_ctrl; >+ struct v4l2_ext_controls ctrls; > >+ bzero(&ext_ctrl, sizeof(struct v4l2_ext_control)); >+ bzero(&ctrls, sizeof(struct v4l2_ext_controls)); >+ >+ ext_ctrl.id = V4L2_CID_MPEG_VIDEO_GOP_SIZE; >+ ext_ctrl.value = 0; >+ > ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG; > ctrls.count = 1; >- ctrls.controls = ext_ctrl + i; >+ ctrls.controls = &ext_ctrl; > >- if (ioctl(chanfd, VIDIOC_S_EXT_CTRLS, &ctrls) < 0) >+ if (ioctl(chanfd, VIDIOC_G_EXT_CTRLS, &ctrls) < 0) > { >- VERBOSE(VB_IMPORTANT, LOC_ERR + >- QString("Could not set %1 to %2") >- .arg(control_description[i]).arg(value) + ENO); >+ VERBOSE(VB_IMPORTANT, LOC_WARN + "Unable to get " >+ "V4L2_CID_MPEG_VIDEO_GOP_SIZE, defaulting to 12" + ENO); >+ ext_ctrl.value = 12; > } >- } > >- // Get controls >- ext_ctrl[0].id = V4L2_CID_MPEG_VIDEO_GOP_SIZE; >- ext_ctrl[0].value = 0; >- >- ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG; >- ctrls.count = 1; >- ctrls.controls = ext_ctrl; >- >- if (ioctl(chanfd, VIDIOC_G_EXT_CTRLS, &ctrls) < 0) >- { >- VERBOSE(VB_IMPORTANT, LOC_WARN + "Unable to get " >- "V4L2_CID_MPEG_VIDEO_GOP_SIZE, defaulting to 12" + ENO); >- ext_ctrl[0].value = 12; >+ _keyframedist = ext_ctrl.value; > } > >- keyframedist = ext_ctrl[0].value; >- > return true; > } > >@@ -687,6 +882,9 @@ > if (!vbimode) > return true; > >+ if (driver == "hdpvr") >+ return true; >+ > if (has_buggy_vbi) > { > cout<<" *********************** WARNING ***********************"<<endl; >@@ -809,21 +1007,49 @@ > { > if (!Open()) > { >- errored = true; >- return; >+ _error = true; >+ return; > } > >- if (!SetupRecording()) >+ bool has_select = true; >+ >+#if defined(__FreeBSD__) >+ // HACK. FreeBSD PVR150/500 driver doesn't currently support select() >+ has_select = false; >+#endif >+ >+ _start_code = 0xffffffff; >+ _last_gop_seen = 0; >+ _frames_written_count = 0; >+ >+ if (driver == "hdpvr") > { >- VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to setup recording."); >- errored = true; >- return; >+ SetPositionMapType(MARK_GOP_BYFRAME); >+ >+ int progNum = 1; >+ MPEGStreamData *sd = new MPEGStreamData(progNum, true); >+ sd->SetRecordingType(_recording_type); >+ SetStreamData(sd); >+ >+ _stream_data->AddAVListener(this); >+ _stream_data->AddWritingListener(this); >+ >+ // Make sure the first things in the file are a PAT & PMT >+ _wait_for_keyframe_option = false; >+ HandleSingleProgramPAT(_stream_data->PATSingleProgram()); >+ HandleSingleProgramPMT(_stream_data->PMTSingleProgram()); >+ _wait_for_keyframe_option = true; > } >+ else >+ { >+ SetPositionMapType(MARK_GOP_START); >+ } > > encoding = true; > recording = true; > unsigned char *buffer = new unsigned char[bufferSize + 1]; >- int ret; >+ int len; >+ uint remainder = 0; > > MythTimer elapsedTimer; > float elapsed; >@@ -833,224 +1059,325 @@ > > if (deviceIsMpegFile) > elapsedTimer.start(); >+ else if (_device_read_buffer) >+ _device_read_buffer->Start(); > >- while (encoding) >+ needs_resolution = (driver == "hdpvr"); >+ >+ while (encoding && !_error) > { > if (PauseAndWait(100)) > continue; >+ HandleResolutionChanges(); > >- if ((deviceIsMpegFile) && (framesWritten)) >+ if (deviceIsMpegFile) > { >- elapsed = (elapsedTimer.elapsed() / 1000.0) + 1; >- while ((framesWritten / elapsed) > 30) >+ if (GetFramesWritten()) > { >- usleep(50000); > elapsed = (elapsedTimer.elapsed() / 1000.0) + 1; >+ while ((GetFramesWritten() / elapsed) > 30) >+ { >+ usleep(50000); >+ elapsed = (elapsedTimer.elapsed() / 1000.0) + 1; >+ } > } > } >+ else >+ { >+ if (readfd < 0) >+ { >+ if (!Open()) >+ { >+ _error = true; >+ return; >+ } > >- if (readfd < 0) >- readfd = open(videodevice.ascii(), O_RDWR); >+ if (readfd < 0) >+ { >+ VERBOSE(VB_IMPORTANT, LOC_ERR + >+ QString("Failed to open device '%1'") >+ .arg(videodevice)); >+ continue; >+ } >+ } >+ } > >- tv.tv_sec = 5; >- tv.tv_usec = 0; >- FD_ZERO(&rdset); >- FD_SET(readfd, &rdset); >- >-#if defined(__FreeBSD__) >- // HACK. FreeBSD PVR150/500 driver doesn't currently support select() >-#else >- switch (select(readfd + 1, &rdset, NULL, NULL, &tv)) >+ if (_device_read_buffer) > { >- case -1: >- if (errno == EINTR) >- continue; >+ len = _device_read_buffer->Read( >+ &(buffer[remainder]), bufferSize - remainder); > >- VERBOSE(VB_IMPORTANT, LOC_ERR + "Select error" + ENO); >- continue; >+ // Check for DRB errors >+ if (_device_read_buffer->IsErrored()) >+ { >+ VERBOSE(VB_IMPORTANT, LOC_ERR + "Device error detected"); > >- case 0: >- VERBOSE(VB_IMPORTANT, LOC_ERR + "select timeout - " >- "ivtv driver has stopped responding"); >+ _device_read_buffer->Stop(); > >- if (close(readfd) != 0) >+ QMutexLocker locker(&start_stop_encoding_lock); >+ >+ StopEncoding(readfd); >+ >+ // Make sure the next things in the file are a PAT & PMT >+ if (_stream_data->PATSingleProgram() && >+ _stream_data->PMTSingleProgram()) > { >- VERBOSE(VB_IMPORTANT, LOC_ERR + "Close error" + ENO); >+ bool tmp = _wait_for_keyframe_option; >+ _wait_for_keyframe_option = false; >+ HandleSingleProgramPAT(_stream_data->PATSingleProgram()); >+ HandleSingleProgramPMT(_stream_data->PMTSingleProgram()); >+ _wait_for_keyframe_option = tmp; > } > >- readfd = -1; // Force PVR card to be reopened on next iteration >- continue; >+ if (StartEncoding(readfd)) >+ { >+ _device_read_buffer->Start(); >+ } >+ else >+ { >+ if (0 != close(readfd)) >+ VERBOSE(VB_IMPORTANT, LOC_ERR + "Close error" + ENO); > >- default: break; >+ // Force card to be reopened on next iteration.. >+ readfd = -1; >+ } >+ } >+ else if (_device_read_buffer->IsEOF()) >+ { >+ VERBOSE(VB_IMPORTANT, LOC_ERR + "Device EOF detected"); >+ _error = true; >+ } > } >-#endif >+ else >+ { >+ if (has_select) >+ { >+ tv.tv_sec = 5; >+ tv.tv_usec = 0; >+ FD_ZERO(&rdset); >+ FD_SET(readfd, &rdset); > >- ret = read(readfd, buffer, bufferSize); >+ switch (select(readfd + 1, &rdset, NULL, NULL, &tv)) >+ { >+ case -1: >+ if (errno == EINTR) >+ continue; > >- if ((ret == 0) && >- (deviceIsMpegFile)) >- { >- close(readfd); >- readfd = open(videodevice.ascii(), O_RDONLY); >+ VERBOSE(VB_IMPORTANT, LOC_ERR + "Select error" + ENO); >+ continue; > >- if (readfd >= 0) >- ret = read(readfd, buffer, bufferSize); >- if (ret <= 0) >+ case 0: >+ VERBOSE(VB_IMPORTANT, LOC_ERR + "select timeout - " >+ "driver has stopped responding"); >+ >+ if (close(readfd) != 0) >+ { >+ VERBOSE(VB_IMPORTANT, LOC_ERR + >+ "Close error" + ENO); >+ } >+ >+ // Force card to be reopened on next iteration.. >+ readfd = -1; >+ continue; >+ >+ default: >+ break; >+ } >+ } >+ >+ len = read(readfd, &(buffer[remainder]), bufferSize - remainder); >+ >+ if (len < 0 && !has_select) > { >- encoding = false; >+ usleep(25 * 1000); > continue; > } >+ >+ if ((len == 0) && (deviceIsMpegFile)) >+ { >+ close(readfd); >+ readfd = open(videodevice.ascii(), O_RDONLY); >+ >+ if (readfd >= 0) >+ { >+ len = read(readfd, >+ &(buffer[remainder]), bufferSize - remainder); >+ } >+ >+ if (len <= 0) >+ { >+ encoding = false; >+ continue; >+ } >+ } >+ else if (len < 0 && errno != EAGAIN) >+ { >+ VERBOSE(VB_IMPORTANT, >+ LOC_ERR + QString("error reading from: %1") >+ .arg(videodevice) + ENO); >+ continue; >+ } > } >- else if (ret < 0 && errno != EAGAIN) >+ >+ if (len > 0) > { >- VERBOSE(VB_IMPORTANT, LOC_ERR + QString("error reading from: %1") >- .arg(videodevice) + ENO); >+ len += remainder; > >- continue; >+ if (driver == "hdpvr") { >+ remainder = _stream_data->ProcessData(buffer, len); >+ int start_remain = len - remainder; >+ if (remainder && (start_remain >= remainder)) >+ memcpy(buffer, buffer+start_remain, remainder); >+ else if (remainder) >+ memmove(buffer, buffer+start_remain, remainder); >+ } >+ else >+ { >+ FindPSKeyFrames(buffer, len); >+ } > } >- else if (ret > 0) >- { >- ProcessData(buffer, ret); >- } > } > >+ if (_device_read_buffer) >+ { >+ if (_device_read_buffer->IsRunning()) >+ _device_read_buffer->Stop(); >+ >+ delete _device_read_buffer; >+ _device_read_buffer = NULL; >+ } >+ StopEncoding(readfd); >+ > FinishRecording(); > > delete[] buffer; >+ SetStreamData(NULL); > recording = false; >+ QMutexLocker locker(&recording_wait_lock); >+ recording_wait.wakeAll(); > } > >-bool MpegRecorder::SetupRecording(void) >+bool MpegRecorder::ProcessTSPacket(const TSPacket &tspacket_real) > { >- leftovers = 0xFFFFFFFF; >- numgops = 0; >- lastseqstart = 0; >- return true; >-} >+ const uint pid = tspacket_real.PID(); > >-void MpegRecorder::FinishRecording(void) >-{ >- ringBuffer->WriterFlush(); >- >- if (curRecording) >+ TSPacket *tspacket_fake = NULL; >+ if ((driver == "hdpvr") && (pid == 0x1001)) // PCRPID for HD-PVR > { >- curRecording->SetFilesize(ringBuffer->GetRealFileSize()); >- SavePositionMap(true); >+ tspacket_fake = tspacket_real.CreateClone(); >+ uint cc = (_continuity_counter[pid] == 0xFF) ? >+ 0 : (_continuity_counter[pid] + 1) & 0xf; >+ tspacket_fake->SetContinuityCounter(cc); > } >- positionMapLock.lock(); >- positionMap.clear(); >- positionMapDelta.clear(); >- positionMapLock.unlock(); >-} > >-#define PACK_HEADER 0x000001BA >-#define GOP_START 0x000001B8 >-#define SEQ_START 0x000001B3 >-#define SLICE_MIN 0x00000101 >-#define SLICE_MAX 0x000001af >+ const TSPacket *tspacket = (tspacket_fake) ? >+ tspacket_fake : &tspacket_real; > >-void MpegRecorder::ProcessData(unsigned char *buffer, int len) >-{ >- unsigned char *bufptr = buffer, *bufstart = buffer; >- unsigned int state = leftovers, v = 0; >- int leftlen = len; >+ // Check continuity counter >+ if ((pid != 0x1fff) && !CheckCC(pid, tspacket->ContinuityCounter())) >+ { >+ VERBOSE(VB_RECORD, LOC + >+ QString("PID 0x%1 discontinuity detected").arg(pid,0,16)); >+ _continuity_error_count++; >+ } > >- while (bufptr < buffer + len) >+ // Only write the packet >+ // if audio/video key-frames have been found >+ if (!(_wait_for_keyframe_option && _first_keyframe < 0)) > { >- v = *bufptr++; >- if (state == 0x000001) >- { >- state = ((state << 8) | v) & 0xFFFFFF; >- >- if (state == PACK_HEADER) >- { >- long long startpos = ringBuffer->GetWritePosition(); >- startpos += buildbuffersize + bufptr - bufstart - 4; >- lastpackheaderpos = startpos; >+ _buffer_packets = true; > >- int curpos = bufptr - bufstart - 4; >- if (curpos < 0) >- { >- // header was split >- buildbuffersize += curpos; >- if (buildbuffersize > 0) >- ringBuffer->Write(buildbuffer, buildbuffersize); >+ BufferedWrite(*tspacket); >+ } > >- buildbuffersize = 4; >- memcpy(buildbuffer, &state, 4); >+ if (tspacket_fake) >+ delete tspacket_fake; > >- leftlen = leftlen - curpos + 4; >- bufstart = bufptr; >- } >- else >- { >- // header was entirely in this packet >- memcpy(buildbuffer + buildbuffersize, bufstart, curpos); >- buildbuffersize += curpos; >- bufstart += curpos; >- leftlen -= curpos; >+ return true; >+} > >- if (buildbuffersize > 0) >- ringBuffer->Write(buildbuffer, buildbuffersize); >+bool MpegRecorder::ProcessVideoTSPacket(const TSPacket &tspacket) >+{ >+ _buffer_packets = !FindH264Keyframes(&tspacket); >+ if (!_seen_sps) >+ return true; > >- buildbuffersize = 0; >- } >- } >+ return ProcessAVTSPacket(tspacket); >+} > >- if (state == SEQ_START) >- { >- lastseqstart = lastpackheaderpos; >- } >+bool MpegRecorder::ProcessAudioTSPacket(const TSPacket &tspacket) >+{ >+ _buffer_packets = !FindAudioKeyframes(&tspacket); >+ return ProcessAVTSPacket(tspacket); >+} > >- if (state == GOP_START && lastseqstart == lastpackheaderpos) >- { >- framesWritten = numgops * keyframedist; >- numgops++; >- HandleKeyframe(); >- } >- } >- else >- state = ((state << 8) | v) & 0xFFFFFF; >+/// Common code for processing either audio or video packets >+bool MpegRecorder::ProcessAVTSPacket(const TSPacket &tspacket) >+{ >+ const uint pid = tspacket.PID(); >+ >+ // Check continuity counter >+ if ((pid != 0x1fff) && !CheckCC(pid, tspacket.ContinuityCounter())) >+ { >+ VERBOSE(VB_RECORD, LOC + >+ QString("PID 0x%1 discontinuity detected").arg(pid,0,16)); >+ _continuity_error_count++; > } > >- leftovers = state; >+ // Sync recording start to first keyframe >+ if (_wait_for_keyframe_option && _first_keyframe < 0) >+ return true; > >- if (buildbuffersize + leftlen > kBuildBufferMaxSize) >+ // Sync streams to the first Payload Unit Start Indicator >+ // _after_ first keyframe iff _wait_for_keyframe_option is true >+ if (!(_pid_status[pid] & kPayloadStartSeen) && tspacket.HasPayload()) > { >- ringBuffer->Write(buildbuffer, buildbuffersize); >- buildbuffersize = 0; >+ if (!tspacket.PayloadStart()) >+ return true; // not payload start - drop packet >+ >+ VERBOSE(VB_RECORD, >+ QString("PID 0x%1 Found Payload Start").arg(pid,0,16)); >+ >+ _pid_status[pid] |= kPayloadStartSeen; > } > >- // copy remaining.. >- memcpy(buildbuffer + buildbuffersize, bufstart, leftlen); >- buildbuffersize += leftlen; >+ BufferedWrite(tspacket); >+ >+ return true; > } > > void MpegRecorder::StopRecording(void) > { >- encoding = false; >+ QMutexLocker locker(&recording_wait_lock); >+ if (encoding) { >+ encoding = false; >+ recording_wait.wait(&recording_wait_lock); >+ } > } > > void MpegRecorder::ResetForNewFile(void) > { >- errored = false; >- framesWritten = 0; >- numgops = 0; >- lastseqstart = lastpackheaderpos = 0; >+ DTVRecorder::ResetForNewFile(); > >- positionMap.clear(); >- positionMapDelta.clear(); >+ bzero(_stream_id, sizeof(_stream_id)); >+ bzero(_pid_status, sizeof(_pid_status)); >+ memset(_continuity_counter, 0xff, sizeof(_continuity_counter)); > } > > void MpegRecorder::Reset(void) > { >+ VERBOSE(VB_RECORD, LOC + "Reset(void)"); > ResetForNewFile(); > >- leftovers = 0xFFFFFFFF; >- buildbuffersize = 0; >+ _start_code = 0xffffffff; > > if (curRecording) >- curRecording->ClearPositionMap(MARK_GOP_START); >+ { >+ curRecording->ClearPositionMap( >+ (driver == "hdpvr") ? MARK_GOP_BYFRAME : MARK_GOP_START); >+ } >+ if (_stream_data) >+ _stream_data->Reset(_stream_data->DesiredProgram()); > } > > void MpegRecorder::Pause(bool clear) >@@ -1064,89 +1391,303 @@ > { > if (request_pause) > { >+ QMutex waitlock; >+ waitlock.lock(); >+ > if (!paused) > { >- if (requires_special_pause) >+ if (_device_read_buffer) > { >- // Some ivtv drivers require streaming to be disabled before >- // an input switch and other channel format setting. >- struct v4l2_encoder_cmd command; >- memset(&command, 0, sizeof(struct v4l2_encoder_cmd)); >- command.cmd = V4L2_ENC_CMD_STOP; >- ioctl(readfd, VIDIOC_ENCODER_CMD, &command); >+ QMutex drb_lock; >+ drb_lock.lock(); >+ >+ _device_read_buffer->SetRequestPause(true); >+ >+ pauseWait.wait(&drb_lock, timeout); > } >+ else >+ { >+ paused = true; >+ pauseWait.wakeAll(); >+ } > >- paused = true; >- pauseWait.wakeAll(); >+ // Some drivers require streaming to be disabled before >+ // an input switch and other channel format setting. >+ if (requires_special_pause) >+ StopEncoding(readfd); >+ > if (tvrec) > tvrec->RecorderPaused(); > } >+ > unpauseWait.wait(timeout); > } > if (!request_pause) > { > if (paused) > { >+ // Some drivers require streaming to be disabled before >+ // an input switch and other channel format setting. > if (requires_special_pause) >- { >- // Some ivtv drivers require streaming to be disabled before >- // an input switch and other channel format setting. >- struct v4l2_encoder_cmd command; >- command.cmd = V4L2_ENC_CMD_START; >- ioctl(readfd, VIDIOC_ENCODER_CMD, &command); >- } >+ StartEncoding(readfd); >+ >+ if (_device_read_buffer) >+ _device_read_buffer->SetRequestPause(false); >+ >+ if (_stream_data) >+ _stream_data->Reset(_stream_data->DesiredProgram()); > } > paused = false; > } > return paused; > } > >-long long MpegRecorder::GetKeyframePosition(long long desired) >+bool MpegRecorder::StartEncoding(int fd) > { >- QMutexLocker locker(&positionMapLock); >- long long ret = -1; >+ QMutexLocker locker(&start_stop_encoding_lock); > >- if (positionMap.find(desired) != positionMap.end()) >- ret = positionMap[desired]; >+ struct v4l2_encoder_cmd command; >+ memset(&command, 0, sizeof(struct v4l2_encoder_cmd)); >+ command.cmd = V4L2_ENC_CMD_START; > >- return ret; >+ VERBOSE(VB_RECORD, LOC + "StartEncoding"); >+ needs_resolution = (driver == "hdpvr"); >+ >+ for (int idx = 0; idx < 10; ++idx) >+ { >+ if (ioctl(fd, VIDIOC_ENCODER_CMD, &command) == 0) >+ { >+ VERBOSE(VB_RECORD, LOC + "Encoding started"); >+ return true; >+ } >+ >+ if (errno != EAGAIN) >+ { >+ VERBOSE(VB_IMPORTANT, LOC_ERR + "StartEncoding" + ENO); >+ return false; >+ } >+ >+ usleep(250 * 1000); >+ } >+ >+ VERBOSE(VB_IMPORTANT, LOC_ERR + "StartEncoding - giving up" + ENO); >+ return false; > } > >-// documented in recorderbase.h >-void MpegRecorder::SetNextRecording(const ProgramInfo *progInf, RingBuffer *rb) >+bool MpegRecorder::StopEncoding(int fd) > { >- // First we do some of the time consuming stuff we can do now >- SavePositionMap(true); >- ringBuffer->WriterFlush(); >- if (curRecording) >- curRecording->SetFilesize(ringBuffer->GetRealFileSize()); >+ QMutexLocker locker(&start_stop_encoding_lock); > >- // Then we set the next info >+ struct v4l2_encoder_cmd command; >+ memset(&command, 0, sizeof(struct v4l2_encoder_cmd)); >+ command.cmd = V4L2_ENC_CMD_STOP; >+ >+ VERBOSE(VB_RECORD, LOC + "StopEncoding"); >+ >+ for (int idx = 0; idx < 10; ++idx) > { >- QMutexLocker locker(&nextRingBufferLock); >- nextRecording = NULL; >- if (progInf) >- nextRecording = new ProgramInfo(*progInf); >- nextRingBuffer = rb; >+ >+ if (ioctl(fd, VIDIOC_ENCODER_CMD, &command) == 0) >+ { >+ VERBOSE(VB_RECORD, LOC + "Encoding stopped"); >+ return true; >+ } >+ >+ if (errno != EAGAIN) >+ { >+ VERBOSE(VB_IMPORTANT, LOC_ERR + "StopEncoding" + ENO); >+ return false; >+ } >+ >+ usleep(250 * 1000); > } >+ >+ VERBOSE(VB_IMPORTANT, LOC_ERR + "StopEncoding - giving up" + ENO); >+ return false; > } > >-/** \fn MpegRecorder::HandleKeyframe(void) >- * \brief This save the current frame to the position maps >- * and handles ringbuffer switching. >- */ >-void MpegRecorder::HandleKeyframe(void) >+void MpegRecorder::SetStreamData(MPEGStreamData *data) > { >- // Add key frame to position map >- positionMapLock.lock(); >- if (!positionMap.contains(numgops)) >+ VERBOSE(VB_RECORD, LOC + "SetStreamData("<<data<<") -- begin"); >+ >+ if (data == _stream_data) > { >- positionMapDelta[numgops] = lastpackheaderpos; >- positionMap[numgops] = lastpackheaderpos; >+ VERBOSE(VB_RECORD, LOC + "SetStreamData("<<data<<") -- end 0"); >+ >+ return; > } >- positionMapLock.unlock(); > >- // Perform ringbuffer switch if needed. >- CheckForRingBufferSwitch(); >+ MPEGStreamData *old_data = _stream_data; >+ _stream_data = data; >+ if (old_data) >+ delete old_data; >+ >+ if (data) >+ { >+ data->AddMPEGSPListener(this); >+ data->SetDesiredProgram(1); >+ } >+ >+ VERBOSE(VB_RECORD, LOC + "SetStreamData("<<data<<") -- end 1"); > } > >+void MpegRecorder::HandleSingleProgramPAT(ProgramAssociationTable *pat) >+{ >+ if (!pat) >+ { >+ VERBOSE(VB_RECORD, LOC + "HandleSingleProgramPAT(NULL)"); >+ return; >+ } >+ >+ if (!ringBuffer) >+ return; >+ >+// uint posA[2] = { ringBuffer->GetWritePosition(), _payload_buffer.size() }; >+ >+ uint next_cc = (pat->tsheader()->ContinuityCounter()+1)&0xf; >+ pat->tsheader()->SetContinuityCounter(next_cc); >+ DTVRecorder::BufferedWrite(*(reinterpret_cast<TSPacket*>(pat->tsheader()))); >+ >+// uint posB[2] = { ringBuffer->GetWritePosition(), _payload_buffer.size() }; >+ >+#if 0 >+ if (posB[0] + posB[1] * TSPacket::SIZE > >+ posA[0] + posA[1] * TSPacket::SIZE) >+ { >+ VERBOSE(VB_RECORD, LOC + "Wrote PAT @" >+ << posA[0] << " + " << (posA[1] * TSPacket::SIZE)); >+ } >+ else >+ { >+ VERBOSE(VB_RECORD, LOC + "Saw PAT but did not write to disk yet"); >+ } >+#endif >+} >+ >+void MpegRecorder::HandleSingleProgramPMT(ProgramMapTable *pmt) >+{ >+ if (!pmt) >+{ >+ return; >+ } >+ >+ // collect stream types for H.264 (MPEG-4 AVC) keyframe detection >+ for (uint i = 0; i < pmt->StreamCount(); i++) >+ _stream_id[pmt->StreamPID(i)] = pmt->StreamType(i); >+ >+ if (!ringBuffer) >+ return; >+ >+ unsigned char buf[8 * 1024]; >+ uint next_cc = (pmt->tsheader()->ContinuityCounter()+1)&0xf; >+ pmt->tsheader()->SetContinuityCounter(next_cc); >+ uint size = pmt->WriteAsTSPackets(buf, next_cc); >+ >+ uint posA[2] = { ringBuffer->GetWritePosition(), _payload_buffer.size() }; >+ >+ for (uint i = 0; i < size ; i += TSPacket::SIZE) >+ DTVRecorder::BufferedWrite(*(reinterpret_cast<TSPacket*>(&buf[i]))); >+ >+ uint posB[2] = { ringBuffer->GetWritePosition(), _payload_buffer.size() }; >+ >+#if 0 >+ if (posB[0] + posB[1] * TSPacket::SIZE > >+ posA[0] + posA[1] * TSPacket::SIZE) >+ { >+ VERBOSE(VB_RECORD, LOC + "Wrote PMT @" >+ << posA[0] << " + " << (posA[1] * TSPacket::SIZE)); >+ } >+ else >+ { >+ VERBOSE(VB_RECORD, LOC + "Saw PMT but did not write to disk yet"); >+ } >+#endif >+} >+ >+void MpegRecorder::HandleResolutionChanges(void) >+{ >+ if (!needs_resolution) >+ return; >+ >+ VERBOSE(VB_RECORD, LOC + "Checking Resolution"); >+ struct v4l2_format vfmt; >+ memset(&vfmt, 0, sizeof(vfmt)); >+ vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; >+ >+ uint pix = 0; >+ if (0 == ioctl(chanfd, VIDIOC_G_FMT, &vfmt)) >+ { >+ VERBOSE(VB_RECORD, LOC + QString("Got Resolution %1x%2") >+ .arg(vfmt.fmt.pix.width).arg(vfmt.fmt.pix.height)); >+ pix = vfmt.fmt.pix.width * vfmt.fmt.pix.height; >+ needs_resolution = false; >+ } >+ >+ if (!pix) >+ return; // nothing to do, we don't have a resolution yet >+ >+ int old_max = maxbitrate, old_avg = bitrate; >+ if (pix <= 768*568) >+ { >+ maxbitrate = low_mpeg4peakbitrate; >+ bitrate = low_mpeg4avgbitrate; >+ } >+ else if (pix >= 1920*1080) >+ { >+ maxbitrate = high_mpeg4peakbitrate; >+ bitrate = high_mpeg4avgbitrate; >+ } >+ else >+ { >+ maxbitrate = medium_mpeg4peakbitrate; >+ bitrate = medium_mpeg4avgbitrate; >+ } >+ maxbitrate = std::max(maxbitrate, bitrate); >+ >+ if ((old_max != maxbitrate) || (old_avg != bitrate)) >+ { >+ if (old_max == old_avg) >+ { >+ VERBOSE(VB_RECORD, LOC + >+ QString("Old bitrate %1 CBR").arg(old_avg)); >+ } >+ else >+ { >+ VERBOSE(VB_RECORD, LOC + >+ QString("Old bitrate %1/%2 VBR") >+ .arg(old_avg).arg(old_max)); >+ } >+ >+ if (maxbitrate == bitrate) >+ { >+ VERBOSE(VB_RECORD, LOC + QString("New bitrate %1 kbps CBR") >+ .arg(bitrate)); >+ } >+ else >+ { >+ VERBOSE(VB_RECORD, LOC + QString("New bitrate %1/%2 kbps VBR") >+ .arg(bitrate).arg(maxbitrate)); >+ } >+ >+ vector<struct v4l2_ext_control> ext_ctrls; >+ add_ext_ctrl(ext_ctrls, V4L2_CID_MPEG_VIDEO_BITRATE_MODE, >+ (maxbitrate == bitrate) ? >+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR : >+ V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); >+ >+ add_ext_ctrl(ext_ctrls, V4L2_CID_MPEG_VIDEO_BITRATE, >+ bitrate * 1000); >+ >+ add_ext_ctrl(ext_ctrls, V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, >+ maxbitrate * 1000); >+ >+ set_ctrls(readfd, ext_ctrls); >+ } >+ >+ // Restart streaming. Shouldn't be needed? seems to be with current driver. >+ QMutexLocker locker(&start_stop_encoding_lock); >+ StopEncoding(readfd); >+ StartEncoding(readfd); >+ >+ needs_resolution = false; >+} >Index: mythtv/libs/libmythtv/tv_rec.cpp >=================================================================== >--- mythtv/libs/libmythtv/tv_rec.cpp (revision 18528) >+++ mythtv/libs/libmythtv/tv_rec.cpp (working copy) >@@ -61,7 +61,7 @@ > #include "firewirerecorder.h" > > #ifdef USING_V4L >-#include "channel.h" >+#include "v4lchannel.h" > #endif > > #define DEBUG_CHANNEL_PREFIX 0 /**< set to 1 to channel prefixing */ >@@ -202,14 +202,14 @@ > else // "V4L" or "MPEG", ie, analog TV > { > #ifdef USING_V4L >- channel = new Channel(this, genOpt.videodev); >+ channel = new V4LChannel(this, genOpt.videodev); > if (!channel->Open()) > return false; > InitChannel(genOpt.defaultinput, startchannel); > CloseChannel(); > init_run = true; > #endif >- if (genOpt.cardtype != "MPEG") >+ if ((genOpt.cardtype != "MPEG") && (genOpt.cardtype != "HDPVR")) > rbFileExt = "nuv"; > } > >@@ -958,7 +958,7 @@ > * \brief Allocates and initializes the RecorderBase instance. > * > * Based on the card type, one of the possible recorders are started. >- * If the card type is "MPEG" a MpegRecorder is started, >+ * If the card type is "MPEG" or "HDPVR" a MpegRecorder is started, > * if the card type is "HDHOMERUN" a HDHRRecorder is started, > * if the card type is "FIREWIRE" a FirewireRecorder is started, > * if the card type is "DVB" a DVBRecorder is started, >@@ -976,6 +976,12 @@ > recorder = new MpegRecorder(this); > #endif // USING_IVTV > } >+ else if (genOpt.cardtype == "HDPVR") >+ { >+#ifdef USING_HDPVR >+ recorder = new MpegRecorder(this); >+#endif // USING_HDPVR >+ } > else if (genOpt.cardtype == "FIREWIRE") > { > #ifdef USING_FIREWIRE >@@ -1230,10 +1236,10 @@ > #endif // USING_FIREWIRE > } > >-Channel *TVRec::GetV4LChannel(void) >+V4LChannel *TVRec::GetV4LChannel(void) > { > #ifdef USING_V4L >- return dynamic_cast<Channel*>(channel); >+ return dynamic_cast<V4LChannel*>(channel); > #else > return NULL; > #endif // USING_V4L >@@ -2536,6 +2542,8 @@ > long long bitrate; > if (genOpt.cardtype == "MPEG") > bitrate = 10080000LL; // use DVD max bit rate >+ if (genOpt.cardtype == "HDPVR") >+ bitrate = 20200000LL; // Peek bit rate for HD-PVR > else if (genOpt.cardtype == "DBOX2") > bitrate = 10080000LL; // use DVD max bit rate > else if (!CardUtil::IsEncoder(genOpt.cardtype)) >Index: mythtv/libs/libmythtv/DeviceReadBuffer.cpp >=================================================================== >--- mythtv/libs/libmythtv/DeviceReadBuffer.cpp (revision 18528) >+++ mythtv/libs/libmythtv/DeviceReadBuffer.cpp (working copy) >@@ -138,9 +138,8 @@ > > void DeviceReadBuffer::SetPaused(bool val) > { >- lock.lock(); >+ QMutexLocker locker(&lock); > paused = val; >- lock.unlock(); > if (val) > pauseWait.wakeAll(); > else >@@ -155,6 +154,8 @@ > > bool DeviceReadBuffer::WaitForUnpause(int timeout) > { >+ QMutexLocker locker(&lock); >+ > if (IsPaused()) > unpauseWait.wait(timeout); > return IsPaused(); >Index: mythtv/libs/libmythtv/recordingprofile.cpp >=================================================================== >--- mythtv/libs/libmythtv/recordingprofile.cpp (revision 18528) >+++ mythtv/libs/libmythtv/recordingprofile.cpp (working copy) >@@ -652,32 +652,43 @@ > }; > }; > >-class MPEG2bitrate : public SliderSetting, public CodecParamStorage >+class AverageBitrate : public SliderSetting, public CodecParamStorage > { > public: >- MPEG2bitrate(const RecordingProfile &parent) : >- SliderSetting(this, 1000, 16000, 100), >- CodecParamStorage(this, parent, "mpeg2bitrate") >+ AverageBitrate(const RecordingProfile &parent, >+ QString setting = "mpeg2bitrate", >+ uint min_br = 1000, uint max_br = 16000, >+ uint default_br = 4500, uint increment = 100, >+ QString label = QString::null) : >+ SliderSetting(this, min_br, max_br, increment), >+ CodecParamStorage(this, parent, setting) > { >- >- setLabel(QObject::tr("Bitrate")); >- setValue(4500); >- setHelpText(QObject::tr("Bitrate in kilobits/second. 2200Kbps is " >- "approximately 1 Gigabyte per hour.")); >+ if (label.isEmpty()) >+ label = QObject::tr("Avg. Bitrate"); >+ setLabel(label); >+ setValue(default_br); >+ setHelpText(QObject::tr( >+ "Average bit rate in kilobits/second. " >+ "2200Kbps is approximately 1 Gigabyte per hour.")); > }; > }; > >-class MPEG2maxBitrate : public SliderSetting, public CodecParamStorage >+class PeakBitrate : public SliderSetting, public CodecParamStorage > { > public: >- MPEG2maxBitrate(const RecordingProfile &parent) : >- SliderSetting(this, 1000, 16000, 100), >- CodecParamStorage(this, parent, "mpeg2maxbitrate") >+ PeakBitrate(const RecordingProfile &parent, >+ QString setting = "mpeg2maxbitrate", >+ uint min_br = 1000, uint max_br = 16000, >+ uint default_br = 6000, uint increment = 100, >+ QString label = QString::null) : >+ SliderSetting(this, min_br, max_br, increment), >+ CodecParamStorage(this, parent, setting) > { >- >- setLabel(QObject::tr("Max. Bitrate")); >- setValue(6000); >- setHelpText(QObject::tr("Maximum Bitrate in kilobits/second. " >+ if (label.isEmpty()) >+ label = QObject::tr("Max. Bitrate"); >+ setLabel(label); >+ setValue(default_br); >+ setHelpText(QObject::tr("Maximum bit rate in kilobits/second. " > "2200Kbps is approximately 1 Gigabyte per hour.")); > }; > }; >@@ -819,7 +830,7 @@ > > params = new VerticalConfigurationGroup(false); > params->setLabel(QObject::tr("MPEG-2 Parameters")); >- params->addChild(new MPEG2bitrate(parent)); >+ params->addChild(new AverageBitrate(parent)); > params->addChild(new ScaleBitrate(parent)); > //params->addChild(new MPEG4MaxQuality(parent)); > //params->addChild(new MPEG4MinQuality(parent)); >@@ -843,17 +854,47 @@ > params->setLabel(QObject::tr("MPEG-2 Hardware Encoder")); > params->addChild(new MPEG2streamType(parent)); > params->addChild(new MPEG2aspectRatio(parent)); >- params->addChild(new MPEG2bitrate(parent)); >- params->addChild(new MPEG2maxBitrate(parent)); >+ params->addChild(new AverageBitrate(parent)); >+ params->addChild(new PeakBitrate(parent)); > > addTarget("MPEG-2 Hardware Encoder", params); >+ >+ params = new VerticalConfigurationGroup(false); >+ params->setLabel(QObject::tr("MPEG-4 AVC Hardware Encoder")); >+ ConfigurationGroup *h0 = new HorizontalConfigurationGroup( >+ true, false, true, true); >+ h0->setLabel(QObject::tr("Low Resolution")); >+ h0->addChild(new AverageBitrate(parent, "low_mpeg4avgbitrate", >+ 1000, 13500, 4500, 500)); >+ h0->addChild(new PeakBitrate(parent, "low_mpeg4peakbitrate", >+ 1100, 20200, 6000, 500)); >+ params->addChild(h0); >+ ConfigurationGroup *h1 = new HorizontalConfigurationGroup( >+ true, false, true, true); >+ h1->setLabel(QObject::tr("Medium Resolution")); >+ h1->addChild(new AverageBitrate(parent, "medium_mpeg4avgbitrate", >+ 1000, 13500, 9000, 500)); >+ h1->addChild(new PeakBitrate(parent, "medium_mpeg4peakbitrate", >+ 1100, 20200, 11000, 500)); >+ params->addChild(h1); >+ ConfigurationGroup *h2 = new HorizontalConfigurationGroup( >+ true, false, true, true); >+ h2->setLabel(QObject::tr("High Resolution")); >+ h2->addChild(new AverageBitrate(parent, "high_mpeg4avgbitrate", >+ 1000, 13500, 13500, 500)); >+ h2->addChild(new PeakBitrate(parent, "high_mpeg4peakbitrate", >+ 1100, 20200, 20200, 500)); >+ params->addChild(h2); >+ addTarget("MPEG-4 AVC Hardware Encoder", params); > } > > void selectCodecs(QString groupType) > { > if (!groupType.isNull()) > { >- if (groupType == "MPEG") >+ if (groupType == "HDPVR") >+ codecName->addSelection("MPEG-4 AVC Hardware Encoder"); >+ else if (groupType == "MPEG") > codecName->addSelection("MPEG-2 Hardware Encoder"); > else if (groupType == "MJPEG") > codecName->addSelection("Hardware MJPEG"); >@@ -1210,13 +1251,17 @@ > if (isEncoder) > { > QString tvFormat = gContext->GetSetting("TVFormat"); >- addChild(new ImageSize(*this, tvFormat, profileName)); >+ if (type.upper() != "HDPVR") >+ addChild(new ImageSize(*this, tvFormat, profileName)); > > videoSettings = new VideoCompressionSettings(*this, profileName); > addChild(videoSettings); > >- audioSettings = new AudioCompressionSettings(*this, profileName); >- addChild(audioSettings); >+ if (type.upper() != "HDPVR") >+ { >+ audioSettings = new AudioCompressionSettings(*this, profileName); >+ addChild(audioSettings); >+ } > > if (profileName && profileName.left(11) == "Transcoders") > { >Index: mythtv/libs/libmythtv/tv_rec.h >=================================================================== >--- mythtv/libs/libmythtv/tv_rec.h (revision 18528) >+++ mythtv/libs/libmythtv/tv_rec.h (working copy) >@@ -37,7 +37,7 @@ > class DTVChannel; > class DVBChannel; > class FirewireChannel; >-class Channel; >+class V4LChannel; > class HDHRChannel; > > class MPEGStreamData; >@@ -281,7 +281,7 @@ > HDHRChannel *GetHDHRChannel(void); > DVBChannel *GetDVBChannel(void); > FirewireChannel *GetFirewireChannel(void); >- Channel *GetV4LChannel(void); >+ V4LChannel *GetV4LChannel(void); > > bool SetupSignalMonitor(bool enable_table_monitoring, bool notify); > bool SetupDTVSignalMonitor(void); >Index: mythtv/libs/libmythtv/scanwizardhelpers.cpp >=================================================================== >--- mythtv/libs/libmythtv/scanwizardhelpers.cpp (revision 18528) >+++ mythtv/libs/libmythtv/scanwizardhelpers.cpp (working copy) >@@ -58,6 +58,9 @@ > # ifdef USING_IVTV > cardTypes += ",'MPEG'"; > # endif // USING_IVTV >+# ifdef USING_HDPVR >+ cardTypes += ",'HDPVR'"; >+# endif // USING_HDPVR > #endif // USING_V4L > > #ifdef USING_IPTV >Index: mythtv/libs/libmythtv/channel.cpp >=================================================================== >--- mythtv/libs/libmythtv/channel.cpp (revision 18528) >+++ mythtv/libs/libmythtv/channel.cpp (working copy) >@@ -1,1320 +0,0 @@ >-// Std C headers >-#include <cstdio> >-#include <cstdlib> >-#include <cerrno> >- >-// POSIX headers >-#include <unistd.h> >-#include <fcntl.h> >-#include <sys/ioctl.h> >-#include <sys/types.h> >-#include <sys/stat.h> >-#include <sys/wait.h> >- >-// C++ headers >-#include <algorithm> >-#include <iostream> >-using namespace std; >- >-// Qt headers >-#include <qsqldatabase.h> >- >-// MythTV headers >-#include "videodev_myth.h" >-#include "channel.h" >-#include "frequencies.h" >-#include "tv_rec.h" >-#include "mythcontext.h" >-#include "mythdbcon.h" >-#include "channelutil.h" >-#include "cardutil.h" >- >-#define DEBUG_ATTRIB 1 >- >-#define LOC QString("Channel(%1): ").arg(device) >-#define LOC_WARN QString("Channel(%1) Warning: ").arg(device) >-#define LOC_ERR QString("Channel(%1) Error: ").arg(device) >- >-static int format_to_mode(const QString& fmt, int v4l_version); >-static QString mode_to_format(int mode, int v4l_version); >- >-/** \class Channel >- * \brief Class implementing ChannelBase interface to tuning hardware >- * when using Video4Linux based drivers. >- */ >- >-Channel::Channel(TVRec *parent, const QString &videodevice) >- : DTVChannel(parent), >- device(videodevice), videofd(-1), >- device_name(QString::null), driver_name(QString::null), >- curList(NULL), totalChannels(0), >- currentFormat(""), is_dtv(false), >- usingv4l2(false), defaultFreqTable(1) >-{ >-} >- >-Channel::~Channel(void) >-{ >- Close(); >-} >- >-bool Channel::Init(QString &inputname, QString &startchannel, bool setchan) >-{ >- if (setchan) >- { >- SetFormat(gContext->GetSetting("TVFormat")); >- SetDefaultFreqTable(gContext->GetSetting("FreqTable")); >- } >- return ChannelBase::Init(inputname, startchannel, setchan); >-} >- >-bool Channel::Open(void) >-{ >-#if FAKE_VIDEO >- return true; >-#endif >- if (videofd >= 0) >- return true; >- >- videofd = open(device.ascii(), O_RDWR); >- if (videofd < 0) >- { >- VERBOSE(VB_IMPORTANT, >- QString("Channel(%1)::Open(): Can't open video device, " >- "error \"%2\"").arg(device).arg(strerror(errno))); >- return false; >- } >- >- usingv4l2 = CardUtil::hasV4L2(videofd); >- CardUtil::GetV4LInfo(videofd, device_name, driver_name); >- VERBOSE(VB_CHANNEL, LOC + QString("Device name '%1' driver '%2'.") >- .arg(device_name).arg(driver_name)); >- >- if (!InitializeInputs()) >- { >- Close(); >- return false; >- } >- >- SetFormat("Default"); >- >- return true; >-} >- >-void Channel::Close(void) >-{ >- if (videofd >= 0) >- close(videofd); >- videofd = -1; >-} >- >-void Channel::SetFd(int fd) >-{ >- if (fd != videofd) >- Close(); >- videofd = (fd >= 0) ? fd : -1; >-} >- >-static int format_to_mode(const QString &fmt, int v4l_version) >-{ >- if (2 == v4l_version) >- { >- if (fmt == "PAL-BG") >- return V4L2_STD_PAL_BG; >- else if (fmt == "PAL-D") >- return V4L2_STD_PAL_D; >- else if (fmt == "PAL-DK") >- return V4L2_STD_PAL_DK; >- else if (fmt == "PAL-I") >- return V4L2_STD_PAL_I; >- else if (fmt == "PAL-60") >- return V4L2_STD_PAL_60; >- else if (fmt == "SECAM") >- return V4L2_STD_SECAM; >- else if (fmt == "SECAM-D") >- return V4L2_STD_SECAM_D; >- else if (fmt == "PAL-NC") >- return V4L2_STD_PAL_Nc; >- else if (fmt == "PAL-M") >- return V4L2_STD_PAL_M; >- else if (fmt == "PAL-N") >- return V4L2_STD_PAL_N; >- else if (fmt == "NTSC-JP") >- return V4L2_STD_NTSC_M_JP; >- // generics... >- else if (fmt.left(4) == "NTSC") >- return V4L2_STD_NTSC; >- else if (fmt.left(4) == "ATSC") >- return V4L2_STD_NTSC; // We've dropped V4L ATSC support... >- else if (fmt.left(3) == "PAL") >- return V4L2_STD_PAL; >- return V4L2_STD_NTSC; >- } >- else if (1 == v4l_version) >- { >- if (fmt == "NTSC-JP") >- return 6; >- else if (fmt.left(5) == "SECAM") >- return VIDEO_MODE_SECAM; >- else if (fmt == "PAL-NC") >- return 3; >- else if (fmt == "PAL-M") >- return 4; >- else if (fmt == "PAL-N") >- return 5; >- // generics... >- else if (fmt.left(3) == "PAL") >- return VIDEO_MODE_PAL; >- else if (fmt.left(4) == "NTSC") >- return VIDEO_MODE_NTSC; >- else if (fmt.left(4) == "ATSC") >- return VIDEO_MODE_NTSC; // We've dropped V4L ATSC support... >- return VIDEO_MODE_NTSC; >- } >- >- VERBOSE(VB_IMPORTANT, >- "format_to_mode() does not recognize V4L" << v4l_version); >- >- return V4L2_STD_NTSC; // assume V4L version 2 >-} >- >-static QString mode_to_format(int mode, int v4l_version) >-{ >- if (2 == v4l_version) >- { >- if (mode == V4L2_STD_NTSC) >- return "NTSC"; >- else if (mode == V4L2_STD_NTSC_M_JP) >- return "NTSC-JP"; >- else if (mode == V4L2_STD_PAL) >- return "PAL"; >- else if (mode == V4L2_STD_PAL_60) >- return "PAL-60"; >- else if (mode == V4L2_STD_PAL_BG) >- return "PAL-BG"; >- else if (mode == V4L2_STD_PAL_D) >- return "PAL-D"; >- else if (mode == V4L2_STD_PAL_DK) >- return "PAL-DK"; >- else if (mode == V4L2_STD_PAL_I) >- return "PAL-I"; >- else if (mode == V4L2_STD_PAL_M) >- return "PAL-M"; >- else if (mode == V4L2_STD_PAL_N) >- return "PAL-N"; >- else if (mode == V4L2_STD_PAL_Nc) >- return "PAL-NC"; >- else if (mode == V4L2_STD_SECAM) >- return "SECAM"; >- else if (mode == V4L2_STD_SECAM_D) >- return "SECAM-D"; >- // generic.. >- else if ((V4L2_STD_NTSC_M == mode) || >- (V4L2_STD_NTSC_443 == mode) || >- (V4L2_STD_NTSC_M_KR == mode)) >- return "NTSC"; >- else if ((V4L2_STD_PAL_B == mode) || >- (V4L2_STD_PAL_B1 == mode) || >- (V4L2_STD_PAL_G == mode) || >- (V4L2_STD_PAL_H == mode) || >- (V4L2_STD_PAL_D1 == mode) || >- (V4L2_STD_PAL_K == mode)) >- return "PAL"; >- else if ((V4L2_STD_SECAM_B == mode) || >- (V4L2_STD_SECAM_DK == mode) || >- (V4L2_STD_SECAM_G == mode) || >- (V4L2_STD_SECAM_H == mode) || >- (V4L2_STD_SECAM_K == mode) || >- (V4L2_STD_SECAM_K1 == mode) || >- (V4L2_STD_SECAM_L == mode) || >- (V4L2_STD_SECAM_LC == mode)) >- return "SECAM"; >- else if ((V4L2_STD_ATSC == mode) || >- (V4L2_STD_ATSC_8_VSB == mode) || >- (V4L2_STD_ATSC_16_VSB == mode)) >- { >- // We've dropped V4L ATSC support, but this still needs to be >- // returned here so we will change the mode if the device is >- // in ATSC mode already. >- return "ATSC"; >- } >- } >- else if (1 == v4l_version) >- { >- if (mode == VIDEO_MODE_NTSC) >- return "NTSC"; >- else if (mode == VIDEO_MODE_ATSC) >- return "ATSC"; >- else if (mode == VIDEO_MODE_PAL) >- return "PAL"; >- else if (mode == VIDEO_MODE_SECAM) >- return "SECAM"; >- else if (mode == 3) >- return "PAL-NC"; >- else if (mode == 4) >- return "PAL-M"; >- else if (mode == 5) >- return "PAL-N"; >- else if (mode == 6) >- return "NTSC-JP"; >- } >- else >- { >- VERBOSE(VB_IMPORTANT, >- "mode_to_format() does not recognize V4L" << v4l_version); >- } >- >- return "Unknown"; >-} >- >-/** \fn Channel::InitializeInputs(void) >- * This enumerates the inputs, converts the format >- * string to something the hardware understands, and >- * if the parent pointer is valid retrieves the >- * channels from the database. >- */ >-bool Channel::InitializeInputs(void) >-{ >- // Get Inputs from DB >- if (!ChannelBase::InitializeInputs()) >- return false; >- >- // Get global TVFormat setting >- QString fmt = gContext->GetSetting("TVFormat"); >- VERBOSE(VB_CHANNEL, QString("Global TVFormat Setting '%1'").arg(fmt)); >- int videomode_v4l1 = format_to_mode(fmt.upper(), 1); >- int videomode_v4l2 = format_to_mode(fmt.upper(), 2); >- >- bool ok = false; >- InputNames v4l_inputs = CardUtil::probeV4LInputs(videofd, ok); >- >- // Insert info from hardware >- uint valid_cnt = 0; >- InputMap::const_iterator it; >- for (it = inputs.begin(); it != inputs.end(); ++it) >- { >- InputNames::const_iterator v4l_it = v4l_inputs.begin(); >- for (; v4l_it != v4l_inputs.end(); ++v4l_it) >- { >- if (*v4l_it == (*it)->name) >- { >- (*it)->inputNumV4L = v4l_it.key(); >- (*it)->videoModeV4L1 = videomode_v4l1; >- (*it)->videoModeV4L2 = videomode_v4l2; >- valid_cnt++; >- } >- } >- } >- >- // print em >- for (it = inputs.begin(); it != inputs.end(); ++it) >- { >- VERBOSE(VB_CHANNEL, LOC + QString("Input #%1: '%2' schan(%3) " >- "tun(%4) v4l1(%5) v4l2(%6)") >- .arg(it.key()).arg((*it)->name).arg((*it)->startChanNum) >- .arg((*it)->tuneToChannel) >- .arg(mode_to_format((*it)->videoModeV4L1,1)) >- .arg(mode_to_format((*it)->videoModeV4L2,2))); >- } >- >- return valid_cnt; >-} >- >-/** \fn Channel::SetFormat(const QString &format) >- * \brief Initializes tuner and modulator variables >- * >- * \param format One of twelve formats: >- * "NTSC", "NTSC-JP", "ATSC", >- * "SECAM", >- * "PAL", "PAL-BG", "PAL-DK", "PAL-I", >- * "PAL-60", "PAL-NC", "PAL-M", or "PAL-N" >- */ >-void Channel::SetFormat(const QString &format) >-{ >- if (!Open()) >- return; >- >- int inputNum = currentInputID; >- if (currentInputID < 0) >- inputNum = GetNextInputNum(); >- >- QString fmt = format; >- if ((fmt == "Default") || format.isEmpty()) >- { >- InputMap::const_iterator it = inputs.find(inputNum); >- if (it != inputs.end()) >- fmt = mode_to_format((*it)->videoModeV4L2, 2); >- } >- >- VERBOSE(VB_CHANNEL, LOC + QString("SetFormat(%1) fmt(%2) input(%3)") >- .arg(format).arg(fmt).arg(inputNum)); >- >- if ((fmt == currentFormat) || SetInputAndFormat(inputNum, fmt)) >- { >- currentFormat = fmt; >- is_dtv = (fmt == "ATSC"); >- } >-} >- >-int Channel::SetDefaultFreqTable(const QString &name) >-{ >- defaultFreqTable = SetFreqTable(name); >- return defaultFreqTable; >-} >- >-void Channel::SetFreqTable(const int index) >-{ >- curList = chanlists[index].list; >- totalChannels = chanlists[index].count; >-} >- >-int Channel::SetFreqTable(const QString &name) >-{ >- int i = 0; >- char *listname = (char *)chanlists[i].name; >- >- curList = NULL; >- while (listname != NULL) >- { >- if (name == listname) >- { >- SetFreqTable(i); >- return i; >- } >- i++; >- listname = (char *)chanlists[i].name; >- } >- >- VERBOSE(VB_CHANNEL, QString("Channel(%1)::SetFreqTable(): Invalid " >- "frequency table name %2, using %3."). >- arg(device).arg(name).arg((char *)chanlists[1].name)); >- SetFreqTable(1); >- return 1; >-} >- >-int Channel::GetCurrentChannelNum(const QString &channame) >-{ >- for (int i = 0; i < totalChannels; i++) >- { >- if (channame == curList[i].name) >- return i; >- } >- >- VERBOSE(VB_IMPORTANT, LOC_ERR + >- QString("GetCurrentChannelNum(%1): " >- "Failed to find Channel").arg(channame)); >- >- return -1; >-} >- >-void Channel::SaveCachedPids(const pid_cache_t &pid_cache) const >-{ >- int chanid = GetChanID(); >- if (chanid > 0) >- DTVChannel::SaveCachedPids(chanid, pid_cache); >-} >- >-void Channel::GetCachedPids(pid_cache_t &pid_cache) const >-{ >- int chanid = GetChanID(); >- if (chanid > 0) >- DTVChannel::GetCachedPids(chanid, pid_cache); >-} >- >-bool Channel::SetChannelByString(const QString &channum) >-{ >- QString loc = LOC + QString("SetChannelByString(%1)").arg(channum); >- QString loc_err = loc + ", Error: "; >- VERBOSE(VB_CHANNEL, loc); >- >- if (!Open()) >- { >- VERBOSE(VB_IMPORTANT, loc_err + "Channel object " >- "will not open, can not change channels."); >- >- return false; >- } >- >- QString inputName; >- if (!CheckChannel(channum, inputName)) >- { >- VERBOSE(VB_IMPORTANT, loc_err + >- "CheckChannel failed.\n\t\t\tPlease verify the channel " >- "in the 'mythtv-setup' Channel Editor."); >- >- return false; >- } >- >- // If CheckChannel filled in the inputName then we need to >- // change inputs and return, since the act of changing >- // inputs will change the channel as well. >- if (!inputName.isEmpty()) >- return ChannelBase::SwitchToInput(inputName, channum); >- >- ClearDTVInfo(); >- >- InputMap::const_iterator it = inputs.find(currentInputID); >- if (it == inputs.end()) >- return false; >- >- uint mplexid_restriction; >- if (!IsInputAvailable(currentInputID, mplexid_restriction)) >- return false; >- >- // Fetch tuning data from the database. >- QString tvformat, modulation, freqtable, freqid, dtv_si_std; >- int finetune; >- uint64_t frequency; >- int mpeg_prog_num; >- uint atsc_major, atsc_minor, mplexid, tsid, netid; >- >- if (!ChannelUtil::GetChannelData( >- (*it)->sourceid, channum, >- tvformat, modulation, freqtable, freqid, >- finetune, frequency, >- dtv_si_std, mpeg_prog_num, atsc_major, atsc_minor, tsid, netid, >- mplexid, commfree)) >- { >- return false; >- } >- >- if (mplexid_restriction && (mplexid != mplexid_restriction)) >- return false; >- >- // If the frequency is zeroed out, don't use it directly. >- bool ok = (frequency > 0); >- >- if (!ok) >- { >- frequency = (freqid.toInt(&ok) + finetune) * 1000; >- mplexid = 0; >- } >- bool isFrequency = ok && (frequency > 10000000); >- >- // If we are tuning to a freqid, rather than an actual frequency, >- // we need to set the frequency table to use. >- if (!isFrequency) >- { >- if (freqtable == "default" || freqtable.isEmpty()) >- SetFreqTable(defaultFreqTable); >- else >- SetFreqTable(freqtable); >- } >- >- // Set NTSC, PAL, ATSC, etc. >- SetFormat(tvformat); >- >- // If a tuneToChannel is set make sure we're still on it >- if (!(*it)->tuneToChannel.isEmpty() && (*it)->tuneToChannel != "Undefined") >- TuneTo((*it)->tuneToChannel, 0); >- >- // Tune to proper frequency >- if ((*it)->externalChanger.isEmpty()) >- { >- if ((*it)->name.contains("composite", false) || >- (*it)->name.contains("s-video", false)) >- { >- VERBOSE(VB_GENERAL, LOC_WARN + "You have not set " >- "an external channel changing" >- "\n\t\t\tscript for a composite or s-video " >- "input. Channel changing will do nothing."); >- } >- else if (isFrequency) >- { >- if (!Tune(frequency, "", (is_dtv) ? "8vsb" : "analog", dtv_si_std)) >- { >- return false; >- } >- } >- else >- { >- if (!TuneTo(freqid, finetune)) >- return false; >- } >- } >- else if (!ChangeExternalChannel(freqid)) >- return false; >- >- // Set the current channum to the new channel's channum >- curchannelname = QDeepCopy<QString>(channum); >- >- // Setup filters & recording picture attributes for framegrabing recorders >- // now that the new curchannelname has been established. >- if (pParent) >- pParent->SetVideoFiltersForChannel(GetCurrentSourceID(), channum); >- InitPictureAttributes(); >- >- // Set the major and minor channel for any additional multiplex tuning >- SetDTVInfo(atsc_major, atsc_minor, netid, tsid, mpeg_prog_num); >- >- // Set this as the future start channel for this source >- inputs[currentInputID]->startChanNum = QDeepCopy<QString>(curchannelname); >- >- return true; >-} >- >-bool Channel::TuneTo(const QString &channum, int finetune) >-{ >- int i = GetCurrentChannelNum(channum); >- VERBOSE(VB_CHANNEL, QString("Channel(%1)::TuneTo(%2): " >- "curList[%3].freq(%4)") >- .arg(device).arg(channum).arg(i) >- .arg((i != -1) ? curList[i].freq : -1)); >- >- if (i == -1) >- { >- VERBOSE(VB_IMPORTANT, QString("Channel(%1)::TuneTo(%2): Error, " >- "failed to find channel.") >- .arg(device).arg(channum)); >- return false; >- } >- >- int frequency = (curList[i].freq + finetune) * 1000; >- >- return Tune(frequency, "", "analog", "analog"); >-} >- >-bool Channel::Tune(const DTVMultiplex &tuning, QString inputname) >-{ >- return Tune(tuning.frequency - 1750000, // to visual carrier >- inputname, tuning.modulation.toString(), tuning.sistandard); >-} >- >-/** \fn Channel::Tune(uint,QString,QString,QString) >- * \brief Tunes to a specific frequency (Hz) on a particular input, using >- * the specified modulation. >- * >- * Note: This function always uses modulator zero. >- * >- * \param frequency Frequency in Hz, this is divided by 62.5 kHz or 62.5 Hz >- * depending on the modulator and sent to the hardware. >- * \param inputname Name of the input (Television, Antenna 1, etc.) >- * \param modulation "radio", "analog", or "digital" >- */ >-bool Channel::Tune(uint frequency, QString inputname, >- QString modulation, QString si_std) >-{ >- VERBOSE(VB_CHANNEL, LOC + QString("Tune(%1, %2, %3, %4)") >- .arg(frequency).arg(inputname).arg(modulation).arg(si_std)); >- >- int ioctlval = 0; >- >- if (modulation == "8vsb") >- SetFormat("ATSC"); >- modulation = (is_dtv) ? "digital" : modulation; >- >- int inputnum = GetInputByName(inputname); >- >- bool ok = true; >- if ((inputnum >= 0) && (GetCurrentInputNum() != inputnum)) >- ok = SwitchToInput(inputnum, false); >- else if (GetCurrentInputNum() < 0) >- ok = SwitchToInput(0, false); >- >- if (!ok) >- return false; >- >- // If the frequency is a center frequency and not >- // a visual carrier frequency, convert it. >- int offset = frequency % 1000000; >- offset = (offset > 500000) ? 1000000 - offset : offset; >- bool is_visual_carrier = (offset > 150000) && (offset < 350000); >- if (!is_visual_carrier && currentFormat == "ATSC") >- { >- VERBOSE(VB_CHANNEL, QString("Channel(%1): ").arg(device) + >- QString("Converting frequency from center frequency " >- "(%1 Hz) to visual carrier frequency (%2 Hz).") >- .arg(frequency).arg(frequency - 1750000)); >- frequency -= 1750000; // convert to visual carrier >- } >- >- // Video4Linux version 2 tuning >- if (usingv4l2) >- { >- bool isTunerCapLow = false; >- struct v4l2_modulator mod; >- bzero(&mod, sizeof(mod)); >- mod.index = 0; >- ioctlval = ioctl(videofd, VIDIOC_G_MODULATOR, &mod); >- if (ioctlval >= 0) >- { >- isTunerCapLow = (mod.capability & V4L2_TUNER_CAP_LOW); >- VERBOSE(VB_CHANNEL, " name: "<<mod.name); >- VERBOSE(VB_CHANNEL, "CapLow: "<<isTunerCapLow); >- } >- >- struct v4l2_frequency vf; >- bzero(&vf, sizeof(vf)); >- >- vf.tuner = 0; // use first tuner >- vf.frequency = (isTunerCapLow) ? >- ((int)(frequency / 62.5)) : (frequency / 62500); >- >- if (modulation.lower() == "digital") >- { >- VERBOSE(VB_CHANNEL, "using digital modulation"); >- vf.type = V4L2_TUNER_DIGITAL_TV; >- if (ioctl(videofd, VIDIOC_S_FREQUENCY, &vf)>=0) >- return true; >- VERBOSE(VB_CHANNEL, "digital modulation failed"); >- } >- >- vf.type = V4L2_TUNER_ANALOG_TV; >- >- ioctlval = ioctl(videofd, VIDIOC_S_FREQUENCY, &vf); >- if (ioctlval < 0) >- { >- VERBOSE(VB_IMPORTANT, >- QString("Channel(%1)::Tune(): Error %2 " >- "while setting frequency (v2): %3") >- .arg(device).arg(ioctlval).arg(strerror(errno))); >- return false; >- } >- ioctlval = ioctl(videofd, VIDIOC_G_FREQUENCY, &vf); >- >- if (ioctlval >= 0) >- { >- VERBOSE(VB_CHANNEL, QString( >- "Channel(%1)::Tune(): Frequency is now %2") >- .arg(device).arg(vf.frequency * 62500)); >- } >- >- return true; >- } >- >- // Video4Linux version 1 tuning >- uint freq = frequency / 62500; >- ioctlval = ioctl(videofd, VIDIOCSFREQ, &freq); >- if (ioctlval < 0) >- { >- VERBOSE(VB_IMPORTANT, >- QString("Channel(%1)::Tune(): Error %2 " >- "while setting frequency (v1): %3") >- .arg(device).arg(ioctlval).arg(strerror(errno))); >- return false; >- } >- >- SetSIStandard(si_std); >- >- return true; >-} >- >-/** \fn Channel::Retune(void) >- * \brief Retunes to last tuned frequency. >- * >- * NOTE: This only works for V4L2 and only for analog tuning. >- */ >-bool Channel::Retune(void) >-{ >- if (usingv4l2) >- { >- struct v4l2_frequency vf; >- bzero(&vf, sizeof(vf)); >- >- vf.tuner = 0; // use first tuner >- vf.type = V4L2_TUNER_ANALOG_TV; >- >- // Get the last tuned frequency >- int ioctlval = ioctl(videofd, VIDIOC_G_FREQUENCY, &vf); >- if (ioctlval < 0) >- { >- VERBOSE(VB_IMPORTANT, LOC_ERR + "Retune failed (1)" + ENO); >- return false; >- } >- >- // Set the last tuned frequency again... >- ioctlval = ioctl(videofd, VIDIOC_S_FREQUENCY, &vf); >- if (ioctlval < 0) >- { >- VERBOSE(VB_IMPORTANT, LOC_ERR + "Retune failed (2)" + ENO); >- return false; >- } >- >- return true; >- } >- >- return false; >-} >- >-// documented in dtvchannel.h >-bool Channel::TuneMultiplex(uint mplexid, QString inputname) >-{ >- VERBOSE(VB_CHANNEL, LOC + QString("TuneMultiplex(%1)").arg(mplexid)); >- >- QString modulation; >- QString si_std; >- uint64_t frequency; >- uint transportid; >- uint dvb_networkid; >- >- if (!ChannelUtil::GetTuningParams( >- mplexid, modulation, frequency, >- transportid, dvb_networkid, si_std)) >- { >- VERBOSE(VB_IMPORTANT, LOC_ERR + "TuneMultiplex(): " + >- QString("Could not find tuning parameters for multiplex %1.") >- .arg(mplexid)); >- >- return false; >- } >- >- if (!Tune(frequency, inputname, modulation, si_std)) >- return false; >- >- return true; >-} >- >-QString Channel::GetFormatForChannel(QString channum, QString inputname) >-{ >- MSqlQuery query(MSqlQuery::InitCon()); >- query.prepare( >- "SELECT tvformat " >- "FROM channel, cardinput " >- "WHERE channum = :CHANNUM AND " >- " inputname = :INPUTNAME AND " >- " cardinput.cardid = :CARDID AND " >- " cardinput.sourceid = channel.sourceid"); >- query.bindValue(":CHANNUM", channum); >- query.bindValue(":INPUTNAME", inputname); >- query.bindValue(":CARDID", GetCardID()); >- >- QString fmt = QString::null; >- if (!query.exec() || !query.isActive()) >- MythContext::DBError("SwitchToInput:find format", query); >- else if (query.next()) >- fmt = query.value(0).toString(); >- return fmt; >-} >- >-bool Channel::SetInputAndFormat(int inputNum, QString newFmt) >-{ >- InputMap::const_iterator it = inputs.find(inputNum); >- if (it == inputs.end() || (*it)->inputNumV4L < 0) >- return false; >- >- int inputNumV4L = (*it)->inputNumV4L; >- bool usingv4l1 = !usingv4l2; >- bool ok = true; >- >- QString msg = >- QString("SetInputAndFormat(%1, %2) ").arg(inputNum).arg(newFmt); >- >- if (usingv4l2) >- { >- VERBOSE(VB_CHANNEL, LOC + msg + "(v4l v2)"); >- >- int ioctlval = ioctl(videofd, VIDIOC_S_INPUT, &inputNumV4L); >- >- // ConvertX (wis-go7007) requires streaming to be disabled >- // before an input switch, do this if initial switch failed. >- bool streamingDisabled = false; >- int streamType = V4L2_BUF_TYPE_VIDEO_CAPTURE; >- if ((ioctlval < 0) && (errno == EBUSY)) >- { >- ioctlval = ioctl(videofd, VIDIOC_STREAMOFF, &streamType); >- if (ioctlval < 0) >- { >- VERBOSE(VB_IMPORTANT, LOC_ERR + msg + >- "\n\t\t\twhile disabling streaming (v4l v2)" + ENO); >- >- ok = false; >- ioctlval = 0; >- } >- else >- { >- streamingDisabled = true; >- >- // Resend the input switch ioctl. >- ioctlval = ioctl(videofd, VIDIOC_S_INPUT, &inputNumV4L); >- } >- } >- >- if (ioctlval < 0) >- { >- VERBOSE(VB_IMPORTANT, LOC_ERR + msg + >- "\n\t\t\twhile setting input (v4l v2)" + ENO); >- >- ok = false; >- } >- >- v4l2_std_id vid_mode = format_to_mode(newFmt, 2); >- ioctlval = ioctl(videofd, VIDIOC_S_STD, &vid_mode); >- if (ioctlval < 0) >- { >- VERBOSE(VB_IMPORTANT, LOC_ERR + msg + >- "\n\t\t\twhile setting format (v4l v2)" + ENO); >- >- ok = false; >- } >- >- // ConvertX (wis-go7007) requires streaming to be disabled >- // before an input switch, here we try to re-enable streaming. >- if (streamingDisabled) >- { >- ioctlval = ioctl(videofd, VIDIOC_STREAMON, &streamType); >- if (ioctlval < 0) >- { >- VERBOSE(VB_IMPORTANT, LOC_ERR + msg + >- "\n\t\t\twhile reenabling streaming (v4l v2)" + ENO); >- >- ok = false; >- } >- } >- } >- >- if (usingv4l1) >- { >- VERBOSE(VB_CHANNEL, LOC + msg + "(v4l v1)"); >- >- // read in old settings >- struct video_channel set; >- bzero(&set, sizeof(set)); >- ioctl(videofd, VIDIOCGCHAN, &set); >- >- // set new settings >- set.channel = inputNumV4L; >- set.norm = format_to_mode(newFmt, 1); >- int ioctlval = ioctl(videofd, VIDIOCSCHAN, &set); >- >- ok = (ioctlval >= 0); >- if (!ok) >- { >- VERBOSE(VB_IMPORTANT, LOC_ERR + msg + >- "\n\t\t\twhile setting format (v4l v1)" + ENO); >- } >- else if (usingv4l2) >- { >- VERBOSE(VB_IMPORTANT, LOC + msg + >- "\n\t\t\tSetting video mode with v4l version 1 worked"); >- } >- } >- return ok; >-} >- >-bool Channel::SwitchToInput(int inputnum, bool setstarting) >-{ >- InputMap::const_iterator it = inputs.find(inputnum); >- if (it == inputs.end()) >- return false; >- >- QString tuneFreqId = (*it)->tuneToChannel; >- QString channum = (*it)->startChanNum; >- QString inputname = (*it)->name; >- >- VERBOSE(VB_CHANNEL, QString("Channel(%1)::SwitchToInput(in %2, '%3')") >- .arg(device).arg(inputnum) >- .arg(setstarting ? channum : QString(""))); >- >- uint mplexid_restriction; >- if (!IsInputAvailable(inputnum, mplexid_restriction)) >- return false; >- >- QString newFmt = mode_to_format((*it)->videoModeV4L2, 2); >- >- // If we are setting a channel, get its video mode... >- bool chanValid = (channum != "Undefined") && !channum.isEmpty(); >- if (setstarting && chanValid) >- { >- QString tmp = GetFormatForChannel(channum, inputname); >- if (tmp != "Default" && !tmp.isEmpty()) >- newFmt = tmp; >- } >- >- bool ok = SetInputAndFormat(inputnum, newFmt); >- >- // Try to set ATSC mode if NTSC fails >- if (!ok && newFmt == "NTSC") >- ok = SetInputAndFormat(inputnum, "ATSC"); >- >- if (!ok) >- { >- VERBOSE(VB_IMPORTANT, LOC + "SetInputAndFormat() failed"); >- return false; >- } >- >- currentFormat = newFmt; >- is_dtv = newFmt == "ATSC"; >- currentInputID = inputnum; >- curchannelname = ""; // this will be set by SetChannelByString >- >- if (!tuneFreqId.isEmpty() && tuneFreqId != "Undefined") >- ok = TuneTo(tuneFreqId, 0); >- >- if (!ok) >- return false; >- >- if (setstarting && chanValid) >- ok = SetChannelByString(channum); >- else if (setstarting && !chanValid) >- { >- VERBOSE(VB_IMPORTANT, LOC + >- QString("SwitchToInput(in %2, set ch): ").arg(inputnum) + >- QString("\n\t\t\tDefault channel '%1' is not valid.") >- .arg(channum)); >- ok = false; >- } >- >- return ok; >-} >- >-static unsigned short *get_v4l1_field( >- int v4l2_attrib, struct video_picture &vid_pic) >-{ >- switch (v4l2_attrib) >- { >- case V4L2_CID_CONTRAST: >- return &vid_pic.contrast; >- case V4L2_CID_BRIGHTNESS: >- return &vid_pic.brightness; >- case V4L2_CID_SATURATION: >- return &vid_pic.colour; >- case V4L2_CID_HUE: >- return &vid_pic.hue; >- default: >- VERBOSE(VB_IMPORTANT, "get_v4l1_field: " >- "invalid attribute argument "<<v4l2_attrib); >- } >- return NULL; >-} >- >-static int get_v4l2_attribute(const QString &db_col_name) >-{ >- if ("brightness" == db_col_name) >- return V4L2_CID_BRIGHTNESS; >- else if ("contrast" == db_col_name) >- return V4L2_CID_CONTRAST; >- else if ("colour" == db_col_name) >- return V4L2_CID_SATURATION; >- else if ("hue" == db_col_name) >- return V4L2_CID_HUE; >- return -1; >-} >- >-bool Channel::InitPictureAttribute(const QString db_col_name) >-{ >- if (!pParent || is_dtv) >- return false; >- >- int v4l2_attrib = get_v4l2_attribute(db_col_name); >- if (v4l2_attrib == -1) >- return false; >- >- int cfield = ChannelUtil::GetChannelValueInt( >- db_col_name, GetCurrentSourceID(), curchannelname); >- int sfield = CardUtil::GetValueInt( >- db_col_name, GetCardID()); >- >- if ((cfield == -1) || (sfield == -1)) >- return false; >- >- int field = (cfield + sfield) & 0xFFFF; >- >- QString loc = LOC + >- QString("InitPictureAttribute(%1): ").arg(db_col_name, 10); >- QString loc_err = LOC_ERR + >- QString("InitPictureAttribute(%1): ").arg(db_col_name, 10); >- >- if (usingv4l2) >- { >- struct v4l2_control ctrl; >- struct v4l2_queryctrl qctrl; >- bzero(&ctrl, sizeof(ctrl)); >- bzero(&qctrl, sizeof(qctrl)); >- >- ctrl.id = qctrl.id = v4l2_attrib; >- if (ioctl(videofd, VIDIOC_QUERYCTRL, &qctrl) < 0) >- { >- VERBOSE(VB_IMPORTANT, loc_err + "failed to query controls." + ENO); >- return false; >- } >- >- float new_range = qctrl.maximum - qctrl.minimum; >- float old_range = 65535 - 0; >- float scl_range = new_range / old_range; >- float dfl = (qctrl.default_value - qctrl.minimum) / new_range; >- int norm_dfl = (0x10000 + (int)(dfl * old_range) - 32768) & 0xFFFF; >- >- if (pict_attr_default.find(db_col_name) == pict_attr_default.end()) >- { >- if (device_name == "pcHDTV HD3000 HDTV") >- { >- pict_attr_default["brightness"] = 9830; >- pict_attr_default["contrast"] = 39322; >- pict_attr_default["colour"] = 45875; >- pict_attr_default["hue"] = 0; >- } >- else >- { >- pict_attr_default[db_col_name] = norm_dfl; >- } >- } >- >- int dfield = pict_attr_default[db_col_name]; >- field = (cfield + sfield + dfield) & 0xFFFF; >- int value0 = (int) ((scl_range * field) + qctrl.minimum); >- int value1 = min(value0, (int)qctrl.maximum); >- ctrl.value = max(value1, (int)qctrl.minimum); >- >-#if DEBUG_ATTRIB >- VERBOSE(VB_CHANNEL, loc + QString(" %1\n\t\t\t" >- "[%2,%3] dflt(%4, %5, %6)") >- .arg(value0).arg(qctrl.minimum, 5).arg(qctrl.maximum, 5) >- .arg(qctrl.default_value, 5).arg(dfl, 4, 'f', 2) >- .arg(norm_dfl)); >-#endif >- >- if (ioctl(videofd, VIDIOC_S_CTRL, &ctrl) < 0) >- { >- VERBOSE(VB_IMPORTANT, loc_err + "failed to set controls" + ENO); >- return false; >- } >- >- return true; >- } >- >- // V4L1 >- unsigned short *setfield; >- struct video_picture vid_pic; >- bzero(&vid_pic, sizeof(vid_pic)); >- >- if (ioctl(videofd, VIDIOCGPICT, &vid_pic) < 0) >- { >- VERBOSE(VB_IMPORTANT, loc_err + "failed to query controls." + ENO); >- return false; >- } >- setfield = get_v4l1_field(v4l2_attrib, vid_pic); >- >- if (!setfield) >- return false; >- >- *setfield = field; >- if (ioctl(videofd, VIDIOCSPICT, &vid_pic) < 0) >- { >- VERBOSE(VB_IMPORTANT, loc_err + "failed to set controls." + ENO); >- return false; >- } >- >- return true; >-} >- >-bool Channel::InitPictureAttributes(void) >-{ >- return (InitPictureAttribute("brightness") && >- InitPictureAttribute("contrast") && >- InitPictureAttribute("colour") && >- InitPictureAttribute("hue")); >-} >- >-int Channel::GetPictureAttribute(PictureAttribute attr) const >-{ >- QString db_col_name = toDBString(attr); >- if (db_col_name.isEmpty()) >- return -1; >- >- int cfield = ChannelUtil::GetChannelValueInt( >- db_col_name, GetCurrentSourceID(), curchannelname); >- int sfield = CardUtil::GetValueInt( >- db_col_name, GetCardID()); >- int dfield = 0; >- >- if (pict_attr_default.find(db_col_name) != pict_attr_default.end()) >- dfield = pict_attr_default[db_col_name]; >- >- int val = (cfield + sfield + dfield) & 0xFFFF; >- >-#if DEBUG_ATTRIB >- VERBOSE(VB_CHANNEL, QString( >- "GetPictureAttribute(%1) -> cdb %2 rdb %3 d %4 -> %5") >- .arg(db_col_name).arg(cfield).arg(sfield) >- .arg(dfield).arg(val)); >-#endif >- >- return val; >-} >- >-static int get_v4l2_attribute_value(int videofd, int v4l2_attrib) >-{ >- struct v4l2_control ctrl; >- struct v4l2_queryctrl qctrl; >- bzero(&ctrl, sizeof(ctrl)); >- bzero(&qctrl, sizeof(qctrl)); >- >- ctrl.id = qctrl.id = v4l2_attrib; >- if (ioctl(videofd, VIDIOC_QUERYCTRL, &qctrl) < 0) >- { >- VERBOSE(VB_IMPORTANT, "get_v4l2_attribute_value: " >- "failed to query controls (1)" + ENO); >- return -1; >- } >- >- if (ioctl(videofd, VIDIOC_G_CTRL, &ctrl) < 0) >- { >- VERBOSE(VB_IMPORTANT, "get_v4l2_attribute_value: " >- "failed to get controls (2)" + ENO); >- return -1; >- } >- >- float mult = 65535.0 / (qctrl.maximum - qctrl.minimum); >- return min(max((int)(mult * (ctrl.value - qctrl.minimum)), 0), 65525); >-} >- >-static int get_v4l1_attribute_value(int videofd, int v4l2_attrib) >-{ >- struct video_picture vid_pic; >- bzero(&vid_pic, sizeof(vid_pic)); >- >- if (ioctl(videofd, VIDIOCGPICT, &vid_pic) < 0) >- { >- VERBOSE(VB_IMPORTANT, "get_v4l1_attribute_value: " >- "failed to get picture control (1)" + ENO); >- return -1; >- } >- >- unsigned short *setfield = get_v4l1_field(v4l2_attrib, vid_pic); >- if (setfield) >- return *setfield; >- >- return -1; >-} >- >-static int get_attribute_value(bool usingv4l2, int videofd, int v4l2_attrib) >-{ >- if (usingv4l2) >- return get_v4l2_attribute_value(videofd, v4l2_attrib); >- return get_v4l1_attribute_value(videofd, v4l2_attrib); >-} >- >-static int set_v4l2_attribute_value(int videofd, int v4l2_attrib, int newvalue) >-{ >- struct v4l2_control ctrl; >- struct v4l2_queryctrl qctrl; >- bzero(&ctrl, sizeof(ctrl)); >- bzero(&qctrl, sizeof(qctrl)); >- >- ctrl.id = qctrl.id = v4l2_attrib; >- if (ioctl(videofd, VIDIOC_QUERYCTRL, &qctrl) < 0) >- { >- VERBOSE(VB_IMPORTANT, "set_v4l2_attribute_value: " >- "failed to query control" + ENO); >- return -1; >- } >- >- float mult = (qctrl.maximum - qctrl.minimum) / 65535.0; >- ctrl.value = (int)(mult * newvalue + qctrl.minimum); >- ctrl.value = min(ctrl.value, qctrl.maximum); >- ctrl.value = max(ctrl.value, qctrl.minimum); >- >- if (ioctl(videofd, VIDIOC_S_CTRL, &ctrl) < 0) >- { >- VERBOSE(VB_IMPORTANT, "set_v4l2_attribute_value: " >- "failed to set control" + ENO); >- return -1; >- } >- >- return 0; >-} >- >-static int set_v4l1_attribute_value(int videofd, int v4l2_attrib, int newvalue) >-{ >- unsigned short *setfield; >- struct video_picture vid_pic; >- bzero(&vid_pic, sizeof(vid_pic)); >- >- if (ioctl(videofd, VIDIOCGPICT, &vid_pic) < 0) >- { >- VERBOSE(VB_IMPORTANT, "set_v4l1_attribute_value: " >- "failed to get picture control." + ENO); >- return -1; >- } >- setfield = get_v4l1_field(v4l2_attrib, vid_pic); >- if (newvalue != -1 && setfield) >- { >- *setfield = newvalue; >- if (ioctl(videofd, VIDIOCSPICT, &vid_pic) < 0) >- { >- VERBOSE(VB_IMPORTANT, "set_v4l1_attribute_value: " >- "failed to set picture control." + ENO); >- return -1; >- } >- } >- else >- { >- // ??? >- return -1; >- } >- >- return 0; >-} >- >-static int set_attribute_value(bool usingv4l2, int videofd, >- int v4l2_attrib, int newvalue) >-{ >- if (usingv4l2) >- return set_v4l2_attribute_value(videofd, v4l2_attrib, newvalue); >- return set_v4l1_attribute_value(videofd, v4l2_attrib, newvalue); >-} >- >-int Channel::ChangePictureAttribute( >- PictureAdjustType type, PictureAttribute attr, bool up) >-{ >- if (!pParent || is_dtv) >- return -1; >- >- QString db_col_name = toDBString(attr); >- if (db_col_name.isEmpty()) >- return -1; >- >- int v4l2_attrib = get_v4l2_attribute(db_col_name); >- if (v4l2_attrib == -1) >- return -1; >- >- // get the old attribute value from the hardware, this is >- // just a sanity check on whether this attribute exists >- if (get_attribute_value(usingv4l2, videofd, v4l2_attrib) < 0) >- return -1; >- >- int old_value = GetPictureAttribute(attr); >- int new_value = old_value + ((up) ? 655 : -655); >- >- // make sure we are within bounds (wrap around for hue) >- if (V4L2_CID_HUE == v4l2_attrib) >- new_value &= 0xffff; >- new_value = min(max(new_value, 0), 65535); >- >-#if DEBUG_ATTRIB >- VERBOSE(VB_CHANNEL, QString( >- "ChangePictureAttribute(%1,%2,%3) cur %4 -> new %5") >- .arg(type).arg(db_col_name).arg(up) >- .arg(old_value).arg(new_value)); >-#endif >- >- // actually set the new attribute value on the hardware >- if (set_attribute_value(usingv4l2, videofd, v4l2_attrib, new_value) < 0) >- return -1; >- >- // tell the DB about the new attribute value >- if (kAdjustingPicture_Channel == type) >- { >- int adj_value = ChannelUtil::GetChannelValueInt( >- db_col_name, GetCurrentSourceID(), curchannelname); >- >- int tmp = new_value - old_value + adj_value; >- tmp = (tmp < 0) ? tmp + 0x10000 : tmp; >- tmp = (tmp > 0xffff) ? tmp - 0x10000 : tmp; >- ChannelUtil::SetChannelValue(db_col_name, QString::number(tmp), >- GetCurrentSourceID(), curchannelname); >- } >- else if (kAdjustingPicture_Recording == type) >- { >- int adj_value = CardUtil::GetValueInt( >- db_col_name, GetCardID()); >- >- int tmp = new_value - old_value + adj_value; >- tmp = (tmp < 0) ? tmp + 0x10000 : tmp; >- tmp = (tmp > 0xffff) ? tmp - 0x10000 : tmp; >- CardUtil::SetValue(db_col_name, GetCardID(), >- GetCurrentSourceID(), tmp); >- } >- >- return new_value; >-}
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 259009
:
182053
|
182055
| 182056 |
182057
|
182059
|
182060