This is an automatic generated email to let you know that the following patch were queued at the http://git.linuxtv.org/v4l-utils.git tree:
Subject: qv4l2: add raw VBI support. Author: Hans Verkuil <[email protected]> Date: Fri Jul 20 12:47:56 2012 +0200 Note: the slicing code was copied from zvbi. Eventually I intend to move the slicing code to a library. It effectively transforms raw VBI into sliced VBI, so that should make it very easy for applications to handle both raw and sliced VBI. Signed-off-by: Hans Verkuil <[email protected]> (cherry picked from commit c7b253738d45d8f7bebeace186fc733bc700879d) Signed-off-by: Gregor Jasny <[email protected]> utils/qv4l2/.gitignore | 1 + utils/qv4l2/general-tab.h | 1 - utils/qv4l2/qv4l2.cpp | 20 ++ utils/qv4l2/qv4l2.h | 4 + utils/qv4l2/qv4l2.pro | 4 +- utils/qv4l2/raw2sliced.cpp | 474 ++++++++++++++++++++++++++++++++++++++++++++ utils/qv4l2/raw2sliced.h | 75 +++++++ utils/qv4l2/vbi-tab.cpp | 367 ++++++++++++++++++++++++++++++++++ utils/qv4l2/vbi-tab.h | 54 +++++ 9 files changed, 997 insertions(+), 3 deletions(-) --- http://git.linuxtv.org/v4l-utils.git?a=commitdiff;h=4ca8436f31b03370bcd150f3c55db445b2f5ceb1 diff --git a/utils/qv4l2/.gitignore b/utils/qv4l2/.gitignore index 502d6fe..e7c7867 100644 --- a/utils/qv4l2/.gitignore +++ b/utils/qv4l2/.gitignore @@ -2,6 +2,7 @@ Makefile moc_capture-win.cpp moc_general-tab.cpp moc_qv4l2.cpp +moc_vbi-tab.cpp qrc_qv4l2.cpp qv4l2 diff --git a/utils/qv4l2/general-tab.h b/utils/qv4l2/general-tab.h index 8e4af75..e2681f4 100644 --- a/utils/qv4l2/general-tab.h +++ b/utils/qv4l2/general-tab.h @@ -166,7 +166,6 @@ private: QComboBox *m_frameInterval; QComboBox *m_vidOutFormats; QComboBox *m_capMethods; - QGridLayout *m_layout; }; #endif diff --git a/utils/qv4l2/qv4l2.cpp b/utils/qv4l2/qv4l2.cpp index 749e421..087f782 100644 --- a/utils/qv4l2/qv4l2.cpp +++ b/utils/qv4l2/qv4l2.cpp @@ -19,6 +19,7 @@ #include "qv4l2.h" #include "general-tab.h" +#include "vbi-tab.h" #include "capture-win.h" #include <QToolBar> @@ -146,6 +147,11 @@ void ApplicationWindow::setDevice(const QString &device, bool rawOpen) m_genTab = new GeneralTab(device, *this, 4, w); m_tabs->addTab(w, "General"); addTabs(); + if (caps() & (V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE)) { + w = new QWidget(m_tabs); + m_vbiTab = new VbiTab(w); + m_tabs->addTab(w, "VBI"); + } if (QWidget *current = m_tabs->currentWidget()) { current->show(); } @@ -239,9 +245,16 @@ void ApplicationWindow::capVbiFrame() } } + struct v4l2_sliced_vbi_format sfmt; + struct v4l2_sliced_vbi_data sdata[m_vbiHandle.count[0] + m_vbiHandle.count[1]]; + + vbi_parse(&m_vbiHandle, data, &sfmt, sdata); + if (m_capMethod != methodRead) qbuf(buf); + m_vbiTab->slicedData(sdata); + QString status, curStatus; struct timeval tv, res; @@ -572,12 +585,19 @@ void ApplicationWindow::capStart(bool start) if (m_genTab->isVbi()) { v4l2_format fmt; + v4l2_std_id std; g_fmt_vbi(fmt); if (fmt.fmt.vbi.sample_format != V4L2_PIX_FMT_GREY) { error("non-grey pixelformat not supported for VBI\n"); return; } + g_std(std); + if (!vbi_prepare(&m_vbiHandle, &fmt.fmt.vbi, std)) { + error("no services possible\n"); + return; + } + m_vbiTab->rawFormat(fmt.fmt.vbi); m_vbiWidth = fmt.fmt.vbi.samples_per_line; if (fmt.fmt.vbi.flags & V4L2_VBI_INTERLACED) m_vbiHeight = fmt.fmt.vbi.count[0]; diff --git a/utils/qv4l2/qv4l2.h b/utils/qv4l2/qv4l2.h index 6ec251a..758e6f4 100644 --- a/utils/qv4l2/qv4l2.h +++ b/utils/qv4l2/qv4l2.h @@ -31,10 +31,12 @@ #include <vector> #include "v4l2-api.h" +#include "raw2sliced.h" class QComboBox; class QSpinBox; class GeneralTab; +class VbiTab; class QCloseEvent; class CaptureWin; @@ -147,6 +149,7 @@ private: void updateFreqChannel(); GeneralTab *m_genTab; + VbiTab *m_vbiTab; QAction *m_capStartAct; QAction *m_showFramesAct; QString m_filename; @@ -163,6 +166,7 @@ private: int m_vbiSize; unsigned m_vbiWidth; unsigned m_vbiHeight; + struct vbi_handle m_vbiHandle; unsigned m_frame; unsigned m_lastFrame; unsigned m_fps; diff --git a/utils/qv4l2/qv4l2.pro b/utils/qv4l2/qv4l2.pro index 87cf097..144d875 100644 --- a/utils/qv4l2/qv4l2.pro +++ b/utils/qv4l2/qv4l2.pro @@ -7,8 +7,8 @@ INCLUDEPATH += . ../libv4l2util ../../lib/include ../../include CONFIG += debug # Input -HEADERS += qv4l2.h general-tab.h v4l2-api.h capture-win.h -SOURCES += qv4l2.cpp general-tab.cpp ctrl-tab.cpp v4l2-api.cpp capture-win.cpp +HEADERS += qv4l2.h general-tab.h v4l2-api.h capture-win.h vbi-tab.h raw2sliced.h +SOURCES += qv4l2.cpp general-tab.cpp ctrl-tab.cpp v4l2-api.cpp capture-win.cpp vbi-tab.cpp raw2sliced.cpp LIBS += -L../../lib/libv4l2 -lv4l2 -L../../lib/libv4lconvert -lv4lconvert -lrt -L../libv4l2util -lv4l2util RESOURCES += qv4l2.qrc diff --git a/utils/qv4l2/raw2sliced.cpp b/utils/qv4l2/raw2sliced.cpp new file mode 100644 index 0000000..6edbc04 --- /dev/null +++ b/utils/qv4l2/raw2sliced.cpp @@ -0,0 +1,474 @@ +#include <stdlib.h> +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include <string.h> +#include <assert.h> +#include <unistd.h> +#include <sys/fcntl.h> + +#include "raw2sliced.h" + +/* + * The slicing code was copied from libzvbi. The original copyright notice is: + * + * Copyright (C) 2000-2004 Michael H. Schimek + * + * The vbi_prepare/vbi_parse functions are: + * + * Copyright (C) 2012 Hans Verkuil <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + */ + +// Modulation used for VBI data transmission. +enum vbi_modulation { + /* + * The data is 'non-return to zero' coded, logical '1' bits + * are described by high sample values, logical '0' bits by + * low values. The data is last significant bit first transmitted. + */ + VBI_MODULATION_NRZ_LSB, + /* + * The data is 'bi-phase' coded. Each data bit is described + * by two complementary signalling elements, a logical '1' + * by a sequence of '10' elements, a logical '0' by a '01' + * sequence. The data is last significant bit first transmitted. + */ + VBI_MODULATION_BIPHASE_LSB, + /* + * 'Bi-phase' coded, most significant bit first transmitted. + */ + VBI_MODULATION_BIPHASE_MSB +}; + +// Service definition struct +struct service { + __u16 service; + v4l2_std_id std; + /* + * Most scan lines used by the data service, first and last + * line of first and second field. ITU-R numbering scheme. + * Zero if no data from this field, requires field sync. + */ + int first[2]; + int last[2]; + + /* + * Leading edge hsync to leading edge first CRI one bit, + * half amplitude points, in nanoseconds. + */ + unsigned int offset; + + unsigned int cri_rate; /* Hz */ + unsigned int bit_rate; /* Hz */ + + /* Clock Run In and FRaming Code, LSB last txed bit of FRC. */ + unsigned int cri_frc; + + /* CRI and FRC bits significant for identification. */ + unsigned int cri_frc_mask; + + /* + * Number of significat cri_bits (at cri_rate), + * frc_bits (at bit_rate). + */ + unsigned int cri_bits; + unsigned int frc_bits; + + unsigned int payload; /* bits */ + enum vbi_modulation modulation; +}; + +// Supported services +static const struct service services[] = { + { + V4L2_SLICED_TELETEXT_B, + V4L2_STD_625_50, + { 6, 318 }, + { 22, 335 }, + 10300, 6937500, 6937500, /* 444 x FH */ + 0x00AAAAE4, 0xFFFF, 18, 6, 42 * 8, + VBI_MODULATION_NRZ_LSB, + }, { + V4L2_SLICED_VPS, + V4L2_STD_PAL_BG, + { 16, 0 }, + { 16, 0 }, + 12500, 5000000, 2500000, /* 160 x FH */ + 0xAAAA8A99, 0xFFFFFF, 32, 0, 13 * 8, + VBI_MODULATION_BIPHASE_MSB, + }, { + V4L2_SLICED_WSS_625, + V4L2_STD_625_50, + { 23, 0 }, + { 23, 0 }, + 11000, 5000000, 833333, /* 160/3 x FH */ + /* ...1000 111 / 0 0011 1100 0111 1000 0011 111x */ + /* ...0010 010 / 0 1001 1001 0011 0011 1001 110x */ + 0x8E3C783E, 0x2499339C, 32, 0, 14 * 1, + VBI_MODULATION_BIPHASE_LSB, + }, { + V4L2_SLICED_CAPTION_525, + V4L2_STD_525_60, + { 21, 284 }, + { 21, 284 }, + 10500, 1006976, 503488, /* 32 x FH */ + /* Test of CRI bits has been removed to handle the + incorrect signal observed by Rich Kandel (see + _VBI_RAW_SHIFT_CC_CRI). */ + 0x03, 0x0F, 4, 0, 2 * 8, + VBI_MODULATION_NRZ_LSB, + } +}; + +static const unsigned int DEF_THR_FRAC = 9; +static const unsigned int LP_AVG = 4; + +static inline unsigned int vbi_sample(const uint8_t *raw, unsigned i) +{ + unsigned ii = i >> 8; + unsigned int raw0 = raw[ii]; + unsigned int raw1 = raw[ii + 1]; + + return (int)(raw1 - raw0) * (i & 255) + (raw0 << 8); +} + +// Slice the raw data +static bool low_pass_bit_slicer_Y8(struct vbi_bit_slicer *bs, uint8_t *buffer, const uint8_t *raw) +{ + unsigned int i, j; + unsigned int cl; /* clock */ + unsigned int thresh0; /* old 0/1 threshold */ + unsigned int tr; /* current threshold */ + unsigned int c; /* current byte */ + unsigned int t; /* t = raw[0] * j + raw[1] * (1 - j) */ + unsigned int raw0; /* oversampling temporary */ + unsigned int raw1; + unsigned char b1; /* previous bit */ + unsigned int oversampling = 4; + + raw += bs->skip; + + thresh0 = bs->thresh; + + c = 0; + cl = 0; + b1 = 0; + + for (i = bs->cri_samples; i > 0; --i) { + int r; + tr = bs->thresh >> bs->thresh_frac; + raw0 = raw[0]; + raw1 = raw[1]; + raw1 -= raw0; + r = raw1; + bs->thresh += (int)(raw0 - tr) * (r < 0 ? -r : r); + t = raw0 * oversampling; + + for (j = oversampling; j > 0; --j) { + unsigned int tavg; + unsigned char b; /* current bit */ + + tavg = (t + (oversampling / 2)) / oversampling; + b = (tavg >= tr); + + if ((b ^ b1)) { + cl = bs->oversampling_rate >> 1; + } else { + cl += bs->cri_rate; + + if (cl >= bs->oversampling_rate) { + cl -= bs->oversampling_rate; + c = c * 2 + b; + if ((c & bs->cri_mask) == bs->cri) + break; + } + } + + b1 = b; + + if (oversampling > 1) + t += raw1; + } + if (j) + break; + + raw++; + } + if (i == 0) { + bs->thresh = thresh0; + return false; + } + + i = bs->phase_shift; /* current bit position << 8 */ + tr *= 256; + c = 0; + + for (j = bs->frc_bits; j > 0; --j) { + raw0 = vbi_sample(raw, i); + c = c * 2 + (raw0 >= tr); + i += bs->step; /* next bit */ + } + + if (c != bs->frc) { + bs->thresh = thresh0; + return false; + } + + c = 0; + + if (bs->endian) { + /* bitwise, lsb first */ + for (j = 0; j < bs->payload; ++j) { + raw0 = vbi_sample(raw, i); + c = (c >> 1) + ((raw0 >= tr) << 7); + i += bs->step; + if ((j & 7) == 7) + *buffer++ = c; + } + *buffer = c >> ((8 - bs->payload) & 7); + } else { + /* bitwise, msb first */ + for (j = 0; j < bs->payload; ++j) { + raw0 = vbi_sample(raw, i); + c = c * 2 + (raw0 >= tr); + i += bs->step; + if ((j & 7) == 7) + *buffer++ = c; + } + *buffer = c & ((1 << (bs->payload & 7)) - 1); + } + + return true; +} + +// Prepare the vbi_bit_slicer struct +static bool vbi_bit_slicer_prepare(struct vbi_bit_slicer *bs, + const struct service *s, + const struct v4l2_vbi_format *fmt) +{ + unsigned int sample_offset = 0; + unsigned int c_mask; + unsigned int f_mask; + unsigned int min_samples_per_bit; + unsigned int oversampling; + unsigned int data_bits; + unsigned int data_samples; + unsigned int cri, cri_mask, frc; + unsigned int cri_end; + unsigned int cri_samples; + unsigned int skip; + + assert (s->cri_bits <= 32); + assert (s->frc_bits <= 32); + assert (s->payload <= 32767); + assert (fmt->samples_per_line <= 32767); + + cri = s->cri_frc >> s->frc_bits; + cri_mask = s->cri_frc_mask >> s->frc_bits; + frc = (s->cri_frc & ((1U << s->frc_bits) - 1)); + if (s->cri_rate > fmt->sampling_rate) { + fprintf(stderr, "cri_rate %u > sampling_rate %u.\n", + s->cri_rate, fmt->sampling_rate); + return false; + } + + if (s->bit_rate > fmt->sampling_rate) { + fprintf(stderr, "bit_rate %u > sampling_rate %u.\n", + s->bit_rate, fmt->sampling_rate); + return false; + } + + min_samples_per_bit = fmt->sampling_rate / ((s->cri_rate > s->bit_rate) ? s->cri_rate : s->bit_rate); + + c_mask = (s->cri_bits == 32) ? ~0U : (1U << s->cri_bits) - 1; + f_mask = (s->frc_bits == 32) ? ~0U : (1U << s->frc_bits) - 1; + + oversampling = 4; + skip = 0; + + /* 0-1 threshold, start value. */ + bs->thresh = 105 << DEF_THR_FRAC; + bs->thresh_frac = DEF_THR_FRAC; + + if (min_samples_per_bit > (3U << (LP_AVG - 1))) { + oversampling = 1; + bs->thresh <<= LP_AVG - 2; + bs->thresh_frac += LP_AVG - 2; + } + + bs->skip = sample_offset + skip; + + bs->cri_mask = cri_mask & c_mask; + bs->cri = cri & bs->cri_mask; + + /* We stop searching for CRI when CRI, FRC and payload + cannot possibly fit anymore. Additionally this eliminates + a data end check in the payload loop. */ + cri_samples = (fmt->sampling_rate * (int64_t) s->cri_bits) / s->cri_rate; + + data_bits = s->payload + s->frc_bits; + data_samples = (fmt->sampling_rate * (int64_t) data_bits) / s->bit_rate; + + if ((fmt->offset > fmt->samples_per_line) + || ((cri_samples + data_samples) + > (fmt->samples_per_line - fmt->offset))) { + /*fprintf(stderr, + "%u samples_per_line too small for " + "sample_offset %u + %u cri_bits (%u samples) " + "+ %u frc_bits and %u payload " + "(%u samples).\n", + fmt->samples_per_line, fmt->offset, + s->cri_bits, cri_samples, + s->frc_bits, s->payload, data_samples);*/ + } + if ((sample_offset > fmt->samples_per_line) + || ((cri_samples + data_samples) + > (fmt->samples_per_line - sample_offset))) { + /* + fprintf(stderr, + "%u samples_per_line too small for " + "sample_offset %u + %u cri_bits (%u samples) " + "+ %u frc_bits and %u payload " + "(%u samples).\n", + fmt->samples_per_line, sample_offset, + s->cri_bits, cri_samples, + s->frc_bits, s->payload, data_samples);*/ + return false; + } + + cri_end = fmt->samples_per_line - data_samples; + + bs->cri_samples = cri_end - sample_offset; + bs->cri_rate = s->cri_rate; + + bs->oversampling_rate = fmt->sampling_rate * oversampling; + + bs->frc = frc & f_mask; + bs->frc_bits = s->frc_bits; + + /* Payload bit distance in 1/256 raw samples. */ + bs->step = (fmt->sampling_rate * (int64_t) 256) / s->bit_rate; + + bs->payload = s->payload; + bs->endian = 1; + + switch (s->modulation) { + case VBI_MODULATION_NRZ_LSB: + bs->phase_shift = (int) + (fmt->sampling_rate * 256.0 / s->cri_rate * .5 + + bs->step * .5 + 128); + break; + + case VBI_MODULATION_BIPHASE_MSB: + bs->endian = 0; + /* fall through */ + case VBI_MODULATION_BIPHASE_LSB: + /* Phase shift between the NRZ modulated CRI and the + biphase modulated rest. */ + bs->phase_shift = (int) + (fmt->sampling_rate * 256.0 / s->cri_rate * .5 + + bs->step * .25 + 128); + break; + } + return true; +} + +bool vbi_prepare(struct vbi_handle *vh, const struct v4l2_vbi_format *fmt, v4l2_std_id std) +{ + unsigned i; + + memset(vh, 0, sizeof(*vh)); + // Sanity check + if ((std & V4L2_STD_525_60) && (std & V4L2_STD_625_50)) + return false; + vh->start_of_field_2 = (std & V4L2_STD_525_60) ? 263 : 313; + vh->stride = fmt->samples_per_line; + vh->interlaced = fmt->flags & V4L2_VBI_INTERLACED; + vh->start[0] = fmt->start[0]; + vh->start[1] = fmt->start[1]; + vh->count[0] = fmt->count[0]; + vh->count[1] = fmt->count[1]; + for (i = 0; i < sizeof(services) / sizeof(services[0]); i++) { + const struct service *s = services + i; + struct vbi_bit_slicer *slicer = vh->slicers + vh->services; + + if (!(std & s->std)) + continue; + if (s->last[0] < vh->start[0] && + s->last[1] < vh->start[1]) + continue; + if (s->first[0] >= vh->start[0] + vh->count[0] && + s->first[1] >= vh->start[1] + vh->count[1]) + continue; + slicer->service = i; + vbi_bit_slicer_prepare(slicer, s, fmt); + vh->services++; + } + return vh->services; +} + +void vbi_parse(struct vbi_handle *vh, const unsigned char *buf, + struct v4l2_sliced_vbi_format *vbi, + struct v4l2_sliced_vbi_data *data) +{ + const unsigned char *p; + unsigned i; + int y; + + memset(vbi, 0, sizeof(*vbi)); + vbi->io_size = sizeof(*data) * (vh->count[0] + vh->count[1]); + for (i = 0; i < vh->services; i++) { + const struct service *s = services + vh->slicers[i].service; + + for (y = s->first[0] - vh->start[0]; y <= s->last[0] - vh->start[0]; y++) { + if (y < 0 || y >= vh->count[0]) + continue; + if (vh->interlaced) + p = buf + vh->stride * y * 2; + else + p = buf + vh->stride * y; + data[y].id = data[y].reserved = 0; + if (low_pass_bit_slicer_Y8(vh->slicers + i, data[y].data, p)) { + vbi->service_set |= s->service; + vbi->service_lines[0][y + vh->start[0]] = s->service; + data[y].id = s->service; + data[y].field = 0; + data[y].line = y + vh->start[0]; + } + } + + for (y = s->first[1] - vh->start[1]; y <= s->last[1] - vh->start[1]; y++) { + unsigned yy = y + vh->count[0]; + + if (y < 0 || y >= vh->count[1]) + continue; + if (vh->interlaced) + p = buf + vh->stride * y * 2 + 1; + else + p = buf + vh->stride * yy; + data[yy].id = data[yy].reserved = 0; + if (low_pass_bit_slicer_Y8(vh->slicers + i, data[yy].data, p)) { + vbi->service_set |= s->service; + vbi->service_lines[1][y + vh->start[1] - vh->start_of_field_2] = s->service; + data[yy].id = s->service; + data[yy].field = 1; + data[yy].line = y + vh->start[1] - vh->start_of_field_2; + } + } + } +} diff --git a/utils/qv4l2/raw2sliced.h b/utils/qv4l2/raw2sliced.h new file mode 100644 index 0000000..bcc134c --- /dev/null +++ b/utils/qv4l2/raw2sliced.h @@ -0,0 +1,75 @@ +/* + * Raw VBI to sliced VBI parser. + * + * The slicing code was copied from libzvbi. The original copyright notice is: + * + * Copyright (C) 2000-2004 Michael H. Schimek + * + * The vbi_prepare/vbi_parse functions are: + * + * Copyright (C) 2012 Hans Verkuil <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + */ + +#ifndef _RAW2SLICED_H +#define _RAW2SLICED_H + +#include <linux/videodev2.h> + +#define VBI_MAX_SERVICES (3) + +// Bit slicer internal struct +struct vbi_bit_slicer { + unsigned int service; + unsigned int cri; + unsigned int cri_mask; + unsigned int thresh; + unsigned int thresh_frac; + unsigned int cri_samples; + unsigned int cri_rate; + unsigned int oversampling_rate; + unsigned int phase_shift; + unsigned int step; + unsigned int frc; + unsigned int frc_bits; + unsigned int payload; + unsigned int endian; + unsigned int skip; +}; + +struct vbi_handle { + unsigned services; + unsigned start_of_field_2; + unsigned stride; + bool interlaced; + int start[2]; + int count[2]; + struct vbi_bit_slicer slicers[VBI_MAX_SERVICES]; +}; + +// Fills in vbi_handle based on the standard and VBI format +// Returns true if one or more services are valid for the fmt/std combination. +bool vbi_prepare(struct vbi_handle *vh, + const struct v4l2_vbi_format *fmt, v4l2_std_id std); + +// Parses the raw buffer and fills in sliced_vbi_format and _data. +// data must be an array of count[0] + count[1] v4l2_sliced_vbi_data structs. +void vbi_parse(struct vbi_handle *vh, const unsigned char *buf, + struct v4l2_sliced_vbi_format *vbi, + struct v4l2_sliced_vbi_data *data); + +#endif diff --git a/utils/qv4l2/vbi-tab.cpp b/utils/qv4l2/vbi-tab.cpp new file mode 100644 index 0000000..92d0703 --- /dev/null +++ b/utils/qv4l2/vbi-tab.cpp @@ -0,0 +1,367 @@ +/* qv4l2: a control panel controlling v4l2 devices. + * + * Copyright (C) 2012 Hans Verkuil <[email protected]> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Vbi Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Vbi Public License for more details. + * + * You should have received a copy of the GNU Vbi Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include <stdint.h> +#include "vbi-tab.h" +#include <QTableWidget> + +#include <stdio.h> +#include <errno.h> + +VbiTab::VbiTab(QWidget *parent) : + QGridLayout(parent) +{ + QStringList q; + + m_tableF1 = new QTableWidget(1, 1, parent); + m_tableF2 = new QTableWidget(1, 1, parent); + q.append("Field 1"); + m_tableF1->setHorizontalHeaderLabels(q); + q.clear(); + q.append("Field 2"); + m_tableF2->setHorizontalHeaderLabels(q); + q.clear(); + q.append("Line"); + m_tableF1->setVerticalHeaderLabels(q); + m_tableF2->setVerticalHeaderLabels(q); + addWidget(m_tableF1, 0, 0); + addWidget(m_tableF2, 0, 1); +} + +void VbiTab::rawFormat(const v4l2_vbi_format &fmt) +{ + QTableWidgetItem *item; + QStringList q; + unsigned i; + + m_tableF1->setRowCount(fmt.count[0]); + m_tableF2->setRowCount(fmt.count[1]); + for (i = 0; i < fmt.count[0]; i++) { + item = new QTableWidgetItem(); + item->setFlags(Qt::ItemIsEnabled); + m_tableF1->setItem(i, 0, item); + q.append("Line " + QString::number(i + fmt.start[0])); + } + m_tableF1->setVerticalHeaderLabels(q); + q.clear(); + for (i = 0; i < fmt.count[1]; i++) { + item = new QTableWidgetItem(); + item->setFlags(Qt::ItemIsEnabled); + m_tableF2->setItem(i, 0, item); + q.append("Line " + QString::number(i + fmt.start[1])); + } + m_tableF2->setVerticalHeaderLabels(q); +} + +static const char *formats[] = { + "Full format 4:3, 576 lines", + "Letterbox 14:9 centre, 504 lines", + "Letterbox 14:9 top, 504 lines", + "Letterbox 16:9 centre, 430 lines", + "Letterbox 16:9 top, 430 lines", + "Letterbox > 16:9 centre", + "Full format 14:9 centre, 576 lines", + "Anamorphic 16:9, 576 lines" +}; + +static const char *subtitles[] = { + "none", + "in active image area", + "out of active image area", + "?" +}; + +static void decode_wss(QTableWidgetItem *item, const struct v4l2_sliced_vbi_data *s) +{ + unsigned char parity; + int wss; + + wss = s->data[0] | (s->data[1] << 8); + + parity = wss & 15; + parity ^= parity >> 2; + parity ^= parity >> 1; + + if (!(parity & 1)) { + item->setStatusTip(""); + return; + } + + item->setToolTip(QString(formats[wss & 7]) + "\n" + + ((wss & 0x10) ? "Film" : "Camera") + " mode\n" + + ((wss & 0x20) ? "Motion Adaptive ColorPlus" : "Standard") + " color coding\n" + + "Helper signals " + ((wss & 0x40) ? "" : "not ") + "present\n" + + ((wss & 0x0100) ? "Teletext subtitles\n" : "") + + "Open subtitles: " + subtitles[(wss >> 9) & 3] + "\n" + + ((wss & 0x0800) ? "Surround sound\n" : "") + + "Copyright " + ((wss & 0x1000) ? "asserted\n" : "unknown\n") + + "Copying " + ((wss & 0x2000) ? "restricted" : "not restricted")); +} + +const uint8_t vbi_bit_reverse[256] = { + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff +}; + +#define printable(c) ((((c) & 0x7F) < 0x20 || ((c) & 0x7F) > 0x7E) ? '.' : ((c) & 0x7F)) + +#define PIL(day, mon, hour, min) \ + (((day) << 15) + ((mon) << 11) + ((hour) << 6) + ((min) << 0)) + +static QString dump_pil(int pil) +{ + int day, mon, hour, min; + char buf[50]; + + day = pil >> 15; + mon = (pil >> 11) & 0xF; + hour = (pil >> 6) & 0x1F; + min = pil & 0x3F; + + if (pil == PIL(0, 15, 31, 63)) + return QString("PDC: Timer-control (no PDC)"); + if (pil == PIL(0, 15, 30, 63)) + return QString("PDC: Recording inhibit/terminate"); + if (pil == PIL(0, 15, 29, 63)) + return QString("PDC: Interruption"); + if (pil == PIL(0, 15, 28, 63)) + return QString("PDC: Continue"); + if (pil == PIL(31, 15, 31, 63)) + return QString("PDC: No time"); + sprintf(buf, "PDC: 20XX-%02d-%02d %02d:%02d", + mon, day, hour, min); + return buf; +} + +static const char *pcs_text[] = { + "unknown", + "mono", + "stereo", + "dual sound", +}; + +static const char *pty_text[] = { + /* 0x00 - 0x0f */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + + /* 0x10 - 0x1f */ + "movie (general)", + "detective/thriller", + "adventure/western/war", + "science fiction/fantasy/horror", + "comedy", + "soap/melodrama/folklore", + "romance", + "serious/classical/religious/historical drama", + "adult movie", + NULL, NULL, NULL, NULL, NULL, NULL, + "user defined", + + /* 0x20 - 0x2f */ + "news/current affairs (general)", + "news/weather report", + "news magazine", + "documentary", + "discussion/interview/debate", + "social/political issues/economics (general)", + "magazines/reports/documentary", + "economics/social advisory", + "remarkable people", + NULL, NULL, NULL, NULL, NULL, NULL, + "user defined", + + /* 0x30 - 0x3f */ + "show/game show (general)", + "game show/quiz/contest", + "variety show", + "talk show", + "leisure hobbies (general)", + "tourism/travel", + "handicraft", + "motoring", + "fitness and health", + "cooking", + "advertisement/shopping", + NULL, NULL, NULL, NULL, + "alarm/emergency identification", + + /* 0x40 - 0x4f */ + "sports (general)", + "special events (Olympic Games, World Cup etc.)", + "sports magazines", + "football/soccer", + "tennis/squash", + "team sports (excluding football)", + "athletics", + "motor sport", + "water sport", + "winter sports", + "equestrian", + "martial sports", + "local sports", + NULL, NULL, + "user defined", + + /* 0x50 - 0x5f */ + "children's/youth programmes (general)", + "pre-school children's programmes", + "entertainment programmes for 6 to 14", + "entertainment programmes for 10 to 16", + "informational/educational/school programmes", + "cartoons/puppets", + "education/science/factual topics (general)", + "nature/animals/environment", + "technology/natural sciences", + "medicine/physiology/psychology", + "foreign countries/expeditions", + "social/spiritual sciences", + "further education", + "languages", + NULL, + "user defined", + + /* 0x60 - 0x6f */ + "music/ballet/dance (general)", + "rock/pop", + "serious music/classical music", + "folk/traditional music", + "jazz", + "musical/opera", + "ballet", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + "user defined", + + /* 0x70 - 0x7f */ + "arts/culture (without music, general)", + "performing arts", + "fine arts", + "religion", + "popular culture/traditional arts", + "literature", + "film/cinema", + "experimental film/video", + "broadcasting/press", + "new media", + "arts/culture magazines", + "fashion", + NULL, NULL, NULL, + "user defined", +}; + +static void decode_vps(QTableWidgetItem *item, const struct v4l2_sliced_vbi_data *s) +{ + QString q; + int cni, pcs, pty, pil; + const char *pty_txt; + const unsigned char *buf = s->data; + + pcs = buf[2] >> 6; + + cni = +((buf[10] & 3) << 10) + + ((buf[11] & 0xC0) << 2) + + ((buf[8] & 0xC0) << 0) + + (buf[11] & 0x3F); + + pil = ((buf[8] & 0x3F) << 14) + (buf[9] << 6) + (buf[10] >> 2); + + pty = buf[12]; + if (pty > 0x7f) + pty_txt = "service specific"; + else if (pty < 0x10) + pty_txt = "undefined content"; + else + pty_txt = pty_text[pty]; + if (pty_txt == NULL) + pty_txt = "reserved for future use"; + + q = "CNI: 0x"; + q += QString::number(cni, 16) + " PCS: " + pcs_text[pcs] + " PTY: " + pty_txt + "\n"; + item->setToolTip(q + dump_pil(pil)); +} + +static void setItem(QTableWidgetItem *item, const v4l2_sliced_vbi_data *data) +{ + switch (data->id) { + case V4L2_SLICED_TELETEXT_B: + item->setText("TXT"); + break; + case V4L2_SLICED_VPS: + item->setText("VPS"); + decode_vps(item, data); + break; + case V4L2_SLICED_CAPTION_525: + item->setText("CC"); + break; + case V4L2_SLICED_WSS_625: + item->setText("WSS"); + decode_wss(item, data); + break; + default: + item->setText(""); + break; + } +} + +void VbiTab::slicedData(const v4l2_sliced_vbi_data *data) +{ + int i; + + for (i = 0; i < m_tableF1->rowCount(); i++) { + QTableWidgetItem *item = m_tableF1->item(i, 0); + + setItem(item, data + i); + } + for (i = 0; i < m_tableF2->rowCount(); i++) { + QTableWidgetItem *item = m_tableF2->item(i, 0); + + setItem(item, data + i + m_tableF1->rowCount()); + } +} diff --git a/utils/qv4l2/vbi-tab.h b/utils/qv4l2/vbi-tab.h new file mode 100644 index 0000000..7950e0d --- /dev/null +++ b/utils/qv4l2/vbi-tab.h @@ -0,0 +1,54 @@ +/* qv4l2: a control panel controlling v4l2 devices. + * + * Copyright (C) 2012 Hans Verkuil <[email protected]> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Vbi Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Vbi Public License for more details. + * + * You should have received a copy of the GNU Vbi Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef VBI_TAB_H +#define VBI_TAB_H + +#include "qv4l2.h" +#include "v4l2-api.h" + +class QTableWidget; + +class VbiTab: public QGridLayout +{ + Q_OBJECT + +public: + VbiTab(QWidget *parent = 0); + virtual ~VbiTab() {} + + void rawFormat(const v4l2_vbi_format &fmt); + void slicedData(const v4l2_sliced_vbi_data *data); + +private: + void info(const QString &info) + { + g_mw->info(info); + } + virtual void error(const QString &error) + { + g_mw->error(error); + } + + QTableWidget *m_tableF1; + QTableWidget *m_tableF2; +}; + +#endif _______________________________________________ linuxtv-commits mailing list [email protected] http://www.linuxtv.org/cgi-bin/mailman/listinfo/linuxtv-commits
