10 #include <sys/types.h>
18 #include <linux/videodev2.h>
31 #define DEBUG_ATTRIB 1
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.0 / (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.0;
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;