Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/README.TXT
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ccextractor, 0.83
ccextractor, 0.83
-----------------
Authors: Carlos Fernández (cfsmp3), Volker Quetschke.
Maintainer: cfsmp3
Expand Down Expand Up @@ -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...)
Expand Down
2 changes: 1 addition & 1 deletion src/lib_ccx/ccx_demuxer.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 *));
}

Expand Down
10 changes: 8 additions & 2 deletions src/lib_ccx/ccx_demuxer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
47 changes: 16 additions & 31 deletions src/lib_ccx/general_loop.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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)
Expand All @@ -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;
Expand Down Expand Up @@ -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)
Expand All @@ -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;
Expand Down
185 changes: 133 additions & 52 deletions src/lib_ccx/ts_functions.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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];
Expand All @@ -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;
Expand All @@ -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,
Expand Down Expand Up @@ -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;
Expand All @@ -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++;
Expand Down Expand Up @@ -698,69 +811,37 @@ 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)
{
//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;
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/lib_ccx/ts_functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down
Loading