/* Helper function for to display debug timing info. */ void print_debug_timing( void ) { // Avoid wrong "Calc. difference" and "Asynchronous by" numbers // for uninitialized min_pts LLONG tempmin_pts = (min_pts==0x01FFFFFFFFLL ? sync_pts : min_pts); mprint("Sync time stamps: PTS: %s ", print_mstime((sync_pts)/(MPEG_CLOCK_FREQ/1000)) ); mprint("GOP: %s \n", print_mstime(gop_time.ms)); // Length first GOP to last GOP LLONG goplenms = LLONG(gop_time.ms - first_gop_time.ms); // Length at last sync point LLONG ptslenms = unsigned((sync_pts-tempmin_pts)/(MPEG_CLOCK_FREQ/1000) + fts_offset); mprint("Last FTS: %s", print_mstime(get_fts_max())); mprint(" GOP start FTS: %s\n", print_mstime(fts_at_gop_start)); // Times are based on last GOP and/or sync time mprint("Max FTS diff. to PTS: %6lldms GOP: %6lldms\n\n", get_fts_max()+LLONG(1000.0/current_fps)-ptslenms, get_fts_max()+LLONG(1000.0/current_fps)-goplenms); }
void calculate_ms_gop_time (struct gop_time_code *g) { int seconds=(g->time_code_hours*3600)+(g->time_code_minutes*60)+g->time_code_seconds; g->ms = LLONG( 1000*(seconds + g->time_code_pictures/current_fps) ); if (gop_rollover) g->ms += 24*60*60*1000; }
// Buffer caption blocks for later sorting/flushing. void store_hdcc(unsigned char *cc_data, int cc_count, int sequence_number, LLONG current_fts_now) { // Uninitialized? if (anchor_seq_number < 0) { anchor_hdcc( sequence_number); } int seq_index = sequence_number - anchor_seq_number + MAXBFRAMES; if (seq_index < 0 || seq_index > 2*MAXBFRAMES) { // Maybe missing an anchor frame - try to recover dbg_print(DMT_VERBOSE, "Too many B-frames, or missing anchor frame. Trying to recover ..\n"); process_hdcc(); anchor_hdcc( sequence_number); seq_index = sequence_number - anchor_seq_number + MAXBFRAMES; } has_ccdata_buffered = 1; // In GOP mode the fts is set only once for the whole GOP. Recreate // the right time according to the sequence number. if (use_gop_as_pts==1) { current_fts_now += LLONG(sequence_number*1000.0/current_fps); } if (cc_count) { if (cc_data) { // Changed by CFS to concat, i.e. don't assume there's no data already for this seq_index. // Needed at least for MP4 samples. // TODO: make sure we don't overflow cc_fts[seq_index] = current_fts_now; // CFS: Maybe do even if there's no data? if (stream_mode!=SM_MP4) // CFS: Very ugly hack, but looks like overwriting is needed for at least some ES cc_data_count[seq_index] = 0; memcpy(cc_data_pkts[seq_index]+cc_data_count[seq_index]*3, cc_data, cc_count*3+1); } cc_data_count[seq_index] += cc_count; } // DEBUG STUFF /* printf("\nCC blocks, channel 0:\n"); for ( int i=0; i < cc_count*3; i+=3) { printf("%s", debug_608toASC( cc_data+i, 0) ); } printf("\n"); */ }
// Buffer caption blocks for later sorting/flushing. void store_hdcc(unsigned char *cc_data, int cc_count, int sequence_number, LLONG current_fts_now) { // Uninitialized? if (anchor_seq_number < 0) { anchor_hdcc( sequence_number); } int seq_index = sequence_number - anchor_seq_number + MAXBFRAMES; if (seq_index < 0 || seq_index > 2*MAXBFRAMES) { // Maybe missing an anchor frame - try to recover if(debug_verbose) printf("Too many B-frames, or missing anchor frame. Trying to recover ..\n"); process_hdcc(); anchor_hdcc( sequence_number); seq_index = sequence_number - anchor_seq_number + MAXBFRAMES; } has_ccdata_buffered = 1; cc_data_count[seq_index] = cc_count; // In GOP mode the fts is set only once for the whole GOP. Recreate // the right time according to the sequence number. if (use_gop_as_pts) { current_fts_now += LLONG(sequence_number*1000.0/current_fps); } cc_fts[seq_index] = current_fts_now; memcpy(cc_data_pkts[seq_index], cc_data, cc_count*3+1); // DEBUG STUFF /* printf("\nCC blocks, channel 0:\n"); for ( int i=0; i < cc_count*3; i+=3) { printf("%s", debug_608toASC( cc_data+i, 0) ); } printf("\n"); */ }
void set_fts(void) { int pts_jump = 0; // ES don't have PTS unless GOP timing is used if (!pts_set && stream_mode==SM_ELEMENTARY_OR_NOT_FOUND) return; // First check for timeline jump (only when min_pts was // set (implies sync_pts). int dif = 0; if (pts_set == 2) { dif=(int) (current_pts-sync_pts)/MPEG_CLOCK_FREQ; // Used to distinguish gaps with missing caption information from // jumps in the timeline. (Currently only used for dvr-ms/NTSC // recordings) if ( CaptionGap ) dif = 0; // Disable sync check for raw formats - they have the right timeline. // Also true for bin formats, but -nosync might have created a // broken timeline for debug purposes. // Disable too in MP4, specs doesn't say that there can't be a jump switch (stream_mode) { case SM_MCPOODLESRAW: case SM_RCWT: case SM_MP4: dif = 0; break; default: break; } if (dif < -0.2 || dif >=5 ) { // ATSC specs: More than 3501 ms means missing component mprint ("\nWarning: Reference clock has changed abruptly (%d seconds), attempting to synchronize\n", (int) dif); mprint ("Last sync PTS value: %lld\n",sync_pts); mprint ("Current PTS value: %lld\n",current_pts); pts_jump = 1; pts_big_change=1; // Discard the gap if it is not on an I-frame or temporal reference // zero. if(current_tref != 0 && current_picture_coding_type != I_FRAME) { fts_now = fts_max; mprint ("Change did not occur on first frame - probably a broken GOP\n"); return; } } } // Set min_pts, fts_offset if (pts_set!=0) { pts_set=2; // Use this part only the first time min_pts is set. Later treat // it as a reference clock change if (current_pts<min_pts && !pts_jump) { // If this is the first GOP, and seq 0 was not encountered yet // we might reset min_pts/fts_offset again min_pts=current_pts; // Avoid next async test sync_pts = LLONG(current_pts -current_tref*1000.0/current_fps *(MPEG_CLOCK_FREQ/1000)); if(current_tref == 0) { // Earliest time in GOP. fts_offset = 0; } else if ( total_frames_count-frames_since_ref_time == 0 ) { // If this is the first frame (PES) there cannot be an offset. // This part is also reached for dvr-ms/NTSC (RAW) as // total_frames_count = frames_since_ref_time = 0 when // this is called for the first time. fts_offset = 0; } else { // It needs to be "+1" because the current frame is // not yet counted. fts_offset = LLONG((total_frames_count -frames_since_ref_time+1) *1000.0/current_fps); } dbg_print(DMT_TIME, "\nFirst sync time PTS: %s %+lldms (time before this PTS)\n", print_mstime(min_pts/(MPEG_CLOCK_FREQ/1000)), fts_offset ); dbg_print(DMT_TIME, "Total_frames_count %u frames_since_ref_time %u\n", total_frames_count, frames_since_ref_time); } // -nosync diasbles syncing if (pts_jump && !nosync) { // The current time in the old time base is calculated using // sync_pts (set at the beginning of the last GOP) plus the // time of the frames since then. fts_offset = fts_offset + (sync_pts-min_pts)/(MPEG_CLOCK_FREQ/1000) + LLONG(frames_since_ref_time*1000/current_fps); fts_max = fts_offset; // Start counting again from here pts_set=1; // Force min to be set again // Avoid next async test - the gap might have occured on // current_tref != 0. sync_pts = LLONG(current_pts -current_tref*1000.0/current_fps *(MPEG_CLOCK_FREQ/1000)); // Set min_pts = sync_pts as this is used for fts_now min_pts = sync_pts; dbg_print(DMT_TIME, "\nNew min PTS time: %s %+lldms (time before this PTS)\n", print_mstime(min_pts/(MPEG_CLOCK_FREQ/1000)), fts_offset ); } } // Set sync_pts, fts_offset if(current_tref == 0) sync_pts = current_pts; // Reset counters cb_field1 = 0; cb_field2 = 0; cb_708 = 0; // Avoid wrong "Calc. difference" and "Asynchronous by" numbers // for uninitialized min_pts if (1) // CFS: Remove or think decent condition { if ( pts_set ) { // If pts_set is TRUE we have min_pts fts_now = LLONG((current_pts-min_pts)/(MPEG_CLOCK_FREQ/1000) + fts_offset); } else { // No PTS info at all!! fatal(EXIT_BUG_BUG, "No PTS info. Please write bug report."); } } if ( fts_now > fts_max ) { fts_max = fts_now; } }
// Return TRUE if the data parsing finished, FALSE otherwise. // estream->pos is advanced. Data is only processed if esstream->error // is FALSE, parsing can set esstream->error to TRUE. static int gop_header(struct bitstream *esstream) { dbg_print(DMT_VERBOSE, "GOP header\n"); if (esstream->error || esstream->bitsleft <= 0) return 0; // We only get here after seeing that start code if (read_u32(esstream) != 0xB8010000) // LSB first (0x000001B8) fatal(EXIT_BUG_BUG, "Impossible!"); unsigned drop_frame_flag = (unsigned) read_bits(esstream,1); struct gop_time_code gtc; gtc.time_code_hours = (int) read_bits(esstream,5); gtc.time_code_minutes = (int) read_bits(esstream,6); skip_bits(esstream,1); // Marker bit gtc.time_code_seconds = (int) read_bits(esstream,6); gtc.time_code_pictures = (int) read_bits(esstream,6); gtc.inited = 1; calculate_ms_gop_time(>c); if (esstream->bitsleft < 0) return 0; if (gop_accepted(>c)) { // Do GOP padding during GOP header. The previous GOP and all // included captions are written. Use the current GOP time to // do the padding. // Flush buffered cc blocks before doing the housekeeping if (has_ccdata_buffered) { process_hdcc(); } // Last GOPs pulldown frames if ((current_pulldownfields>0) != (pulldownfields>0)) { current_pulldownfields = pulldownfields; dbg_print(DMT_VERBOSE, "Pulldown: %s", (pulldownfields ? "on" : "off")); if (pulldownfields) dbg_print(DMT_VERBOSE, " - %u fields in last GOP", pulldownfields); dbg_print(DMT_VERBOSE, "\n"); } pulldownfields = 0; // Report synchronization jumps between GOPs. Warn if there // are 20% or more deviation. if ( (debug_mask & DMT_TIME) && ((gtc.ms - gop_time.ms // more than 20% longer > frames_since_last_gop*1000.0/current_fps*1.2) || (gtc.ms - gop_time.ms // or 20% shorter < frames_since_last_gop*1000.0/current_fps*0.8)) && first_gop_time.inited ) { mprint("\rWarning: Jump in GOP timing.\n"); mprint(" (old) %s", print_mstime(gop_time.ms)); mprint(" + %s (%uF)", print_mstime(LLONG(frames_since_last_gop *1000.0/current_fps)), frames_since_last_gop); mprint(" != (new) %s\n", print_mstime(gtc.ms)); } if (first_gop_time.inited == 0) { first_gop_time = gtc; // It needs to be "+1" because the frame count starts at 0 and we // need the length of all frames. if ( total_frames_count == 0 ) { // If this is the first frame there cannot be an offset fts_fc_offset = 0; // first_gop_time.ms stays unchanged } else { fts_fc_offset = LLONG((total_frames_count+1) *1000.0/current_fps); // Compensate for those written before first_gop_time.ms -= fts_fc_offset; } dbg_print(DMT_TIME, "\nFirst GOP time: %02u:%02u:%02u:%03u %+lldms\n", gtc.time_code_hours, gtc.time_code_minutes, gtc.time_code_seconds, unsigned(1000.0*gtc.time_code_pictures/current_fps), fts_fc_offset); } gop_time = gtc; frames_since_last_gop=0; // Indicate that we read a gop header (since last frame number 0) saw_gop_header = 1; // If we use GOP timing, reconstruct the PTS from the GOP if (use_gop_as_pts) { current_pts = gtc.ms*(MPEG_CLOCK_FREQ/1000); if (pts_set==0) pts_set=1; current_tref = 0; frames_since_ref_time = 0; set_fts(); fts_at_gop_start = get_fts_max(); } else { // FIXME: Wrong when PTS are not increasing but are identical // troughout the GOP and then jump to the next time for the // next GOP. // This effect will also lead to captions being one GOP early // for DVD captions. fts_at_gop_start = get_fts_max() + LLONG(1000.0/current_fps); } if (debug_mask & DMT_TIME) { dbg_print(DMT_TIME, "\nNew GOP:\n"); dbg_print(DMT_TIME, "\nDrop frame flag: %u:\n", drop_frame_flag); print_debug_timing(); } } return 1; }