rendered paste bodyIndex: libs/libmythtv/vbi608extractor.h
===================================================================
--- libs/libmythtv/vbi608extractor.h (revision 0)
+++ libs/libmythtv/vbi608extractor.h (revision 0)
@@ -0,0 +1,57 @@
+/*
+ VBI 608 Extractor, extracts CEA-608 VBI from a line of raw data.
+ Copyright (C) 2010 Digital Nirvana, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General 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 General Public License for more details.
+
+ You should have received a copy of the GNU General 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_608_EXTRACTOR_H_
+#define _VBI_608_EXTRACTOR_H_
+
+#include <stdint.h>
+
+#include <QList>
+
+#include "frame.h"
+
+class VBI608Extractor
+{
+ public:
+ VBI608Extractor();
+
+ uint16_t GetCode1(void) const { return code[0]; }
+ uint16_t GetCode2(void) const { return code[1]; }
+
+ bool ExtractCC(const VideoFrame*, uint max_lines = 4);
+ bool ExtractCC12(const unsigned char *buf, uint width);
+ bool ExtractCC34(const unsigned char *buf, uint width);
+
+ uint FillCCData(uint8_t cc_data[8]);
+
+ private:
+ float GetClockStart(void) const { return start; }
+ float GetClockRate(void) const { return rate; }
+ bool FindClocks(const unsigned char *buf, uint width);
+
+ QList<uint> raw_minimas;
+ QList<uint> raw_maximas;
+ QList<float> maximas;
+ QList<float> minimas;
+ float start;
+ float rate;
+ uint16_t code[2];
+};
+
+#endif // _VBI_608_EXTRACTOR_H_
Index: libs/libmythtv/NuppelVideoRecorder.cpp
===================================================================
--- libs/libmythtv/NuppelVideoRecorder.cpp (revision 25079)
+++ libs/libmythtv/NuppelVideoRecorder.cpp (working copy)
@@ -24,13 +24,13 @@
#include "mythcontext.h"
#include "mythverbose.h"
#include "NuppelVideoRecorder.h"
-#include "vbitext/cc.h"
#include "channelbase.h"
#include "filtermanager.h"
#include "recordingprofile.h"
#include "tv_rec.h"
#include "tv_play.h"
#include "audioinput.h"
+#include "vbi608extractor.h"
#ifdef WORDS_BIGENDIAN
extern "C" {
@@ -181,6 +181,7 @@
volume = 100;
ccr.SetEnabled(true);
+ vbi608 = new VBI608Extractor();
ccd = new CC608Decoder(&ccr);
go7007 = false;
@@ -253,6 +254,8 @@
delete videoFilters;
if (FiltMan)
delete FiltMan;
+ if (vbi608)
+ delete vbi608;
if (ccd)
delete ccd;
}
@@ -2573,7 +2576,7 @@
void NuppelVideoRecorder::FormatTeletextSubtitles(struct VBIData *vbidata) {}
#endif // USING_V4L
-void NuppelVideoRecorder::FormatCC(struct cc *cc)
+void NuppelVideoRecorder::FormatCC(uint code1, uint code2)
{
struct timeval tnow;
gettimeofday (&tnow, &tzone);
@@ -2583,7 +2586,7 @@
int tc = (tnow.tv_sec - stm.tv_sec) * 1000 +
tnow.tv_usec / 1000 - stm.tv_usec / 1000;
- ccd->FormatCC(tc, cc->code1, cc->code2);
+ ccd->FormatCC(tc, code1, code2);
}
void NuppelVideoRecorder::AddTextData(unsigned char *buf, int len,
@@ -2651,10 +2654,9 @@
//VERBOSE(VB_IMPORTANT, LOC + "vbi begin");
struct VBIData vbicallbackdata;
struct vbi *pal_tt = NULL;
- struct cc *ntsc_cc = NULL;
int vbifd = -1;
- char *ptr = NULL;
- char *ptr_end = NULL;
+ unsigned char *buf = NULL, *ptr = NULL, *ptr_end = NULL;
+ uint width = 0, start_line = 0, line_count = 0;
QByteArray vbidev = vbidevice.toAscii();
if (VBIMode::PAL_TT == vbimode)
@@ -2669,17 +2671,7 @@
}
else if (VBIMode::NTSC_CC == vbimode)
{
- ntsc_cc = new struct cc;
- bzero(ntsc_cc, sizeof(struct cc));
- ntsc_cc->fd = open(vbidev.constData(), O_RDONLY|O_NONBLOCK);
- ntsc_cc->code1 = -1;
- ntsc_cc->code2 = -1;
-
- vbifd = ntsc_cc->fd;
- ptr = ntsc_cc->buffer;
-
- if (vbifd < 0)
- delete ntsc_cc;
+ vbifd = open(vbidev.constData(), O_RDONLY|O_NONBLOCK);
}
else
{
@@ -2703,7 +2695,7 @@
{
VERBOSE(VB_IMPORTANT, LOC_ERR +
"Failed to query vbi capabilities (v4l1)");
- cc_close(ntsc_cc);
+ close(vbifd);
return;
}
VERBOSE(VB_RECORD, LOC + "vbi_format rate: "<<vfmt.sampling_rate
@@ -2715,19 +2707,28 @@
<<"\n\t\t\t flags: "
<<QString("0x%1").arg(vfmt.flags));
uint sz = vfmt.samples_per_line * (vfmt.count[0] + vfmt.count[1]);
- ntsc_cc->samples_per_line = vfmt.samples_per_line;
- ntsc_cc->start_line = vfmt.start[0];
- ntsc_cc->line_count = vfmt.count[0];
- ntsc_cc->scale0 = (vfmt.sampling_rate + 503488 / 2) / 503488;
- ntsc_cc->scale1 = (ntsc_cc->scale0 * 2 + 3) / 5; /* 40% */
- ptr_end = ntsc_cc->buffer + sz;
- if (sz > CC_VBIBUFSIZE)
+ buf = ptr = new unsigned char[sz];
+ ptr_end = buf + sz;
+ width = vfmt.samples_per_line;
+ start_line = vfmt.start[0];
+ line_count = vfmt.count[0];
+ if (line_count != vfmt.count[1])
{
VERBOSE(VB_IMPORTANT, LOC_ERR +
- "VBI format has too many samples per frame");
- cc_close(ntsc_cc);
+ "VBI must have the same number of "
+ "odd and even fields for our decoder");
+ delete [] buf;
+ close(vbifd);
return;
}
+ if (start_line > 21 || start_line + line_count < 22)
+ {
+ VERBOSE(VB_IMPORTANT, LOC_ERR +
+ "VBI does not include line 21");
+ delete [] buf;
+ close(vbifd);
+ return;
+ }
}
// Qt4 requires a QMutex as a parameter...
@@ -2774,9 +2775,21 @@
ptr = (ret > 0) ? ptr + ret : ptr;
if ((ptr_end - ptr) == 0)
{
- cc_decode(ntsc_cc);
- FormatCC(ntsc_cc);
- ptr = ntsc_cc->buffer;
+ unsigned char *line21_field1 =
+ ptr + (21 - start_line) * width;
+ unsigned char *line21_field2 =
+ ptr + (line_count + 21 - start_line) * width;
+ bool cc1 = vbi608->ExtractCC12(line21_field1, width);
+ bool cc2 = vbi608->ExtractCC34(line21_field2, width);
+ if (cc1 || cc2)
+ {
+ int code1 = vbi608->GetCode1();
+ int code2 = vbi608->GetCode2();
+ code1 = (0xFFFF==code1) ? -1 : code1;
+ code2 = (0xFFFF==code2) ? -1 : code2;
+ FormatCC(code1, code2);
+ }
+ ptr = buf;
}
else if (ret < 0)
{
@@ -2790,10 +2803,14 @@
{
vbi_del_handler(pal_tt, (void*) vbi_event, &vbicallbackdata);
vbi_close(pal_tt);
+ vbifd = -1;
}
- if (ntsc_cc)
- cc_close(ntsc_cc);
+ if (vbifd >= 0)
+ {
+ close(vbifd);
+ delete [] buf;
+ }
//VERBOSE(VB_RECORD, LOC + "vbi end");
}
Index: libs/libmythtv/avformatdecoder.cpp
===================================================================
--- libs/libmythtv/avformatdecoder.cpp (revision 25079)
+++ libs/libmythtv/avformatdecoder.cpp (working copy)
@@ -22,6 +22,7 @@
#include "mpegtables.h"
#include "atscdescriptors.h"
#include "dvbdescriptors.h"
+#include "vbi608extractor.h"
#include "cc608decoder.h"
#include "cc708decoder.h"
#include "subtitlereader.h"
@@ -488,6 +489,8 @@
no_hardware_decoders(no_hardware_decode),
maxkeyframedist(-1),
// Closed Caption & Teletext decoders
+ use_vbi(true),
+ vbi608(new VBI608Extractor()),
ccd608(new CC608Decoder(parent->GetCC608Reader())),
ccd708(new CC708Decoder(parent->GetCC708Reader())),
ttd(new TeletextDecoder()), subReader(parent->GetSubReader()),
@@ -552,6 +555,7 @@
}
CloseContext();
+ delete vbi608;
delete ccd608;
delete ccd708;
delete ttd;
@@ -3166,10 +3170,11 @@
// Decode CEA-608 and CEA-708 captions
for (uint i = 0; i < (uint)mpa_pic.atsc_cc_len;
- i += ((mpa_pic.atsc_cc_buf[i] & 0x1f) * 3) + 2)
+ i += ((mpa_pic.atsc_cc_buf[i] & 0x1f) * 3) + 2)
{
DecodeDTVCC(mpa_pic.atsc_cc_buf + i,
mpa_pic.atsc_cc_len - i);
+ use_vbi = false;
}
VideoFrame *picframe = (VideoFrame *)(mpa_pic.opaque);
@@ -3268,6 +3273,14 @@
picframe->repeat_pict = mpa_pic.repeat_pict;
picframe->frameNumber = framesPlayed;
+
+ if ((!directrendering || IS_DR1_PIX_FMT(context->pix_fmt)) &&
+ use_vbi && vbi608->ExtractCC(picframe))
+ {
+ uint8_t cc_data[8];
+ DecodeDTVCC(cc_data, vbi608->FillCCData(cc_data));
+ }
+
GetNVP()->ReleaseNextVideoFrame(picframe, temppts);
if (d->HasMPEG2Dec() && mpa_pic.data[3])
context->release_buffer(context, &mpa_pic);
Index: libs/libmythtv/libmythtv.pro
===================================================================
--- libs/libmythtv/libmythtv.pro (revision 25079)
+++ libs/libmythtv/libmythtv.pro (working copy)
@@ -147,6 +147,7 @@
HEADERS += remoteencoder.h videosource.h
HEADERS += cardutil.h sourceutil.h
HEADERS += videometadatautil.h
+HEADERS += vbi608extractor.h
HEADERS += cc608decoder.h cc608reader.h
HEADERS += cc708decoder.h cc708reader.h
HEADERS += cc708window.h subtitlereader.h
@@ -171,6 +172,7 @@
SOURCES += remoteencoder.cpp videosource.cpp
SOURCES += cardutil.cpp sourceutil.cpp
SOURCES += videometadatautil.cpp
+SOURCES += vbi608extractor.cpp
SOURCES += cc608decoder.cpp cc608reader.cpp
SOURCES += cc708decoder.cpp cc708reader.cpp
SOURCES += cc708window.cpp subtitlereader.cpp
Index: libs/libmythtv/NuppelVideoRecorder.h
===================================================================
--- libs/libmythtv/NuppelVideoRecorder.h (revision 25079)
+++ libs/libmythtv/NuppelVideoRecorder.h (working copy)
@@ -37,13 +37,13 @@
struct video_audio;
struct VBIData;
-struct cc;
class RTjpeg;
class RingBuffer;
class ChannelBase;
class FilterManager;
class FilterChain;
class AudioInput;
+class VBI608Extractor;
class MPUBLIC NuppelVideoRecorder : public RecorderBase
{
@@ -138,7 +138,7 @@
void DoMJPEG(void);
void FormatTeletextSubtitles(struct VBIData *vbidata);
- void FormatCC(struct cc *cc);
+ void FormatCC(uint code1, uint code2);
void AddTextData(unsigned char *buf, int len,
long long timecode, char type);
@@ -285,6 +285,7 @@
int volume;
+ VBI608Extractor *vbi608;
CC608Decoder *ccd;
CC608Reader ccr;
Index: libs/libmythtv/avformatdecoder.h
===================================================================
--- libs/libmythtv/avformatdecoder.h (revision 25079)
+++ libs/libmythtv/avformatdecoder.h (working copy)
@@ -22,6 +22,7 @@
#include "avfringbuffer.h"
class TeletextDecoder;
+class VBI608Extractor;
class CC608Decoder;
class CC708Decoder;
class SubtitleReader;
@@ -268,6 +269,9 @@
int maxkeyframedist;
// Caption/Subtitle/Teletext decoders
+ /// How often has 608 data been found in the first lines of video
+ bool use_vbi;
+ VBI608Extractor *vbi608;
CC608Decoder *ccd608;
CC708Decoder *ccd708;
TeletextDecoder *ttd;
Index: libs/libmythtv/vbi608extractor.cpp
===================================================================
--- libs/libmythtv/vbi608extractor.cpp (revision 0)
+++ libs/libmythtv/vbi608extractor.cpp (revision 0)
@@ -0,0 +1,392 @@
+/*
+ VBI 608 Extractor, extracts CEA-608 VBI from a line of raw data.
+ Copyright (C) 2010 Digital Nirvana, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General 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 General Public License for more details.
+
+ You should have received a copy of the GNU General 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.
+*/
+
+#define __STDC_LIMIT_MACROS
+#include <stdint.h>
+
+#include <algorithm>
+using namespace std;
+
+#include "vbi608extractor.h"
+#include "mythverbose.h"
+
+static void print(
+ const QList<uint> &raw_minimas, const QList<uint> &raw_maximas,
+ const QList<float> &minimas, const QList<float> &maximas)
+{
+ QString raw_mins, raw_maxs;
+ for (uint i = 0; i < uint(raw_minimas.size()); i++)
+ raw_mins += QString("%1,").arg(raw_minimas[i]);
+ for (uint i = 0; i < uint(raw_maximas.size()); i++)
+ raw_maxs += QString("%1,").arg(raw_maximas[i]);
+ VERBOSE(VB_VBI|VB_EXTRA, QString("raw mins: %1").arg(raw_mins));
+ VERBOSE(VB_VBI|VB_EXTRA, QString("raw maxs: %1").arg(raw_maxs));
+
+ QString mins, maxs;
+ for (uint i = 0; i < uint(minimas.size()); i++)
+ mins += QString("%1,").arg(minimas[i]);
+ for (uint i = 0; i < uint(maximas.size()); i++)
+ maxs += QString("%1,").arg(maximas[i]);
+ VERBOSE(VB_VBI|VB_EXTRA, QString("mins: %1 maxs: %2")
+ .arg(mins).arg(maxs));
+}
+
+static float find_clock_diff(const QList<float> &list)
+{
+ float min_diff = INT32_MAX;
+ float max_diff = 0.0f;
+ float avg_diff = 0.0f;
+ for (uint i = 1; i < uint(list.size()); i++)
+ {
+ float diff = list[i] - list[i-1];
+ min_diff = min(diff, min_diff);
+ max_diff = max(diff, max_diff);
+ avg_diff += diff;
+ }
+ avg_diff /= (list.size() - 1);
+ if (avg_diff * 1.15 < max_diff)
+ {
+ VERBOSE(VB_VBI|VB_EXTRA, "max_diff too big");
+ return 0.0f;
+ }
+ if (avg_diff * 0.85 > max_diff)
+ {
+ VERBOSE(VB_VBI|VB_EXTRA, "min_diff too small");
+ return 0.0f;
+ }
+
+ return avg_diff;
+}
+
+VBI608Extractor::VBI608Extractor() : start(0.0f), rate(0.0f)
+{
+ code[0] = UINT16_MAX;
+ code[1] = UINT16_MAX;
+}
+
+bool VBI608Extractor::FindClocks(const unsigned char *buf, uint width)
+{
+ raw_minimas.clear();
+ raw_maximas.clear();
+ maximas.clear();
+ minimas.clear();
+
+ // find our threshold
+ uint maxv = 0;
+ for (uint j = width / 8; j < width / 4; j++)
+ maxv = max(uint(buf[j]), maxv);
+ uint avgv = maxv / 2;
+
+ // get the raw minima and maxima list
+ uint noise_flr_sm = max(uint(0.003 * width), 2U);
+ uint noise_flr_lg = max(uint(0.007 * width), noise_flr_sm+1);
+ int last_max = -1, last_min = -1;
+ for (uint i = 0; i < (width/3); i++)
+ {
+ if (buf[i] > avgv+10)
+ raw_maximas.push_back(last_max=i);
+ else if (last_max>=0 && (i - last_max) <= noise_flr_sm)
+ raw_maximas.push_back(i);
+ else if (buf[i] < avgv-10)
+ raw_minimas.push_back(last_min=i);
+ else if (last_min>=0 && (i - last_min) <= noise_flr_lg)
+ raw_minimas.push_back(i);
+ }
+
+ for (uint i = 0; i < uint(raw_maximas.size()); i++)
+ {
+ uint start_idx = raw_maximas[i];
+ while ((i+1) < uint(raw_maximas.size()) &&
+ (raw_maximas[i+1] == raw_maximas[i] + 1)) i++;
+ uint end_idx = raw_maximas[i];
+ if (end_idx - start_idx > noise_flr_lg)
+ maximas.push_back((start_idx + end_idx) * 0.5f);
+ }
+
+ if (maximas.size() < 7)
+ {
+ VERBOSE(VB_VBI|VB_EXTRA, QString("%1 < 7").arg(maximas.size()));
+ print(raw_minimas, raw_maximas, minimas, maximas);
+ return false;
+ }
+
+ // drop outliers on edges
+ bool dropped = true;
+ while (maximas.size() > 7 && dropped)
+ {
+ float min_diff = width * 8;
+ float max_diff = 0.0f;
+ float avg_diff = 0.0f;
+ for (uint i = 1; i < uint(maximas.size()); i++)
+ {
+ float diff = maximas[i] - maximas[i-1];
+ min_diff = min(diff, min_diff);
+ max_diff = max(diff, max_diff);
+ avg_diff += diff;
+ }
+ avg_diff -= min_diff;
+ avg_diff -= max_diff;
+ avg_diff /= (maximas.size() - 3);
+
+ dropped = false;
+ if (avg_diff * 1.1f < max_diff)
+ {
+ float last_diff = maximas.back() - maximas[maximas.size()-2];
+ if (last_diff*1.01f >= max_diff || last_diff > avg_diff * 1.2f)
+ {
+ maximas.pop_back();
+ dropped = true;
+ }
+ float first_diff = maximas[1] - maximas[0];
+ if ((maximas.size() > 7) && (first_diff*1.01f >= max_diff))
+ {
+ maximas.pop_front();
+ dropped = true;
+ }
+ }
+
+ if (avg_diff * 0.9f > min_diff)
+ {
+ float last_diff = maximas.back() - maximas[maximas.size()-2];
+ if ((maximas.size() > 7) &&
+ (last_diff*0.99f <= min_diff || last_diff < avg_diff * 0.80f))
+ {
+ maximas.pop_back();
+ dropped = true;
+ }
+ float first_diff = maximas[1] - maximas[0];
+ if ((maximas.size() > 7) && (first_diff*0.99f <= min_diff))
+ {
+ maximas.pop_front();
+ dropped = true;
+ }
+ }
+ }
+
+ if (maximas.size() != 7)
+ {
+ VERBOSE(VB_VBI|VB_EXTRA, QString("maximas: %1 != 7")
+ .arg(maximas.size()));
+ print(raw_minimas, raw_maximas, minimas, maximas);
+ return false;
+ }
+
+ // find the minimas
+ for (uint i = 0; i < uint(raw_minimas.size()); i++)
+ {
+ uint start_idx = raw_minimas[i];
+ while ((i+1) < uint(raw_minimas.size()) &&
+ (raw_minimas[i+1] == raw_minimas[i] + 1)) i++;
+ uint end_idx = raw_minimas[i];
+ float center = (start_idx + end_idx) * 0.5f;
+ if (end_idx - start_idx > noise_flr_lg &&
+ center > maximas[0] && center < maximas.back())
+ {
+ minimas.push_back(center);
+ }
+ }
+
+ if (minimas.size() != 6)
+ {
+ VERBOSE(VB_VBI|VB_EXTRA, QString("minimas: %1 != 6")
+ .arg(minimas.size()));
+ print(raw_minimas, raw_maximas, minimas, maximas);
+ return false;
+ }
+
+ // get the average clock rate in samples
+ float maxima_avg_diff = find_clock_diff(maximas);
+ float minima_avg_diff = find_clock_diff(minimas);
+ rate = (maxima_avg_diff * 7 + minima_avg_diff * 6) / 13.0f;
+ if (maxima_avg_diff == 0.0f || minima_avg_diff == 0.0f)
+ return false;
+
+ // get the estimated location of the first maxima
+ // based on the rate and location of all maximas
+ start = maximas[0];
+ for (uint i = 1; i < uint(maximas.size()); i++)
+ start += maximas[i] - i * rate;
+ start /= maximas.size();
+ // then move it back by a third to make each sample
+ // more or less in the center of each encoded byte.
+ start -= rate * 0.33f;
+
+ // if the last bit is after the last sample...
+ // 7 clocks + 3 bits run in + 16 bits data
+ if (start+((7+3+8+8-1) * rate) > width)
+ {
+ VERBOSE(VB_VBI|VB_EXTRA, QString("end %1 > width %2")
+ .arg(start+((7+3+8+8-1) * rate)).arg(width));
+
+ return false;
+ }
+
+ //VERBOSE(VB_VBI|VB_EXTRA, QString("Clock start %1, rate %2")
+ // .arg(start).arg(avg_diff));
+
+ return true;
+}
+
+bool VBI608Extractor::ExtractCC(const VideoFrame *picframe, uint max_lines)
+{
+ int ypitch = picframe->pitches[0];
+ int ywidth = picframe->width;
+
+ code[0] = UINT16_MAX;
+ code[1] = UINT16_MAX;
+
+ // find CC
+ uint found_cnt = 0;
+ for (uint i = 0; i < max_lines; i++)
+ {
+ const unsigned char *y = picframe->buf +
+ picframe->offsets[0] + (i * ypitch);
+ if (FindClocks(y, ywidth))
+ {
+ uint maxv = 0;
+ for (uint j = 0; j < start + 8 * rate; j++)
+ maxv = max(uint((y+(i * ypitch))[j]), maxv);
+ uint avgv = maxv / 2;
+
+ if (y[uint(start + (0+7) * rate)] > avgv ||
+ y[uint(start + (1+7) * rate)] > avgv ||
+ y[uint(start + (2+7) * rate)] < avgv)
+ {
+ continue; // need 001 at run in..
+ }
+
+ code[found_cnt] = 0;
+ for (uint j = 0; j < 8+8; j++)
+ {
+ bool bit = y[uint(start + (j+7+3) * rate)] > avgv;
+ code[found_cnt] =
+ (code[found_cnt]>>1) | (bit?(1<<15):0);
+ }
+
+ found_cnt++;
+ if (found_cnt>=2)
+ break;
+#if 0
+ unsigned char *Y = const_cast<unsigned char*>(y);
+ unsigned char *u = const_cast<unsigned char*>
+ (picframe->buf + picframe->offsets[1] +
+ (i*picframe->pitches[1]));
+ unsigned char *v = const_cast<unsigned char*>
+ (picframe->buf + picframe->offsets[2] +
+ (i*picframe->pitches[2]));
+ unsigned uwidth = picframe->pitches[1];
+ v[uwidth / 3] = 0x40;
+ for (uint j = 0; j < 7+3+8+8; j++)
+ {
+ uint yloc = uint (start + j * rate + 0.5);
+ Y[yloc] = 0xFF;
+ uint uloc = uint (uwidth * (start + j * rate + 0.5) / ywidth);
+ u[uloc] = 0x40;
+ }
+#endif
+ }
+ }
+
+ return found_cnt;
+}
+
+bool VBI608Extractor::ExtractCC12(const unsigned char *buf, uint width)
+{
+ code[0] = UINT16_MAX;
+ if (FindClocks(buf, width))
+ {
+ uint maxv = 0;
+ for (uint j = 0; j < start + 8 * rate; j++)
+ maxv = max(uint(buf[j]), maxv);
+ uint avgv = maxv / 2;
+
+ if (buf[uint(start + (0+7) * rate)] > avgv ||
+ buf[uint(start + (1+7) * rate)] > avgv ||
+ buf[uint(start + (2+7) * rate)] < avgv)
+ {
+ return false;
+ }
+
+ code[0] = 0;
+ for (uint j = 0; j < 8+8; j++)
+ {
+ bool bit = buf[uint(start + (j+7+3) * rate)] > avgv;
+ code[0] = (code[0]>>1) | (bit?(1<<15):0);
+ }
+
+ return true;
+ }
+ return false;
+}
+
+bool VBI608Extractor::ExtractCC34(const unsigned char *buf, uint width)
+{
+ code[1] = UINT16_MAX;
+ if (FindClocks(buf, width))
+ {
+ uint maxv = 0;
+ for (uint j = 0; j < start + 8 * rate; j++)
+ maxv = max(uint(buf[j]), maxv);
+ uint avgv = maxv / 2;
+
+ if (buf[uint(start + (0+7) * rate)] > avgv ||
+ buf[uint(start + (1+7) * rate)] > avgv ||
+ buf[uint(start + (2+7) * rate)] < avgv)
+ {
+ return false;
+ }
+
+ code[1] = 0;
+ for (uint j = 0; j < 8+8; j++)
+ {
+ bool bit = buf[uint(start + (j+7+3) * rate)] > avgv;
+ code[1] = (code[1]>>1) | (bit?(1<<15):0);
+ }
+ return true;
+ }
+ return false;
+}
+
+uint VBI608Extractor::FillCCData(uint8_t cc_data[8])
+{
+ uint cc_count = 0;
+ if (code[0] != UINT16_MAX)
+ {
+ cc_data[2] = 0x04;
+ cc_data[3] = (code[0]) & 0xff;
+ cc_data[4] = (code[0]>>8) & 0xff;
+ cc_count++;
+ }
+
+ if (code[1] != UINT16_MAX)
+ {
+ cc_data[2+3*cc_count] = 0x04|0x01;
+ cc_data[3+3*cc_count] = (code[1]) & 0xff;
+ cc_data[4+3*cc_count] = (code[1]>>8) & 0xff;
+ cc_count++;
+ }
+
+ if (cc_count)
+ {
+ cc_data[0] = 0x40 | cc_count;
+ cc_data[1] = 0x00;
+ return 2+(3*cc_count);
+ }
+ return 0;
+}