-
Notifications
You must be signed in to change notification settings - Fork 574
Expand file tree
/
Copy pathgeneral_loop.c
More file actions
1806 lines (1647 loc) · 53.2 KB
/
Copy pathgeneral_loop.c
File metadata and controls
1806 lines (1647 loc) · 53.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include "lib_ccx.h"
#include "ccx_common_option.h"
#ifdef _WIN32
#include <io.h>
#else
#include <unistd.h>
#endif
#include "dvb_subtitle_decoder.h"
#include "ccx_encoders_common.h"
#include "ccx_encoders_mcc.h"
#include "activity.h"
#include "utility.h"
#include "ccx_demuxer.h"
#include "file_buffer.h"
#include "ccx_decoders_isdb.h"
#include "ffmpeg_intgr.h"
#include "ccx_gxf.h"
#include "dvd_subtitle_decoder.h"
#include "ccx_demuxer_mxf.h"
#include "ccx_dtvcc.h"
int end_of_file = 0; // End of file?
// Program stream specific data grabber
int ps_get_more_data(struct lib_ccx_ctx *ctx, struct demuxer_data **ppdata)
{
int enough = 0;
int payload_read = 0;
size_t result;
static unsigned vpesnum = 0;
unsigned char nextheader[512]; // Next header in PS
int falsepack = 0;
struct demuxer_data *data;
if (!*ppdata)
{
*ppdata = alloc_demuxer_data();
if (!*ppdata)
return -1;
data = *ppdata;
// TODO Set to dummy, find and set actual value
data->program_number = 1;
data->stream_pid = 1;
data->codec = CCX_CODEC_ATSC_CC;
}
else
{
data = *ppdata;
}
// Read and return the next video PES payload
do
{
if (BUFSIZE - data->len < 500)
{
mprint("Less than 500 left\n");
enough = 1; // Stop when less than 500 bytes are left in buffer
}
else
{
result = buffered_read(ctx->demux_ctx, nextheader, 6);
ctx->demux_ctx->past += result;
if (result != 6)
{
// Consider this the end of the show.
end_of_file = 1;
break;
}
// Search for a header that is not a picture header (nextheader[3]!=0x00)
while (!(nextheader[0] == 0x00 && nextheader[1] == 0x00 && nextheader[2] == 0x01 && nextheader[3] != 0x00))
{
if (!ctx->demux_ctx->strangeheader)
{
mprint("\nNot a recognized header. Searching for next header.\n");
dump(CCX_DMT_PARSE, nextheader, 6, 0, 0);
// Only print the message once per loop / unrecognized header
ctx->demux_ctx->strangeheader = 1;
}
unsigned char *newheader;
// The amount of bytes read into nextheader by the buffered_read above
int hlen = 6;
// Find first 0x00
// If there is a 00 in the first element we need to advance
// one step as clearly bytes 1,2,3 are wrong
newheader = (unsigned char *)memchr(nextheader + 1, 0, hlen - 1);
if (newheader != NULL)
{
int atpos = newheader - nextheader;
memmove(nextheader, newheader, (size_t)(hlen - atpos));
result = buffered_read(ctx->demux_ctx, nextheader + (hlen - atpos), atpos);
ctx->demux_ctx->past += result;
if (result != atpos)
{
end_of_file = 1;
break;
}
}
else
{
result = buffered_read(ctx->demux_ctx, nextheader, hlen);
ctx->demux_ctx->past += result;
if (result != hlen)
{
end_of_file = 1;
break;
}
}
}
if (end_of_file)
{
// No more headers
break;
}
// Found 00-00-01 in nextheader, assume a regular header
ctx->demux_ctx->strangeheader = 0;
// PACK header
if (nextheader[3] == 0xBA)
{
dbg_print(CCX_DMT_VERBOSE, "PACK header\n");
result = buffered_read(ctx->demux_ctx, nextheader + 6, 8);
ctx->demux_ctx->past += result;
if (result != 8)
{
// Consider this the end of the show.
end_of_file = 1;
break;
}
if ((nextheader[4] & 0xC4) != 0x44 || !(nextheader[6] & 0x04) || !(nextheader[8] & 0x04) || !(nextheader[9] & 0x01) || (nextheader[12] & 0x03) != 0x03)
{
// broken pack header
falsepack = 1;
}
// We don't need SCR/SCR_ext
int stufflen = nextheader[13] & 0x07;
if (falsepack)
{
mprint("Warning: Defective Pack header\n");
}
// If not defect, load stuffing
buffered_skip(ctx->demux_ctx, (int)stufflen);
ctx->demux_ctx->past += stufflen;
// fake a result value as something was skipped
result = 1;
continue;
}
// PES Header
// Private Stream 1 (non MPEG audio , subpictures)
else if (nextheader[3] == 0xBD)
{
uint16_t packetlength = (nextheader[4] << 8) | nextheader[5];
int ret, datalen;
// TODO: read PES Header extension , skipped for now
// read_video_pes_header() might do
buffered_skip(ctx->demux_ctx, 2);
ctx->demux_ctx->past += 2;
ret = buffered_read(ctx->demux_ctx, nextheader + 6, 1); // PES header data length (extension)
ctx->demux_ctx->past += 1;
if (ret != 1)
{
end_of_file = 1;
break;
}
buffered_skip(ctx->demux_ctx, (int)nextheader[6]);
ctx->demux_ctx->past += (int)nextheader[6];
// Substream ID
ret = buffered_read(ctx->demux_ctx, nextheader + 7, 1);
ctx->demux_ctx->past += 1;
if (ret != 1)
{
end_of_file = 1;
break;
}
datalen = packetlength - 4 - nextheader[6];
// dbg_print(CCX_DMT_VERBOSE, "datalen :%d packetlen :%" PRIu16 " pes header ext :%d\n", datalen, packetlength, nextheader[6]);
// Subtitle substream ID 0x20 - 0x39 (32 possible)
if (nextheader[7] >= 0x20 && nextheader[7] < 0x40)
{
dbg_print(CCX_DMT_VERBOSE, "Subtitle found Stream id:%02x\n", nextheader[7]);
result = buffered_read(ctx->demux_ctx, data->buffer, datalen);
ctx->demux_ctx->past += datalen;
if (result != datalen)
{
end_of_file = 1;
break;
}
if (result > 0)
{
payload_read += (int)result;
}
// FIXME: Temporary bypass
data->bufferdatatype = CCX_DVD_SUBTITLE;
data->len = result;
enough = 1;
continue;
}
else
{
data->bufferdatatype = CCX_PES;
// Non Subtitle packet
buffered_skip(ctx->demux_ctx, datalen);
ctx->demux_ctx->past += datalen;
// fake a result value as something was skipped
result = 1;
continue;
}
}
// Some PES stream
else if (nextheader[3] >= 0xBB && nextheader[3] <= 0xDF)
{
// System header
// nextheader[3]==0xBB
// 0xBD Private 1
// 0xBE PAdding
// 0xBF Private 2
// 0xC0-0DF audio
unsigned headerlen = nextheader[4] << 8 | nextheader[5];
dbg_print(CCX_DMT_VERBOSE, "non Video PES (type 0x%2X) - len %u\n",
nextheader[3], headerlen);
// The 15000 here is quite arbitrary, the longest packages I
// know of are 12302 bytes (Private 1 data in RTL recording).
if (headerlen > 15000)
{
mprint("Suspicious non Video PES (type 0x%2X) - len %u\n",
nextheader[3], headerlen);
mprint("Do not skip over, search for next.\n");
headerlen = 2;
}
data->bufferdatatype = CCX_PES;
// Skip over it
buffered_skip(ctx->demux_ctx, (int)headerlen);
ctx->demux_ctx->past += headerlen;
// fake a result value as something was skipped
result = 1;
continue;
}
// Read the next video PES
else if ((nextheader[3] & 0xf0) == 0xe0)
{
int hlen; // Dummy variable, unused
int ret;
size_t peslen;
ret = read_video_pes_header(ctx->demux_ctx, data, nextheader, &hlen, 0);
if (ret < 0)
{
end_of_file = 1;
break;
}
else
{
peslen = ret;
}
vpesnum++;
dbg_print(CCX_DMT_VERBOSE, "PES video packet #%u\n", vpesnum);
// peslen is unsigned since loop would have already broken if it was negative
int want = (int)((BUFSIZE - data->len) > peslen ? peslen : (BUFSIZE - data->len));
if (want != peslen)
{
mprint("General LOOP: want(%d) != peslen(%d) \n", want, peslen);
continue;
}
if (want == 0) // Found package with header but without payload
{
continue;
}
data->bufferdatatype = CCX_PES;
result = buffered_read(ctx->demux_ctx, data->buffer + data->len, want);
ctx->demux_ctx->past = ctx->demux_ctx->past + result;
if (result > 0)
{
payload_read += (int)result;
}
data->len += result;
if (result != want)
{ // Not complete - EOF
end_of_file = 1;
break;
}
enough = 1; // We got one PES
}
else
{
// If we are here this is an unknown header type
mprint("Unknown header %02X\n", nextheader[3]);
ctx->demux_ctx->strangeheader = 1;
}
}
} while (result != 0 && !enough && BUFSIZE != data->len);
dbg_print(CCX_DMT_VERBOSE, "PES data read: %d\n", payload_read);
// printf("payload\n");
if (!payload_read)
return CCX_EOF;
return payload_read;
}
// Returns number of bytes read, or CCX_OF for EOF
int general_get_more_data(struct lib_ccx_ctx *ctx, struct demuxer_data **data)
{
int bytesread = 0;
int want;
int result;
struct demuxer_data *ptr;
if (!*data)
{
*data = alloc_demuxer_data();
if (!*data)
return -1;
ptr = *data;
// Dummy program numbers
ptr->program_number = 1;
ptr->stream_pid = 0x100;
ptr->codec = CCX_CODEC_ATSC_CC;
}
ptr = *data;
do
{
want = (int)(BUFSIZE - ptr->len);
result = buffered_read(ctx->demux_ctx, ptr->buffer + ptr->len, want); // This is a macro.
// 'result' HAS the number of bytes read
ctx->demux_ctx->past = ctx->demux_ctx->past + result;
ptr->len += result;
bytesread += (int)result;
} while (result != 0 && result != want);
if (!bytesread)
return CCX_EOF;
return bytesread;
}
#ifdef WTV_DEBUG
// Hexadecimal dump process
void process_hex(struct lib_ccx_ctx *ctx, char *filename)
{
size_t max = (size_t)ctx->inputsize + 1; // Enough for the whole thing. Hex dumps are small so we can be lazy here
char *line = (char *)malloc(max);
if (!line)
{
fatal(EXIT_NOT_ENOUGH_MEMORY, "In process_hex: Out of memory allocating line buffer.");
}
/* const char *mpeg_header="00 00 01 b2 43 43 01 f8 "; // Always present */
FILE *fr = fopen(filename, "rt");
if (!fr)
{
free(line);
fatal(EXIT_FAILURE, "In process_hex: Cannot open %s.", filename);
}
unsigned char *bytes = NULL;
unsigned byte_count = 0;
int warning_shown = 0;
struct demuxer_data *data = alloc_demuxer_data();
if (!data)
{
fclose(fr);
free(line);
fatal(EXIT_NOT_ENOUGH_MEMORY, "In process_hex: Out of memory allocating demuxer data.");
}
while (fgets(line, max - 1, fr) != NULL)
{
char *c1, *c2 = NULL; // Positions for first and second colons
/* int len; */
long timing;
if (line[0] == ';') // Skip comments
continue;
c1 = strchr(line, ':');
if (c1)
c2 = strchr(c1 + 1, ':');
if (!c2) // Line doesn't contain what we want
continue;
*c1 = 0;
*c2 = 0;
/* len=atoi (line); */
timing = atol(c1 + 2) * (MPEG_CLOCK_FREQ / 1000);
current_pts = timing;
if (pts_set == 0)
pts_set = 1;
set_fts();
c2++;
/*
if (strlen (c2)==8)
{
unsigned char high1=c2[1];
unsigned char low1=c2[2];
int value1=hex_to_int (high1,low1);
unsigned char high2=c2[4];
unsigned char low2=c2[5];
int value2=hex_to_int (high2,low2);
buffer[0]=value1;
buffer[1]=value2;
data->windedx =2;
process_raw();
continue;
}
if (strlen (c2)<=(strlen (mpeg_header)+1))
continue; */
c2++; // Skip blank
/*
if (strncmp (c2,mpeg_header,strlen (mpeg_header))) // No idea how to deal with this.
{
if (!warning_shown)
{
warning_shown=1;
mprint ("\nWarning: This file contains data I can't process: Please submit .hex for analysis!\n");
}
continue;
}
// OK, seems like a decent chunk of CCdata.
c2+=strlen (mpeg_header);
*/
byte_count = strlen(c2) / 3;
/*
if (atoi (line)!=byte_count+strlen (mpeg_header)/3) // Number of bytes reported don't match actual contents
continue;
*/
if (atoi(line) != byte_count) // Number of bytes reported don't match actual contents
continue;
if (!byte_count) // Nothing to get from this line except timing info, already done.
continue;
bytes = (unsigned char *)malloc(byte_count);
if (!bytes)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In process_hex: Out of memory to store processed hex value.\n");
for (unsigned i = 0; i < byte_count; i++)
{
unsigned char high = c2[0];
unsigned char low = c2[1];
int value = hex_to_int(high, low);
if (value == -1)
fatal(EXIT_FAILURE, "In process_hex: Incorrect format, unexpected non-hex string.");
bytes[i] = value;
c2 += 3;
}
memcpy(data->buffer, bytes, byte_count);
data->len = byte_count;
process_raw();
continue;
// New wtv format, everything else hopefully obsolete
int ok = 0; // Were we able to process the line?
// Attempt to detect how the data is encoded.
// Case 1 (seen in all elderman's samples):
// 18 : 467 : 00 00 01 b2 43 43 01 f8 03 42 ff fd 54 80 fc 94 2c ff
// Always 03 after header, then something unknown (seen 42, 43, c2, c3...),
// then ff, then data with field info, and terminated with ff.
if (byte_count > 3 && bytes[0] == 0x03 &&
bytes[2] == 0xff && bytes[byte_count - 1] == 0xff)
{
ok = 1;
for (unsigned i = 3; i < byte_count - 2; i += 3)
{
data->len = 3;
ctx->buffer[0] = bytes[i];
ctx->buffer[1] = bytes[i + 1];
ctx->buffer[2] = bytes[i + 2];
process_raw_with_field();
}
}
// Case 2 (seen in P2Pfiend samples):
// Seems to match McPoodle's descriptions on DVD encoding
else
{
unsigned char magic = bytes[0];
/* unsigned extra_field_flag=magic&1; */
unsigned caption_count = ((magic >> 1) & 0x1F);
unsigned filler = ((magic >> 6) & 1);
/* unsigned pattern=((magic>>7)&1); */
int always_ff = 1;
int current_field = 0;
if (filler == 0 && caption_count * 6 == byte_count - 1) // Note that we are ignoring the extra field for now...
{
ok = 1;
for (unsigned i = 1; i < byte_count - 2; i += 3)
if (bytes[i] != 0xff)
{
// If we only find FF in the first byte then either there's only field 1 data, OR
// there's alternating field 1 and field 2 data. Don't know how to tell apart. For now
// let's assume that always FF means alternating.
always_ff = 0;
break;
}
for (unsigned i = 1; i < byte_count - 2; i += 3)
{
inbuf = 3;
if (always_ff) // Try to tell apart the fields based on the pattern field.
{
ctx->buffer[0] = current_field | 4; // | 4 to enable the 'valid' bit
current_field = !current_field;
}
else
ctx->buffer[0] = bytes[i];
ctx->buffer[1] = bytes[i + 1];
ctx->buffer[2] = bytes[i + 2];
process_raw_with_field();
}
}
}
if (!ok && !warning_shown)
{
warning_shown = 1;
mprint("\nWarning: This file contains data I can't process: Please submit .hex for analysis!\n");
}
free(bytes);
}
fclose(fr);
}
#endif
/* Process raw caption data (2-byte pairs) and encode directly to MCC format.
* This bypasses the 608 decoder and writes raw cc_data to MCC.
* Used when -out=mcc is specified with raw input files (issue #1542). */
static size_t process_raw_for_mcc(struct encoder_ctx *enc_ctx, struct lib_cc_decode *dec_ctx,
unsigned char *buffer, size_t len)
{
unsigned char cc_data[3];
size_t i;
size_t pairs_processed = 0;
// Set frame rate to 29.97fps (code 4) for raw 608 captions
// This is needed for proper MCC timing
if (dec_ctx->current_frame_rate == 0)
dec_ctx->current_frame_rate = 4; // CCX_FR_29_97
cc_data[0] = 0x04; // Field 1, cc_valid=1, cc_type=0 (NTSC field 1)
for (i = 0; i < len; i += 2)
{
// Skip broadcast header (0xff 0xff)
if (!dec_ctx->saw_caption_block && buffer[i] == 0xff && buffer[i + 1] == 0xff)
continue;
dec_ctx->saw_caption_block = 1;
cc_data[1] = buffer[i];
cc_data[2] = buffer[i + 1];
// Update timing for this CC pair (each pair is one frame at 29.97fps)
// Timing: 1001/30 ms per pair = ~33.37ms
set_fts(enc_ctx->timing);
// Encode this CC pair to MCC format
mcc_encode_cc_data(enc_ctx, dec_ctx, cc_data, 1);
// Advance timing for next pair
add_current_pts(enc_ctx->timing, 1001 * (MPEG_CLOCK_FREQ / 1000) / 30);
pairs_processed++;
}
return pairs_processed;
}
// Raw file process
// Parse raw CDP (Caption Distribution Packet) data
// Returns number of bytes processed
static size_t process_raw_cdp(struct encoder_ctx *enc_ctx, struct lib_cc_decode *dec_ctx,
struct cc_subtitle *sub, unsigned char *buffer, size_t len)
{
size_t pos = 0;
int cdp_count = 0;
while (pos + 10 < len) // Minimum CDP size
{
// Check for CDP identifier
if (buffer[pos] != 0x96 || buffer[pos + 1] != 0x69)
{
pos++;
continue;
}
unsigned char cdp_length = buffer[pos + 2];
if (pos + cdp_length > len)
break; // Incomplete CDP packet
unsigned char framerate_byte = buffer[pos + 3];
int framerate_code = framerate_byte >> 4;
// Skip to find cc_data section (0x72)
size_t cdp_pos = pos + 4; // After identifier, length, framerate
int cc_count = 0;
unsigned char *cc_data = NULL;
// Skip header sequence counter (2 bytes)
cdp_pos += 2;
// Look for cc_data section (0x72) within CDP
while (cdp_pos < pos + cdp_length - 4)
{
if (buffer[cdp_pos] == 0x72) // cc_data section
{
cc_count = buffer[cdp_pos + 1] & 0x1F;
cc_data = buffer + cdp_pos + 2;
break;
}
else if (buffer[cdp_pos] == 0x71) // time code section
{
cdp_pos += 5; // Skip time code section
}
else if (buffer[cdp_pos] == 0x73) // service info section
{
break; // Past cc_data
}
else if (buffer[cdp_pos] == 0x74) // footer
{
break;
}
else
{
cdp_pos++;
}
}
if (cc_count > 0 && cc_data != NULL)
{
// Calculate PTS based on CDP frame count and frame rate
static const int fps_table[] = {0, 24, 24, 25, 30, 30, 50, 60, 60};
int fps = (framerate_code < 9) ? fps_table[framerate_code] : 30;
LLONG pts = (LLONG)cdp_count * 90000 / fps;
// Set timing if not already set
if (dec_ctx->timing->pts_set == 0)
{
dec_ctx->timing->min_pts = pts;
dec_ctx->timing->pts_set = 2;
dec_ctx->timing->sync_pts = pts;
}
set_current_pts(dec_ctx->timing, pts);
set_fts(dec_ctx->timing);
#ifndef DISABLE_RUST
// Enable DTVCC decoder for CEA-708 captions
if (dec_ctx->dtvcc_rust)
{
int is_active = ccxr_dtvcc_is_active(dec_ctx->dtvcc_rust);
if (!is_active)
{
ccxr_dtvcc_set_active(dec_ctx->dtvcc_rust, 1);
}
}
#endif
// Process cc_data triplets through process_cc_data for 708 support
process_cc_data(enc_ctx, dec_ctx, cc_data, cc_count, sub);
cdp_count++;
}
pos += cdp_length;
}
return pos;
}
int raw_loop(struct lib_ccx_ctx *ctx)
{
LLONG ret;
struct demuxer_data *data = NULL;
struct cc_subtitle *dec_sub = NULL;
struct encoder_ctx *enc_ctx = update_encoder_list(ctx);
struct lib_cc_decode *dec_ctx = NULL;
int caps = 0;
int is_dvdraw = 0; // Flag to track if this is DVD raw format
int is_scc = 0; // Flag to track if this is SCC format
int is_cdp = 0; // Flag to track if this is raw CDP format
int is_mcc_output = 0; // Flag for MCC output format
dec_ctx = update_decoder_list(ctx);
dec_sub = &dec_ctx->dec_sub;
// Check if MCC output is requested (issue #1542)
if (enc_ctx && dec_ctx->write_format == CCX_OF_MCC)
{
is_mcc_output = 1;
// Share timing context between decoder and encoder for MCC
enc_ctx->timing = dec_ctx->timing;
}
// For raw mode, timing is derived from the caption block counter (cb_field1).
// We set min_pts=0 and pts_set=MinPtsSet so set_fts() will calculate fts_now.
// Initialize timing for raw mode - no video PTS, just caption block counting.
dec_ctx->timing->min_pts = 0;
dec_ctx->timing->sync_pts = 0;
dec_ctx->timing->pts_set = 2; // MinPtsSet
set_current_pts(dec_ctx->timing, 0);
set_fts(dec_ctx->timing);
do
{
if (terminate_asap)
break;
ret = general_get_more_data(ctx, &data);
if (ret == CCX_EOF)
break;
// Check if this is DVD raw format using Rust detection
if (!is_dvdraw && !is_scc && ccxr_is_dvdraw_header(data->buffer, (unsigned int)data->len))
{
is_dvdraw = 1;
mprint("Detected McPoodle's DVD raw format\n");
}
// Check if this is SCC format using Rust detection
if (!is_scc && !is_dvdraw && ccxr_is_scc_file(data->buffer, (unsigned int)data->len))
{
is_scc = 1;
mprint("Detected SCC (Scenarist Closed Caption) format\n");
}
// Check if this is raw CDP format (starts with 0x9669)
if (!is_cdp && !is_scc && !is_dvdraw && data->len >= 2 &&
data->buffer[0] == 0x96 && data->buffer[1] == 0x69)
{
is_cdp = 1;
mprint("Detected raw CDP (Caption Distribution Packet) format\n");
}
if (is_mcc_output && !is_dvdraw && !is_scc && !is_cdp)
{
// For MCC output, encode raw data directly without decoding
// This preserves the original CEA-608 byte pairs in CDP format
size_t pairs = process_raw_for_mcc(enc_ctx, dec_ctx, data->buffer, data->len);
if (pairs > 0)
caps = 1;
}
else if (is_dvdraw)
{
// Use Rust implementation - handles timing internally
ret = ccxr_process_dvdraw(dec_ctx, dec_sub, data->buffer, (unsigned int)data->len);
}
else if (is_scc)
{
// Use Rust SCC implementation - handles timing internally via SMPTE timecodes
ret = ccxr_process_scc(dec_ctx, dec_sub, data->buffer, (unsigned int)data->len, ccx_options.scc_framerate);
}
else if (is_cdp)
{
// Process raw CDP packets (e.g., from SDI VANC capture)
ret = process_raw_cdp(enc_ctx, dec_ctx, dec_sub, data->buffer, data->len);
if (ret > 0)
caps = 1;
}
else
{
ret = process_raw(dec_ctx, dec_sub, data->buffer, data->len);
// For raw mode, cb_field1 is incremented by do_cb() for each CC pair.
// After processing each chunk, add the accumulated time to current_pts
// and call set_fts() to update fts_now. set_fts() resets cb_field1 to 0,
// so each chunk's timing is added incrementally.
// Note: Cast cb_field1 to LLONG to prevent 32-bit integer overflow
// when calculating ticks for large raw files (issue #1565).
add_current_pts(dec_ctx->timing, (LLONG)cb_field1 * 1001 / 30 * (MPEG_CLOCK_FREQ / 1000));
set_fts(dec_ctx->timing);
}
if (!is_mcc_output && dec_sub->got_output)
{
caps = 1;
encode_sub(enc_ctx, dec_sub);
dec_sub->got_output = 0;
}
// Reset buffer length after processing so we can read more data
// Without this, data->len stays at BUFSIZE and general_get_more_data
// returns CCX_EOF prematurely (it calculates want = BUFSIZE - len = 0)
data->len = 0;
} while (1); // Loop exits via break on CCX_EOF or terminate_asap
free(data);
return caps;
}
/* Process inbuf bytes in buffer holding raw caption data (three byte packets, the first being the field).
* The number of processed bytes is returned. */
size_t process_raw_with_field(struct lib_cc_decode *dec_ctx, struct cc_subtitle *sub, unsigned char *buffer, size_t len)
{
unsigned char data[3];
size_t i;
data[0] = 0x04; // Field 1
dec_ctx->current_field = 1;
for (i = 0; i < len; i = i + 3)
{
if (!dec_ctx->saw_caption_block && *(buffer + i) == 0xff && *(buffer + i + 1) == 0xff)
{
// Skip broadcast header
}
else
{
data[0] = buffer[i];
data[1] = buffer[i + 1];
data[2] = buffer[i + 2];
// do_cb increases the cb_field1 counter so that get_fts()
// is correct.
do_cb(dec_ctx, data, sub);
}
}
return len;
}
/* Process inbuf bytes in buffer holding raw caption data (two byte packets).
* The number of processed bytes is returned. */
size_t process_raw(struct lib_cc_decode *ctx, struct cc_subtitle *sub, unsigned char *buffer, size_t len)
{
unsigned char data[3];
size_t i;
data[0] = 0x04; // Field 1
for (i = 0; i < len; i = i + 2)
{
if (!ctx->saw_caption_block && *(buffer + i) == 0xff && *(buffer + i + 1) == 0xff)
{
// Skip broadcast header
}
else
{
data[1] = buffer[i];
data[2] = buffer[i + 1];
// do_cb increases the cb_field1 counter so that get_fts()
// is correct.
do_cb(ctx, data, sub);
}
}
return len;
}
/* NOTE: process_dvdraw() has been migrated to Rust.
* The implementation is now in src/rust/src/demuxer/dvdraw.rs
* and exported via ccxr_process_dvdraw() in src/rust/src/libccxr_exports/demuxer.rs
*/
void delete_datalist(struct demuxer_data *list)
{
struct demuxer_data *slist = list;
while (list)
{
slist = list;
list = list->next_stream;
delete_demuxer_data(slist);
}
}
int process_data(struct encoder_ctx *enc_ctx, struct lib_cc_decode *dec_ctx, struct demuxer_data *data_node)
{
size_t got; // Means 'consumed' from buffer actually
int ret = 0;
static LLONG last_pts = 0x01FFFFFFFFLL;
struct cc_subtitle *dec_sub = &dec_ctx->dec_sub;
if (dec_ctx->hauppauge_mode)
{
got = process_raw_with_field(dec_ctx, dec_sub, data_node->buffer, data_node->len);
if (dec_ctx->timing->pts_set)
set_fts(dec_ctx->timing); // Try to fix timing from TS data
}
else if (data_node->bufferdatatype == CCX_DVB_SUBTITLE)
{
ret = dvbsub_decode(enc_ctx, dec_ctx, data_node->buffer + 2, data_node->len - 2, dec_sub);
if (ret < 0)
mprint("Return from dvbsub_decode: %d\n", ret);
set_fts(dec_ctx->timing);
got = data_node->len;
}
else if (data_node->bufferdatatype == CCX_PES)
{
dec_ctx->in_bufferdatatype = CCX_PES;
got = process_m2v(enc_ctx, dec_ctx, data_node->buffer, data_node->len, dec_sub);
}
else if (data_node->bufferdatatype == CCX_DVD_SUBTITLE)
{
if (dec_ctx->is_alloc == 0)
{
dec_ctx->private_data = init_dvdsub_decode();
dec_ctx->is_alloc = 1;
}
process_spu(dec_ctx, data_node->buffer, data_node->len, dec_sub);
got = data_node->len;
}
else if (data_node->bufferdatatype == CCX_TELETEXT)
{
// telxcc_update_gt(dec_ctx->private_data, ctx->demux_ctx->global_timestamp);
/* Check if teletext context is still valid (may have been freed by dinit_cap
during PAT change while stream was being processed) */
if (!dec_ctx->private_data)
{
got = data_node->len; // Skip processing, context was freed
}
else
{
/* Process Teletext packets even when no encoder context exists (e.g. -out=report).
This enables tlt_process_pes_packet() to detect subtitle pages by populating
the seen_sub_page[] array inside the teletext decoder. */
int sentence_cap = enc_ctx ? enc_ctx->sentence_cap : 0;
ret = tlt_process_pes_packet(
dec_ctx,
data_node->buffer,
data_node->len,
dec_sub,
sentence_cap);
/* If Teletext decoding fails with invalid data, abort processing */
if (ret == CCX_EINVAL)
return ret;
/* Mark processed byte count */
got = data_node->len;
}
}
else if (data_node->bufferdatatype == CCX_RAW) // Raw two byte 608 data from DVR-MS/ASF
{
// The asf_get_more_data() loop sets current_pts when possible
if (dec_ctx->timing->pts_set == 0)
{
mprint("DVR-MS/ASF file without useful time stamps - count blocks.\n");
// Otherwise rely on counting blocks
dec_ctx->timing->current_pts = 12345; // Pick a valid PTS time
dec_ctx->timing->pts_set = 1;
}
if (dec_ctx->timing->current_pts != last_pts)
{
// Only initialize the FTS values and reset the cb
// counters when the PTS is different. This happens frequently
// with ASF files.
if (dec_ctx->timing->min_pts == 0x01FFFFFFFFLL)
{
// First call
fts_at_gop_start = 0;
}
else
fts_at_gop_start = get_fts(dec_ctx->timing, dec_ctx->current_field);
frames_since_ref_time = 0;
set_fts(dec_ctx->timing);
last_pts = dec_ctx->timing->current_pts;
}
dbg_print(CCX_DMT_VIDES, "PTS: %s (%8u)",
print_mstime_static(dec_ctx->timing->current_pts / (MPEG_CLOCK_FREQ / 1000)),
(unsigned)(dec_ctx->timing->current_pts));
dbg_print(CCX_DMT_VIDES, " FTS: %s\n", print_mstime_static(get_fts(dec_ctx->timing, dec_ctx->current_field)));
got = process_raw(dec_ctx, dec_sub, data_node->buffer, data_node->len);
}
else if (data_node->bufferdatatype == CCX_H264) // H.264 data from TS file
{
dec_ctx->in_bufferdatatype = CCX_H264;
dec_ctx->avc_ctx->is_hevc = 0;
got = process_avc(enc_ctx, dec_ctx, data_node->buffer, data_node->len, dec_sub);
}
else if (data_node->bufferdatatype == CCX_HEVC) // HEVC data from TS file
{
dec_ctx->in_bufferdatatype = CCX_H264; // Use same internal type for NAL processing
dec_ctx->avc_ctx->is_hevc = 1;
got = process_avc(enc_ctx, dec_ctx, data_node->buffer, data_node->len, dec_sub);
}
else if (data_node->bufferdatatype == CCX_RAW_TYPE)
{
// CCX_RAW_TYPE contains cc_data triplets (cc_type + 2 data bytes each)
// Used by MXF and GXF demuxers
// Initialize timing if not set (use caption PTS as reference)
if (dec_ctx->timing->pts_set == 0 && data_node->pts != CCX_NOPTS)
{
dec_ctx->timing->min_pts = data_node->pts;
dec_ctx->timing->pts_set = 2; // MinPtsSet
dec_ctx->timing->sync_pts = data_node->pts;
set_fts(dec_ctx->timing);
}