Ticket #668: ccdecoder-r7892.patch
File ccdecoder-r7892.patch, 49.9 KB (added by , 20 years ago) |
---|
-
libs/libmythtv/vbitext/cc.cpp
194 194 cc->code1 = -1; 195 195 cc->code2 = -1; 196 196 197 for (int i = 0; i < 2; i++)198 {199 cc->badvbi[i] = 0;200 cc->lasttc[i] = 0;201 cc->lastcode[i] = -1;202 cc->lastcodetc[i] = 0;203 cc->ccmode[i] = -1;204 cc->txtmode[i*2 + 0] = 0;205 cc->txtmode[i*2 + 1] = 0;206 cc->xds[i] = 0;207 }208 209 for (int i = 0; i < 8; i++)210 {211 cc->lastrow[i] = 0;212 cc->newrow[i] = 0;213 cc->newcol[i] = 0;214 cc->timecode[i] = 0;215 cc->row[i] = 0;216 cc->col[i] = 0;217 cc->rowcount[i] = 0;218 cc->style[i] = 0;219 cc->linecont[i] = 0;220 cc->resumetext[i] = 0;221 cc->lastclr[i] = 0;222 cc->ccbuf[i] = "";223 }224 225 197 return cc; 226 198 } 227 199 -
libs/libmythtv/vbitext/cc.h
1 1 #ifndef CC_H 2 2 #define CC_H 3 3 4 #include <qstring.h>5 6 4 #define CC_VBIBUFSIZE 65536 7 5 8 6 //cc is 32 columns per row, this allows for extra characters … … 14 12 char buffer[CC_VBIBUFSIZE]; 15 13 int code1; 16 14 int code2; 17 18 // per-field19 int badvbi[2];20 int lasttc[2];21 int lastcode[2];22 int lastcodetc[2];23 int ccmode[2]; // 0=cc1/txt1, 1=cc2/txt224 int txtmode[4];25 int xds[2];26 27 // per-mode state28 int lastrow[8];29 int newrow[8];30 int newcol[8];31 int timecode[8];32 int row[8];33 int col[8];34 int rowcount[8];35 int style[8];36 int linecont[8];37 int resumetext[8];38 int lastclr[8];39 QString ccbuf[8];40 15 }; 41 16 42 17 int cc_decode(unsigned char *vbiline); -
libs/libmythtv/NuppelVideoRecorder.cpp
169 169 prev_bframe_save_pos = -1; 170 170 171 171 volume = 100; 172 173 ccd = new CCDecoder(this); 174 172 175 go7007 = false; 173 176 } 174 177 … … 228 231 delete videoFilters; 229 232 if (FiltMan) 230 233 delete FiltMan; 234 235 delete ccd; 231 236 } 232 237 233 238 void NuppelVideoRecorder::deleteLater(void) … … 2463 2468 int tc = (tnow.tv_sec - stm.tv_sec) * 1000 + 2464 2469 tnow.tv_usec / 1000 - stm.tv_usec / 1000; 2465 2470 2466 for (int field = 0; field < 2; field++) 2467 FormatCCField(cc, tc, field); 2471 ccd->FormatCC(tc, cc->code1, cc->code2); 2468 2472 } 2469 2473 2470 void NuppelVideoRecorder::FormatCCField(struct cc *cc, int tc, int field) 2474 void NuppelVideoRecorder::AddTextData(unsigned char *buf, int len, 2475 long long timecode, char type) 2471 2476 { 2472 const int rowdata[] = { 11, -1, 1, 2, 3, 4, 12, 13,2473 14, 15, 5, 6, 7, 8, 9, 10 };2474 const QChar specialchar[] =2475 { '®', '°', 'œ', '¿', 0x2122 /* TM */, '¢', '£', 0x266A /* 1/8 note */,2476 'à', ' ', 'è', 'â', 'ê', 'î', 'ô', 'û'2477 };2478 const QChar extendedchar2[] =2479 { 'Á', 'É', 'Ó', 'Ú', 'Ü', 'ü', '`', '¡',2480 '*', '\'', 0x2014 /* dash */, '©', 0x2120 /* SM */, '·', 0x201C, 0x201D /* dquotes */,2481 'À', 'Â', 'Ç', 'È', 'Ê', 'Ë', 'ë', 'Î',2482 'Ï', 'ï', 'Ô', 'Ù', 'ù', 'Û', '«', '»'2483 };2484 const QChar extendedchar3[] =2485 { 'Ã', 'ã', 'Í', 'Ì', 'ì', 'Ò', 'ò', 'Õ',2486 'õ', '{', '}', '\\', '^', '_', 'Š', '~',2487 'Ä', 'ä', 'Ö', 'ö', 'ß', '¥', '€', '|',2488 'Å', 'å', 'Ø', 'ø', 0x250C, 0x2510, 0x2514, 0x2518 /* box drawing */2489 };2490 int b1, b2, len, x;2491 int mode;2492 int data;2493 2494 if (field == 0)2495 data = cc->code1;2496 else2497 data = cc->code2;2498 2499 if (data == -1) // invalid data. flush buffers to be safe.2500 {2501 // TODO: write textbuffer[act]2502 //printf (" TODO: write textbuffer[act]\n");2503 if (cc->ccmode[field] != -1)2504 {2505 for (mode = field*4; mode < (field*4 + 4); mode++)2506 ResetCC(cc, mode);2507 cc->xds[field] = 0;2508 cc->badvbi[field] = 0;2509 cc->ccmode[field] = -1;2510 cc->txtmode[field*2] = 0;2511 cc->txtmode[field*2 + 1] = 0;2512 }2513 return;2514 }2515 2516 b1 = data & 0x7f;2517 b2 = (data >> 8) & 0x7f;2518 if (cc->ccmode[field] >= 0)2519 {2520 mode = field << 2 |2521 (cc->txtmode[field*2 + cc->ccmode[field]] << 1) |2522 cc->ccmode[field];2523 len = cc->ccbuf[mode].length();2524 }2525 else2526 {2527 mode = -1;2528 len = 0;2529 }2530 2531 // bttv-0.9 VBI reads are pretty reliable (1 read/33367us).2532 // bttv-0.7 reads don't seem to work as well so if read intervals2533 // vary from this, be more conservative in detecting duplicate2534 // CC codes.2535 int dup_text_fudge, dup_ctrl_fudge;2536 if (cc->badvbi[field] < 100 && b1 != 0 && b2 != 0)2537 {2538 int d = tc - cc->lasttc[field];2539 if (d < 25 || d > 42)2540 cc->badvbi[field]++;2541 else if (cc->badvbi[field] > 0)2542 cc->badvbi[field]--;2543 }2544 if (cc->badvbi[field] < 4)2545 {2546 dup_text_fudge = -2; // should pick up all codes2547 dup_ctrl_fudge = 33 - 4; // should pick up 1st, 4th, 6th, 8th, ... codes2548 }2549 else2550 {2551 dup_text_fudge = 4;2552 dup_ctrl_fudge = 33 - 4;2553 }2554 2555 if (data == cc->lastcode[field])2556 {2557 int false_dup = 1;2558 if ((b1 & 0x70) == 0x10)2559 {2560 if (tc > (cc->lastcodetc[field] + 67 + dup_ctrl_fudge))2561 false_dup = 0;2562 }2563 else if (b1)2564 {2565 // text, XDS2566 if (tc > (cc->lastcodetc[field] + 33 + dup_text_fudge))2567 false_dup = 0;2568 }2569 2570 if (false_dup)2571 goto skip;2572 }2573 2574 if ((field == 1) &&2575 (cc->xds[field] || b1 && ((b1 & 0x70) == 0x00)))2576 // 0x01 <= b1 <= 0x0F2577 // start XDS2578 // or inside XDS packet2579 {2580 int xds_packet = 1;2581 2582 // TODO: process XDS packets2583 if (b1 == 0x0F)2584 {2585 // end XDS2586 cc->xds[field] = 0;2587 xds_packet = 1;2588 }2589 else if ((b1 & 0x70) == 0x10)2590 {2591 // ctrl code -- interrupt XDS2592 cc->xds[field] = 0;2593 xds_packet = 0;2594 }2595 else2596 {2597 cc->xds[field] = 1;2598 xds_packet = 1;2599 }2600 2601 if (xds_packet)2602 goto skip;2603 }2604 2605 if (b1 & 0x60)2606 // 0x20 <= b1 <= 0x7F2607 // text codes2608 {2609 if (mode >= 0)2610 {2611 cc->lastcodetc[field] += 33;2612 cc->timecode[mode] = tc;2613 2614 // commit row number only when first text code2615 // comes in2616 if (cc->newrow[mode])2617 len = NewRowCC(cc, mode, len);2618 2619 cc->ccbuf[mode] += CharCC(b1);2620 len++;2621 cc->col[mode]++;2622 if (b2 & 0x60)2623 {2624 cc->ccbuf[mode] += CharCC(b2);2625 len++;2626 cc->col[mode]++;2627 }2628 }2629 }2630 2631 else if ((b1 & 0x10) && (b2 > 0x1F))2632 // 0x10 <= b1 <= 0x1F2633 // control codes2634 {2635 cc->lastcodetc[field] += 67;2636 2637 int newccmode = (b1 >> 3) & 1;2638 int newtxtmode = cc->txtmode[field*2 + newccmode];2639 if ((b1 & 0x06) == 0x04)2640 {2641 switch (b2)2642 {2643 case 0x29:2644 case 0x2C:2645 case 0x20:2646 case 0x2F:2647 case 0x25:2648 case 0x26:2649 case 0x27:2650 // CC1,22651 newtxtmode = 0;2652 break;2653 case 0x2A:2654 case 0x2B:2655 // TXT1,22656 newtxtmode = 1;2657 break;2658 }2659 }2660 cc->ccmode[field] = newccmode;2661 cc->txtmode[field*2 + newccmode] = newtxtmode;2662 mode = (field << 2) | (newtxtmode << 1) | cc->ccmode[field];2663 2664 cc->timecode[mode] = tc;2665 len = cc->ccbuf[mode].length();2666 2667 if (b2 & 0x40) //preamble address code (row & indent)2668 {2669 if (newtxtmode)2670 // no address codes in TXT mode?2671 goto skip;2672 2673 cc->newrow[mode] = rowdata[((b1 << 1) & 14) | ((b2 >> 5) & 1)];2674 if (cc->newrow[mode] == -1)2675 // bogus code?2676 cc->newrow[mode] = cc->lastrow[mode] + 1;2677 2678 if (b2 & 0x10) //row contains indent flag2679 cc->newcol[mode] = (b2 & 0x0E) << 1;2680 else2681 cc->newcol[mode] = 0;2682 2683 // row, indent settings are not final2684 // until text code arrives2685 }2686 else2687 {2688 switch (b1 & 0x07)2689 {2690 case 0x00: //attribute2691 /*2692 printf ("<ATTRIBUTE %d %d>\n", b1, b2);2693 fflush (stdout);2694 */2695 break;2696 case 0x01: //midrow or char2697 if (cc->newrow[mode])2698 len = NewRowCC(cc, mode, len);2699 2700 switch (b2 & 0x70)2701 {2702 case 0x20: //midrow attribute change2703 // TODO: we _do_ want colors, is that an attribute?2704 cc->ccbuf[mode] += ' ';2705 len = cc->ccbuf[mode].length();2706 cc->col[mode]++;2707 break;2708 case 0x30: //special character..2709 cc->ccbuf[mode] += specialchar[b2 & 0x0f];2710 len++;2711 cc->col[mode]++;2712 break;2713 }2714 break;2715 case 0x02: //extended char2716 // extended char is preceded by alternate char2717 // - if there's no alternate, it could be noise2718 if (!len)2719 break;2720 2721 if (b2 & 0x30)2722 {2723 cc->ccbuf[mode].remove(len - 1, 1);2724 cc->ccbuf[mode] += extendedchar2[b2 - 0x20];2725 len = cc->ccbuf[mode].length();2726 break;2727 }2728 break;2729 case 0x03: //extended char2730 // extended char is preceded by alternate char2731 // - if there's no alternate, it could be noise2732 if (!len)2733 break;2734 2735 if (b2 & 0x30)2736 {2737 cc->ccbuf[mode].remove(len - 1, 1);2738 cc->ccbuf[mode] += extendedchar3[b2 - 0x20];2739 len = cc->ccbuf[mode].length();2740 break;2741 }2742 break;2743 case 0x04: //misc2744 case 0x05: //misc + F2745 // printf("ccmode %d cmd %02x\n",ccmode,b2);2746 switch (b2)2747 {2748 case 0x21: //backspace2749 // add backspace if line has been encoded already2750 if (cc->newrow[mode])2751 len = NewRowCC(cc, mode, len);2752 2753 if (len == 0 ||2754 cc->ccbuf[mode].left(1) == "\b")2755 {2756 cc->ccbuf[mode] += (char)'\b';2757 len++;2758 cc->col[mode]--;2759 }2760 else2761 {2762 cc->ccbuf[mode].remove(len - 1, 1);2763 len = cc->ccbuf[mode].length();2764 cc->col[mode]--;2765 }2766 break;2767 case 0x25: //2 row caption2768 case 0x26: //3 row caption2769 case 0x27: //4 row caption2770 if (cc->style[mode] == CC_STYLE_PAINT && len)2771 {2772 // flush2773 BufferCC(cc, mode, len, 0);2774 cc->ccbuf[mode] = "";2775 cc->row[mode] = 0;2776 cc->col[mode] = 0;2777 }2778 else if (cc->style[mode] == CC_STYLE_POPUP)2779 ResetCC(cc, mode);2780 2781 cc->rowcount[mode] = b2 - 0x25 + 2;2782 cc->style[mode] = CC_STYLE_ROLLUP;2783 break;2784 case 0x2D: //carriage return2785 if (cc->style[mode] != CC_STYLE_ROLLUP)2786 break;2787 2788 if (cc->newrow[mode])2789 cc->row[mode] = cc->newrow[mode];2790 2791 // flush if there is text or need to scroll2792 // TODO: decode ITV (WebTV) link in TXT22793 if (len ||2794 cc->row[mode] != 0 &&2795 !cc->linecont[mode] &&2796 (!newtxtmode || cc->row[mode] >= 16))2797 BufferCC(cc, mode, len, 0);2798 2799 if (newtxtmode)2800 {2801 if (cc->row[mode] < 16)2802 cc->newrow[mode] = cc->row[mode] + 1;2803 else2804 // scroll up previous lines2805 cc->newrow[mode] = 16;2806 }2807 2808 cc->ccbuf[mode] = "";2809 cc->col[mode] = 0;2810 cc->linecont[mode] = 0;2811 break;2812 2813 case 0x29: //resume direct caption (paint-on style)2814 if (cc->style[mode] == CC_STYLE_ROLLUP && len)2815 {2816 // flush2817 BufferCC(cc, mode, len, 0);2818 cc->ccbuf[mode] = "";2819 cc->row[mode] = 0;2820 cc->col[mode] = 0;2821 }2822 else if (cc->style[mode] == CC_STYLE_POPUP)2823 ResetCC(cc, mode);2824 2825 cc->style[mode] = CC_STYLE_PAINT;2826 cc->rowcount[mode] = 0;2827 cc->linecont[mode] = 0;2828 break;2829 2830 case 0x2B: //resume text display2831 cc->resumetext[mode] = 1;2832 if (cc->row[mode] == 0)2833 {2834 cc->newrow[mode] = 1;2835 cc->newcol[mode] = 0;2836 }2837 cc->style[mode] = CC_STYLE_ROLLUP;2838 break;2839 case 0x2C: //erase displayed memory2840 if ((tc - cc->lastclr[mode]) > 5000 ||2841 cc->lastclr[mode] == 0)2842 // don't overflow the frontend with2843 // too many redundant erase codes2844 BufferCC(cc, mode, 0, 1);2845 if (cc->style[mode] != CC_STYLE_POPUP)2846 {2847 cc->row[mode] = 0;2848 cc->col[mode] = 0;2849 }2850 cc->linecont[mode] = 0;2851 break;2852 2853 case 0x20: //resume caption (pop-up style)2854 if (cc->style[mode] != CC_STYLE_POPUP)2855 {2856 if (len)2857 // flush2858 BufferCC(cc, mode, len, 0);2859 cc->ccbuf[mode] = "";2860 cc->row[mode] = 0;2861 cc->col[mode] = 0;2862 }2863 cc->style[mode] = CC_STYLE_POPUP;2864 cc->rowcount[mode] = 0;2865 cc->linecont[mode] = 0;2866 break;2867 case 0x2F: //end caption + swap memory2868 if (cc->style[mode] != CC_STYLE_POPUP)2869 {2870 if (len)2871 // flush2872 BufferCC(cc, mode, len, 0);2873 }2874 else if ((tc - cc->lastclr[mode]) > 5000 ||2875 cc->lastclr[mode] == 0)2876 // clear and flush2877 BufferCC(cc, mode, len, 1);2878 else if (len)2879 // flush2880 BufferCC(cc, mode, len, 0);2881 cc->ccbuf[mode] = "";2882 cc->row[mode] = 0;2883 cc->col[mode] = 0;2884 cc->style[mode] = CC_STYLE_POPUP;2885 cc->rowcount[mode] = 0;2886 cc->linecont[mode] = 0;2887 break;2888 2889 case 0x2A: //text restart2890 // clear display2891 BufferCC(cc, mode, 0, 1);2892 ResetCC(cc, mode);2893 // TXT starts at row 12894 cc->newrow[mode] = 1;2895 cc->newcol[mode] = 0;2896 cc->style[mode] = CC_STYLE_ROLLUP;2897 break;2898 2899 case 0x2E: //erase non-displayed memory2900 ResetCC(cc, mode);2901 break;2902 }2903 break;2904 case 0x07: //misc (TAB)2905 if (cc->newrow[mode])2906 {2907 cc->newcol[mode] += (b2 & 0x03);2908 len = NewRowCC(cc, mode, len);2909 }2910 else2911 // illegal?2912 for (x = 0; x < (b2 & 0x03); x++)2913 {2914 cc->ccbuf[mode] += ' ';2915 len++;2916 cc->col[mode]++;2917 }2918 break;2919 }2920 }2921 }2922 2923 skip:2924 for (mode = field*4; mode < (field*4 + 4); mode++)2925 {2926 len = cc->ccbuf[mode].length();2927 if (((tc - cc->timecode[mode]) > 100) &&2928 (cc->style[mode] != CC_STYLE_POPUP) && len)2929 {2930 // flush unfinished line if waiting too long2931 // in paint-on or scroll-up mode2932 cc->timecode[mode] = tc;2933 BufferCC(cc, mode, len, 0);2934 cc->ccbuf[mode] = "";2935 cc->row[mode] = cc->lastrow[mode];2936 cc->linecont[mode] = 1;2937 }2938 }2939 2940 if (data != cc->lastcode[field])2941 {2942 cc->lastcode[field] = data;2943 cc->lastcodetc[field] = tc;2944 }2945 cc->lasttc[field] = tc;2946 }2947 2948 QChar NuppelVideoRecorder::CharCC(int code)2949 {2950 switch (code)2951 {2952 case 42: return 'á';2953 case 92: return 'é';2954 case 94: return 'í';2955 case 95: return 'ó';2956 case 96: return 'ú';2957 case 123: return 'ç';2958 case 124: return '÷';2959 case 125: return 'Ñ';2960 case 126: return 'ñ';2961 case 127: return 0x2588; /* full block */2962 default : return QChar(code);2963 }2964 }2965 2966 void NuppelVideoRecorder::ResetCC(struct cc *cc, int mode)2967 {2968 // cc->lastrow[mode] = 0;2969 // cc->newrow[mode] = 0;2970 // cc->newcol[mode] = 0;2971 // cc->timecode[mode] = 0;2972 cc->row[mode] = 0;2973 cc->col[mode] = 0;2974 cc->rowcount[mode] = 0;2975 // cc->style[mode] = CC_STYLE_POPUP;2976 cc->linecont[mode] = 0;2977 cc->resumetext[mode] = 0;2978 cc->lastclr[mode] = 0;2979 cc->ccbuf[mode] = "";2980 }2981 2982 void NuppelVideoRecorder::BufferCC(struct cc *cc, int mode, int len, int clr)2983 {2984 2477 int act = act_text_buffer; 2985 2478 if (!textbuffer[act]->freeToBuffer) 2986 2479 { … … 2988 2481 return; 2989 2482 } 2990 2483 2991 textbuffer[act]->timecode = cc->timecode[mode]; 2484 textbuffer[act]->timecode = timecode; 2485 memcpy(textbuffer[act]->buffer, buf, len); 2486 textbuffer[act]->bufferlen = len + sizeof(ccsubtitle); 2992 2487 2993 // NOTE: text_buffer_size happens to be > (sizeof(ccsubtitle)+255)2994 QCString tmpbuf;2995 if (len)2996 {2997 // calculate UTF-8 encoding length2998 tmpbuf = cc->ccbuf[mode].utf8();2999 len = tmpbuf.length();3000 if (len > 255)3001 len = 255;3002 }3003 3004 unsigned char f;3005 unsigned char *bp = textbuffer[act]->buffer;3006 *(bp++) = cc->row[mode];3007 *(bp++) = cc->rowcount[mode];3008 *(bp++) = cc->style[mode];3009 // overload resumetext field3010 f = cc->resumetext[mode];3011 f |= mode << 4;3012 if (cc->linecont[mode])3013 f |= CC_LINE_CONT;3014 *(bp++) = f;3015 *(bp++) = clr;3016 *(bp++) = len;3017 if (len)3018 {3019 memcpy(bp,3020 tmpbuf,3021 len);3022 textbuffer[act]->bufferlen = len + sizeof(ccsubtitle);3023 }3024 else3025 textbuffer[act]->bufferlen = sizeof(ccsubtitle);3026 3027 2488 textbuffer[act]->freeToBuffer = 0; 3028 2489 act_text_buffer++; 3029 2490 if (act_text_buffer >= text_buffer_count) 3030 2491 act_text_buffer = 0; 3031 2492 textbuffer[act]->freeToEncode = 1; 3032 3033 cc->resumetext[mode] = 0;3034 if (clr && !len)3035 cc->lastclr[mode] = cc->timecode[mode];3036 else if (len)3037 cc->lastclr[mode] = 0;3038 2493 } 3039 2494 3040 int NuppelVideoRecorder::NewRowCC(struct cc *cc, int mode, int len)3041 {3042 if (cc->style[mode] == CC_STYLE_ROLLUP)3043 {3044 // previous line was likely missing a carriage return3045 cc->row[mode] = cc->newrow[mode];3046 if (len)3047 {3048 BufferCC(cc, mode, len, 0);3049 cc->ccbuf[mode] = "";3050 len = 0;3051 }3052 cc->col[mode] = 0;3053 cc->linecont[mode] = 0;3054 }3055 else3056 {3057 // popup/paint style3058 3059 if (cc->row[mode] == 0)3060 {3061 if (len == 0)3062 cc->row[mode] = cc->newrow[mode];3063 else3064 {3065 // previous line was missing a row address3066 // - assume it was one row up3067 cc->ccbuf[mode] += (char)'\n';3068 len++;3069 if (cc->row[mode] == 0)3070 cc->row[mode] = cc->newrow[mode] - 1;3071 else3072 cc->row[mode]--;3073 }3074 }3075 else if (cc->newrow[mode] > cc->lastrow[mode])3076 {3077 // next line can be more than one row away3078 for (int i = 0; i < (cc->newrow[mode] - cc->lastrow[mode]); i++)3079 {3080 cc->ccbuf[mode] += (char)'\n';3081 len++;3082 }3083 cc->col[mode] = 0;3084 }3085 else if (cc->newrow[mode] == cc->lastrow[mode])3086 {3087 // same row3088 if (cc->newcol[mode] >= cc->col[mode])3089 // new line appends to current line3090 cc->newcol[mode] -= cc->col[mode];3091 else3092 {3093 // new line overwrites current line;3094 // could be legal (overwrite spaces?) but3095 // more likely we have bad address codes3096 // - just move to next line; may exceed row 153097 // but frontend will adjust3098 cc->ccbuf[mode] += (char)'\n';3099 len++;3100 cc->col[mode] = 0;3101 }3102 }3103 else3104 {3105 // next line goes upwards (not legal?)3106 // - flush3107 BufferCC(cc, mode, len, 0);3108 cc->ccbuf[mode] = "";3109 cc->row[mode] = cc->newrow[mode];3110 cc->col[mode] = 0;3111 cc->linecont[mode] = 0;3112 len = 0;3113 }3114 }3115 3116 cc->lastrow[mode] = cc->newrow[mode];3117 cc->newrow[mode] = 0;3118 3119 for (int x = 0; x < cc->newcol[mode]; x++)3120 {3121 cc->ccbuf[mode] += ' ';3122 len++;3123 cc->col[mode]++;3124 }3125 cc->newcol[mode] = 0;3126 3127 return len;3128 }3129 3130 2495 static void vbi_event(struct VBIData *data, struct vt_event *ev) 3131 2496 { 3132 2497 switch (ev->type) -
libs/libmythtv/libmythtv.pro
90 90 HEADERS += recordingtypes.h jobqueue.h 91 91 HEADERS += filtermanager.h recordingprofile.h 92 92 HEADERS += remoteencoder.h videosource.h 93 HEADERS += ccdecoder.h 93 94 HEADERS += sr_dialog.h sr_root.h 94 95 HEADERS += sr_items.h scheduledrecording.h 95 96 HEADERS += signalmonitorvalue.h … … 104 105 SOURCES += recordingtypes.cpp jobqueue.cpp 105 106 SOURCES += filtermanager.cpp recordingprofile.cpp 106 107 SOURCES += remoteencoder.cpp videosource.cpp 108 SOURCES += ccdecoder.cpp 107 109 SOURCES += sr_dialog.cpp sr_root.cpp 108 110 SOURCES += sr_items.cpp scheduledrecording.cpp 109 111 SOURCES += signalmonitorvalue.cpp -
libs/libmythtv/ccdecoder.h
1 #ifndef CCDECODER_H_ 2 #define CCDECODER_H_ 3 4 #include <qstringlist.h> 5 6 #include "format.h" 7 8 class CCReader 9 { 10 public: 11 virtual ~CCReader() { } 12 virtual void AddTextData(unsigned char *buf, int len, long long timecode, char type) = 0; 13 }; 14 15 class CCDecoder 16 { 17 public: 18 CCDecoder(CCReader *ccr); 19 ~CCDecoder(); 20 21 void FormatCC(int tc, int code1, int code2); 22 void FormatCCField(int tc, int field, int data); 23 24 private: 25 QChar CharCC(int code) { return stdchar[code]; } 26 void ResetCC(int mode); 27 void BufferCC(int mode, int len, int clr); 28 int NewRowCC(int mode, int len); 29 30 CCReader *reader; 31 32 // per-field 33 int badvbi[2]; 34 int lasttc[2]; 35 int lastcode[2]; 36 int lastcodetc[2]; 37 int ccmode[2]; // 0=cc1/txt1, 1=cc2/txt2 38 int txtmode[4]; 39 int xds[2]; 40 41 // per-mode state 42 int lastrow[8]; 43 int newrow[8]; 44 int newcol[8]; 45 int timecode[8]; 46 int row[8]; 47 int col[8]; 48 int rowcount[8]; 49 int style[8]; 50 int linecont[8]; 51 int resumetext[8]; 52 int lastclr[8]; 53 QString ccbuf[8]; 54 55 // translation table 56 QChar stdchar[128]; 57 58 // temporary buffer 59 unsigned char *rbuf; 60 }; 61 62 #endif -
libs/libmythtv/ccdecoder.cpp
1 #include <qstringlist.h> 2 #include <iostream> 3 using namespace std; 4 5 #include "format.h" 6 #include "ccdecoder.h" 7 8 CCDecoder::CCDecoder(CCReader *ccr) 9 { 10 reader = ccr; 11 12 for (int i = 0; i < 2; i++) 13 { 14 badvbi[i] = 0; 15 lasttc[i] = 0; 16 lastcode[i] = -1; 17 lastcodetc[i] = 0; 18 ccmode[i] = -1; 19 txtmode[i*2 + 0] = 0; 20 txtmode[i*2 + 1] = 0; 21 xds[i] = 0; 22 } 23 24 for (int i = 0; i < 8; i++) 25 { 26 lastrow[i] = 0; 27 newrow[i] = 0; 28 newcol[i] = 0; 29 timecode[i] = 0; 30 row[i] = 0; 31 col[i] = 0; 32 rowcount[i] = 0; 33 style[i] = 0; 34 linecont[i] = 0; 35 resumetext[i] = 0; 36 lastclr[i] = 0; 37 ccbuf[i] = ""; 38 } 39 40 // fill translation table 41 for (int i = 0; i < 128; i++) 42 stdchar[i] = QChar(i); 43 44 // exceptions 45 stdchar[42] = 'á'; 46 stdchar[92] = 'é'; 47 stdchar[94] = 'í'; 48 stdchar[95] = 'ó'; 49 stdchar[96] = 'ú'; 50 stdchar[123] = 'ç'; 51 stdchar[124] = '÷'; 52 stdchar[125] = 'Ñ'; 53 stdchar[126] = 'ñ'; 54 stdchar[127] = 0x2588; /* full block */ 55 56 rbuf = new unsigned char[sizeof(ccsubtitle)+255]; 57 } 58 59 CCDecoder::~CCDecoder(void) 60 { 61 delete rbuf; 62 } 63 64 void CCDecoder::FormatCC(int tc, int code1, int code2) 65 { 66 FormatCCField(tc, 0, code1); 67 FormatCCField(tc, 1, code2); 68 } 69 70 void CCDecoder::FormatCCField(int tc, int field, int data) 71 { 72 const int rowdata[] = { 11, -1, 1, 2, 3, 4, 12, 13, 73 14, 15, 5, 6, 7, 8, 9, 10 }; 74 const QChar specialchar[] = 75 { '®', '°', 'œ', '¿', 0x2122 /* TM */, '¢', '£', 0x266A /* 1/8 note */, 76 'à', ' ', 'è', 'â', 'ê', 'î', 'ô', 'û' 77 }; 78 const QChar extendedchar2[] = 79 { 'Á', 'É', 'Ó', 'Ú', 'Ü', 'ü', '`', '¡', 80 '*', '\'', 0x2014 /* dash */, '©', 0x2120 /* SM */, '·', 0x201C, 0x201D /* dquotes */, 81 'À', 'Â', 'Ç', 'È', 'Ê', 'Ë', 'ë', 'Î', 82 'Ï', 'ï', 'Ô', 'Ù', 'ù', 'Û', '«', '»' 83 }; 84 const QChar extendedchar3[] = 85 { 'Ã', 'ã', 'Í', 'Ì', 'ì', 'Ò', 'ò', 'Õ', 86 'õ', '{', '}', '\\', '^', '_', 'Š', '~', 87 'Ä', 'ä', 'Ö', 'ö', 'ß', '¥', '€', '|', 88 'Å', 'å', 'Ø', 'ø', 0x250C, 0x2510, 0x2514, 0x2518 /* box drawing */ 89 }; 90 int b1, b2, len, x; 91 int mode; 92 93 if (data == -1) // invalid data. flush buffers to be safe. 94 { 95 // TODO: flush reader buffer 96 if (ccmode[field] != -1) 97 { 98 for (mode = field*4; mode < (field*4 + 4); mode++) 99 ResetCC(mode); 100 xds[field] = 0; 101 badvbi[field] = 0; 102 ccmode[field] = -1; 103 txtmode[field*2] = 0; 104 txtmode[field*2 + 1] = 0; 105 } 106 return; 107 } 108 109 b1 = data & 0x7f; 110 b2 = (data >> 8) & 0x7f; 111 // printf("%10d: %02x %02x\n", tc, b1, b2); 112 if (ccmode[field] >= 0) 113 { 114 mode = field << 2 | 115 (txtmode[field*2 + ccmode[field]] << 1) | 116 ccmode[field]; 117 len = ccbuf[mode].length(); 118 } 119 else 120 { 121 mode = -1; 122 len = 0; 123 } 124 125 // bttv-0.9 VBI reads are pretty reliable (1 read/33367us). 126 // bttv-0.7 reads don't seem to work as well so if read intervals 127 // vary from this, be more conservative in detecting duplicate 128 // CC codes. 129 int dup_text_fudge, dup_ctrl_fudge; 130 if (badvbi[field] < 100 && b1 != 0 && b2 != 0) 131 { 132 int d = tc - lasttc[field]; 133 if (d < 25 || d > 42) 134 badvbi[field]++; 135 else if (badvbi[field] > 0) 136 badvbi[field]--; 137 } 138 if (badvbi[field] < 4) 139 { 140 dup_text_fudge = -2; // should pick up all codes 141 dup_ctrl_fudge = 33 - 4; // should pick up 1st, 4th, 6th, 8th, ... codes 142 } 143 else 144 { 145 dup_text_fudge = 4; 146 dup_ctrl_fudge = 33 - 4; 147 } 148 149 if (data == lastcode[field]) 150 { 151 int false_dup = 1; 152 if ((b1 & 0x70) == 0x10) 153 { 154 if (tc > (lastcodetc[field] + 67 + dup_ctrl_fudge)) 155 false_dup = 0; 156 } 157 else if (b1) 158 { 159 // text, XDS 160 if (tc > (lastcodetc[field] + 33 + dup_text_fudge)) 161 false_dup = 0; 162 } 163 164 if (false_dup) 165 goto skip; 166 } 167 168 if ((field == 1) && 169 (xds[field] || b1 && ((b1 & 0x70) == 0x00))) 170 // 0x01 <= b1 <= 0x0F 171 // start XDS 172 // or inside XDS packet 173 { 174 int xds_packet = 1; 175 176 // TODO: process XDS packets 177 if (b1 == 0x0F) 178 { 179 // end XDS 180 xds[field] = 0; 181 xds_packet = 1; 182 } 183 else if ((b1 & 0x70) == 0x10) 184 { 185 // ctrl code -- interrupt XDS 186 xds[field] = 0; 187 xds_packet = 0; 188 } 189 else 190 { 191 xds[field] = 1; 192 xds_packet = 1; 193 } 194 195 if (xds_packet) 196 goto skip; 197 } 198 199 if (b1 & 0x60) 200 // 0x20 <= b1 <= 0x7F 201 // text codes 202 { 203 if (mode >= 0) 204 { 205 lastcodetc[field] += 33; 206 timecode[mode] = tc; 207 208 // commit row number only when first text code 209 // comes in 210 if (newrow[mode]) 211 len = NewRowCC(mode, len); 212 213 ccbuf[mode] += CharCC(b1); 214 len++; 215 col[mode]++; 216 if (b2 & 0x60) 217 { 218 ccbuf[mode] += CharCC(b2); 219 len++; 220 col[mode]++; 221 } 222 } 223 } 224 225 else if ((b1 & 0x10) && (b2 > 0x1F)) 226 // 0x10 <= b1 <= 0x1F 227 // control codes 228 { 229 lastcodetc[field] += 67; 230 231 int newccmode = (b1 >> 3) & 1; 232 int newtxtmode = txtmode[field*2 + newccmode]; 233 if ((b1 & 0x06) == 0x04) 234 { 235 switch (b2) 236 { 237 case 0x29: 238 case 0x2C: 239 case 0x20: 240 case 0x2F: 241 case 0x25: 242 case 0x26: 243 case 0x27: 244 // CC1,2 245 newtxtmode = 0; 246 break; 247 case 0x2A: 248 case 0x2B: 249 // TXT1,2 250 newtxtmode = 1; 251 break; 252 } 253 } 254 ccmode[field] = newccmode; 255 txtmode[field*2 + newccmode] = newtxtmode; 256 mode = (field << 2) | (newtxtmode << 1) | ccmode[field]; 257 258 timecode[mode] = tc; 259 len = ccbuf[mode].length(); 260 261 if (b2 & 0x40) //preamble address code (row & indent) 262 { 263 if (newtxtmode) 264 // no address codes in TXT mode? 265 goto skip; 266 267 newrow[mode] = rowdata[((b1 << 1) & 14) | ((b2 >> 5) & 1)]; 268 if (newrow[mode] == -1) 269 // bogus code? 270 newrow[mode] = lastrow[mode] + 1; 271 272 if (b2 & 0x10) //row contains indent flag 273 newcol[mode] = (b2 & 0x0E) << 1; 274 else 275 newcol[mode] = 0; 276 277 // row, indent settings are not final 278 // until text code arrives 279 } 280 else 281 { 282 switch (b1 & 0x07) 283 { 284 case 0x00: //attribute 285 /* 286 printf ("<ATTRIBUTE %d %d>\n", b1, b2); 287 fflush (stdout); 288 */ 289 break; 290 case 0x01: //midrow or char 291 if (newrow[mode]) 292 len = NewRowCC(mode, len); 293 294 switch (b2 & 0x70) 295 { 296 case 0x20: //midrow attribute change 297 // TODO: we _do_ want colors, is that an attribute? 298 ccbuf[mode] += ' '; 299 len = ccbuf[mode].length(); 300 col[mode]++; 301 break; 302 case 0x30: //special character.. 303 ccbuf[mode] += specialchar[b2 & 0x0f]; 304 len++; 305 col[mode]++; 306 break; 307 } 308 break; 309 case 0x02: //extended char 310 // extended char is preceded by alternate char 311 // - if there's no alternate, it could be noise 312 if (!len) 313 break; 314 315 if (b2 & 0x30) 316 { 317 ccbuf[mode].remove(len - 1, 1); 318 ccbuf[mode] += extendedchar2[b2 - 0x20]; 319 len = ccbuf[mode].length(); 320 break; 321 } 322 break; 323 case 0x03: //extended char 324 // extended char is preceded by alternate char 325 // - if there's no alternate, it could be noise 326 if (!len) 327 break; 328 329 if (b2 & 0x30) 330 { 331 ccbuf[mode].remove(len - 1, 1); 332 ccbuf[mode] += extendedchar3[b2 - 0x20]; 333 len = ccbuf[mode].length(); 334 break; 335 } 336 break; 337 case 0x04: //misc 338 case 0x05: //misc + F 339 // printf("ccmode %d cmd %02x\n",ccmode,b2); 340 switch (b2) 341 { 342 case 0x21: //backspace 343 // add backspace if line has been encoded already 344 if (newrow[mode]) 345 len = NewRowCC(mode, len); 346 347 if (len == 0 || 348 ccbuf[mode].left(1) == "\b") 349 { 350 ccbuf[mode] += (char)'\b'; 351 len++; 352 col[mode]--; 353 } 354 else 355 { 356 ccbuf[mode].remove(len - 1, 1); 357 len = ccbuf[mode].length(); 358 col[mode]--; 359 } 360 break; 361 case 0x25: //2 row caption 362 case 0x26: //3 row caption 363 case 0x27: //4 row caption 364 if (style[mode] == CC_STYLE_PAINT && len) 365 { 366 // flush 367 BufferCC(mode, len, 0); 368 ccbuf[mode] = ""; 369 row[mode] = 0; 370 col[mode] = 0; 371 } 372 else if (style[mode] == CC_STYLE_POPUP) 373 ResetCC(mode); 374 375 rowcount[mode] = b2 - 0x25 + 2; 376 style[mode] = CC_STYLE_ROLLUP; 377 break; 378 case 0x2D: //carriage return 379 if (style[mode] != CC_STYLE_ROLLUP) 380 break; 381 382 if (newrow[mode]) 383 row[mode] = newrow[mode]; 384 385 // flush if there is text or need to scroll 386 // TODO: decode ITV (WebTV) link in TXT2 387 if (len || 388 row[mode] != 0 && 389 !linecont[mode] && 390 (!newtxtmode || row[mode] >= 16)) 391 BufferCC(mode, len, 0); 392 393 if (newtxtmode) 394 { 395 if (row[mode] < 16) 396 newrow[mode] = row[mode] + 1; 397 else 398 // scroll up previous lines 399 newrow[mode] = 16; 400 } 401 402 ccbuf[mode] = ""; 403 col[mode] = 0; 404 linecont[mode] = 0; 405 break; 406 407 case 0x29: //resume direct caption (paint-on style) 408 if (style[mode] == CC_STYLE_ROLLUP && len) 409 { 410 // flush 411 BufferCC(mode, len, 0); 412 ccbuf[mode] = ""; 413 row[mode] = 0; 414 col[mode] = 0; 415 } 416 else if (style[mode] == CC_STYLE_POPUP) 417 ResetCC(mode); 418 419 style[mode] = CC_STYLE_PAINT; 420 rowcount[mode] = 0; 421 linecont[mode] = 0; 422 break; 423 424 case 0x2B: //resume text display 425 resumetext[mode] = 1; 426 if (row[mode] == 0) 427 { 428 newrow[mode] = 1; 429 newcol[mode] = 0; 430 } 431 style[mode] = CC_STYLE_ROLLUP; 432 break; 433 case 0x2C: //erase displayed memory 434 if ((tc - lastclr[mode]) > 5000 || 435 lastclr[mode] == 0) 436 // don't overflow the frontend with 437 // too many redundant erase codes 438 BufferCC(mode, 0, 1); 439 if (style[mode] != CC_STYLE_POPUP) 440 { 441 row[mode] = 0; 442 col[mode] = 0; 443 } 444 linecont[mode] = 0; 445 break; 446 447 case 0x20: //resume caption (pop-up style) 448 if (style[mode] != CC_STYLE_POPUP) 449 { 450 if (len) 451 // flush 452 BufferCC(mode, len, 0); 453 ccbuf[mode] = ""; 454 row[mode] = 0; 455 col[mode] = 0; 456 } 457 style[mode] = CC_STYLE_POPUP; 458 rowcount[mode] = 0; 459 linecont[mode] = 0; 460 break; 461 case 0x2F: //end caption + swap memory 462 if (style[mode] != CC_STYLE_POPUP) 463 { 464 if (len) 465 // flush 466 BufferCC(mode, len, 0); 467 } 468 else if ((tc - lastclr[mode]) > 5000 || 469 lastclr[mode] == 0) 470 // clear and flush 471 BufferCC(mode, len, 1); 472 else if (len) 473 // flush 474 BufferCC(mode, len, 0); 475 ccbuf[mode] = ""; 476 row[mode] = 0; 477 col[mode] = 0; 478 style[mode] = CC_STYLE_POPUP; 479 rowcount[mode] = 0; 480 linecont[mode] = 0; 481 break; 482 483 case 0x2A: //text restart 484 // clear display 485 BufferCC(mode, 0, 1); 486 ResetCC(mode); 487 // TXT starts at row 1 488 newrow[mode] = 1; 489 newcol[mode] = 0; 490 style[mode] = CC_STYLE_ROLLUP; 491 break; 492 493 case 0x2E: //erase non-displayed memory 494 ResetCC(mode); 495 break; 496 } 497 break; 498 case 0x07: //misc (TAB) 499 if (newrow[mode]) 500 { 501 newcol[mode] += (b2 & 0x03); 502 len = NewRowCC(mode, len); 503 } 504 else 505 // illegal? 506 for (x = 0; x < (b2 & 0x03); x++) 507 { 508 ccbuf[mode] += ' '; 509 len++; 510 col[mode]++; 511 } 512 break; 513 } 514 } 515 } 516 517 skip: 518 for (mode = field*4; mode < (field*4 + 4); mode++) 519 { 520 len = ccbuf[mode].length(); 521 if (((tc - timecode[mode]) > 100) && 522 (style[mode] != CC_STYLE_POPUP) && len) 523 { 524 // flush unfinished line if waiting too long 525 // in paint-on or scroll-up mode 526 timecode[mode] = tc; 527 BufferCC(mode, len, 0); 528 ccbuf[mode] = ""; 529 row[mode] = lastrow[mode]; 530 linecont[mode] = 1; 531 } 532 } 533 534 if (data != lastcode[field]) 535 { 536 lastcode[field] = data; 537 lastcodetc[field] = tc; 538 } 539 lasttc[field] = tc; 540 } 541 542 void CCDecoder::ResetCC(int mode) 543 { 544 // lastrow[mode] = 0; 545 // newrow[mode] = 0; 546 // newcol[mode] = 0; 547 // timecode[mode] = 0; 548 row[mode] = 0; 549 col[mode] = 0; 550 rowcount[mode] = 0; 551 // style[mode] = CC_STYLE_POPUP; 552 linecont[mode] = 0; 553 resumetext[mode] = 0; 554 lastclr[mode] = 0; 555 ccbuf[mode] = ""; 556 } 557 558 void CCDecoder::BufferCC(int mode, int len, int clr) 559 { 560 QCString tmpbuf; 561 if (len) 562 { 563 // calculate UTF-8 encoding length 564 tmpbuf = ccbuf[mode].utf8(); 565 len = tmpbuf.length(); 566 if (len > 255) 567 len = 255; 568 } 569 570 unsigned char f; 571 unsigned char *bp = rbuf; 572 *(bp++) = row[mode]; 573 *(bp++) = rowcount[mode]; 574 *(bp++) = style[mode]; 575 // overload resumetext field 576 f = resumetext[mode]; 577 f |= mode << 4; 578 if (linecont[mode]) 579 f |= CC_LINE_CONT; 580 *(bp++) = f; 581 *(bp++) = clr; 582 *(bp++) = len; 583 if (len) 584 { 585 memcpy(bp, 586 tmpbuf, 587 len); 588 len += sizeof(ccsubtitle); 589 } 590 else 591 len = sizeof(ccsubtitle); 592 593 reader->AddTextData(rbuf, len, timecode[mode], 'C'); 594 595 resumetext[mode] = 0; 596 if (clr && !len) 597 lastclr[mode] = timecode[mode]; 598 else if (len) 599 lastclr[mode] = 0; 600 } 601 602 int CCDecoder::NewRowCC(int mode, int len) 603 { 604 if (style[mode] == CC_STYLE_ROLLUP) 605 { 606 // previous line was likely missing a carriage return 607 row[mode] = newrow[mode]; 608 if (len) 609 { 610 BufferCC(mode, len, 0); 611 ccbuf[mode] = ""; 612 len = 0; 613 } 614 col[mode] = 0; 615 linecont[mode] = 0; 616 } 617 else 618 { 619 // popup/paint style 620 621 if (row[mode] == 0) 622 { 623 if (len == 0) 624 row[mode] = newrow[mode]; 625 else 626 { 627 // previous line was missing a row address 628 // - assume it was one row up 629 ccbuf[mode] += (char)'\n'; 630 len++; 631 if (row[mode] == 0) 632 row[mode] = newrow[mode] - 1; 633 else 634 row[mode]--; 635 } 636 } 637 else if (newrow[mode] > lastrow[mode]) 638 { 639 // next line can be more than one row away 640 for (int i = 0; i < (newrow[mode] - lastrow[mode]); i++) 641 { 642 ccbuf[mode] += (char)'\n'; 643 len++; 644 } 645 col[mode] = 0; 646 } 647 else if (newrow[mode] == lastrow[mode]) 648 { 649 // same row 650 if (newcol[mode] >= col[mode]) 651 // new line appends to current line 652 newcol[mode] -= col[mode]; 653 else 654 { 655 // new line overwrites current line; 656 // could be legal (overwrite spaces?) but 657 // more likely we have bad address codes 658 // - just move to next line; may exceed row 15 659 // but frontend will adjust 660 ccbuf[mode] += (char)'\n'; 661 len++; 662 col[mode] = 0; 663 } 664 } 665 else 666 { 667 // next line goes upwards (not legal?) 668 // - flush 669 BufferCC(mode, len, 0); 670 ccbuf[mode] = ""; 671 row[mode] = newrow[mode]; 672 col[mode] = 0; 673 linecont[mode] = 0; 674 len = 0; 675 } 676 } 677 678 lastrow[mode] = newrow[mode]; 679 newrow[mode] = 0; 680 681 for (int x = 0; x < newcol[mode]; x++) 682 { 683 ccbuf[mode] += ' '; 684 len++; 685 col[mode]++; 686 } 687 newcol[mode] = 0; 688 689 return len; 690 } 691 -
libs/libmythtv/NuppelVideoRecorder.h
30 30 // MythTV headers 31 31 #include "recorderbase.h" 32 32 #include "format.h" 33 #include "ccdecoder.h" 33 34 34 35 struct video_audio; 35 36 struct VBIData; … … 40 41 class FilterManager; 41 42 class FilterChain; 42 43 43 class NuppelVideoRecorder : public RecorderBase 44 class NuppelVideoRecorder : public RecorderBase, public CCReader 44 45 { 45 46 public: 46 47 NuppelVideoRecorder(TVRec *rec, ChannelBase *channel); … … 131 132 132 133 void FormatTeletextSubtitles(struct VBIData *vbidata); 133 134 void FormatCC(struct cc *cc); 134 void FormatCCField(struct cc *cc, int tc, int field); 135 QChar CharCC(int code); 136 void ResetCC(struct cc *cc, int mode); 137 void BufferCC(struct cc *cc, int mode, int len, int clr); 138 int NewRowCC(struct cc *cc, int mode, int len); 135 void AddTextData(unsigned char *buf, int len, long long timecode, char type); 139 136 140 137 bool encoding; 141 138 … … 281 278 bool correct_bttv; 282 279 283 280 int volume; 281 282 CCDecoder *ccd; 283 284 284 bool go7007; 285 285 }; 286 286