/[tivodecode]/t2sami/trunk/CC.cpp
ViewVC logotype

Contents of /t2sami/trunk/CC.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 243 - (show annotations) (download)
Tue Aug 28 05:01:16 2007 UTC (14 years, 11 months ago) by jeremyd2019
File size: 28515 byte(s)
change to allow reading and writing to stdin/stdout, based loosely on a patch by mikel111, but make it consitent with tivodecode (and other unix apps) in that an input or output file of '-' means stdin or stdout respectively

1 // Cc.cpp : implementation file
2 //
3 //********************************************************************
4 //
5 // Copyright (c) 2005-2007 James Memmott <jmemmott@braeman.com>
6 //
7 // This software is provided 'as-is', without any express or implied warranty.
8 // In no event will the author be held liable for any damages arising from the
9 // use of this software.
10 //
11 // Permission is granted to anyone to use this software for any purpose, including
12 // commercial applications, and to alter it and redistribute it freely, subject to
13 // the following restrictions:
14 //
15 // 1. Redistributions of source code must retain the above copyright
16 // notice, this list of conditions and the disclaimer.
17 //
18 // 2. The origin of this software must not be misrepresented; you must not claim
19 // that you wrote the original software. If you use this software in a product,
20 // an acknowledgment in the product documentation would be appreciated but is
21 // not required.
22 //
23 // 3. Altered source versions must be plainly marked as such, and must not be
24 // misrepresented as being the original software.
25 //
26 //********************************************************************
27 //
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32 #include <limits.h>
33 #include <fcntl.h>
34 #include <errno.h>
35 #include <string.h>
36 #include <boost/format.hpp>
37 #include "Utility.h"
38 #include "TScan.h"
39 #include "CC.h"
40
41 #ifndef O_BINARY
42 # define O_BINARY 0
43 #endif
44
45 // UTF-8 music note
46 //static const unsigned char MUSIC_NOTE[] = {0xe2, 0x99, 0xa9};
47 // SGML music note
48 //static const unsigned char MUSIC_NOTE[] = "&sung;";
49 // HTML-escaped unicode music note
50 //static const unsigned char MUSIC_NOTE[] = "&#9833;";
51 // Space
52 static const unsigned char MUSIC_NOTE[] = {0x20};
53
54 //********************************************************************
55 // CCNode
56
57 CCNode::CCNode( double PTS, byte b1, byte b2 ) :
58 m_PTS(PTS),
59 m_b1(b1),
60 m_b2(b2),
61 m_link(NULL)
62 {
63 }
64
65 CCNode::CCNode() :
66 m_PTS(0.0),
67 m_b1(0x80),
68 m_b2(0x80),
69 m_link(NULL)
70 {
71 }
72
73 void CClosedCaption::InsertNode( CCNode *node )
74 {
75
76 CCNode *n = m_head[0];
77 CCNode *l = NULL;
78
79 while ( n != NULL ) {
80
81 if( node->m_PTS < n->m_PTS ) {
82
83 if( node->m_b1 == 0x80 && node->m_b2 == 0x80 ) { // Compress out marking spaces
84
85 if( n->m_b1 == 0x80 && n->m_b2 == 0x80 ) {
86 delete node;
87 return;
88 }
89
90 } else if( (node->m_b1 == 0x91 ) ||
91 (node->m_b1 == 0x94 ) ||
92 (node->m_b1 == 0x1C ) ){
93
94 switch( node->m_b2 ) {
95
96 case 0x37: // Musical note
97 case 0xB9: // Special Characters
98
99 break;
100
101 default: // Commands may be duplicated - remove duplicates
102
103 if( n->m_b1 == node->m_b1 && n->m_b2 == node->m_b2 ) {
104 n->m_PTS = node->m_PTS;
105 delete node;
106 return;
107 }
108
109 } // switch (b2)
110
111 }
112
113 node->m_link = n;
114 m_lastNodeAdded = node;
115
116 if( l != NULL )
117 l->m_link = node;
118
119 else
120 m_head[0] = node;
121
122 return;
123
124 } else {
125 l = n;
126 n = n->m_link;
127 }
128
129 }
130
131 if( l == NULL ) {
132
133 if( (node->m_b1 == 0x94 && node->m_b2 == 0x2F) || // Never start with EOC
134 (node->m_b1 == 0x94 && node->m_b2 == 0xAD) ) { // Never start with CR
135 delete node;
136 return;
137 }
138
139 if( node->m_b1 == 0x80 && node->m_b2 == 0x80 ) { // Compress out marking spaces
140 delete node;
141 return;
142 }
143
144 node->m_link = m_head[0];
145 m_head[0] = node;
146 m_lastNodeAdded = node;
147
148 } else {
149
150 if( node->m_b1 == 0x80 && node->m_b2 == 0x80 ) { // Compress out marking spaces
151
152 if( l->m_b1 == 0x80 && l->m_b2 == 0x80 ) {
153 l->m_PTS = node->m_PTS;
154 delete node;
155 return;
156 }
157
158 } else if( node->m_b1 == 0x91 ) {
159
160 switch( node->m_b2 ) {
161
162 case 0x37: // Musical Note
163 case 0xB9: // Special Characters
164
165 break;
166
167 default: // Commands may be duplicated - remove duplicates
168
169 if( l->m_b1 == node->m_b1 && l->m_b2 == node->m_b2 ) {
170 delete node;
171 return;
172 }
173
174 } // switch (b2)
175
176 }
177
178 l->m_link = node;
179
180 }
181
182 }
183
184 void CClosedCaption::ClearNodes( int line )
185 {
186
187 if( line < 0 || line > 3 )
188 return;
189
190 CCNode *n = m_head[line];
191 CCNode *l = NULL;
192
193 while ( n != NULL ) {
194
195 l = n;
196 n = l->m_link;
197
198 delete l;
199
200 }
201
202 m_head[line] = NULL;
203
204 }
205
206 void CClosedCaption::ClearNodes()
207 {
208
209 for( int i=0; i<4; i++ )
210 ClearNodes(i);
211
212 m_bEocValid = false;
213
214 }
215
216 void CClosedCaption::ClearNodes( double dTimeStamp )
217 {
218
219 for( int i=0; i<4; i++ )
220 {
221
222 CCNode *n = m_head[i];
223 if( n != NULL )
224 if( n->m_PTS < dTimeStamp )
225 ClearNodes( i );
226
227 }
228
229 }
230
231 std::string CClosedCaption::HtmlEncode( unsigned char c )
232 {
233
234 std::string repl;
235
236 if( m_pParent->m_bDoHtmlEncode && m_pParent->m_nCaptionFormat == FMT_SAMI ) {
237
238 switch( c )
239 {
240 // case 32:
241 // repl = "&nbsp;";
242 // break;
243 case 34:
244 repl = "&quot;";
245 break;
246 case 38:
247 repl = "&amp;";
248 break;
249 case 60:
250 repl = "&lt;";
251 break;
252 case 62:
253 repl = "&gt;";
254 break;
255 default:
256
257 if( c>=32 && c<=127 )
258 repl = c;
259 else
260 #ifdef WIN32
261 repl = t2sprintf("&#%d;", (int)c );
262 #else
263 repl = boost::str (boost::format ("&#%d;") % (int)c );
264 #endif
265
266 } // switch
267
268 } else
269 if( c>=32 && c<=127 )
270 repl = c;
271 else
272 repl = " ";
273
274 return repl;
275
276 }
277
278 std::string CClosedCaption::NodesToString( int line )
279 {
280
281 std::string cso;
282
283 if( line < 0 || line > 3 )
284 return cso;
285
286 CCNode *n = m_head[line];
287 CCNode *l = NULL;
288 bool bItalic = false;
289
290 while ( n != NULL ) {
291
292 l = n;
293
294 if( (l->m_b1 == 0x94 && l->m_b2 == 0x2F ) || // EOC
295 (l->m_b1 == 0x94 && l->m_b2 == 0xAD) ) { // CR
296
297 cso += (char)0x20;
298
299 } else if( l->m_b1 == 0x91 && l->m_b2 == 0x20 ) { // Normal
300
301 switch( m_pParent->m_nCaptionFormat ) {
302
303 case FMT_SAMI:
304
305 if( bItalic ) {
306 cso += "</I>";
307 bItalic = false;
308 }
309
310 cso += (char)0x20;
311
312 break;
313
314 case FMT_SRT:
315 default:
316
317 break;
318
319 } // switch
320
321 } else if( l->m_b1 == 0x91 && l->m_b2 == 0xAE ) { // Italic Normal
322
323 switch( m_pParent->m_nCaptionFormat ) {
324
325 case FMT_SAMI:
326
327 if( !bItalic ) {
328 cso += "<I>";
329 bItalic = true;
330 }
331
332 break;
333
334 case FMT_SRT:
335 default:
336
337 break;
338
339 } // switch
340
341 } else if( l->m_b1 == 0x91 && l->m_b2 == 0xB9 ) {
342
343 cso += (char)0x20;
344
345 } else if( l->m_b1 == 0x91 && l->m_b2 == 0x37 ) {
346
347 cso += std::string((const char *)MUSIC_NOTE, sizeof(MUSIC_NOTE));
348
349 } else if( l->m_b1 == 0x80 && l->m_b2 == 0x80 ) {
350
351 if( m_pParent->m_bAddSpaces )
352 cso += (char)0x20;
353
354 } else if( l->m_b1 == 0x94 && l->m_b2 == 0x20 ) {
355
356 cso += "{0}";
357
358 } else if( l->m_b1 == 0x1C && l->m_b2 == 0x20 ) {
359
360 cso += "{1}";
361
362 } else {
363
364 if( getPreambleRow( l->m_b1, l->m_b2 ) >= 0 )
365 cso += (char)0x20;
366 else {
367
368 char c = l->m_b1 & 0x07F;
369 if( c>=32 && c<=127 )
370 cso += HtmlEncode( c );
371 else
372 if( m_pParent->m_bAddSpaces )
373 cso += ' ';
374
375 c = l->m_b2 & 0x07F;
376 if( c>=32 && c<=127 )
377 cso += HtmlEncode( c );
378 else
379 if( m_pParent->m_bAddSpaces )
380 cso += ' ';
381
382 }
383
384 }
385
386 n = l->m_link;
387
388 }
389
390 switch( m_pParent->m_nCaptionFormat ) {
391
392 case FMT_SAMI:
393
394 if( bItalic )
395 cso += "</I>";
396
397 break;
398
399 case FMT_SRT:
400 default:
401
402 break;
403
404 } // switch
405
406 return cso;
407
408 }
409
410 double CClosedCaption::FirstNodePTS( int line )
411 {
412
413 CCNode *n = m_head[line];
414 CCNode *l = NULL;
415
416 while ( n != NULL ) {
417
418 l = n;
419
420 if( getPreambleRow( l->m_b1, l->m_b2 ) >= 0 )
421 n = l->m_link;
422 else
423 break;
424
425 }
426
427 if( l != NULL )
428 return l->m_PTS;
429 else
430 return 0.0;
431
432 }
433
434 double CClosedCaption::LastNodePTS( int line )
435 {
436
437 CCNode *n = m_head[line];
438 CCNode *l = NULL;
439
440 while ( n != NULL ) {
441
442 l = n;
443
444 if( (l->m_b1 == 0x94 && l->m_b2 == 0x2F) || // EOC
445 (l->m_b1 == 0x94 && l->m_b2 == 0xAD) ) { // CR
446 break;
447 }
448
449 n = l->m_link;
450
451 }
452
453 if( l != NULL )
454 return l->m_PTS;
455 else
456 return 0.0;
457
458 }
459
460 bool CClosedCaption::IsEoc()
461 {
462
463 if( m_head[0]== NULL )
464 return false;
465 else
466 return true;
467
468 }
469
470 int CClosedCaption::getSyncTime( int line )
471 {
472
473 if( line < 0 )
474 for( line=0; line<4; line++ )
475 if( m_head[line] != NULL )
476 break;
477
478 if( line < 0 || line > 3 )
479 return 0;
480
481 int nSync = (int)FirstNodePTS(line) + m_pParent->m_nSyncBias;
482
483 CCNode *n = m_head[line];
484 CCNode *l = NULL;
485
486 while ( n != NULL ) {
487
488 l = n;
489
490 if( (l->m_b1 == 0x94 && l->m_b2 == 0x2F) ) { // EOC
491 nSync = (int)LastNodePTS(line) + m_pParent->m_nSyncBias;
492 break;
493 } else if( (l->m_b1 == 0x94 && l->m_b2 == 0xAD) ) { // CR
494 break;
495 }
496
497 n = l->m_link;
498
499 }
500
501 if( nSync < 0 ) nSync = 0;
502 return nSync;
503
504 }
505
506 void CClosedCaption::doRollup( double dPTS )
507 {
508
509 if( (m_captionMode & CAP_ROLL) == 0 )
510 return;
511
512 int maxlines = getSubtitleLineCount();
513 if( maxlines <= 1 ) return;
514
515 CCNode *l = NULL;
516 CCNode *e = NULL;
517 CCNode *n = m_head[0];
518 while ( n != NULL ) {
519
520 if( n->m_PTS < dPTS ) {
521
522 l = n;
523 n = n->m_link;
524
525 } else {
526
527 e = n;
528
529 if( l != NULL )
530 l->m_link = NULL;
531 else
532 m_head[0] = NULL;
533
534 n = NULL;
535
536 }
537
538 } // while
539
540
541 if( m_head[0] != NULL ) {
542
543 std::string cbuf;
544
545 cbuf = NodesToString(0);
546 cbuf = CompressString(cbuf);
547 if( cbuf.length() > 0 )
548 WriteSubtitles();
549
550 }
551
552 ClearNodes( maxlines - 1 );
553
554 for( int i=maxlines-1; i>0; i-- )
555 m_head[i] = m_head[i-1];
556
557 m_head[0] = e;
558
559 if( m_pParent->m_bUseCutoffDuration ) {
560 double dTimeStamp = m_pParent->m_nLastSync + m_pParent->m_nCutoffDuration;
561 ClearNodes( dTimeStamp );
562 }
563
564 }
565
566 bool CClosedCaption::validPreamble( byte b, byte *set )
567 {
568
569 for( byte c=*set; c != 0x00; c=*(++set) )
570 if( c == b )
571 return true;
572
573 return false;
574
575 }
576
577 int CClosedCaption::getPreambleRow( byte b1, byte b2 )
578 {
579
580 static byte set0[] = { 0x70, 0xf1, 0x62, 0xe3, 0x64, 0xe5, 0xe6, 0x67,
581 0x68, 0xe9, 0xea, 0x6b, 0xec, 0x6d, 0xf2, 0x73,
582 0xf4, 0x75, 0x76, 0xf7, 0xf8, 0x79, 0x7a, 0xfb,
583 0x7c, 0xfd, 0xfe, 0x7f, 0x00 };
584
585 static byte set1[] = { 0xd0, 0x51, 0xc2, 0x43, 0xc4, 0x45, 0x46, 0xc7,
586 0xc8, 0x49, 0x4a, 0xcb, 0x4c, 0xcd, 0x52, 0xd3,
587 0x54, 0xd5, 0xd6, 0x57, 0x58, 0xd9, 0xda, 0x5b,
588 0xdc, 0x5d, 0x5e, 0xdf, 0x00 };
589
590 switch( b1 ) {
591
592 case 0x94:
593
594 if( validPreamble( b2, set0 ) )
595 return 0;
596 else if( validPreamble( b2, set1 ) )
597 return 1;
598 else
599 return -1;
600
601 case 0x13:
602
603 if( validPreamble( b2, set0 ) )
604 return 2;
605 else if( validPreamble( b2, set1 ) )
606 return 3;
607 else
608 return -1;
609
610 case 0x10:
611
612 if( validPreamble( b2, set1 ) )
613 return 4;
614 else
615 return -1;
616
617 case 0x97:
618
619 if( validPreamble( b2, set0 ) )
620 return 5;
621 else if( validPreamble( b2, set1 ) )
622 return 6;
623 else
624 return -1;
625
626 case 0x16:
627
628 if( validPreamble( b2, set0 ) )
629 return 7;
630 else if( validPreamble( b2, set1 ) )
631 return 8;
632 else
633 return -1;
634
635 case 0x15:
636
637 if( validPreamble( b2, set0 ) )
638 return 9;
639 else if( validPreamble( b2, set1 ) )
640 return 10;
641 else
642 return -1;
643
644 case 0x92:
645
646 if( validPreamble( b2, set0 ) )
647 return 11;
648 else if( validPreamble( b2, set1 ) )
649 return 12;
650 else
651 return -1;
652
653 case 0x91:
654
655 if( validPreamble( b2, set0 ) )
656 return 13;
657 else if( validPreamble( b2, set1 ) )
658 return 14;
659 else
660 return -1;
661
662 default:
663
664 return -1;
665
666 } // switch
667
668 }
669
670 //********************************************************************
671 // CClosedCaption
672
673 CClosedCaption::CClosedCaption() :
674 m_nFile(NULL)
675 {
676
677 CC_last = 0;
678 m_ChannelExtra = NULL;
679 m_nNowChannel = 0;
680 m_bEocValid = false;
681 m_popLast = 0;
682
683 m_head[0] = m_head[1] = m_head[2] = m_head[3] = NULL;
684 m_captionMode = CAP_UNKNOWN;
685
686 m_pParent = NULL;
687 m_lastNodeAdded = NULL;
688
689 }
690
691 CClosedCaption::~CClosedCaption()
692 {
693 ClearNodes();
694 }
695
696 void CClosedCaption::WriteLine( const char * lpMessage )
697 {
698 fprintf(m_nFile, "%s\n", lpMessage);
699 }
700
701 bool CClosedCaption::Initialize( std::string szOutputName )
702 {
703
704 std::string message;
705
706 CC_last = 0;
707 m_popLast = 0;
708 m_head[0] = m_head[1] = m_head[2] = m_head[3] = NULL;
709 m_ChannelExtra = NULL;
710 m_nNowChannel = m_pParent->m_nCcChannel - 1;
711
712 m_szOutputFile = szOutputName;
713
714 switch( m_pParent->m_nCaptionFormat ) {
715
716 case FMT_SAMI:
717
718 m_szOutputFile += ".smi\0";
719 break;
720
721 case FMT_SRT:
722
723 m_szOutputFile += ".srt\0";
724 break;
725
726 } // switch
727
728 if (szOutputName == "-")
729 m_nFile = stdout;
730 else
731 m_nFile = fopen( m_szOutputFile.c_str(), "wb");
732 if(!m_nFile) {
733 #ifdef _DEBUG
734 TRACE("File (%s) could not be opened %0x\n", m_szOutputFile.c_str(), errno );
735 #endif
736 return false;
737 }
738
739 switch( m_pParent->m_nCaptionFormat ) {
740
741 case FMT_SAMI:
742
743 WriteLine( "<SAMI>" );
744 WriteLine( "<HEAD>" );
745
746 message = "<Title>T2Sami Tivo to Sami Closed Caption Converter (";
747 message += VERSION;
748 message += ")</Title>";
749 WriteLine(message.c_str());
750
751 WriteLine( "<SAMIParam>" );
752
753 message = "Media {";
754 message += GetFileNameFromPath(m_szOutputFile.c_str());
755 message += "}";
756 WriteLine(message.c_str());
757
758 WriteLine( "Metrics {time:ms;}" );
759 WriteLine( "Spec {MSFT:1.0;}" );
760
761 WriteLine( "</SAMIParam>" );
762
763 WriteLine( "<STYLE TYPE=\"text/css\">" );
764 WriteLine( "<!--" );
765 WriteLine( "P { " );
766
767 // font-size:12pt;
768 message = "font-size: ";
769 message += m_pParent->m_csFontSize;
770 message += "; ";
771 WriteLine(message.c_str());
772
773 // font-family: Arial;
774 message = "font-family: ";
775 message += m_pParent->m_csFontFamily;
776 message += "; ";
777 WriteLine(message.c_str());
778
779 // font-weight: normal;
780 message = "font-weight: ";
781 message += m_pParent->m_csFontWeight;
782 message += "; ";
783 WriteLine(message.c_str());
784
785 WriteLine( "text-align: center; " );
786 WriteLine( "}" );
787
788 WriteLine( ".ENUSCC { Name: English; lang: EN-US-CC; }" );
789 WriteLine( "-->" );
790 WriteLine( "</STYLE>" );
791
792 WriteLine( "</HEAD>" );
793 WriteLine( "<BODY>" );
794
795 WriteLine( "<SYNC Start=\"0\">" );
796 WriteLine( "<P CLASS=ENUSCC>&nbsp;</P>" );
797 WriteLine( "</SYNC>" );
798
799 m_pParent->m_nCaptionCount = 1;
800 break;
801
802 case FMT_SRT:
803
804 m_pParent->m_nCaptionCount = 0;
805 m_pParent->m_csCurrentCaption.erase();
806
807 break;
808
809 default:
810
811 return false;
812
813 } // switch
814
815 m_pParent->m_nLastSync = -1;
816
817 return true;
818
819 }
820
821 bool CClosedCaption::Close()
822 {
823
824 switch( m_pParent->m_nCaptionFormat ) {
825
826 case FMT_SAMI:
827
828 WriteLine( "</BODY>" );
829 WriteLine( "</SAMI>" );
830 break;
831
832 case FMT_SRT:
833
834 break;
835
836 default:
837
838 break;
839
840 } // switch
841
842 fclose( m_nFile );
843 m_nFile = NULL;
844
845 return true;
846
847 }
848
849 static inline void trim2(std::string& str)
850 {
851 std::string::size_type pos = str.find_last_not_of(' ');
852 if(pos != std::string::npos) {
853 str.erase(pos + 1);
854 pos = str.find_first_not_of(' ');
855 if(pos != std::string::npos) str.erase(0, pos);
856 }
857 else str.erase(str.begin(), str.end());
858 }
859
860 std::string CClosedCaption::CompressString( std::string &cs )
861 {
862
863 std::string cso;
864
865 bool bSpace = true;
866 std::string::size_type n = cs.length();
867 for( std::string::size_type i=0; i<n; i++ ) {
868
869 int ch = cs[i];
870 if( isprint(ch) ) {
871 if( isspace(ch) ) {
872 if( !bSpace ) {
873 bSpace = true;
874 cso += " ";
875 }
876 } else {
877 bSpace = false;
878 cso += (char)ch;
879 }
880 }
881 }
882
883
884 trim2(cso);
885 return cso;
886
887 }
888
889 int CClosedCaption::getSubtitleLineCount()
890 {
891
892 if( (m_captionMode & CAP_ROLL) == 0 )
893 return 4;
894 else
895 return (int)(m_captionMode & 0x0f);
896
897 }
898
899 bool CClosedCaption::WriteSubtitleLine( bool &bDoBR, int line )
900 {
901
902 if( m_pParent->m_bUseCutoffDuration ) {
903 if( (m_pParent->m_nLastSync - getSyncTime(line)) > m_pParent->m_nCutoffDuration ) {
904 ClearNodes(line);
905 return false;
906 }
907 }
908
909 std::string cbuf;
910
911 cbuf = NodesToString(line);
912 cbuf = CompressString(cbuf);
913
914 if( cbuf.length() > 0 ) {
915
916 std::string message;
917 if( bDoBR ) {
918 message = "<BR>";
919 message += cbuf;
920 } else {
921 message = cbuf;
922 }
923
924 fprintf(m_nFile, "%s\n", message.c_str() );
925 #if defined(DumpClosedCapData)
926 fprintf(m_pParent->m_nFile, "%s\n", message.c_str() );
927 #endif
928 bDoBR = true;
929 return true;
930
931 } else
932 return false;
933 }
934
935 void CClosedCaption::SplitSubtitles()
936 {
937
938 CCNode *l = NULL;
939 CCNode *n;
940 m_popLast = 0;
941 m_popPTS = m_pParent->m_lastPTS;
942 int line;
943
944 CCNode *e[15];
945 CCNode *eh[15];
946
947 int i;
948 for( i=0; i<15; i++ )
949 eh[i] = e[i] = NULL;
950
951 n = m_head[0];
952 while ( n != NULL ) {
953
954 if( (n->m_b1 == 0x94 && n->m_b2 == 0x20 ) ||
955 (n->m_b1 == 0x1C && n->m_b2 == 0x20) ) {
956
957 int current = ( ( n->m_b1 & 0x08 ) >> 3 );
958 if( current == m_nNowChannel || m_pParent->m_nCcChannel == 0 ) {
959 m_nNowChannel = current;
960 l = n->m_link;
961 delete n;
962 } else {
963 m_nNowChannel = current;
964 m_ChannelExtra = n;
965 l = NULL;
966 }
967
968 } else if( m_pParent->m_nCcChannel == 0 || ( m_pParent->m_nCcChannel - 1 ) == m_nNowChannel ) {
969
970 if( (line = getPreambleRow( n->m_b1, n->m_b2 )) >= 0 ) {
971
972 m_popLast = line;
973 m_popPTS = n->m_PTS;
974 l = n->m_link;
975 delete n;
976
977 } else {
978
979 l = n->m_link;
980
981 if( eh[m_popLast] == NULL )
982 eh[m_popLast] = n;
983 else
984 e[m_popLast]->m_link = n;
985
986 e[m_popLast] = n;
987 n->m_link = NULL;
988
989 }
990
991 } else {
992
993 l = n->m_link;
994 delete n;
995
996 }
997
998 n = l;
999
1000 } // while
1001
1002 for( i=0; i<4; i++ )
1003 m_head[i] = NULL;
1004
1005 int k;
1006 for( k=i=0; i<15 && k<4; i++ )
1007 if( eh[i] != NULL )
1008 m_head[k++] = eh[i];
1009
1010 }
1011
1012 void CClosedCaption::WriteSubtitles()
1013 {
1014
1015 std::string cbuf;
1016 int nl;
1017
1018 if( m_captionMode == CAP_POPON )
1019 SplitSubtitles();
1020
1021 int nSync = getSyncTime();
1022 if( nSync <= m_pParent->m_nLastSync )
1023 return;
1024
1025 bool bDoBR = false;
1026 switch( m_pParent->m_nCaptionFormat ) {
1027
1028 case FMT_SAMI:
1029
1030 // If the user wants to limit caption duration, check to see if we should have
1031 // turned them off earlier. If so, add sync to do so before we add the current
1032 // caption.
1033
1034 if( m_pParent->m_bUseCutoffDuration ) {
1035
1036 if( (nSync - m_pParent->m_nLastSync) > m_pParent->m_nCutoffDuration ) {
1037
1038 fprintf(m_nFile, "<SYNC Start=%d>\n", m_pParent->m_nLastSync + m_pParent->m_nCutoffDuration);
1039 #if defined(DumpClosedCapData)
1040 fprintf(m_pParent->m_nFile, "<SYNC Start=%d>\n", m_pParent->m_nLastSync + m_pParent->m_nCutoffDuration);
1041 #endif
1042 m_pParent->m_nCaptionCount++;
1043
1044 WriteLine("<P CLASS=ENUSCC>&nbsp;</P>");
1045 #if defined(DumpClosedCapData)
1046 fprintf(m_pParent->m_nFile, "<P CLASS=ENUSCC>&nbsp;</P>\n");
1047 #endif
1048
1049 WriteLine("</SYNC>");
1050 #if defined(DumpClosedCapData)
1051 fprintf(m_pParent->m_nFile, "</SYNC>\n");
1052 #endif
1053
1054 }
1055
1056 }
1057
1058 m_pParent->m_nLastSync = nSync;
1059
1060 fprintf(m_nFile, "<SYNC Start=%d>\n", nSync );
1061 #if defined(DumpClosedCapData)
1062 fprintf(m_pParent->m_nFile, "<SYNC Start=%d>\n", nSync );
1063 #endif
1064 m_pParent->m_nCaptionCount++;
1065
1066 fprintf(m_nFile, "<P CLASS=ENUSCC>\n" );
1067 #if defined(DumpClosedCapData)
1068 fprintf(m_pParent->m_nFile, "<P CLASS=ENUSCC>\n" );
1069 #endif
1070
1071 nl = getSubtitleLineCount();
1072 if( nl >= 4 )
1073 WriteSubtitleLine( bDoBR, 3 );
1074 if( nl >= 3 )
1075 WriteSubtitleLine( bDoBR, 2 );
1076 if( nl >= 2 )
1077 WriteSubtitleLine( bDoBR, 1 );
1078
1079 cbuf = NodesToString( 0 );
1080 cbuf = CompressString(cbuf);
1081 if( cbuf.length() == 0 ) {
1082
1083 if( !bDoBR ) {
1084 cbuf = "&nbsp;";
1085 fprintf(m_nFile, "%s\n", cbuf.c_str() );
1086 #if defined(DumpClosedCapData)
1087 fprintf(m_pParent->m_nFile, "%s\n", cbuf.c_str() );
1088 #endif
1089 }
1090
1091 } else {
1092
1093 std::string message;
1094 if( bDoBR ) {
1095 message = "<BR>";
1096 message += cbuf;
1097 } else {
1098 message = cbuf;
1099 }
1100 fprintf(m_nFile, "%s\n", message.c_str() );
1101 #if defined(DumpClosedCapData)
1102 fprintf(m_pParent->m_nFile, "%s\n", message.c_str() );
1103 #endif
1104
1105 }
1106
1107 fprintf(m_nFile, "</P>\n" );
1108 #if defined(DumpClosedCapData)
1109 fprintf(m_pParent->m_nFile, "</P>\n" );
1110 #endif
1111 WriteLine("</SYNC>");
1112 #if defined(DumpClosedCapData)
1113 fprintf(m_pParent->m_nFile, "</SYNC>\n");
1114 #endif
1115 break;
1116
1117 case FMT_SRT:
1118
1119 if( m_pParent->m_csCurrentCaption.length() > 0 ) {
1120
1121 int nEndTime = nSync - 1 ;
1122 if( m_pParent->m_bUseCutoffDuration )
1123 if( (nSync - m_pParent->m_nLastSync) > m_pParent->m_nCutoffDuration )
1124 nEndTime = m_pParent->m_nLastSync + m_pParent->m_nCutoffDuration;
1125 int nStartTime = m_pParent->m_nLastSync;
1126
1127 m_pParent->m_nCaptionCount++;
1128 fprintf(m_nFile, "%d\n", m_pParent->m_nCaptionCount );
1129 #if defined(DumpClosedCapData)
1130 fprintf(m_pParent->m_nFile, "%d\n", m_pParent->m_nCaptionCount );
1131 #endif
1132
1133 fprintf(m_nFile, "%s --> %s\n", SrtTimeString(nStartTime).c_str(), SrtTimeString(nEndTime).c_str() );
1134 #if defined(DumpClosedCapData)
1135 fprintf(m_pParent->m_nFile, "%s --> %s\n", SrtTimeString(nStartTime).c_str(), SrtTimeString(nEndTime).c_str() );
1136 #endif
1137 fprintf(m_nFile, "%s\n\n", m_pParent->m_csCurrentCaption.c_str() );
1138 #if defined(DumpClosedCapData)
1139 fprintf(m_pParent->m_nFile, "%s\n\n", m_pParent->m_csCurrentCaption.c_str() );
1140 #endif
1141
1142 }
1143
1144 bDoBR = false;
1145 m_pParent->m_csCurrentCaption.erase();
1146
1147 nl = getSubtitleLineCount();
1148 if( nl >= 4 )
1149 {
1150 cbuf = NodesToString( 3 );
1151 cbuf = CompressString(cbuf);
1152 if( cbuf.length() > 0 ) {
1153 m_pParent->m_csCurrentCaption += cbuf;
1154 bDoBR = true;
1155 }
1156 }
1157
1158 if( nl >= 3 )
1159 {
1160 cbuf = NodesToString( 2 );
1161 cbuf = CompressString(cbuf);
1162 if( cbuf.length() > 0 ) {
1163 if( bDoBR )
1164 m_pParent->m_csCurrentCaption += "\n";
1165 m_pParent->m_csCurrentCaption += cbuf;
1166 bDoBR = true;
1167 }
1168 }
1169
1170 if( nl >= 2 ) {
1171 cbuf = NodesToString( 1 );
1172 cbuf = CompressString(cbuf);
1173 if( cbuf.length() > 0 ) {
1174 if( bDoBR )
1175 m_pParent->m_csCurrentCaption += "\n";
1176 m_pParent->m_csCurrentCaption += cbuf;
1177 bDoBR = true;
1178 }
1179 }
1180
1181 cbuf = NodesToString( 0 );
1182 cbuf = CompressString(cbuf);
1183 if( cbuf.length() > 0 ) {
1184 if( bDoBR )
1185 m_pParent->m_csCurrentCaption += "\n";
1186 m_pParent->m_csCurrentCaption += cbuf;
1187 }
1188
1189 m_pParent->m_nLastSync = nSync;
1190
1191 break;
1192
1193 } // switch
1194
1195 }
1196
1197 int CClosedCaption::CcDecode( byte b1, byte b2 )
1198 {
1199
1200 CCNode *node;
1201
1202 unsigned int data = ( b2 << 8 ) + b1;
1203
1204 if ( data == 0x8080 ) {
1205
1206 node = new CCNode( m_pParent->m_lastPTS, b1, b2 );
1207 InsertNode( node );
1208 return -1;
1209
1210 } else if (data == 0xFFFF ) //invalid data.
1211 return -1;
1212
1213 if( !m_pParent->m_validSampleDiff ) {
1214 m_pParent->m_validSampleDiff = true;
1215 m_pParent->m_SampleDiff = (int)m_pParent->m_lastPTS - 1000 * ( 60 * 60 * m_pParent->m_lastGOPHour + 60 * m_pParent->m_lastGOPMinute + m_pParent->m_lastGOPSecond );
1216 #if defined(DumpClosedCapData)
1217 fprintf(m_pParent->m_nFile, " CClosedCaption::CcDecode : SampleDiff = %d\n", m_pParent->m_SampleDiff );
1218 #endif
1219 }
1220
1221 if ( ( b1 & 0x60 ) ) { // Text Data
1222
1223 if( (b1 & 0x7f) == 0 )
1224 b1 = 0x80;
1225
1226 if( (b2 & 0x60) == 0 ) {
1227 b2 = 0x80;
1228 }
1229
1230 #if defined(DumpClosedCapData)
1231 int DisplayTime = (int)m_pParent->m_lastPTS;
1232 fprintf(m_pParent->m_nFile, "CClosedCaption::CcDecode : Display Time = %d, %2x(%c), %2x(%c)\n", DisplayTime, b1, b1&0x7f, b2, b2&0x7f );
1233 #endif
1234
1235 if( m_bEocValid )
1236 if( m_pParent->m_lastPTS >= m_EocPTS ) {
1237
1238 WriteSubtitles();
1239 ClearNodes();
1240 if( m_ChannelExtra != NULL ) {
1241
1242 m_head[0] = m_ChannelExtra;
1243 m_ChannelExtra = NULL;
1244
1245 } else {
1246
1247 if( m_captionMode == CAP_POPON ) {
1248 switch(m_popLast) {
1249 case 0:
1250 node = new CCNode( m_popPTS, 0x94, 0x70 );
1251 break;
1252 case 1:
1253 node = new CCNode( m_popPTS, 0x94, 0xD0 );
1254 break;
1255 case 2:
1256 node = new CCNode( m_popPTS, 0x13, 0x70 );
1257 break;
1258 case 3:
1259 node = new CCNode( m_popPTS, 0x13, 0xD0 );
1260 break;
1261 case 4:
1262 node = new CCNode( m_popPTS, 0x10, 0xD0 );
1263 break;
1264 case 5:
1265 node = new CCNode( m_popPTS, 0x97, 0x70 );
1266 break;
1267 case 6:
1268 node = new CCNode( m_popPTS, 0x97, 0xD0 );
1269 break;
1270 case 7:
1271 node = new CCNode( m_popPTS, 0x16, 0x70 );
1272 break;
1273 case 8:
1274 node = new CCNode( m_popPTS, 0x16, 0xD0 );
1275 break;
1276 case 9:
1277 node = new CCNode( m_popPTS, 0x15, 0x70 );
1278 break;
1279 case 10:
1280 node = new CCNode( m_popPTS, 0x15, 0xD0 );
1281 break;
1282 case 11:
1283 node = new CCNode( m_popPTS, 0x92, 0x70 );
1284 break;
1285 case 12:
1286 node = new CCNode( m_popPTS, 0x92, 0xD0 );
1287 break;
1288 case 13:
1289 node = new CCNode( m_popPTS, 0x91, 0x70 );
1290 break;
1291 case 14:
1292 node = new CCNode( m_popPTS, 0x91, 0xD0 );
1293 break;
1294 default:
1295 node = NULL;
1296 } // switch
1297
1298 if( node != NULL )
1299 InsertNode( node );
1300
1301 }
1302
1303 }
1304
1305 }
1306
1307 node = new CCNode( m_pParent->m_lastPTS, b1, b2 );
1308 InsertNode( node );
1309
1310 } else if ( ( b1 & 0x10 ) && ( b2 > 0x1F ) && ( data != CC_last ) ) {
1311
1312 //codes are typically transmitted twice (ignore the second occurance)
1313
1314 if( b2 & 0x40 ) { //preamble address code (row & indent)
1315
1316 #if defined(DumpClosedCapData)
1317 fprintf(m_pParent->m_nFile, "CClosedCaption::CcDecode : Preamble Address Code %2x, %2x \n", b1, b2 );
1318 #endif
1319
1320 if( m_captionMode ==CAP_UNKNOWN )
1321 m_captionMode = CAP_POPON;
1322
1323 if( getPreambleRow( b1, b2 ) >= 0 ) {
1324 node = new CCNode( m_pParent->m_lastPTS, b1, b2 );
1325 InsertNode( node );
1326 }
1327
1328 } else { // !(b2 & 0x40)
1329
1330 switch ( b1 & 0x07 ) {
1331
1332 case 0x00: //attribute
1333
1334 #if defined(DumpClosedCapData)
1335 fprintf(m_pParent->m_nFile, "CClosedCaption::CcDecode : <ATTRIBUTE: %d>\n", b2 );
1336 #endif
1337
1338 break;
1339
1340 case 0x01: //midrow or char
1341
1342 #if defined(DumpClosedCapData)
1343 fprintf(m_pParent->m_nFile, "CClosedCaption::CcDecode : Midrow or Char Code %2x, %2x \n", b1, b2 );
1344 #endif
1345
1346 switch( b2 ) {
1347
1348 case 0x20: // Default : white, no format
1349 case 0x37: // Music note
1350 case 0xB9: // Transparent Space
1351 case 0xAE: // Italic
1352
1353 node = new CCNode( m_pParent->m_lastPTS, b1, b2 );
1354 InsertNode( node );
1355 break;
1356
1357 default:
1358
1359 break;
1360
1361 } // switch (b2)
1362
1363 break;
1364
1365 case 0x04: //misc
1366 case 0x05: //misc + F
1367
1368 switch( b2 & 0x7f ) {
1369
1370 case 0x20: //resume caption loading
1371
1372 m_captionMode = CAP_POPON;
1373
1374 node = new CCNode( m_pParent->m_lastPTS, b1, b2 );
1375 InsertNode( node );
1376
1377 break;
1378
1379 case 0x21: //backspace
1380
1381 #if defined(DumpClosedCapData)
1382 fprintf(m_pParent->m_nFile, "CClosedCaption::CcDecode : Backspace Code %2x, %2x \n", b1, b2 );
1383 #endif
1384
1385 break;
1386
1387 case 0x25: //2 row caption
1388
1389 #if defined(DumpClosedCapData)
1390 fprintf(m_pParent->m_nFile, "CClosedCaption::CcDecode : Rollup - 2 rows\n" );
1391 #endif
1392 if( isRollup() )
1393 doRollup(m_pParent->m_lastPTS);
1394
1395 m_captionMode = CAP_ROLL2;
1396 break;
1397
1398 case 0x26: //3 row caption
1399
1400 #if defined(DumpClosedCapData)
1401 fprintf(m_pParent->m_nFile, "CClosedCaption::CcDecode : Rollup - 3 rows\n" );
1402 #endif
1403 if( isRollup() )
1404 doRollup(m_pParent->m_lastPTS);
1405
1406 m_captionMode = CAP_ROLL3;
1407 break;
1408
1409 case 0x27: //4 row caption
1410
1411 #if defined(DumpClosedCapData)
1412 fprintf(m_pParent->m_nFile, "CClosedCaption::CcDecode : Rollup - 4 rows\n" );
1413 #endif
1414 if( isRollup() )
1415 doRollup(m_pParent->m_lastPTS);
1416
1417 m_captionMode = CAP_ROLL4;
1418 break;
1419
1420 case 0x29: //resume direct caption
1421 case 0x2B: //resume text display
1422
1423 #if defined(DumpClosedCapData)
1424 fprintf(m_pParent->m_nFile, "CClosedCaption::CcDecode : Position Code %2x, %2x \n", b1, b2 );
1425 #endif
1426
1427 break;
1428
1429 case 0x2C: //erase displayed memory
1430
1431 #if defined(DumpClosedCapData)
1432 fprintf(m_pParent->m_nFile, "CClosedCaption::CcDecode : Erase Displayed Memory %2x, %2x \n", b1, b2 );
1433 #endif
1434
1435 m_bEocValid = true;
1436 m_EocPTS =m_pParent->m_lastPTS;
1437
1438 break;
1439
1440 case 0x2D: //carriage return
1441
1442 #if defined(DumpClosedCapData)
1443 fprintf(m_pParent->m_nFile, "CClosedCaption::CcDecode : Carriage Return Code %2x, %2x \n", b1, b2 );
1444 #endif
1445
1446 if( m_bEocValid ) {
1447 if( m_pParent->m_lastPTS >= LastNodePTS(0) )
1448 WriteSubtitles();
1449 }
1450
1451 if( isRollup() )
1452 doRollup(m_pParent->m_lastPTS);
1453
1454 node = new CCNode( m_pParent->m_lastPTS, b1, b2 );
1455 InsertNode( node );
1456
1457 break;
1458
1459 case 0x2F: // end caption + swap memory
1460
1461 #if defined(DumpClosedCapData)
1462 fprintf(m_pParent->m_nFile, "CClosedCaption::CcDecode : End Caption Code %2x, %2x \n", b1, b2 );
1463 #endif
1464
1465 if( (m_bEocValid = IsEoc() ) )
1466 m_EocPTS = m_pParent->m_lastPTS;
1467 break;
1468
1469 case 0x2A: //text restart
1470 case 0x2E: //erase non-displayed memory
1471
1472 #if defined(DumpClosedCapData)
1473 fprintf(m_pParent->m_nFile, "CClosedCaption::CcDecode : Text Restart Code %2x, %2x \n", b1, b2 );
1474 #endif
1475
1476 break;
1477
1478 default:
1479 #if defined(DumpClosedCapData)
1480 fprintf(m_pParent->m_nFile, "CClosedCaption::CcDecode : Unknown Misc Code %2x, %2x \n", b1, b2 );
1481 #endif
1482 break;
1483
1484 } // switch (b2)
1485
1486 break;
1487
1488 case 0x07: //misc (TAB)
1489
1490 #if defined(DumpClosedCapData)
1491 fprintf(m_pParent->m_nFile, "CClosedCaption::CcDecode : Tab Code %2x, %2x \n", b1, b2 );
1492 #endif
1493
1494 break;
1495
1496 default:
1497
1498 #if defined(DumpClosedCapData)
1499 fprintf(m_pParent->m_nFile, "CClosedCaption::CcDecode : Unknown Code %2x, %2x \n", b1, b2 );
1500 #endif
1501 break;
1502
1503 } // switch
1504
1505 }
1506
1507 }
1508
1509 CC_last = data;
1510
1511 return 0;
1512
1513 }

cvs@jdrake.com
ViewVC Help
Powered by ViewVC 1.1.13