18#include <linux/videodev2.h>
33#define LOC QString("V4LChannel[%1](%2): ") \
34 .arg(m_inputId).arg(GetDevice())
62 QByteArray ascii_device =
m_device.toLatin1();
63 m_videoFd = open(ascii_device.constData(), O_RDWR);
66 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Can't open video device." +
ENO);
71 uint32_t capabilities = 0;
75 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to query capabilities." +
ENO);
81 m_hasStdIO = ((capabilities & V4L2_CAP_READWRITE) != 0U);
82 m_hasAsyncIO = ((capabilities & V4L2_CAP_ASYNCIO) != 0U);
83 m_hasTuner = ((capabilities & V4L2_CAP_TUNER) != 0U);
84 m_hasSlicedVbi = ((capabilities & V4L2_CAP_SLICED_VBI_CAPTURE) != 0U);
90 LOG(VB_CHANNEL, LOG_INFO,
LOC + QString(
"Device name '%1' driver '%2'.")
93 LOG(VB_CHANNEL, LOG_INFO,
LOC +
94 QString(
"v4l2: stream io: %2 std io: %3 async io: %4 "
95 "tuner %5 sliced vbi %6")
127 return V4L2_STD_PAL_BG;
129 return V4L2_STD_PAL_D;
131 return V4L2_STD_PAL_DK;
133 return V4L2_STD_PAL_I;
135 return V4L2_STD_PAL_60;
137 return V4L2_STD_SECAM;
138 if (fmt ==
"SECAM-D")
139 return V4L2_STD_SECAM_D;
140 if (fmt ==
"SECAM-DK")
141 return V4L2_STD_SECAM_DK;
143 return V4L2_STD_PAL_Nc;
145 return V4L2_STD_PAL_M;
147 return V4L2_STD_PAL_N;
148 if (fmt ==
"NTSC-JP")
149 return V4L2_STD_NTSC_M_JP;
151 if (fmt.startsWith(
"NTSC"))
152 return V4L2_STD_NTSC;
153 if (fmt.startsWith(
"ATSC"))
154 return V4L2_STD_NTSC;
155 if (fmt.startsWith(
"PAL"))
157 return V4L2_STD_NTSC;
162 if (mode == V4L2_STD_NTSC)
164 if (mode == V4L2_STD_NTSC_M_JP)
166 if (mode == V4L2_STD_PAL)
168 if (mode == V4L2_STD_PAL_60)
170 if (mode == V4L2_STD_PAL_BG)
172 if (mode == V4L2_STD_PAL_D)
174 if (mode == V4L2_STD_PAL_DK)
176 if (mode == V4L2_STD_PAL_I)
178 if (mode == V4L2_STD_PAL_M)
180 if (mode == V4L2_STD_PAL_N)
182 if (mode == V4L2_STD_PAL_Nc)
184 if (mode == V4L2_STD_SECAM)
186 if (mode == V4L2_STD_SECAM_D)
189 if ((V4L2_STD_NTSC_M == mode) ||
190 (V4L2_STD_NTSC_443 == mode) ||
191 (V4L2_STD_NTSC_M_KR == mode))
193 if ((V4L2_STD_PAL_B == mode) ||
194 (V4L2_STD_PAL_B1 == mode) ||
195 (V4L2_STD_PAL_G == mode) ||
196 (V4L2_STD_PAL_H == mode) ||
197 (V4L2_STD_PAL_D1 == mode) ||
198 (V4L2_STD_PAL_K == mode))
200 if ((V4L2_STD_SECAM_B == mode) ||
201 (V4L2_STD_SECAM_DK == mode) ||
202 (V4L2_STD_SECAM_G == mode) ||
203 (V4L2_STD_SECAM_H == mode) ||
204 (V4L2_STD_SECAM_K == mode) ||
205 (V4L2_STD_SECAM_K1 == mode) ||
206 (V4L2_STD_SECAM_L == mode) ||
207 (V4L2_STD_SECAM_LC == mode))
209 if ((V4L2_STD_ATSC == mode) ||
210 (V4L2_STD_ATSC_8_VSB == mode) ||
211 (V4L2_STD_ATSC_16_VSB == mode))
236 LOG(VB_CHANNEL, LOG_INFO, QString(
"Global TVFormat Setting '%1'").arg(fmt));
244 for (
auto v4l_it = v4l_inputs.cbegin(); v4l_it != v4l_inputs.cend(); ++v4l_it)
255 LOG(VB_CHANNEL, LOG_INFO,
LOC +
256 QString(
"Input #%1: '%2' schan(%3) tun(%4) v4l2(%6)")
261 return valid_cnt != 0U;
280 QString fmt = format;
281 if ((fmt ==
"Default") || format.isEmpty())
287 LOG(VB_CHANNEL, LOG_INFO,
LOC + QString(
"SetFormat(%1) fmt(%2) input(%3)")
288 .arg(format, fmt, QString::number(inputNum)));
291 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to set format." +
ENO);
308 const QString& name = tablename;
309 bool use_default = (name.toLower() ==
"default" || name.isEmpty());
312 for (
size_t i = 0; i <
gChanLists.size(); i++)
324 else if (name == listname)
331 LOG(VB_CHANNEL, LOG_ERR,
332 QString(
"Channel(%1)::SetFreqTable(): Invalid "
333 "frequency table name %2, using %3.").
341 for (
size_t i = 0; i <
m_curList.size(); i++)
347 LOG(VB_GENERAL, LOG_ERR,
LOC +
348 QString(
"GetCurrentChannelNum(%1): Failed to find Channel")
357 LOG(VB_CHANNEL, LOG_INFO,
358 QString(
"Channel(%1)::Tune(%2): curList[%3].freq(%4)")
359 .arg(
m_device, freqid, QString::number(i))
364 LOG(VB_GENERAL, LOG_ERR,
365 QString(
"Channel(%1)::Tune(%2): Error, failed to find channel.")
370 int frequency = (
m_curList[i].freq + finetune) * 1000;
372 return Tune(frequency);
391 LOG(VB_CHANNEL, LOG_INFO,
LOC + QString(
"Tune(%1)").arg(frequency));
395 uint mplexid_restriction = 0;
396 uint chanid_restriction = 0;
401 bool isTunerCapLow =
false;
402 struct v4l2_modulator mod {};
404 ioctlval = ioctl(
m_videoFd, VIDIOC_G_MODULATOR, &mod);
407 isTunerCapLow = ((mod.capability & V4L2_TUNER_CAP_LOW) != 0U);
408 LOG(VB_CHANNEL, LOG_INFO,
409 QString(
" name: %1").arg((
char *)mod.name));
410 LOG(VB_CHANNEL, LOG_INFO, QString(
"CapLow: %1").arg(isTunerCapLow));
413 struct v4l2_frequency vf {};
415 vf.frequency = (isTunerCapLow) ?
416 ((
int)(frequency / 62.5)) : (frequency / 62500);
418 vf.type = V4L2_TUNER_ANALOG_TV;
420 ioctlval = ioctl(
m_videoFd, VIDIOC_S_FREQUENCY, &vf);
423 LOG(VB_GENERAL, LOG_ERR,
424 QString(
"Channel(%1)::Tune(): Error %2 "
425 "while setting frequency (v2): %3")
426 .arg(
m_device).arg(ioctlval).arg(strerror(errno)));
429 ioctlval = ioctl(
m_videoFd, VIDIOC_G_FREQUENCY, &vf);
433 LOG(VB_CHANNEL, LOG_INFO,
434 QString(
"Channel(%1)::Tune(): Frequency is now %2")
435 .arg(
m_device).arg(vf.frequency * 62500));
448 struct v4l2_frequency vf {};
450 vf.type = V4L2_TUNER_ANALOG_TV;
453 int ioctlval = ioctl(
m_videoFd, VIDIOC_G_FREQUENCY, &vf);
456 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Retune failed (1)" +
ENO);
461 ioctlval = ioctl(
m_videoFd, VIDIOC_S_FREQUENCY, &vf);
464 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Retune failed (2)" +
ENO);
476 "FROM channel, capturecard "
477 "WHERE deleted IS NULL AND "
478 " channum = :CHANNUM AND "
479 " inputname = :INPUTNAME AND "
480 " capturecard.cardid = :INPUTID AND "
481 " capturecard.sourceid = channel.sourceid");
483 query.
bindValue(
":INPUTNAME", inputname);
489 else if (query.
next())
490 fmt = query.
value(0).toString();
503 QString(
"SetInputAndFormat(%1, %2) ").arg(inputNum).arg(newFmt);
505 struct v4l2_input input {};
506 int ioctlval = ioctl(
m_videoFd, VIDIOC_G_INPUT, &input);
507 bool input_switch = (0 != ioctlval || (
uint)inputNumV4L != input.index);
510 v4l2_std_id cur_vid_mode = 0;
511 ioctlval = ioctl(
m_videoFd, VIDIOC_G_STD, &cur_vid_mode);
512 bool mode_switch = (0 != ioctlval || new_vid_mode != cur_vid_mode);
513 bool needs_switch = input_switch || mode_switch;
515 LOG(VB_GENERAL, LOG_INFO,
LOC + msg +
"(v4l v2) " +
516 QString(
"input_switch: %1 mode_switch: %2")
517 .arg(input_switch).arg(mode_switch));
521 bool streamingDisabled =
false;
522 int streamType = V4L2_BUF_TYPE_VIDEO_CAPTURE;
525 ioctlval = ioctl(
m_videoFd, VIDIOC_STREAMOFF, &streamType);
528 LOG(VB_GENERAL, LOG_ERR,
LOC + msg +
529 "\n\t\t\twhile disabling streaming (v4l v2)" +
ENO);
534 streamingDisabled =
true;
541 ioctlval = ioctl(
m_videoFd, VIDIOC_S_INPUT, &inputNumV4L);
544 LOG(VB_GENERAL, LOG_ERR,
LOC + msg +
545 "\n\t\t\twhile setting input (v4l v2)" +
ENO);
553 ioctlval = ioctl(
m_videoFd, VIDIOC_S_STD, &new_vid_mode);
556 LOG(VB_GENERAL, LOG_ERR,
LOC + msg +
557 "\n\t\t\twhile setting format (v4l v2)" +
ENO);
563 if (streamingDisabled)
565 ioctlval = ioctl(
m_videoFd, VIDIOC_STREAMON, &streamType);
568 LOG(VB_GENERAL, LOG_ERR,
LOC + msg +
569 "\n\t\t\twhile reenabling streaming (v4l v2)" +
ENO);
580 if (
"brightness" == db_col_name)
581 return V4L2_CID_BRIGHTNESS;
582 if (
"contrast" == db_col_name)
583 return V4L2_CID_CONTRAST;
584 if (
"colour" == db_col_name)
585 return V4L2_CID_SATURATION;
586 if (
"hue" == db_col_name)
597 if (v4l2_attrib == -1)
605 if ((cfield == -1) || (sfield == -1))
609 QString(
"InitPictureAttribute(%1): ").arg(db_col_name, 10);
611 struct v4l2_control ctrl {};
612 struct v4l2_queryctrl qctrl {};
614 ctrl.id = qctrl.id = v4l2_attrib;
615 if (ioctl(
m_videoFd, VIDIOC_QUERYCTRL, &qctrl) < 0)
617 LOG(VB_GENERAL, LOG_ERR, loc +
"failed to query controls." +
ENO);
621 float new_range = qctrl.maximum - qctrl.minimum;
622 float old_range = 65535 - 0;
623 float scl_range = new_range / old_range;
624 float dfl = (qctrl.default_value - qctrl.minimum) / new_range;
625 int norm_dfl = (0x10000 + (int)(dfl * old_range) - 32768) & 0xFFFF;
643 int field = (cfield + sfield + dfield) & 0xFFFF;
644 int value0 = (int) ((scl_range * field) + qctrl.minimum);
645 int value1 = std::min(value0, qctrl.maximum);
646 ctrl.value = std::max(value1, qctrl.minimum);
649 LOG(VB_CHANNEL, LOG_DEBUG, loc + QString(
" %1\n\t\t\t"
650 "[%2,%3] dflt(%4, %5, %6)")
651 .arg(value0).arg(qctrl.minimum, 5).arg(qctrl.maximum, 5)
652 .arg(qctrl.default_value, 5).arg(dfl, 4,
'f', 2)
656 if (ioctl(
m_videoFd, VIDIOC_S_CTRL, &ctrl) < 0)
658 LOG(VB_GENERAL, LOG_ERR, loc +
"failed to set controls" +
ENO);
676 if (db_col_name.isEmpty())
688 int val = (cfield + sfield + dfield) & 0xFFFF;
691 LOG(VB_CHANNEL, LOG_DEBUG,
692 QString(
"GetPictureAttribute(%1) -> cdb %2 rdb %3 d %4 -> %5")
693 .arg(db_col_name).arg(cfield).arg(sfield)
694 .arg(dfield).arg(val));
702 struct v4l2_control ctrl {};
703 struct v4l2_queryctrl qctrl {};
705 ctrl.id = qctrl.id = v4l2_attrib;
706 if (ioctl(videofd, VIDIOC_QUERYCTRL, &qctrl) < 0)
708 LOG(VB_GENERAL, LOG_ERR,
709 "get_v4l2_attribute_value: failed to query controls (1)" +
ENO);
713 if (ioctl(videofd, VIDIOC_G_CTRL, &ctrl) < 0)
715 LOG(VB_GENERAL, LOG_ERR,
716 "get_v4l2_attribute_value: failed to get controls (2)" +
ENO);
720 float mult = 65535.0F / (qctrl.maximum - qctrl.minimum);
721 return std::clamp((
int)(mult * (ctrl.value - qctrl.minimum)), 0, 65525);
726 struct v4l2_control ctrl {};
727 struct v4l2_queryctrl qctrl {};
729 ctrl.id = qctrl.id = v4l2_attrib;
730 if (ioctl(videofd, VIDIOC_QUERYCTRL, &qctrl) < 0)
732 LOG(VB_GENERAL, LOG_ERR,
733 "set_v4l2_attribute_value: failed to query control" +
ENO);
737 float mult = (qctrl.maximum - qctrl.minimum) / 65535.0F;
738 ctrl.value = (int)((mult * newvalue) + qctrl.minimum);
739 ctrl.value = std::min(ctrl.value, qctrl.maximum);
740 ctrl.value = std::max(ctrl.value, qctrl.minimum);
742 if (ioctl(videofd, VIDIOC_S_CTRL, &ctrl) < 0)
744 LOG(VB_GENERAL, LOG_ERR,
745 "set_v4l2_attribute_value: failed to set control" +
ENO);
759 if (db_col_name.isEmpty())
763 if (v4l2_attrib == -1)
772 int new_value = old_value + ((up) ? 655 : -655);
775 if (V4L2_CID_HUE == v4l2_attrib)
780 LOG(VB_CHANNEL, LOG_DEBUG,
781 QString(
"ChangePictureAttribute(%1,%2,%3) cur %4 -> new %5")
782 .arg(
type).arg(db_col_name).arg(up)
783 .arg(old_value).arg(new_value));
796 int tmp = new_value - old_value + adj_value;
807 int tmp = new_value - old_value + adj_value;
QMap< int, QString > InputNames
static InputNames ProbeV4LVideoInputs(int videofd, bool &ok)
static bool SetValue(const QString &col, uint inputid, int val)
static int GetValueInt(const QString &col, uint inputid)
static bool GetV4LInfo(int videofd, QString &input, QString &driver, uint32_t &version, uint32_t &capabilities)
virtual uint GetSourceID(void) const
virtual bool IsInputAvailable(uint &mplexid_restriction, uint &chanid_restriction) const
Switches to another input on hardware, and sets the channel is setstarting is true.
virtual bool Init(QString &startchannel, bool setchan)
virtual bool InitializeInput(void)
Fills in input map from DB.
static bool SetChannelValue(const QString &field_name, const QString &value, uint sourceid, const QString &channum)
static int GetChannelValueInt(const QString &channel_field, uint sourceid, const QString &channum)
QSqlQuery wrapper that fetches a DB connection from the connection pool.
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
QVariant value(int i) const
bool isActive(void) const
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
QString GetSetting(const QString &key, const QString &defaultval="")
static void DBError(const QString &where, const MSqlQuery &query)
int GetCurrentChannelNum(const QString &channame)
~V4LChannel(void) override
bool SetInputAndFormat(int inputNum, const QString &newFmt)
int ChangePictureAttribute(PictureAdjustType type, PictureAttribute attr, bool up) override
int GetPictureAttribute(PictureAttribute attr) const override
QString GetFormatForChannel(const QString &channum, const QString &inputname)
QMap< QString, int > m_pictAttrDefault
bool InitializeInputs(void)
This enumerates the inputs, converts the format string to something the hardware understands,...
int SetDefaultFreqTable(const QString &name)
bool InitPictureAttribute(const QString &db_col_name)
void Close(void) override
Closes the channel changing hardware to use.
bool Retune(void) override
Retunes to last tuned frequency.
bool InitPictureAttributes(void) override
bool Open(void) override
Opens the channel changing hardware for use.
bool Init(QString &startchannel, bool setchan) override
void SetFreqTable(int index)
void SetFormat(const QString &format) override
Initializes tuner and modulator variables.
void SetFd(int fd) override
Sets file descriptor.
bool Tune(const DTVMultiplex &tuning) override
This performs the actual frequency tuning and in some cases input switching.
static const std::array< const uint32_t, 4 > freq
const CHANLISTS_vec gChanLists
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define ENO
This can be appended to the LOG args with "+".
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
static eu8 clamp(eu8 value, eu8 low, eu8 high)
@ kAdjustingPicture_Recording
@ kAdjustingPicture_Channel
static QString mode_to_format(int mode)
static int get_v4l2_attribute_value(int videofd, int v4l2_attrib)
static int set_v4l2_attribute_value(int videofd, int v4l2_attrib, int newvalue)
static int get_v4l2_attribute(const QString &db_col_name)
static int format_to_mode(const QString &fmt)
QString toDBString(PrimariesMode Mode)