10 #include <sys/types.h>
18 #include <linux/videodev2.h>
29 #define DEBUG_ATTRIB 1
31 #define LOC QString("V4LChannel[%1](%2): ") \
32 .arg(m_inputId).arg(GetDevice())
60 QByteArray ascii_device =
m_device.toLatin1();
61 m_videoFd = open(ascii_device.constData(), O_RDWR);
64 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Can't open video device." +
ENO);
69 uint32_t capabilities = 0;
73 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to query capabilities." +
ENO);
79 m_hasStdIO = ((capabilities & V4L2_CAP_READWRITE) != 0U);
80 m_hasAsyncIO = ((capabilities & V4L2_CAP_ASYNCIO) != 0U);
81 m_hasTuner = ((capabilities & V4L2_CAP_TUNER) != 0U);
82 m_hasSlicedVbi = ((capabilities & V4L2_CAP_SLICED_VBI_CAPTURE) != 0U);
88 LOG(VB_CHANNEL, LOG_INFO,
LOC + QString(
"Device name '%1' driver '%2'.")
91 LOG(VB_CHANNEL, LOG_INFO,
LOC +
92 QString(
"v4l2: stream io: %2 std io: %3 async io: %4 "
93 "tuner %5 sliced vbi %6")
125 return V4L2_STD_PAL_BG;
127 return V4L2_STD_PAL_D;
129 return V4L2_STD_PAL_DK;
131 return V4L2_STD_PAL_I;
133 return V4L2_STD_PAL_60;
135 return V4L2_STD_SECAM;
136 if (fmt ==
"SECAM-D")
137 return V4L2_STD_SECAM_D;
138 if (fmt ==
"SECAM-DK")
139 return V4L2_STD_SECAM_DK;
141 return V4L2_STD_PAL_Nc;
143 return V4L2_STD_PAL_M;
145 return V4L2_STD_PAL_N;
146 if (fmt ==
"NTSC-JP")
147 return V4L2_STD_NTSC_M_JP;
149 if (fmt.startsWith(
"NTSC"))
150 return V4L2_STD_NTSC;
151 if (fmt.startsWith(
"ATSC"))
152 return V4L2_STD_NTSC;
153 if (fmt.startsWith(
"PAL"))
155 return V4L2_STD_NTSC;
160 if (mode == V4L2_STD_NTSC)
162 if (mode == V4L2_STD_NTSC_M_JP)
164 if (mode == V4L2_STD_PAL)
166 if (mode == V4L2_STD_PAL_60)
168 if (mode == V4L2_STD_PAL_BG)
170 if (mode == V4L2_STD_PAL_D)
172 if (mode == V4L2_STD_PAL_DK)
174 if (mode == V4L2_STD_PAL_I)
176 if (mode == V4L2_STD_PAL_M)
178 if (mode == V4L2_STD_PAL_N)
180 if (mode == V4L2_STD_PAL_Nc)
182 if (mode == V4L2_STD_SECAM)
184 if (mode == V4L2_STD_SECAM_D)
187 if ((V4L2_STD_NTSC_M == mode) ||
188 (V4L2_STD_NTSC_443 == mode) ||
189 (V4L2_STD_NTSC_M_KR == mode))
191 if ((V4L2_STD_PAL_B == mode) ||
192 (V4L2_STD_PAL_B1 == mode) ||
193 (V4L2_STD_PAL_G == mode) ||
194 (V4L2_STD_PAL_H == mode) ||
195 (V4L2_STD_PAL_D1 == mode) ||
196 (V4L2_STD_PAL_K == mode))
198 if ((V4L2_STD_SECAM_B == mode) ||
199 (V4L2_STD_SECAM_DK == mode) ||
200 (V4L2_STD_SECAM_G == mode) ||
201 (V4L2_STD_SECAM_H == mode) ||
202 (V4L2_STD_SECAM_K == mode) ||
203 (V4L2_STD_SECAM_K1 == mode) ||
204 (V4L2_STD_SECAM_L == mode) ||
205 (V4L2_STD_SECAM_LC == mode))
207 if ((V4L2_STD_ATSC == mode) ||
208 (V4L2_STD_ATSC_8_VSB == mode) ||
209 (V4L2_STD_ATSC_16_VSB == mode))
234 LOG(VB_CHANNEL, LOG_INFO, QString(
"Global TVFormat Setting '%1'").arg(fmt));
242 for (
auto v4l_it = v4l_inputs.cbegin(); v4l_it != v4l_inputs.cend(); ++v4l_it)
253 LOG(VB_CHANNEL, LOG_INFO,
LOC +
254 QString(
"Input #%1: '%2' schan(%3) tun(%4) v4l2(%6)")
259 return valid_cnt != 0U;
278 QString fmt = format;
279 if ((fmt ==
"Default") || format.isEmpty())
285 LOG(VB_CHANNEL, LOG_INFO,
LOC + QString(
"SetFormat(%1) fmt(%2) input(%3)")
286 .arg(format, fmt, QString::number(inputNum)));
289 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to set format." +
ENO);
306 const QString& name = tablename;
307 bool use_default = (name.toLower() ==
"default" || name.isEmpty());
310 for (
size_t i = 0; i <
gChanLists.size(); i++)
322 else if (name == listname)
329 LOG(VB_CHANNEL, LOG_ERR,
330 QString(
"Channel(%1)::SetFreqTable(): Invalid "
331 "frequency table name %2, using %3.").
339 for (
size_t i = 0; i <
m_curList.size(); i++)
345 LOG(VB_GENERAL, LOG_ERR,
LOC +
346 QString(
"GetCurrentChannelNum(%1): Failed to find Channel")
355 LOG(VB_CHANNEL, LOG_INFO,
356 QString(
"Channel(%1)::Tune(%2): curList[%3].freq(%4)")
357 .arg(
m_device, freqid, QString::number(i))
362 LOG(VB_GENERAL, LOG_ERR,
363 QString(
"Channel(%1)::Tune(%2): Error, failed to find channel.")
368 int frequency = (
m_curList[i].freq + finetune) * 1000;
370 return Tune(frequency);
389 LOG(VB_CHANNEL, LOG_INFO,
LOC + QString(
"Tune(%1)").arg(frequency));
393 uint mplexid_restriction = 0;
394 uint chanid_restriction = 0;
399 bool isTunerCapLow =
false;
400 struct v4l2_modulator mod {};
402 ioctlval = ioctl(
m_videoFd, VIDIOC_G_MODULATOR, &mod);
405 isTunerCapLow = ((mod.capability & V4L2_TUNER_CAP_LOW) != 0U);
406 LOG(VB_CHANNEL, LOG_INFO,
407 QString(
" name: %1").arg((
char *)mod.name));
408 LOG(VB_CHANNEL, LOG_INFO, QString(
"CapLow: %1").arg(isTunerCapLow));
411 struct v4l2_frequency vf {};
413 vf.frequency = (isTunerCapLow) ?
414 ((
int)(frequency / 62.5)) : (frequency / 62500);
416 vf.type = V4L2_TUNER_ANALOG_TV;
418 ioctlval = ioctl(
m_videoFd, VIDIOC_S_FREQUENCY, &vf);
421 LOG(VB_GENERAL, LOG_ERR,
422 QString(
"Channel(%1)::Tune(): Error %2 "
423 "while setting frequency (v2): %3")
424 .arg(
m_device).arg(ioctlval).arg(strerror(errno)));
427 ioctlval = ioctl(
m_videoFd, VIDIOC_G_FREQUENCY, &vf);
431 LOG(VB_CHANNEL, LOG_INFO,
432 QString(
"Channel(%1)::Tune(): Frequency is now %2")
433 .arg(
m_device).arg(vf.frequency * 62500));
446 struct v4l2_frequency vf {};
448 vf.type = V4L2_TUNER_ANALOG_TV;
451 int ioctlval = ioctl(
m_videoFd, VIDIOC_G_FREQUENCY, &vf);
454 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Retune failed (1)" +
ENO);
459 ioctlval = ioctl(
m_videoFd, VIDIOC_S_FREQUENCY, &vf);
462 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Retune failed (2)" +
ENO);
474 "FROM channel, capturecard "
475 "WHERE deleted IS NULL AND "
476 " channum = :CHANNUM AND "
477 " inputname = :INPUTNAME AND "
478 " capturecard.cardid = :INPUTID AND "
479 " capturecard.sourceid = channel.sourceid");
481 query.
bindValue(
":INPUTNAME", inputname);
487 else if (query.
next())
488 fmt = query.
value(0).toString();
501 QString(
"SetInputAndFormat(%1, %2) ").arg(inputNum).arg(newFmt);
503 struct v4l2_input input {};
504 int ioctlval = ioctl(
m_videoFd, VIDIOC_G_INPUT, &input);
505 bool input_switch = (0 != ioctlval || (
uint)inputNumV4L != input.index);
508 v4l2_std_id cur_vid_mode = 0;
509 ioctlval = ioctl(
m_videoFd, VIDIOC_G_STD, &cur_vid_mode);
510 bool mode_switch = (0 != ioctlval || new_vid_mode != cur_vid_mode);
511 bool needs_switch = input_switch || mode_switch;
513 LOG(VB_GENERAL, LOG_INFO,
LOC + msg +
"(v4l v2) " +
514 QString(
"input_switch: %1 mode_switch: %2")
515 .arg(input_switch).arg(mode_switch));
519 bool streamingDisabled =
false;
520 int streamType = V4L2_BUF_TYPE_VIDEO_CAPTURE;
523 ioctlval = ioctl(
m_videoFd, VIDIOC_STREAMOFF, &streamType);
526 LOG(VB_GENERAL, LOG_ERR,
LOC + msg +
527 "\n\t\t\twhile disabling streaming (v4l v2)" +
ENO);
532 streamingDisabled =
true;
539 ioctlval = ioctl(
m_videoFd, VIDIOC_S_INPUT, &inputNumV4L);
542 LOG(VB_GENERAL, LOG_ERR,
LOC + msg +
543 "\n\t\t\twhile setting input (v4l v2)" +
ENO);
551 ioctlval = ioctl(
m_videoFd, VIDIOC_S_STD, &new_vid_mode);
554 LOG(VB_GENERAL, LOG_ERR,
LOC + msg +
555 "\n\t\t\twhile setting format (v4l v2)" +
ENO);
561 if (streamingDisabled)
563 ioctlval = ioctl(
m_videoFd, VIDIOC_STREAMON, &streamType);
566 LOG(VB_GENERAL, LOG_ERR,
LOC + msg +
567 "\n\t\t\twhile reenabling streaming (v4l v2)" +
ENO);
578 if (
"brightness" == db_col_name)
579 return V4L2_CID_BRIGHTNESS;
580 if (
"contrast" == db_col_name)
581 return V4L2_CID_CONTRAST;
582 if (
"colour" == db_col_name)
583 return V4L2_CID_SATURATION;
584 if (
"hue" == db_col_name)
595 if (v4l2_attrib == -1)
603 if ((cfield == -1) || (sfield == -1))
607 QString(
"InitPictureAttribute(%1): ").arg(db_col_name, 10);
609 struct v4l2_control ctrl {};
610 struct v4l2_queryctrl qctrl {};
612 ctrl.id = qctrl.id = v4l2_attrib;
613 if (ioctl(
m_videoFd, VIDIOC_QUERYCTRL, &qctrl) < 0)
615 LOG(VB_GENERAL, LOG_ERR, loc +
"failed to query controls." +
ENO);
619 float new_range = qctrl.maximum - qctrl.minimum;
620 float old_range = 65535 - 0;
621 float scl_range = new_range / old_range;
622 float dfl = (qctrl.default_value - qctrl.minimum) / new_range;
623 int norm_dfl = (0x10000 + (int)(dfl * old_range) - 32768) & 0xFFFF;
641 int field = (cfield + sfield + dfield) & 0xFFFF;
642 int value0 = (int) ((scl_range * field) + qctrl.minimum);
643 int value1 = std::min(value0, qctrl.maximum);
644 ctrl.value = std::max(value1, qctrl.minimum);
647 LOG(VB_CHANNEL, LOG_DEBUG, loc + QString(
" %1\n\t\t\t"
648 "[%2,%3] dflt(%4, %5, %6)")
649 .arg(value0).arg(qctrl.minimum, 5).arg(qctrl.maximum, 5)
650 .arg(qctrl.default_value, 5).arg(dfl, 4,
'f', 2)
654 if (ioctl(
m_videoFd, VIDIOC_S_CTRL, &ctrl) < 0)
656 LOG(VB_GENERAL, LOG_ERR, loc +
"failed to set controls" +
ENO);
674 if (db_col_name.isEmpty())
686 int val = (cfield + sfield + dfield) & 0xFFFF;
689 LOG(VB_CHANNEL, LOG_DEBUG,
690 QString(
"GetPictureAttribute(%1) -> cdb %2 rdb %3 d %4 -> %5")
691 .arg(db_col_name).arg(cfield).arg(sfield)
692 .arg(dfield).arg(val));
700 struct v4l2_control ctrl {};
701 struct v4l2_queryctrl qctrl {};
703 ctrl.id = qctrl.id = v4l2_attrib;
704 if (ioctl(videofd, VIDIOC_QUERYCTRL, &qctrl) < 0)
706 LOG(VB_GENERAL, LOG_ERR,
707 "get_v4l2_attribute_value: failed to query controls (1)" +
ENO);
711 if (ioctl(videofd, VIDIOC_G_CTRL, &ctrl) < 0)
713 LOG(VB_GENERAL, LOG_ERR,
714 "get_v4l2_attribute_value: failed to get controls (2)" +
ENO);
718 float mult = 65535.0 / (qctrl.maximum - qctrl.minimum);
719 return std::clamp((
int)(mult * (ctrl.value - qctrl.minimum)), 0, 65525);
724 struct v4l2_control ctrl {};
725 struct v4l2_queryctrl qctrl {};
727 ctrl.id = qctrl.id = v4l2_attrib;
728 if (ioctl(videofd, VIDIOC_QUERYCTRL, &qctrl) < 0)
730 LOG(VB_GENERAL, LOG_ERR,
731 "set_v4l2_attribute_value: failed to query control" +
ENO);
735 float mult = (qctrl.maximum - qctrl.minimum) / 65535.0;
736 ctrl.value = (int)((mult * newvalue) + qctrl.minimum);
737 ctrl.value = std::min(ctrl.value, qctrl.maximum);
738 ctrl.value = std::max(ctrl.value, qctrl.minimum);
740 if (ioctl(videofd, VIDIOC_S_CTRL, &ctrl) < 0)
742 LOG(VB_GENERAL, LOG_ERR,
743 "set_v4l2_attribute_value: failed to set control" +
ENO);
757 if (db_col_name.isEmpty())
761 if (v4l2_attrib == -1)
770 int new_value = old_value + ((up) ? 655 : -655);
773 if (V4L2_CID_HUE == v4l2_attrib)
778 LOG(VB_CHANNEL, LOG_DEBUG,
779 QString(
"ChangePictureAttribute(%1,%2,%3) cur %4 -> new %5")
780 .arg(
type).arg(db_col_name).arg(up)
781 .arg(old_value).arg(new_value));
794 int tmp = new_value - old_value + adj_value;
805 int tmp = new_value - old_value + adj_value;