diff --git a/docs/README.TXT b/docs/README.TXT index 6ef1569a2..910dbef3c 100644 --- a/docs/README.TXT +++ b/docs/README.TXT @@ -1,4 +1,4 @@ -ccextractor, 0.83 +ccextractor, 0.83 ----------------- Authors: Carlos Fernández (cfsmp3), Volker Quetschke. Maintainer: cfsmp3 @@ -31,6 +31,7 @@ Google Summer of Code 2016 students Google Code-in 2016 students - Evgeny Shulgin +- Alexandru Bratosin - Manveer Basra - Alexandru Bratosin (more, but they forgot to add themselves...) diff --git a/src/lib_ccx/ccx_demuxer.c b/src/lib_ccx/ccx_demuxer.c index 63aed2f46..d176ce165 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 e7dfcba4e..a6ede59ac 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 0b70fcac0..74594689e 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 9a4d2af8f..e36965d77 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++; @@ -698,23 +811,17 @@ long ts_readstream(struct ccx_demuxer *ctx, struct demuxer_data **data) } //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 (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 { - //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; - // 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 pes_prefix = (payload.start[0] << 16) | (payload.start[1] << 8) | payload.start[2]; + uint8_t pes_stream_id = payload.start[3]; + + uint64_t pts = 0; // check for PES header if (pes_prefix == 0x000001) @@ -722,45 +829,19 @@ long ts_readstream(struct ccx_demuxer *ctx, struct demuxer_data **data) //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) - - /*if (pes_packet_length == 6) - { - // great, there is only a header and no extension + payload - return; - }*/ - - // optional PES header marker bits (10.. ....) - if ((payload.start[6] & 0xc0) == 0x80) - { - optional_pes_header_included = YES; - optional_pes_header_length = payload.start[8]; - } - - 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); - - //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) - - /*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; - } + pts = get_pts(payload.start); + + //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) + + /*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; } } } diff --git a/src/lib_ccx/ts_functions.h b/src/lib_ccx/ts_functions.h index 0a629fd29..65e956ce2 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 ba31dbac4..8876c2a99 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.