/* TStoATSC by Jacob Balazer Licensed free for personal use. Distribution is prohibited. */ // #define DISTRIBUTE_EVENLY #include #include #include #include #include #include #include #include #define CORRECT_PCRS #define BUFFER_SIZE_MB 10 #define PCR_TICKS 358072 /* ratio of 27-MHz clock ticks per ATSC packet */ #define ATSC_PACKETS 171 int main(int argc, char **argv); void usage(char *argv); int getPacket(__int64 packet_no, unsigned char *buffer); void changePCR(unsigned char *buffer, __int64 pcr); void write_packet(unsigned char *buffer); __int64 pcr_add(__int64 pcr1, __int64 pcr2); __int64 pcr_diff(__int64 pcr2, __int64 pcr1); __int64 getPCRpacket_no_discontinutity(__int64 packet_no, __int64 *pcr); __int64 getPCRpacket(__int64 packet_no, __int64 *pcr, int *discontinuity); int get_pid(unsigned char *buffer); void changePID(unsigned char *buffer, int pid); findmax(int *array, int size); void write_null_packet(void); __int64 input_file_size, highest_packet_no, lowest_packet_no; int input_fd; int vpid, apid; FILE *output_file; char output_filename[1024], *outputbasename; int output_filenumber, packets_this_output_file, total_packets_written=0; int num_packets_written_since_last_discontinuity=0; int alignment_offset; unsigned char null[188], pat[188], pmt[188]; __int64 last_pat=0, last_pmt=0; int pat_continuity=0, pmt_continuity=0; int file_split_num_packets=0; int pcr_ticks = PCR_TICKS; int atsc_packets = ATSC_PACKETS; int main(int argc, char **argv) { char *inputfilename; unsigned char buffer[188], *find_sync_buffer; int r, y; struct __stat64 stat_struct; __int64 first_pcr_value; __int64 start_pcr_value, start_pcr_packet; __int64 end_pcr_value, end_pcr_packet; __int64 block_pcr_packet_diff; __int64 should_be_packet_no, last_should_be_packet_no=0; __int64 output_packet_diff, last_good_output_packet_diff; __int64 end_pcr_corrected_value, would_be_end_pcr_corrected_value, start_pcr_corrected_value; __int64 output_start_packet, relevant_packets_left_to_write, nulls_left_to_write; int wrap_arounds=0, x, start_discontinuous, end_discontinuous; int bytes_to_read, alignment[188], unmarked_discontinuities=0; float last_good_expansion_ratio = 1, expansion_ratio; __int64 overflow, max_overflow=0, total_overflow=0, instances_of_overflow=0; int correct_pcrs=0, quiet=0, n; /* read arguments */ n=1; if(argc<5) usage(argv[0]); while(argv[n][0] == '-') { if(argv[n][1] == 'c') { n++; correct_pcrs=1; continue; } if(argv[n][1] == 'q') { n++; quiet=1; continue; } if(argv[n][1] == 's') { float split_megabytes; n++; if(1!=sscanf(argv[n], "%f", &split_megabytes)) usage(argv[0]); n++; file_split_num_packets = (int) ( split_megabytes * 1024*1024 / 188 ); continue; } if(argv[n][1] == 'b') { float mbps; n++; if(1!=sscanf(argv[n], "%f", &mbps)) usage(argv[0]); n++; atsc_packets = 1000; pcr_ticks = (int) ( ((__int64)(188*8)) * 27000000 * 1000 / ((int)(mbps*1000000)) ); /* pcr_ticks per 1000 packets = bits per packet / bit rate * ticks per second * 1000 */ continue; } usage(argv[0]); /* invalid command line option specified */ } if(argc<(n+4)) usage(argv[0]); inputfilename = argv[n++]; outputbasename = argv[n++]; if(1!=sscanf(argv[n++], "%x", &vpid)) usage(argv[0]); if(1!=sscanf(argv[n++], "%x", &apid)) usage(argv[0]); /* open input file */ input_fd = _open(inputfilename, _O_RDONLY | _O_BINARY ); if( input_fd == -1 ) { perror( "Error opening input file.\n"); exit(1); } /* get input file size */ r = _fstat64(input_fd, &stat_struct); if(r<0) { printf("Error getting the input file size\n"); exit(1); } input_file_size = stat_struct.st_size; highest_packet_no = (input_file_size / 188) -1 -1; /* an extra -1, in case alignment has a partial packet at the beginning */ /* determine packet alignment */ for(x=0; x<188; x++) /* initialize alignment array counts to zero */ alignment[x]=0; bytes_to_read = BUFFER_SIZE_MB*1024*1024; if(bytes_to_read > input_file_size) bytes_to_read = (int) input_file_size; find_sync_buffer = malloc(bytes_to_read); if(!find_sync_buffer) { printf("Error allocating memory for find-sync buffer\n"); exit(1); } r = _read(input_fd, find_sync_buffer, bytes_to_read); if(r!=bytes_to_read) { printf("Error reading a buffer's worth of data from the input file in order to determine packet alignment\n"); exit(1); } for(x=0; x 5000)) // if((expansion_ratio > 200 ) || (expansion_ratio < 0.1)) { end_discontinuous=1; /* treat same as if end of interval is a discontinuity point. i.e., use known good ratio for this interval, and start over fresh with next interval, assuming that the PCR at the beginning of that interval is new and good */ printf("Unmarked discontinuity in output file no. %d, packet no. %d (PCR output packet diff %I64d)\n", output_filenumber, packets_this_output_file, should_be_packet_no - output_start_packet); unmarked_discontinuities++; } if(end_discontinuous) /* discontinuity on the end of this block */ // should_be_packet_no = (__int64) output_start_packet + (__int64) ((1+relevant_packets_left_to_write)*last_good_expansion_ratio); // should_be_packet_no = (__int64) output_start_packet + (__int64) (block_pcr_packet_diff*3/2); /* 3/2 seems reasonable, yes? reduces the chance of overflow from previous intervals */ should_be_packet_no = (__int64) output_start_packet + (__int64) max(1+relevant_packets_left_to_write, last_good_output_packet_diff); /* this strategy should work well when the PCRs are spaced evenly in time */ else last_good_expansion_ratio = expansion_ratio; /* this ratio is good; save it for next time */ output_packet_diff = should_be_packet_no - output_start_packet; if(!end_discontinuous) last_good_output_packet_diff = output_packet_diff; if(!quiet) printf("%5I64d (%4I64d) becomes %4I64d (%f)", block_pcr_packet_diff, relevant_packets_left_to_write+1, output_packet_diff, expansion_ratio); /* check for overflow */ if((should_be_packet_no-output_start_packet-1) < relevant_packets_left_to_write) { overflow = relevant_packets_left_to_write - (should_be_packet_no-output_start_packet-1); if(overflow > max_overflow) max_overflow = overflow; total_overflow += overflow; instances_of_overflow++; // printf("\nATSC rate exceeded: need to put %d packets in what should be only %d ATSC packets ", // (int)relevant_packets_left_to_write+1, ((int)(should_be_packet_no-output_start_packet))); printf(" overflow %4d ", overflow); if(quiet) printf("\n"); should_be_packet_no = output_start_packet + relevant_packets_left_to_write + 1; output_packet_diff = should_be_packet_no - output_start_packet; /* recalculate to reflect new should_be_packet_no */ } else if(!quiet) printf(" "); /* calculate new end-of-block PCR value */ if(!end_discontinuous) { would_be_end_pcr_corrected_value = (first_pcr_value + ((should_be_packet_no * pcr_ticks) / atsc_packets)) % ((((__int64)1)<<33)*300); if(correct_pcrs) end_pcr_corrected_value = would_be_end_pcr_corrected_value; else { end_pcr_corrected_value = end_pcr_value; /* leave PCR as-is */ if(!quiet) printf("would-be"); } if(would_be_end_pcr_corrected_value >= end_pcr_value) { if(!quiet) printf(" PCR correction: +%I64d\n", pcr_diff(would_be_end_pcr_corrected_value, end_pcr_value)); } else { if(!quiet) printf(" PCR correction: -%I64d\n", pcr_diff(end_pcr_value, would_be_end_pcr_corrected_value)); } } else if(!quiet) printf("\n"); /* no PCR correction if end of block is a discontinuity */ /* write the first packet of the block, with corrected PCR */ r = getPacket(start_pcr_packet, buffer); assert(r==1); if(!start_discontinuous) /* change PCR value only if not a discontinuity */ changePCR(buffer, start_pcr_corrected_value); changePID(buffer, 0x11); write_packet(buffer); num_packets_written_since_last_discontinuity++; /* write each of the remaining packets in the input block to the output block, inserting null packets as necessary */ nulls_left_to_write = output_packet_diff-1 - relevant_packets_left_to_write; assert(nulls_left_to_write >= 0); #ifdef DISTRIBUTE_EVENLY /* distribute packets between PCRs evenly throughout the interval */ y=1; for(x=0; x= nulls_left_to_write) { do { r = (int) getPacket(start_pcr_packet + y++, buffer); if(r==-1) { /* end of file. don't bother writing the packets of this last block. just close & quit */ fclose(output_file); return(0); } } while(r!=1 || !(get_pid(buffer)==vpid || get_pid(buffer)==apid)); if(get_pid(buffer)==vpid) changePID(buffer, 0x11); else if(get_pid(buffer)==apid) changePID(buffer, 0x14); write_packet(buffer); relevant_packets_left_to_write--; num_packets_written_since_last_discontinuity++; } else { write_null_packet(); num_packets_written_since_last_discontinuity++; nulls_left_to_write--; } } #else /* new way - put all relevant packets at the beginning of the interval */ y=1; /* write all the relevant packets */ while(relevant_packets_left_to_write) { r = (int) getPacket(start_pcr_packet + y, buffer); y++; if(r==-1) { /* end of file. don't bother writing the packets of this last block. just close & quit */ fclose(output_file); return(0); } if(r==1 && (get_pid(buffer)==vpid || get_pid(buffer)==apid)) { if(get_pid(buffer)==vpid) changePID(buffer, 0x11); else if(get_pid(buffer)==apid) changePID(buffer, 0x14); write_packet(buffer); num_packets_written_since_last_discontinuity++; relevant_packets_left_to_write--; } } /* write all the null packets */ while(nulls_left_to_write) { write_null_packet(); num_packets_written_since_last_discontinuity++; nulls_left_to_write--; } #endif assert(nulls_left_to_write==0); assert(relevant_packets_left_to_write==0); /* copy corrected end PCR value to start of next block */ output_start_packet = num_packets_written_since_last_discontinuity; if(!end_discontinuous) start_pcr_corrected_value = end_pcr_corrected_value; start_discontinuous = end_discontinuous; start_pcr_value = end_pcr_value; start_pcr_packet = end_pcr_packet; } printf("# of PCR intervals with overflow: %I64d\n", instances_of_overflow); printf("Max # of packets overflowed: %I64d\n", max_overflow); if(instances_of_overflow) printf("Average # of packets overflowed: %f\n", (double)(total_overflow/(double)instances_of_overflow)); printf("# of unmarked discontinuities/bad PCRs: %d\n", unmarked_discontinuities); return(0); } void usage(char *argv) { printf("Usage: %s [-c] [-s ] [-b ] [-q] \n", argv); printf(" specify PIDs in hex witout a leading 0x\n"); printf(" -c : correct PCRs\n"); printf(" -s : split output into files of megabytes\n"); printf(" -b : output bit rate of Mbps\n"); printf(" -q : quiet mode - print less information to the console\n"); exit(1); } /* read packet packet_no, store it into buffer. returns 1 on success, 0 on bad sync, -1 on EOF */ int getPacket(__int64 packet_no, unsigned char *buffer) { __int64 r, seek_pos; if(packet_no > highest_packet_no) return(-1); seek_pos = ((__int64) packet_no)*(__int64)188 + (__int64)alignment_offset; r = _lseeki64(input_fd, seek_pos, SEEK_SET); if(r==-1L) { printf("Error seeking to packet no. %I64d\n", packet_no); exit(1); } r = _read(input_fd, buffer, 188); if(r != 188) { printf("Error reading from file\n"); exit(1); } if(buffer[0] != 0x47) { /* bad sync */ printf("Bad sync at packet number %I64d in input.\n", packet_no); return(0); } return(1); } void changePCR(unsigned char *buffer, __int64 pcr) { __int64 pcr_base, pcr_ext; pcr_ext = pcr % 300; pcr_base = (pcr / 300) % (((__int64)1)<<33); buffer[6] = (unsigned char) (pcr_base>>25) & 0xFF; buffer[7] = (unsigned char) (pcr_base>>17) & 0xFF; buffer[8] = (unsigned char) (pcr_base>>9) & 0xFF; buffer[9] = (unsigned char) (pcr_base>>1) & 0xFF; buffer[10] = (unsigned char) ( ((pcr_base<<7) & 0x80) | ((pcr_ext>>8) & 0x01) ); buffer[11] = (unsigned char) pcr_ext & 0xFF; } /* find the next PCR packet at or above packet packet_no. return the packet number of that packet, or -1 on EOF. Optionally return the PCR and discontinuity indicator */ __int64 getPCRpacket(__int64 packet_no, __int64 *pcr, int *discontinuity) { unsigned char buffer[188]; unsigned char adaptation_control, num_of_adaptation_bytes; int pid, r, discontinuity_flag; int n=0; while(1) { /* exit if no PCR can be found within the next several dozen megabytes */ n++; if(n>400000) { printf("Couldn't find PCR packet. Bad stream or wrong video PID?\n"); exit(-1); } r=getPacket(packet_no, buffer); if(r==-1) /* EOF */ return(-1); if(r==0) /* bad sync */ { packet_no++; continue; } pid = get_pid(buffer); if(pid != vpid) { packet_no++; continue; } adaptation_control = (buffer[3]>>4) & 0x03; if((adaptation_control == 0x02) || (adaptation_control == 0x03)) { num_of_adaptation_bytes = buffer[4]; if(num_of_adaptation_bytes >= 7) /* this is the number of bytes that follow in the adaptation field */ /* PCR is in bytes 1-6 of the following adaptation field bytes */ { if(buffer[5] & 0x10) /* PCR is present in this packet*/ { discontinuity_flag = (buffer[5] & 0x80) >> 7; if(discontinuity) *discontinuity = discontinuity_flag; if(pcr) *pcr = ((__int64)300)*((((__int64)buffer[6])<<25) | (((__int64)buffer[7])<<17) | (((__int64)buffer[8])<<9) | (((__int64)buffer[9])<<1) | ((__int64)((buffer[10] & 0x80)>>7))) + (((__int64)(((buffer[10] & 0x01)<<8))) | ((__int64)buffer[11])); return(packet_no); } } } /* not PCR packet. try the next packet. */ packet_no++; } } /* get the first PCR packet without the discontinuity flag set, at or after packet_no */ __int64 getPCRpacket_no_discontinutity(__int64 packet_no, __int64 *pcr) { int discontinuity; __int64 output_packet_no; while(1) { output_packet_no = getPCRpacket(packet_no, pcr, &discontinuity); if(discontinuity) { packet_no++; continue; } break; } return(output_packet_no); } /* pcr2 - pcr1 (pcr2 must be later than pcr1) */ __int64 pcr_diff(__int64 pcr2, __int64 pcr1) { if(pcr2 < pcr1) pcr2 += (((__int64)1)<<33)*300; /* verified: += 2^33 * 300 */ return(pcr2 - pcr1); } __int64 pcr_add(__int64 pcr1, __int64 pcr2) { if((pcr1 + pcr2) > (((__int64)1)<<33)*300) return(pcr1 + pcr2 - (((__int64)1)<<33)*300); else return(pcr1 + pcr2); } void write_packet(unsigned char *buffer) { int r; /* split output into files of size max file_split_num_packets */ if(file_split_num_packets && (packets_this_output_file == file_split_num_packets)) { fclose(output_file); output_filenumber++; sprintf(output_filename, "%s.%04d.ts", outputbasename, output_filenumber); output_file = fopen(output_filename, "wb"); if(!output_file) { printf("Error opening output file %s%\n", output_filename); exit(1); } packets_this_output_file = 0; } r = (int) fwrite(buffer, 1, 188, output_file); if(r != 188) { printf("Error writing to file %s\n", output_filename); exit(1); } packets_this_output_file++; total_packets_written++; } int get_pid(unsigned char *buffer) { int pid; pid = ((buffer[1] & 0x1F)<<8) + buffer[2]; return(pid); } void changePID(unsigned char *buffer, int pid) { buffer[1] = (buffer[1] & 0xE0) | ((pid>>8) & 0x1F); buffer[2] = pid & 0xFF; } findmax(int *array, int size) { int max, maxval, x; max=0; maxval=0; for(x=0; xmaxval) { max=x; maxval=array[x]; } } return(max); } void write_null_packet(void) { if((last_pat==0) || ((total_packets_written-last_pat) > 1280)) /* 1289 packets ~= 100 ms */ { /* time to write a pat */ pat[3] = (pat[3] & 0xF0) | (pat_continuity & 0x0F); write_packet(pat); last_pat = total_packets_written; pat_continuity = (pat_continuity+1)%16; } else if((last_pmt==0) || ((total_packets_written-last_pmt) > 1280)) { /* time to write a pmt */ pmt[3] = (pmt[3] & 0xF0) | (pmt_continuity & 0x0F); write_packet(pmt); last_pmt = total_packets_written; pmt_continuity = (pmt_continuity+1)%16; } else write_packet(null); } xval) { max=x; maxval=array[x]; } } return(max); } void write_null_packet(void) { if((last_pat==0) || ((total_packets_written-last_pat) > 1280)) /* 1289 packets ~= 100 ms */ { /* time to write a pat */ pat[3] = (pat[3] & 0xF0) | (pat_continuity & 0x0F); write_packet(pat); last_pat = total_packets_written; pat_continuity = (pat_continuity+1)%16; } else if((last_pmt==0) || ((total_packets_written-last_pmt) > 1280)) { /* time to write a pmt */ pmt[3] = (pmt[3] & 0xF0) | (pmt_continuity & 0x0F); write_packet(pmt); last_pmt = total_packets_written; pmt_continuity = (pmt_continuity+1)%16; } else write_packet(null); }