#include #include #include #include #include #include #include #define ENTRIES(a) (sizeof(a)/sizeof(*(a))) #define CONCURRENT_READS 180 #define READ_BUFFER_SIZE 2 /* Update statistics every n milliseconds (16ms is generally too fast) */ #define UPDATE_PERIOD 100 void PrintStatistics( ICCDecode &Driver, int row, int column, BOOL bSavePosition) { HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE); COORD Pos = {(short)row, (short)column}; CONSOLE_SCREEN_BUFFER_INFO SavedPos; VBICODECFILTERING_STATISTICS_CC Statistics; VBICODECFILTERING_STATISTICS_CC_PIN PinStatistics; VBICODECFILTERING_SCANLINES ScanlinesRequested, ScanlinesDiscovered; VBICODECFILTERING_CC_SUBSTREAMS VideoFieldsRequested, VideoFieldsDiscovered; char szBuffer[11][80]; if ( bSavePosition ) GetConsoleScreenBufferInfo( hStdout, &SavedPos ); SetConsoleCursorPosition( hStdout, Pos ); if ( Driver.GetCodecStatistics( Statistics ) == 0 ) { memset( &ScanlinesRequested, 0, sizeof(ScanlinesRequested) ); memset( &ScanlinesDiscovered, 0, sizeof(ScanlinesDiscovered) ); memset( &VideoFieldsRequested, 0, sizeof(VideoFieldsRequested) ); memset( &VideoFieldsDiscovered, 0, sizeof(VideoFieldsDiscovered) ); Driver.m_ScanlinesRequested.GetValue(&ScanlinesRequested); Driver.m_SubstreamsRequested.GetValue(&VideoFieldsRequested); Driver.m_ScanlinesDiscovered.GetValue(&ScanlinesDiscovered); Driver.m_SubstreamsDiscovered.GetValue(&VideoFieldsDiscovered); sprintf(szBuffer[0], "-----R:%08x:%08x----- CC Codec Statistics ------D:%08x:%08x-----", ScanlinesRequested.DwordBitArray[0], VideoFieldsRequested.SubstreamMask, ScanlinesDiscovered.DwordBitArray[0], VideoFieldsDiscovered.SubstreamMask ); sprintf(szBuffer[1], "InputSRBsProcessed: %u, OutputSRBsProcessed: %u, SRBsIgnored: %u", Statistics.Common.InputSRBsProcessed, Statistics.Common.OutputSRBsProcessed, Statistics.Common.SRBsIgnored ); sprintf(szBuffer[2], "InputSRBsMissing: %u, OutputSRBsMissing: %u, OutputFailures: %u", Statistics.Common.InputSRBsMissing, Statistics.Common.OutputSRBsMissing, Statistics.Common.OutputFailures ); sprintf(szBuffer[3], "InternalErrors: %u, ExternalErrors: %u, InputDiscontinuities: %u", Statistics.Common.InternalErrors, Statistics.Common.ExternalErrors, Statistics.Common.InputDiscontinuities ); sprintf(szBuffer[4], "DSPFailures: %u, TvTunerChanges: %u, VBIHeaderChanges: %u", Statistics.Common.DSPFailures, Statistics.Common.TvTunerChanges, Statistics.Common.VBIHeaderChanges ); sprintf(szBuffer[5], "LineConfidenceAvg: %u, BytesOutput: %u", Statistics.Common.LineConfidenceAvg, Statistics.Common.BytesOutput ); } if ( Driver.GetPinStatistics( PinStatistics ) == 0 ) { memset( &ScanlinesRequested, 0, sizeof(ScanlinesRequested) ); memset( &ScanlinesDiscovered, 0, sizeof(ScanlinesDiscovered) ); memset( &VideoFieldsRequested, 0, sizeof(VideoFieldsRequested) ); memset( &VideoFieldsDiscovered, 0, sizeof(VideoFieldsDiscovered) ); Driver.m_OutputPin.m_ScanlinesRequested.GetValue(&ScanlinesRequested); Driver.m_OutputPin.m_SubstreamsRequested.GetValue(&VideoFieldsRequested); Driver.m_OutputPin.m_ScanlinesDiscovered.GetValue(&ScanlinesDiscovered); Driver.m_OutputPin.m_SubstreamsDiscovered.GetValue(&VideoFieldsDiscovered); sprintf(szBuffer[6], "-----R:%08x:%08x------- CCPin Statistics -------D:%08x:%08x-----", ScanlinesRequested.DwordBitArray[0], VideoFieldsRequested.SubstreamMask, ScanlinesDiscovered.DwordBitArray[0], VideoFieldsDiscovered.SubstreamMask ); sprintf(szBuffer[7], "SRBsProcessed: %u, SRBsMissing: %u, SRBsIgnored: %u", PinStatistics.Common.SRBsProcessed, PinStatistics.Common.SRBsMissing, PinStatistics.Common.SRBsIgnored ); sprintf(szBuffer[8], "InternalErrors: %u, ExternalErrors: %u, Discontinuities: %u", PinStatistics.Common.InternalErrors, PinStatistics.Common.ExternalErrors, PinStatistics.Common.Discontinuities ); sprintf(szBuffer[9], "LineConfidenceAvg: %u, BytesOutput: %u", PinStatistics.Common.LineConfidenceAvg, PinStatistics.Common.BytesOutput ); sprintf(szBuffer[10], "==============================================================================="); printf("%-79.79s\n%-79.79s\n%-79.79s\n%-79.79s\n%-79.79s\n%-79.79s\n%-79.79s\n%-79.79s\n%-79.79s\n%-79.79s\n%-79.79s\n", szBuffer[0], szBuffer[1], szBuffer[2], szBuffer[3], szBuffer[4], szBuffer[5], szBuffer[6], szBuffer[7], szBuffer[8], szBuffer[9], szBuffer[10] ); } if ( bSavePosition ) SetConsoleCursorPosition( hStdout, SavedPos.dwCursorPosition ); } void interpret_Vchip(unsigned char *buf, int buflen) { static int m_bGetVChip = FALSE; static int m_bGetCurrentOnEven = FALSE; //long lLevel = -1; unsigned char *bp, *ep; bp = buf; ep = &buf[buflen]; while (bp < ep) { UCHAR ucData = *bp++; if ( m_bGetVChip ) { // state is we are looking for vchip data if ( ucData & 0x40 ) { UCHAR ucData2; if (bp >= ep) break; // it's a valid char, get the next one ucData2 = *bp++; // we have a valid one, determine what it is if ( !(ucData & 0x08) ) { // MPAA UCHAR ucMPAA = (ucData & 0x07); printf("\bVChip MPAA-" ); switch ( ucMPAA ) { case 0x00: printf("NONE"); break; case 0x01: printf("G"); break; case 0x02: printf("PG"); break; case 0x03: printf("PG13"); break; case 0x04: printf("R"); break; case 0x05: printf("NC17"); break; case 0x06: printf("X"); break; case 0x07: printf("NOTRATED"); break; } printf("\n"); } else if ( !(ucData & 0x10 ) ) { // TV Parental Guidelines if ( ucData2 & 0x40 ) { UCHAR ucTVParental = (ucData2 & 0x07); printf("\bVChip TVP-" ); switch ( ucTVParental ) { case 0x00: printf("NONE"); break; case 0x01: printf("Y"); break; case 0x02: printf("Y7"); break; case 0x03: printf("G"); break; case 0x04: printf("PG"); break; case 0x05: printf("TV14"); break; case 0x06: printf("TVMA"); break; case 0x07: printf("NONE"); break; } // Now do the TV sub-codes: if (ucData & 0x20) printf(" D"); if (ucData2 & 0x20) { printf(" "); if (0x02 == ucTVParental) printf("F"); printf("V"); } if (ucData2 & 0x10) printf(" S"); if (ucData2 & 0x08) printf(" L"); printf("\n"); } else { printf("VChip NonUS- 0x%02x 0x%02x\n", ucData, ucData2 ); // non-US system } } else { // it's not a valid vchip char m_bGetVChip = FALSE; } } else { // it's not a valid vchip char m_bGetVChip = FALSE; } } else if ( m_bGetCurrentOnEven ) { // state is we have started Current Class if ( ucData == 0x05 ) { m_bGetVChip = TRUE; } m_bGetCurrentOnEven = FALSE; } else { // state is something else if ( ucData == 0x01 || ucData == 0x02 ) { m_bGetCurrentOnEven = TRUE; } } } } int __cdecl main( int argc, char *argv[] ) { int nStatus = 0; int arg = 1; // Next unparsed command line parameter const int bDoVchip = arg < argc && strcmp(argv[arg],"-v") == 0 ? arg++ : 0; const int bStatistics = arg < argc && strcmp(argv[arg],"-s") == 0 ? arg++ : 0; long nLastUpdate = 0; const int nScanline = arg < argc ? atoi(argv[arg++]) : 21; // Closed Captioning Scanline(21) int nSubstream = KS_CC_SUBSTREAM_ODD; unsigned char VchipBuffer[128], *vcp = VchipBuffer; int VchipBytes = 0; if ( nScanline ) { try { ICCDecode Driver; if ( Driver.IsValid() ) { SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL ); if ( bStatistics ) PrintStatistics( Driver, 0, 0, FALSE ); if ( (nStatus = Driver.ClearRequestedScanlines() ) == 0) { printf( "Starting decoding%s on line %d", bDoVchip? " [VCHIP mode]":"", nScanline ); // No newline, see below if ( (nStatus = Driver.AddRequestedScanline(nScanline) ) != 0) { fprintf( stderr, "\nFailed to AddRequestedScanlines(%d)=%d\n", nScanline, nStatus ); return nStatus; } } else { fprintf( stderr, "\nFailed to ClearRequestedScanlines()=%d\n", nStatus ); return nStatus; } if ( ( nStatus = Driver.ClearRequestedVideoFields() ) == 0 ) { do { if ( arg < argc ) if ( !(nSubstream = atoi( argv[arg] ) ) ) printf( "Invalid substream: '%s'\n", argv[arg] ); printf( ", Substream %d", nSubstream ); if ( ( nStatus = Driver.AddRequestedVideoField(nSubstream) ) != 0 ) { fprintf( stderr, "\nFailed to AddRequestedVideoField(%d)=%d\n", nSubstream, nStatus ); return nStatus; } } while ( ++arg < argc ); } else { fprintf( stderr, "\nFailed to ClearRequestedVideoFields()=%d\n", nStatus ); return nStatus; } printf("\n"); int nNextRead = 0;//, nNextCompleted = 0; DWORD nBytes = 0; OVERLAPPED Overlapped[CONCURRENT_READS] = {0}; BYTE ccdata[CONCURRENT_READS][READ_BUFFER_SIZE]; for( nNextRead = 0; !nStatus && nNextRead < CONCURRENT_READS; nNextRead++ ) { if ( !( Overlapped[nNextRead].hEvent = CreateEvent( NULL, TRUE, FALSE, NULL ) ) ) { nStatus = GetLastError(); break; } nStatus = Driver.ReadData( ccdata[nNextRead], READ_BUFFER_SIZE, &nBytes, Overlapped + nNextRead ); if ( !nStatus || nStatus == ERROR_IO_PENDING ) nStatus = 0; else break; } nNextRead = 0; while ( !nStatus && !_kbhit() ) { if ( !(nStatus = Driver.GetOverlappedResult(Overlapped+nNextRead, &nBytes, FALSE ) ) ) { nBytes = min(nBytes, READ_BUFFER_SIZE); for(DWORD i=0; i= sizeof (VchipBuffer)) { printf("."); //printf( "interpret_Vchip([%.*s], %d)\n", VchipBytes, ccdata[nNextRead], VchipBytes ); interpret_Vchip((unsigned char *)VchipBuffer, VchipBytes); vcp = VchipBuffer; VchipBytes = 0; } } else printf("%.*s", nBytes, ccdata[nNextRead]); nStatus = Driver.ReadData( ccdata[nNextRead], READ_BUFFER_SIZE, &nBytes, Overlapped + nNextRead ); if ( !nStatus || nStatus == ERROR_IO_PENDING ) { nNextRead = ++nNextRead % CONCURRENT_READS; nStatus = 0; } } else if ( nStatus == ERROR_IO_INCOMPLETE || nStatus == ERROR_IO_PENDING ) { Sleep(10); // Chill out a few milliseconds so we don't run full tilt. nStatus = 0; } if ( bStatistics && GetTickCount()-nLastUpdate > UPDATE_PERIOD ) { PrintStatistics( Driver, 0, 0, TRUE ); nLastUpdate = GetTickCount(); } } // Drain any chars pressed while ( _kbhit() ) _getch(); } } catch (...) { nStatus = GetLastError(); } } else printf( "CC TESTAPP Syntax: TESTAPP [-v][-s] [scanline [substream1 [substream2] ] ]\n" ); if ( nStatus ) fprintf( stderr, "Program failed with LastErrorCode=%d!\n", nStatus ); return nStatus; }