diff --git a/src/lib_ccx/ccx_demuxer.c b/src/lib_ccx/ccx_demuxer.c index 63aed2f4..d176ce16 100644 --- a/src/lib_ccx/ccx_demuxer.c +++ b/src/lib_ccx/ccx_demuxer.c @@ -11,7 +11,7 @@ static void ccx_demuxer_reset(struct ccx_demuxer *ctx) memset (ctx->PIDs_seen, 0, 65536*sizeof (int)); memset(ctx->min_pts, UINT64_MAX, 65536 * sizeof(uint64_t)); memset(ctx->found_stream_ids, 0, MAX_NUM_OF_STREAMIDS * sizeof(uint8_t)); - memset(ctx->got_first_pts, UINT64_MAX, 3 * sizeof(uint64_t)); + memset(ctx->got_important_streams_min_pts, UINT64_MAX, 3 * sizeof(uint64_t)); memset (ctx->PIDs_programs, 0, 65536*sizeof (struct PMT_entry *)); } diff --git a/src/lib_ccx/ccx_demuxer.h b/src/lib_ccx/ccx_demuxer.h index e7dfcba4..a6ede59a 100644 --- a/src/lib_ccx/ccx_demuxer.h +++ b/src/lib_ccx/ccx_demuxer.h @@ -69,7 +69,13 @@ struct cap_info struct list_head pg_stream; }; - +enum STREAM_TYPE +{ + PRIVATE_STREAM_1 = 0, + AUDIO, + VIDEO, + COUNT +}; struct ccx_demuxer { int m2ts; @@ -111,7 +117,7 @@ struct ccx_demuxer struct PSI_buffer *PID_buffers[MAX_PSI_PID]; int PIDs_seen[MAX_PID]; uint64_t min_pts[MAX_PID]; - uint64_t got_first_pts[3]; //0 is pvs1 (private stream 1), 1 is audio and 2 is video + uint64_t got_important_streams_min_pts[COUNT]; //0 is pvs1 (private stream 1), 1 is audio and 2 is video /*51 possible stream ids in total, 0xbd is private stream, 0xbe is padding stream, 0xbf private stream 2, 0xc0 - 0xdf audio, 0xe0 - 0xef video diff --git a/src/lib_ccx/general_loop.c b/src/lib_ccx/general_loop.c index 0b70fcac..74594689 100644 --- a/src/lib_ccx/general_loop.c +++ b/src/lib_ccx/general_loop.c @@ -858,21 +858,6 @@ int general_loop(struct lib_ccx_ctx *ctx) if(!datalist) break; } - //this part is for getting the min_pts of every possible stream ID (optional) - /*if (!got_pts && (ctx->demux_ctx->got_first_pts[0] || ctx->demux_ctx->got_first_pts[1] || ctx->demux_ctx->got_first_pts[2])) //it means we got the first pts for video, audio and sub :) - { - for (int i = 0; i < 65536; i++) // we parse the array with the min_pts for each stream - { - if (ctx->demux_ctx->min_pts[i] != UINT64_MAX) //PTS is 33 bit, array is 64 so we set the default value tu UINT64_MAX instead of 0 because PTS can also be 0 - { - //printf("Got pts: %" PRId64 " for PID %d\n", ctx->demux_ctx->min_pts[i], i); - if (ctx->demux_ctx->min_pts[i] < min_pts) - min_pts = ctx->demux_ctx->min_pts[i]; - } - } - ctx->demux_ctx->pinfo->min_pts = min_pts; - got_pts = 1; - }*/ if (!datalist) continue; position_sanity_check(ctx->demux_ctx); @@ -898,18 +883,18 @@ int general_loop(struct lib_ccx_ctx *ctx) if (!set_pts && dec_ctx->codec == CCX_CODEC_TELETEXT) //even if there's no sub data, we still need to set the min_pts { - if (!got_pts && (ctx->demux_ctx->got_first_pts[0] != UINT64_MAX || ctx->demux_ctx->got_first_pts[1] != UINT64_MAX || ctx->demux_ctx->got_first_pts[2] != UINT64_MAX)) //it means we got the first pts for either sub, audio or video :) + if (!got_pts && (ctx->demux_ctx->got_important_streams_min_pts[PRIVATE_STREAM_1] != UINT64_MAX || ctx->demux_ctx->got_important_streams_min_pts[AUDIO] != UINT64_MAX || ctx->demux_ctx->got_important_streams_min_pts[VIDEO] != UINT64_MAX)) //it means we got the first pts for either sub, audio or video :) { /*we don't need to parse the entire min_pts array since we are only interested in sub, audio and video stream pts - and we have got_first_pts array for that*/ - for (int i = 0; i < 3; i++) + and we have got_important_streams_min_pts array for that*/ + for (int i = 0; i < COUNT; i++) { - if (ctx->demux_ctx->got_first_pts[i] != UINT64_MAX) //PTS is 33 bit, array is 64 so we set the default value to UINT64_MAX instead of 0 because a PTS can also be 0 + if (ctx->demux_ctx->got_important_streams_min_pts[i] != UINT64_MAX) //PTS is 33 bit, array is 64 so we set the default value to UINT64_MAX instead of 0 because a PTS can also be 0 { //printf("Got pts: %" PRId64 " for PID %d\n", ctx->demux_ctx->min_pts[i], i); - if (ctx->demux_ctx->got_first_pts[i] < min_pts) - min_pts = ctx->demux_ctx->got_first_pts[i]; + if (ctx->demux_ctx->got_important_streams_min_pts[i] < min_pts) + min_pts = ctx->demux_ctx->got_important_streams_min_pts[i]; } } ctx->demux_ctx->pinfo->min_pts = min_pts; //we set the program's min_pts (not exactly perfect since we would need to parse the entire min_pts array to get the real min_pts for the program, but for now it's a good approximation) @@ -919,9 +904,9 @@ int general_loop(struct lib_ccx_ctx *ctx) } if (!set_pts && dec_ctx->codec == CCX_CODEC_DVB) //DVB will always have to be in sync with video (no matter the min_pts of the other streams) { - if (!got_pts && ctx->demux_ctx->got_first_pts[2] != UINT64_MAX) //it means we got the first pts for video + if (!got_pts && ctx->demux_ctx->got_important_streams_min_pts[AUDIO] != UINT64_MAX) //it means we got the first pts for video { - min_pts = ctx->demux_ctx->got_first_pts[2]; + min_pts = ctx->demux_ctx->got_important_streams_min_pts[AUDIO]; set_current_pts(dec_ctx->timing, min_pts); set_fts(dec_ctx->timing); got_pts = 1; @@ -993,18 +978,18 @@ int general_loop(struct lib_ccx_ctx *ctx) if (!set_pts && dec_ctx->codec == CCX_CODEC_TELETEXT) //even if there's no sub data, we still need to set the min_pts { - if (!got_pts && (ctx->demux_ctx->got_first_pts[0] != UINT64_MAX || ctx->demux_ctx->got_first_pts[1] != UINT64_MAX || ctx->demux_ctx->got_first_pts[2] != UINT64_MAX)) //it means we got the first pts for either sub, audio or video :) + if (!got_pts && (ctx->demux_ctx->got_important_streams_min_pts[PRIVATE_STREAM_1] != UINT64_MAX || ctx->demux_ctx->got_important_streams_min_pts[AUDIO] != UINT64_MAX || ctx->demux_ctx->got_important_streams_min_pts[VIDEO] != UINT64_MAX)) //it means we got the first pts for either sub, audio or video :) { /*we don't need to parse the entire min_pts array since we are only interested in sub, audio and video stream pts - and we have got_first_pts array for that*/ - for (int i = 0; i < 3; i++) + and we have got_important_streams_min_pts array for that*/ + for (int i = 0; i < COUNT; i++) { - if (ctx->demux_ctx->got_first_pts[i] != UINT64_MAX) //PTS is 33 bit, array is 64 so we set the default value to UINT64_MAX instead of 0 because a PTS can also be 0 + if (ctx->demux_ctx->got_important_streams_min_pts[i] != UINT64_MAX) //PTS is 33 bit, array is 64 so we set the default value to UINT64_MAX instead of 0 because a PTS can also be 0 { //printf("Got pts: %" PRId64 " for PID %d\n", ctx->demux_ctx->min_pts[i], i); - if (ctx->demux_ctx->got_first_pts[i] < min_pts) - min_pts = ctx->demux_ctx->got_first_pts[i]; + if (ctx->demux_ctx->got_important_streams_min_pts[i] < min_pts) + min_pts = ctx->demux_ctx->got_important_streams_min_pts[i]; } } ctx->demux_ctx->pinfo->min_pts = min_pts; //we set the program's min_pts (not exactly perfect since we would need to parse the entire min_pts array to get the real min_pts for the program, but for now it's a good approximation) @@ -1014,9 +999,9 @@ int general_loop(struct lib_ccx_ctx *ctx) } if (!set_pts && dec_ctx->codec == CCX_CODEC_DVB) //DVB will always have to be in sync with video (no matter the min_pts of the other streams) { - if (!got_pts && ctx->demux_ctx->got_first_pts[2] != UINT64_MAX) //it means we got the first pts for video + if (!got_pts && ctx->demux_ctx->got_important_streams_min_pts[AUDIO] != UINT64_MAX) //it means we got the first pts for video { - min_pts = ctx->demux_ctx->got_first_pts[2]; + min_pts = ctx->demux_ctx->got_important_streams_min_pts[AUDIO]; set_current_pts(dec_ctx->timing, min_pts); set_fts(dec_ctx->timing); got_pts = 1; diff --git a/src/lib_ccx/ts_functions.c b/src/lib_ccx/ts_functions.c index dce6a74a..e36965d7 100644 --- a/src/lib_ccx/ts_functions.c +++ b/src/lib_ccx/ts_functions.c @@ -7,6 +7,8 @@ #include "ccx_decoders_isdb.h" #include "file_buffer.h" +#define RAI_MASK 0x40 //byte mask to check if RAI bit is set (random access indicator) + unsigned char tspacket[188]; // Current packet //struct ts_payload payload; @@ -275,7 +277,7 @@ int ts_readpacket(struct ccx_demuxer* ctx, struct ts_payload *payload) payload->start = tspacket + 4; payload->length = 188 - 4; - if ( adaptation_field_control & 2 ) + if (adaptation_field_control & 2) { // Take the PCR (Program Clock Reference) from here, in case PTS is not available (copied from telxcc). adaptation_field_length = tspacket[4]; @@ -294,9 +296,11 @@ int ts_readpacket(struct ccx_demuxer* ctx, struct ts_payload *payload) // payload->pcr |= tspacket[11]; } + payload->has_random_access_indicator = (tspacket[5] & RAI_MASK) != 0; + // Catch bad packages with adaptation_field_length > 184 and // the unsigned nature of payload_length leading to huge numbers. - if(adaptation_field_length < payload->length) + if (adaptation_field_length < payload->length) { payload->start += adaptation_field_length + 1; payload->length -= adaptation_field_length + 1; @@ -308,6 +312,8 @@ int ts_readpacket(struct ccx_demuxer* ctx, struct ts_payload *payload) dbg_print(CCX_DMT_PARSE, " Reject package - set length to zero.\n"); } } + else + payload->has_random_access_indicator = 0; dbg_print(CCX_DMT_PARSE, "TS pid: %d PES start: %d counter: %u payload length: %u adapt length: %d\n", payload->pid, payload->start, payload->counter, payload->length, @@ -607,6 +613,110 @@ int copy_payload_to_capbuf(struct cap_info *cinfo, struct ts_payload *payload) // Read ts packets until a complete video PES element can be returned. // The data is read into capbuf and the function returns the number of // bytes read. + +uint64_t get_pts(uint8_t* buffer) +{ + uint64_t pes_prefix; + uint8_t pes_stream_id; + uint16_t pes_packet_length; + uint8_t optional_pes_header_included = NO; + uint16_t optional_pes_header_length = 0; + uint64_t pts = 0; + + // Packetized Elementary Stream (PES) 32-bit start code + pes_prefix = (buffer[0] << 16) | (buffer[1] << 8) | buffer[2]; + pes_stream_id = buffer[3]; + + // check for PES header + if (pes_prefix == 0x000001) + { + pes_packet_length = 6 + ((buffer[4] << 8) | buffer[5]); // 5th and 6th byte of the header define the length of the rest of the packet (+6 is for the prefix, stream ID and packet length) + + printf("Packet start code prefix: %04x # ", pes_prefix); + printf("Stream ID: %04x # ", pes_stream_id); + printf("Packet length: %d ", pes_packet_length); + + // optional PES header marker bits (10.. ....) + if ((buffer[6] & 0xc0) == 0x80) + { + optional_pes_header_included = YES; + optional_pes_header_length = buffer[8]; + } + + if (optional_pes_header_included == YES && optional_pes_header_length > 0 && (buffer[7] & 0x80) > 0) + { + //get associated PTS as it exists + pts = (buffer[9] & 0x0e); + pts <<= 29; + pts |= (buffer[10] << 22); + pts |= ((buffer[11] & 0xfe) << 14); + pts |= (buffer[12] << 7); + pts |= ((buffer[13] & 0xfe) >> 1); + return pts; + } + } + return UINT64_MAX; +} +uint64_t get_video_min_pts(struct ccx_demuxer *context) +{ + struct ccx_demuxer *ctx = malloc(sizeof(struct ccx_demuxer)); + memcpy(ctx, context, sizeof(struct ccx_demuxer)); + + int ret = CCX_EAGAIN; + + struct ts_payload payload; + + int got_pts = 0; + + uint64_t min_pts = UINT64_MAX; + uint64_t *pts_array = NULL; + int num_of_remembered_pts = 0; + int pcount = 0; + uint64_t pts = UINT64_MAX; + do + { + pcount++; + // Exit the loop at EOF + ret = ts_readpacket(ctx, &payload); + if (ret != CCX_OK) + break; + + if (payload.pesstart) + { + // Packetized Elementary Stream (PES) 32-bit start code + uint64_t pes_prefix = (payload.start[0] << 16) | (payload.start[1] << 8) | payload.start[2]; + uint8_t pes_stream_id = payload.start[3]; + + if (pes_prefix == 0x000001) + if(pes_stream_id >= 0xe0 && pes_stream_id <= 0xef) + pts = get_pts(payload.start); + } + + if (pts != UINT64_MAX) + { + num_of_remembered_pts++; + pts_array = realloc(pts_array, num_of_remembered_pts * sizeof(uint64_t)); + ((uint64_t*)pts_array)[num_of_remembered_pts - 1] = pts; + } + if (num_of_remembered_pts >= 1 && payload.has_random_access_indicator) + got_pts = 1; + + } while (!got_pts); + + //search for smallest pts + uint64_t* p = pts_array; + for (int i = 0; i < num_of_remembered_pts; i++) + { + if (*p < min_pts) + min_pts = *p; + p++; + } + + freep(ctx); + freep(&pts_array); + + return min_pts; +} long ts_readstream(struct ccx_demuxer *ctx, struct demuxer_data **data) { int gotpes = 0; @@ -621,6 +731,9 @@ long ts_readstream(struct ccx_demuxer *ctx, struct demuxer_data **data) memset(&payload, 0, sizeof(payload)); + if (ctx->got_important_streams_min_pts[VIDEO] == UINT64_MAX) + ctx->got_important_streams_min_pts[VIDEO] = get_video_min_pts(ctx); + do { pcount++; @@ -630,288 +743,256 @@ long ts_readstream(struct ccx_demuxer *ctx, struct demuxer_data **data) if ( ret != CCX_OK) break; - //// Skip damaged packets, they could do more harm than good - //if (payload.transport_error) - //{ - // dbg_print(CCX_DMT_VERBOSE, "Packet (pid %u) skipped - transport error.\n", - // payload.pid); - // continue; - //} + // Skip damaged packets, they could do more harm than good + if (payload.transport_error) + { + dbg_print(CCX_DMT_VERBOSE, "Packet (pid %u) skipped - transport error.\n", + payload.pid); + continue; + } - //// Check for PAT - //if( payload.pid == 0) // This is a PAT - //{ - // ts_buffer_psi_packet(ctx); - // if(ctx->PID_buffers[payload.pid]!=NULL && ctx->PID_buffers[payload.pid]->buffer_length>0) - // parse_PAT(ctx); // Returns 1 if there was some data in the buffer already - // continue; - //} + // Check for PAT + if( payload.pid == 0) // This is a PAT + { + ts_buffer_psi_packet(ctx); + if(ctx->PID_buffers[payload.pid]!=NULL && ctx->PID_buffers[payload.pid]->buffer_length>0) + parse_PAT(ctx); // Returns 1 if there was some data in the buffer already + continue; + } - //if( ccx_options.xmltv >= 1 && payload.pid == 0x11) {// This is SDT (or BAT) - // ts_buffer_psi_packet(ctx); - // if(ctx->PID_buffers[payload.pid]!=NULL && ctx->PID_buffers[payload.pid]->buffer_length>0) - // parse_SDT(ctx); - //} + if( ccx_options.xmltv >= 1 && payload.pid == 0x11) {// This is SDT (or BAT) + ts_buffer_psi_packet(ctx); + if(ctx->PID_buffers[payload.pid]!=NULL && ctx->PID_buffers[payload.pid]->buffer_length>0) + parse_SDT(ctx); + } - //if( ccx_options.xmltv >= 1 && payload.pid == 0x12) // This is DVB EIT - // parse_EPG_packet(ctx->parent); - //if( ccx_options.xmltv >= 1 && payload.pid >= 0x1000) // This may be ATSC EPG packet - // parse_EPG_packet(ctx->parent); + if( ccx_options.xmltv >= 1 && payload.pid == 0x12) // This is DVB EIT + parse_EPG_packet(ctx->parent); + if( ccx_options.xmltv >= 1 && payload.pid >= 0x1000) // This may be ATSC EPG packet + parse_EPG_packet(ctx->parent); - //for (j = 0; j < ctx->nb_program; j++) - //{ - // if (ctx->pinfo[j].analysed_PMT_once == CCX_TRUE && - // ctx->pinfo[j].pcr_pid == payload.pid && - // payload.have_pcr) - // { - // ctx->last_global_timestamp = ctx->global_timestamp; - // ctx->global_timestamp = (uint32_t) payload.pcr / 90; - // if (!ctx->global_timestamp_inited) - // { - // ctx->min_global_timestamp = ctx->global_timestamp; - // ctx->global_timestamp_inited = 1; - // } - // if (ctx->min_global_timestamp > ctx->global_timestamp) - // { - // ctx->offset_global_timestamp = ctx->last_global_timestamp - ctx->min_global_timestamp; - // ctx->min_global_timestamp = ctx->global_timestamp; - // } + for (j = 0; j < ctx->nb_program; j++) + { + if (ctx->pinfo[j].analysed_PMT_once == CCX_TRUE && + ctx->pinfo[j].pcr_pid == payload.pid && + payload.have_pcr) + { + ctx->last_global_timestamp = ctx->global_timestamp; + ctx->global_timestamp = (uint32_t) payload.pcr / 90; + if (!ctx->global_timestamp_inited) + { + ctx->min_global_timestamp = ctx->global_timestamp; + ctx->global_timestamp_inited = 1; + } + if (ctx->min_global_timestamp > ctx->global_timestamp) + { + ctx->offset_global_timestamp = ctx->last_global_timestamp - ctx->min_global_timestamp; + ctx->min_global_timestamp = ctx->global_timestamp; + } - // } - // if (ctx->pinfo[j].pid == payload.pid) - // { - // if (!ctx->PIDs_seen[payload.pid]) - // dbg_print(CCX_DMT_PAT, "This PID (%u) is a PMT for program %u.\n",payload.pid, ctx->pinfo[j].program_number); - // pinfo = ctx->pinfo + j; - // break; - // } - //} - //if (j != ctx->nb_program) - //{ - // ctx->PIDs_seen[payload.pid]=2; - // ts_buffer_psi_packet(ctx); - // if(ctx->PID_buffers[payload.pid]!=NULL && ctx->PID_buffers[payload.pid]->buffer_length>0) - // if(parse_PMT(ctx, ctx->PID_buffers[payload.pid]->buffer+1, ctx->PID_buffers[payload.pid]->buffer_length-1, pinfo)) - // gotpes=1; // Signals that something changed and that we must flush the buffer - // continue; - //} + } + if (ctx->pinfo[j].pid == payload.pid) + { + if (!ctx->PIDs_seen[payload.pid]) + dbg_print(CCX_DMT_PAT, "This PID (%u) is a PMT for program %u.\n",payload.pid, ctx->pinfo[j].program_number); + pinfo = ctx->pinfo + j; + break; + } + } + if (j != ctx->nb_program) + { + ctx->PIDs_seen[payload.pid]=2; + ts_buffer_psi_packet(ctx); + if(ctx->PID_buffers[payload.pid]!=NULL && ctx->PID_buffers[payload.pid]->buffer_length>0) + if(parse_PMT(ctx, ctx->PID_buffers[payload.pid]->buffer+1, ctx->PID_buffers[payload.pid]->buffer_length-1, pinfo)) + gotpes=1; // Signals that something changed and that we must flush the buffer + continue; + } - ////PTS calculation - //if (ctx->got_first_pts[0] == UINT64_MAX || ctx->got_first_pts[1] == UINT64_MAX || ctx->got_first_pts[2] == UINT64_MAX) //if we didn't already get the first PTS of the important streams - //{ - // if (payload.pesstart) //if there is PES Header data in the payload and we didn't get the first pts of that stream - // { - // if (ctx->min_pts[payload.pid] == UINT64_MAX) //check if we don't have the min_pts of that packet's pid - // { - // //Write the PES Header to console - // uint64_t pes_prefix; - // uint8_t pes_stream_id; - // uint16_t pes_packet_length; - // uint8_t optional_pes_header_included = NO; - // uint16_t optional_pes_header_length = 0; - // uint64_t pts = 0; + //PTS calculation + if (ctx->got_important_streams_min_pts[PRIVATE_STREAM_1] == UINT64_MAX || ctx->got_important_streams_min_pts[AUDIO] == UINT64_MAX || ctx->got_important_streams_min_pts[VIDEO] == UINT64_MAX) //if we didn't already get the first PTS of the important streams + { + if (payload.pesstart) //if there is PES Header data in the payload and we didn't get the first pts of that stream + { + if (ctx->min_pts[payload.pid] == UINT64_MAX) //check if we don't have the min_pts of that packet's pid + { + // Packetized Elementary Stream (PES) 32-bit start code + uint64_t pes_prefix = (payload.start[0] << 16) | (payload.start[1] << 8) | payload.start[2]; + uint8_t pes_stream_id = payload.start[3]; - // // Packetized Elementary Stream (PES) 32-bit start code - // pes_prefix = (payload.start[0] << 16) | (payload.start[1] << 8) | payload.start[2]; - // pes_stream_id = payload.start[3]; + uint64_t pts = 0; - // // check for PES header - // if (pes_prefix == 0x000001) - // { - // //if we didn't already have this stream id with its first pts then calculate - // if (pes_stream_id != ctx->found_stream_ids[pes_stream_id - 0xbd]) - // { - // pes_packet_length = 6 + ((payload.start[4] << 8) | payload.start[5]); // 5th and 6th byte of the header define the length of the rest of the packet (+6 is for the prefix, stream ID and packet length) + // check for PES header + if (pes_prefix == 0x000001) + { + //if we didn't already have this stream id with its first pts then calculate + if (pes_stream_id != ctx->found_stream_ids[pes_stream_id - 0xbd]) + { + pts = get_pts(payload.start); - // /*if (pes_packet_length == 6) - // { - // // great, there is only a header and no extension + payload - // return; - // }*/ + //keep in mind we already checked if we have this stream id + ctx->found_stream_ids[pes_stream_id - 0xbd] = pes_stream_id; //add it + ctx->min_pts[payload.pid] = pts; //and add its packet pts (we still have this array in case someone wants the global PTS for all stream_id not only for pvs1, audio and video) - // // optional PES header marker bits (10.. ....) - // if ((payload.start[6] & 0xc0) == 0x80) - // { - // optional_pes_header_included = YES; - // optional_pes_header_length = payload.start[8]; - // } + /*we already checked if we got that packet's pts + but we still need to check if we got the min_pts of the stream type + because we might have multiple audio streams for example (audio and subs are sent in order)*/ + if (pes_stream_id == 0xbd && ctx->got_important_streams_min_pts[PRIVATE_STREAM_1] == UINT64_MAX) //private stream 1 + ctx->got_important_streams_min_pts[PRIVATE_STREAM_1] = pts; + if (pes_stream_id >= 0xC0 && pes_stream_id <= 0xDF && ctx->got_important_streams_min_pts[AUDIO] == UINT64_MAX) //audio + ctx->got_important_streams_min_pts[AUDIO] = pts; + } + } + } + } + } + switch (ctx->PIDs_seen[payload.pid]) + { + case 0: // First time we see this PID + if (ctx->PIDs_programs[payload.pid]) + { + dbg_print(CCX_DMT_PARSE, "\nNew PID found: %u (%s), belongs to program: %u\n", payload.pid, + desc[ctx->PIDs_programs[payload.pid]->printable_stream_type], + ctx->PIDs_programs[payload.pid]->program_number); + ctx->PIDs_seen[payload.pid]=2; + } + else + { + dbg_print(CCX_DMT_PARSE, "\nNew PID found: %u, program number still unknown\n", payload.pid); + ctx->PIDs_seen[payload.pid]=1; + } + break; + case 1: // Saw it before but we didn't know what program it belonged to. Luckier now? + if (ctx->PIDs_programs[payload.pid]) + { + dbg_print(CCX_DMT_PARSE, "\nProgram for PID: %u (previously unknown) is: %u (%s)\n", payload.pid, + ctx->PIDs_programs[payload.pid]->program_number, + desc[ctx->PIDs_programs[payload.pid]->printable_stream_type] + ); + ctx->PIDs_seen[payload.pid]=2; + } + break; + case 2: // Already seen and reported with correct program + break; + case 3: // Already seen, reported, and inspected for CC data (and found some) + break; + } - // if (optional_pes_header_included == YES && optional_pes_header_length > 0 && (payload.start[7] & 0x80) > 0) - // { - // //get associated PTS as it exists - // pts = (payload.start[9] & 0x0e); - // pts <<= 29; - // pts |= (payload.start[10] << 22); - // pts |= ((payload.start[11] & 0xfe) << 14); - // pts |= (payload.start[12] << 7); - // pts |= ((payload.start[13] & 0xfe) >> 1); + if (payload.pid==1003 && !ctx->hauppauge_warning_shown && !ccx_options.hauppauge_mode) + { + // TODO: Change this very weak test for something more decent such as size. + mprint ("\n\nNote: This TS could be a recording from a Hauppage card. If no captions are detected, try --hauppauge\n\n"); + ctx->hauppauge_warning_shown=1; + } - // //keep in mind we already checked if we have this stream id - // ctx->found_stream_ids[pes_stream_id - 0xbd] = pes_stream_id; //add it - // ctx->min_pts[payload.pid] = pts; //and add its packet pts (we still have this array in case someone wants the global PTS for all stream_id not only for pvs1, audio and video) + if (ccx_options.hauppauge_mode && payload.pid==HAUPPAGE_CCPID) + { + // Haup packets processed separately, because we can't mix payloads. So they go in their own buffer + // copy payload to capbuf + int haup_newcapbuflen = haup_capbuflen + payload.length; + if ( haup_newcapbuflen > haup_capbufsize) { + haup_capbuf = (unsigned char*)realloc(haup_capbuf, haup_newcapbuflen); + if (!haup_capbuf) + fatal(EXIT_NOT_ENOUGH_MEMORY, "Out of memory"); + haup_capbufsize = haup_newcapbuflen; + } + memcpy(haup_capbuf+haup_capbuflen, payload.start, payload.length); + haup_capbuflen = haup_newcapbuflen; - // /*we already checked if we got that packet's pts - // but we still need to check if we got the min_pts of the stream type - // because we might have multiple audio streams for example*/ - // if (pes_stream_id == 0xbd && ctx->got_first_pts[0] == UINT64_MAX) //private stream 1 - // ctx->got_first_pts[0] = pts; - // if (pes_stream_id >= 0xC0 && pes_stream_id <= 0xDF && ctx->got_first_pts[1] == UINT64_MAX) //audio - // ctx->got_first_pts[1] = pts; - // if (pes_stream_id >= 0xE0 && pes_stream_id <= 0xEF && ctx->got_first_pts[2] == UINT64_MAX) //video - // ctx->got_first_pts[2] = pts; - // } - // } - // } - // } - // } - //} - //switch (ctx->PIDs_seen[payload.pid]) - //{ - // case 0: // First time we see this PID - // if (ctx->PIDs_programs[payload.pid]) - // { - // dbg_print(CCX_DMT_PARSE, "\nNew PID found: %u (%s), belongs to program: %u\n", payload.pid, - // desc[ctx->PIDs_programs[payload.pid]->printable_stream_type], - // ctx->PIDs_programs[payload.pid]->program_number); - // ctx->PIDs_seen[payload.pid]=2; - // } - // else - // { - // dbg_print(CCX_DMT_PARSE, "\nNew PID found: %u, program number still unknown\n", payload.pid); - // ctx->PIDs_seen[payload.pid]=1; - // } - // break; - // case 1: // Saw it before but we didn't know what program it belonged to. Luckier now? - // if (ctx->PIDs_programs[payload.pid]) - // { - // dbg_print(CCX_DMT_PARSE, "\nProgram for PID: %u (previously unknown) is: %u (%s)\n", payload.pid, - // ctx->PIDs_programs[payload.pid]->program_number, - // desc[ctx->PIDs_programs[payload.pid]->printable_stream_type] - // ); - // ctx->PIDs_seen[payload.pid]=2; - // } - // break; - // case 2: // Already seen and reported with correct program - // break; - // case 3: // Already seen, reported, and inspected for CC data (and found some) - // break; - //} + } - //if (payload.pid==1003 && !ctx->hauppauge_warning_shown && !ccx_options.hauppauge_mode) - //{ - // // TODO: Change this very weak test for something more decent such as size. - // mprint ("\n\nNote: This TS could be a recording from a Hauppage card. If no captions are detected, try --hauppauge\n\n"); - // ctx->hauppauge_warning_shown=1; - //} + // Skip packets with no payload. This also fixes the problems + // with the continuity counter not being incremented in empty + // packets. + if ( !payload.length ) + { + dbg_print(CCX_DMT_VERBOSE, "Packet (pid %u) skipped - no payload.\n", + payload.pid); + continue; + } - //if (ccx_options.hauppauge_mode && payload.pid==HAUPPAGE_CCPID) - //{ - // // Haup packets processed separately, because we can't mix payloads. So they go in their own buffer - // // copy payload to capbuf - // int haup_newcapbuflen = haup_capbuflen + payload.length; - // if ( haup_newcapbuflen > haup_capbufsize) { - // haup_capbuf = (unsigned char*)realloc(haup_capbuf, haup_newcapbuflen); - // if (!haup_capbuf) - // fatal(EXIT_NOT_ENOUGH_MEMORY, "Out of memory"); - // haup_capbufsize = haup_newcapbuflen; - // } - // memcpy(haup_capbuf+haup_capbuflen, payload.start, payload.length); - // haup_capbuflen = haup_newcapbuflen; + cinfo = get_cinfo(ctx, payload.pid); + if(cinfo == NULL) + { + if (!packet_analysis_mode) + dbg_print(CCX_DMT_PARSE, "Packet (pid %u) skipped - no stream with captions identified yet.\n", + payload.pid); + else + look_for_caption_data (ctx, &payload); + continue; + } + else if (cinfo->ignore) + { + if(cinfo->codec_private_data) + { + switch(cinfo->codec) + { + case CCX_CODEC_TELETEXT: + telxcc_close(&cinfo->codec_private_data, NULL); + break; + case CCX_CODEC_DVB: + dvbsub_close_decoder(&cinfo->codec_private_data); + break; + case CCX_CODEC_ISDB_CC: + delete_isdb_decoder(&cinfo->codec_private_data); + default: + break; + } + cinfo->codec_private_data = NULL; + } - //} + if (cinfo->capbuflen > 0) + { + freep(&cinfo->capbuf); + cinfo->capbufsize = 0; + cinfo->capbuflen = 0; + delete_demuxer_data_node_by_pid(data, cinfo->pid); + } + continue; + } - //// Skip packets with no payload. This also fixes the problems - //// with the continuity counter not being incremented in empty - //// packets. - //if ( !payload.length ) - //{ - // dbg_print(CCX_DMT_VERBOSE, "Packet (pid %u) skipped - no payload.\n", - // payload.pid); - // continue; - //} + // Video PES start + if (payload.pesstart) + { + cinfo->saw_pesstart = 1; + cinfo->prev_counter = payload.counter - 1; + } - //cinfo = get_cinfo(ctx, payload.pid); - //if(cinfo == NULL) - //{ - // if (!packet_analysis_mode) - // dbg_print(CCX_DMT_PARSE, "Packet (pid %u) skipped - no stream with captions identified yet.\n", - // payload.pid); - // else - // look_for_caption_data (ctx, &payload); - // continue; - //} - //else if (cinfo->ignore) - //{ - // if(cinfo->codec_private_data) - // { - // switch(cinfo->codec) - // { - // case CCX_CODEC_TELETEXT: - // telxcc_close(&cinfo->codec_private_data, NULL); - // break; - // case CCX_CODEC_DVB: - // dvbsub_close_decoder(&cinfo->codec_private_data); - // break; - // case CCX_CODEC_ISDB_CC: - // delete_isdb_decoder(&cinfo->codec_private_data); - // default: - // break; - // } - // cinfo->codec_private_data = NULL; - // } - - // if (cinfo->capbuflen > 0) - // { - // freep(&cinfo->capbuf); - // cinfo->capbufsize = 0; - // cinfo->capbuflen = 0; - // delete_demuxer_data_node_by_pid(data, cinfo->pid); - // } - // continue; - //} - - //// Video PES start - //if (payload.pesstart) - //{ - // cinfo->saw_pesstart = 1; - // cinfo->prev_counter = payload.counter - 1; - //} - - //// Discard packets when no pesstart was found. - //if ( !cinfo->saw_pesstart ) - // continue; + // Discard packets when no pesstart was found. + if ( !cinfo->saw_pesstart ) + continue; - //if ( (cinfo->prev_counter == 15 ? 0 : cinfo->prev_counter + 1) != payload.counter ) - //{ - // mprint("TS continuity counter not incremented prev/curr %u/%u\n", - // cinfo->prev_counter, payload.counter); - //} - //cinfo->prev_counter = payload.counter; + if ( (cinfo->prev_counter == 15 ? 0 : cinfo->prev_counter + 1) != payload.counter ) + { + mprint("TS continuity counter not incremented prev/curr %u/%u\n", + cinfo->prev_counter, payload.counter); + } + cinfo->prev_counter = payload.counter; - //// If the buffer is empty we just started this function - //if (payload.pesstart && cinfo->capbuflen > 0) - //{ - // dbg_print(CCX_DMT_PARSE, "\nPES finished (%ld bytes/%ld PES packets/%ld total packets)\n", - // cinfo->capbuflen, pespcount, pcount); + // If the buffer is empty we just started this function + if (payload.pesstart && cinfo->capbuflen > 0) + { + dbg_print(CCX_DMT_PARSE, "\nPES finished (%ld bytes/%ld PES packets/%ld total packets)\n", + cinfo->capbuflen, pespcount, pcount); - // // Keep the data from capbuf to be worked on - // ret = copy_capbuf_demux_data(ctx, data, cinfo); - // cinfo->capbuflen = 0; - // gotpes = 1; - //} + // Keep the data from capbuf to be worked on + ret = copy_capbuf_demux_data(ctx, data, cinfo); + cinfo->capbuflen = 0; + gotpes = 1; + } - //copy_payload_to_capbuf(cinfo, &payload); - //if(ret < 0) - //{ - // if(errno == EINVAL) - // continue; - // else - // break; - //} + copy_payload_to_capbuf(cinfo, &payload); + if(ret < 0) + { + if(errno == EINVAL) + continue; + else + break; + } - //pespcount++; + pespcount++; } while( !gotpes ); // gotpes==1 never arrives here because of the breaks diff --git a/src/lib_ccx/ts_functions.h b/src/lib_ccx/ts_functions.h index 0a629fd2..65e956ce 100644 --- a/src/lib_ccx/ts_functions.h +++ b/src/lib_ccx/ts_functions.h @@ -9,6 +9,7 @@ struct ts_payload unsigned pid; // Stream PID int counter; // continuity counter int transport_error; // 0 = packet OK, non-zero damaged + int has_random_access_indicator; //1 = start of new GOP (Set when the stream may be decoded without errors from this point) int have_pcr; int64_t pcr; unsigned char section_buf[4098]; diff --git a/src/lib_ccx/ts_tables.c b/src/lib_ccx/ts_tables.c index ba31dbac..8876c2a9 100644 --- a/src/lib_ccx/ts_tables.c +++ b/src/lib_ccx/ts_tables.c @@ -588,7 +588,7 @@ int parse_PAT (struct ccx_demuxer *ctx) memset (ctx->PIDs_seen,0,sizeof (int) *65536); // Forget all we saw memset(ctx->min_pts, UINT64_MAX, 65536 * sizeof(uint64_t)); memset(ctx->found_stream_ids, 0, MAX_NUM_OF_STREAMIDS * sizeof(uint8_t)); - memset(ctx->got_first_pts, UINT64_MAX, 3 * sizeof(uint64_t)); + memset(ctx->got_important_streams_min_pts, UINT64_MAX, 3 * sizeof(uint64_t)); if (!tlt_config.user_page) // If the user didn't select a page... tlt_config.page=0; // ..forget whatever we detected.