00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #ifndef __STDC_LIMIT_MACROS
00021 #define __STDC_LIMIT_MACROS
00022 #endif
00023
00024 #include <stdint.h>
00025
00026 #include <algorithm>
00027 using namespace std;
00028
00029 #include "vbi608extractor.h"
00030 #include "mythlogging.h"
00031
00032 #define LOC QString("VBI608Extractor: ")
00033
00034 static void print(
00035 const QList<uint> &raw_minimas, const QList<uint> &raw_maximas,
00036 const QList<float> &minimas, const QList<float> &maximas)
00037 {
00038 QString raw_mins, raw_maxs;
00039 for (uint i = 0; i < uint(raw_minimas.size()); i++)
00040 raw_mins += QString("%1,").arg(raw_minimas[i]);
00041 for (uint i = 0; i < uint(raw_maximas.size()); i++)
00042 raw_maxs += QString("%1,").arg(raw_maximas[i]);
00043 LOG(VB_VBI, LOG_DEBUG, QString("raw mins: %1").arg(raw_mins));
00044 LOG(VB_VBI, LOG_DEBUG, QString("raw maxs: %1").arg(raw_maxs));
00045
00046 QString mins, maxs;
00047 for (uint i = 0; i < uint(minimas.size()); i++)
00048 mins += QString("%1,").arg(minimas[i]);
00049 for (uint i = 0; i < uint(maximas.size()); i++)
00050 maxs += QString("%1,").arg(maximas[i]);
00051 LOG(VB_VBI, LOG_DEBUG, QString("mins: %1 maxs: %2")
00052 .arg(mins).arg(maxs));
00053 }
00054
00055 static float find_clock_diff(const QList<float> &list)
00056 {
00057 float min_diff = INT32_MAX;
00058 float max_diff = 0.0f;
00059 float avg_diff = 0.0f;
00060 for (uint i = 1; i < uint(list.size()); i++)
00061 {
00062 float diff = list[i] - list[i-1];
00063 min_diff = min(diff, min_diff);
00064 max_diff = max(diff, max_diff);
00065 avg_diff += diff;
00066 }
00067 if (list.size() >= 2)
00068 avg_diff /= (list.size() - 1);
00069 if (avg_diff * 1.15 < max_diff)
00070 {
00071 LOG(VB_VBI, LOG_DEBUG, "max_diff too big");
00072 return 0.0f;
00073 }
00074 if (avg_diff * 0.85 > max_diff)
00075 {
00076 LOG(VB_VBI, LOG_DEBUG, "min_diff too small");
00077 return 0.0f;
00078 }
00079
00080 return avg_diff;
00081 }
00082
00083 VBI608Extractor::VBI608Extractor() : start(0.0f), rate(0.0f)
00084 {
00085 code[0] = UINT16_MAX;
00086 code[1] = UINT16_MAX;
00087 }
00088
00089 bool VBI608Extractor::FindClocks(const unsigned char *buf, uint width)
00090 {
00091 raw_minimas.clear();
00092 raw_maximas.clear();
00093 maximas.clear();
00094 minimas.clear();
00095
00096
00097 uint minv = 255;
00098 for (uint j = width / 8; j < width / 4; j++)
00099 minv = min(uint(buf[j]), minv);
00100 uint maxv = 0;
00101 for (uint j = width / 8; j < width / 4; j++)
00102 maxv = max(uint(buf[j]), maxv);
00103 uint avgv = (maxv<minv) ? 0 : minv + ((maxv-minv) / 2);
00104 if (avgv <= 11)
00105 {
00106 LOG(VB_VBI, LOG_DEBUG, QString("FindClocks: avgv(%1) <= 11").arg(avgv));
00107 return false;
00108 }
00109
00110
00111 uint noise_flr_sm = max(uint(0.003 * width), 2U);
00112 uint noise_flr_lg = max(uint(0.007 * width), noise_flr_sm+1);
00113 int last_max = -1, last_min = -1;
00114 for (uint i = 0; i < (width/3); i++)
00115 {
00116 if (buf[i] > avgv+10)
00117 raw_maximas.push_back(last_max=i);
00118 else if (last_max>=0 && (i - last_max) <= noise_flr_sm)
00119 raw_maximas.push_back(i);
00120 else if (buf[i] < avgv-10)
00121 raw_minimas.push_back(last_min=i);
00122 else if (last_min>=0 && (i - last_min) <= noise_flr_lg)
00123 raw_minimas.push_back(i);
00124 }
00125
00126 for (uint i = 0; i < uint(raw_maximas.size()); i++)
00127 {
00128 uint start_idx = raw_maximas[i];
00129 while ((i+1) < uint(raw_maximas.size()) &&
00130 (raw_maximas[i+1] == raw_maximas[i] + 1)) i++;
00131 uint end_idx = raw_maximas[i];
00132 if (end_idx - start_idx > noise_flr_lg)
00133 maximas.push_back((start_idx + end_idx) * 0.5f);
00134 }
00135
00136 if (maximas.size() < 7)
00137 {
00138 LOG(VB_VBI, LOG_DEBUG, LOC +
00139 QString("FindClocks: maximas %1 < 7").arg(maximas.size()));
00140 print(raw_minimas, raw_maximas, minimas, maximas);
00141 return false;
00142 }
00143
00144
00145 bool dropped = true;
00146 while (maximas.size() > 7 && dropped)
00147 {
00148 float min_diff = width * 8;
00149 float max_diff = 0.0f;
00150 float avg_diff = 0.0f;
00151 for (uint i = 1; i < uint(maximas.size()); i++)
00152 {
00153 float diff = maximas[i] - maximas[i-1];
00154 min_diff = min(diff, min_diff);
00155 max_diff = max(diff, max_diff);
00156 avg_diff += diff;
00157 }
00158 avg_diff -= min_diff;
00159 avg_diff -= max_diff;
00160 avg_diff /= (maximas.size() - 3);
00161
00162 dropped = false;
00163 if (avg_diff * 1.1f < max_diff)
00164 {
00165 float last_diff = maximas.back() -
00166 maximas[(uint)(maximas.size())-2];
00167 if (last_diff*1.01f >= max_diff || last_diff > avg_diff * 1.2f)
00168 {
00169 maximas.pop_back();
00170 dropped = true;
00171 }
00172 float first_diff = maximas[1] - maximas[0];
00173 if ((maximas.size() > 7) && (first_diff*1.01f >= max_diff))
00174 {
00175 maximas.pop_front();
00176 dropped = true;
00177 }
00178 }
00179
00180 if (avg_diff * 0.9f > min_diff)
00181 {
00182 float last_diff = maximas.back() -
00183 maximas[(uint)(maximas.size())-2];
00184 if ((maximas.size() > 7) &&
00185 (last_diff*0.99f <= min_diff || last_diff < avg_diff * 0.80f))
00186 {
00187 maximas.pop_back();
00188 dropped = true;
00189 }
00190 float first_diff = maximas[1] - maximas[0];
00191 if ((maximas.size() > 7) && (first_diff*0.99f <= min_diff))
00192 {
00193 maximas.pop_front();
00194 dropped = true;
00195 }
00196 }
00197 }
00198
00199 if (maximas.size() != 7)
00200 {
00201 LOG(VB_VBI, LOG_DEBUG, LOC + QString("FindClocks: maximas: %1 != 7")
00202 .arg(maximas.size()));
00203 print(raw_minimas, raw_maximas, minimas, maximas);
00204 return false;
00205 }
00206
00207
00208 for (uint i = 0; i < uint(raw_minimas.size()); i++)
00209 {
00210 uint start_idx = raw_minimas[i];
00211 while ((i+1) < uint(raw_minimas.size()) &&
00212 (raw_minimas[i+1] == raw_minimas[i] + 1)) i++;
00213 uint end_idx = raw_minimas[i];
00214 float center = (start_idx + end_idx) * 0.5f;
00215 if (end_idx - start_idx > noise_flr_lg &&
00216 center > maximas[0] && center < maximas.back())
00217 {
00218 minimas.push_back(center);
00219 }
00220 }
00221
00222 if (minimas.size() != 6)
00223 {
00224 LOG(VB_VBI, LOG_DEBUG, LOC + QString("FindClocks: minimas: %1 != 6")
00225 .arg(minimas.size()));
00226 print(raw_minimas, raw_maximas, minimas, maximas);
00227 return false;
00228 }
00229
00230
00231 float maxima_avg_diff = find_clock_diff(maximas);
00232 float minima_avg_diff = find_clock_diff(minimas);
00233 rate = (maxima_avg_diff * 7 + minima_avg_diff * 6) / 13.0f;
00234 if (maxima_avg_diff == 0.0f || minima_avg_diff == 0.0f)
00235 return false;
00236
00237
00238
00239 start = maximas[0];
00240 for (uint i = 1; i < uint(maximas.size()); i++)
00241 start += maximas[i] - i * rate;
00242 start /= maximas.size();
00243
00244
00245 start -= rate * 0.33f;
00246
00247
00248
00249 if (start+((7+3+8+8-1) * rate) > width)
00250 {
00251 LOG(VB_VBI, LOG_DEBUG, LOC + QString("FindClocks: end %1 > width %2")
00252 .arg(start+((7+3+8+8-1) * rate)).arg(width));
00253
00254 return false;
00255 }
00256
00257 #if 0
00258 LOG(VB_VBI, LOG_DEBUG, LOC + QString("FindClocks: Clock start %1, rate %2")
00259 .arg(start).arg(rate));
00260 #endif
00261
00262 return true;
00263 }
00264
00265 bool VBI608Extractor::ExtractCC(const VideoFrame *picframe, uint max_lines)
00266 {
00267 int ypitch = picframe->pitches[0];
00268 int ywidth = picframe->width;
00269
00270 code[0] = UINT16_MAX;
00271 code[1] = UINT16_MAX;
00272
00273
00274 uint found_cnt = 0;
00275 for (uint i = 0; i < max_lines; i++)
00276 {
00277 const unsigned char *y = picframe->buf +
00278 picframe->offsets[0] + (i * ypitch);
00279 if (FindClocks(y, ywidth))
00280 {
00281 uint maxv = 0;
00282 for (uint j = 0; j < start + 8 * rate; j++)
00283 maxv = max(uint((y+(i * ypitch))[j]), maxv);
00284 uint avgv = maxv / 2;
00285
00286 if (y[uint(start + (0+7) * rate)] > avgv ||
00287 y[uint(start + (1+7) * rate)] > avgv ||
00288 y[uint(start + (2+7) * rate)] < avgv)
00289 {
00290 continue;
00291 }
00292
00293 code[found_cnt] = 0;
00294 for (uint j = 0; j < 8+8; j++)
00295 {
00296 bool bit = y[uint(start + (j+7+3) * rate)] > avgv;
00297 code[found_cnt] =
00298 (code[found_cnt]>>1) | (bit?(1<<15):0);
00299 }
00300
00301 found_cnt++;
00302 if (found_cnt>=2)
00303 break;
00304 #if 0
00305 unsigned char *Y = const_cast<unsigned char*>(y);
00306 unsigned char *u = const_cast<unsigned char*>
00307 (picframe->buf + picframe->offsets[1] +
00308 (i*picframe->pitches[1]));
00309 unsigned char *v = const_cast<unsigned char*>
00310 (picframe->buf + picframe->offsets[2] +
00311 (i*picframe->pitches[2]));
00312 unsigned uwidth = picframe->pitches[1];
00313 v[uwidth / 3] = 0x40;
00314 for (uint j = 0; j < 7+3+8+8; j++)
00315 {
00316 uint yloc = uint (start + j * rate + 0.5);
00317 Y[yloc] = 0xFF;
00318 uint uloc = uint (uwidth * (start + j * rate + 0.5) / ywidth);
00319 u[uloc] = 0x40;
00320 }
00321 #endif
00322 }
00323 }
00324
00325 return found_cnt;
00326 }
00327
00328 bool VBI608Extractor::ExtractCC12(const unsigned char *buf, uint width)
00329 {
00330 code[0] = UINT16_MAX;
00331 if (FindClocks(buf, width))
00332 {
00333 uint maxv = 0;
00334 for (uint j = 0; j < start + 8 * rate; j++)
00335 maxv = max(uint(buf[j]), maxv);
00336 uint avgv = maxv / 2;
00337
00338 if (buf[uint(start + (0+7) * rate)] > avgv ||
00339 buf[uint(start + (1+7) * rate)] > avgv ||
00340 buf[uint(start + (2+7) * rate)] < avgv)
00341 {
00342 LOG(VB_VBI, LOG_DEBUG, LOC + "did not find VBI 608 header");
00343 return false;
00344 }
00345
00346 code[0] = 0;
00347 for (uint j = 0; j < 8+8; j++)
00348 {
00349 bool bit = buf[uint(start + (j+7+3) * rate)] > avgv;
00350 code[0] = (code[0]>>1) | (bit?(1<<15):0);
00351 }
00352
00353 return true;
00354 }
00355 return false;
00356 }
00357
00358 bool VBI608Extractor::ExtractCC34(const unsigned char *buf, uint width)
00359 {
00360 code[1] = UINT16_MAX;
00361 if (FindClocks(buf, width))
00362 {
00363 uint maxv = 0;
00364 for (uint j = 0; j < start + 8 * rate; j++)
00365 maxv = max(uint(buf[j]), maxv);
00366 uint avgv = maxv / 2;
00367
00368 if (buf[uint(start + (0+7) * rate)] > avgv ||
00369 buf[uint(start + (1+7) * rate)] > avgv ||
00370 buf[uint(start + (2+7) * rate)] < avgv)
00371 {
00372 return false;
00373 }
00374
00375 code[1] = 0;
00376 for (uint j = 0; j < 8+8; j++)
00377 {
00378 bool bit = buf[uint(start + (j+7+3) * rate)] > avgv;
00379 code[1] = (code[1]>>1) | (bit?(1<<15):0);
00380 }
00381 return true;
00382 }
00383 return false;
00384 }
00385
00386 uint VBI608Extractor::FillCCData(uint8_t cc_data[8]) const
00387 {
00388 uint cc_count = 0;
00389 if (code[0] != UINT16_MAX)
00390 {
00391 cc_data[2] = 0x04;
00392 cc_data[3] = (code[0]) & 0xff;
00393 cc_data[4] = (code[0]>>8) & 0xff;
00394 cc_count++;
00395 }
00396
00397 if (code[1] != UINT16_MAX)
00398 {
00399 cc_data[2+3*cc_count] = 0x04|0x01;
00400 cc_data[3+3*cc_count] = (code[1]) & 0xff;
00401 cc_data[4+3*cc_count] = (code[1]>>8) & 0xff;
00402 cc_count++;
00403 }
00404
00405 if (cc_count)
00406 {
00407 cc_data[0] = 0x40 | cc_count;
00408 cc_data[1] = 0x00;
00409 return 2+(3*cc_count);
00410 }
00411 return 0;
00412 }