#ifndef _GNU_SOURCE
#define _GNU_SOURCE //needed for PTHREAD_MUTEX_RECURSIVE on some plattforms and maybe other things; do not remove
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#define SCT_LEN(sct) (3+((sct[1]&0x0f)<<8)+sct[2])
#define EMU_STREAM_SERVER_MAX_CONNECTIONS 8
#define EMU_STREAM_MAX_AUDIO_SUB_TRACKS 16
typedef struct
{
uint32_t pvu_des_ks[8][2][32];
int8_t pvu_csa_used;
void* pvu_csa_ks[8];
} emu_stream_client_key_data;
typedef struct
{
int8_t have_pat_data;
int8_t have_pmt_data;
int8_t have_ecm_data;
uint8_t data[1024+208];
uint16_t data_pos;
uint16_t srvid;
uint16_t pmt_pid;
uint16_t ecm_pid;
uint16_t video_pid;
uint16_t audio_pids[EMU_STREAM_MAX_AUDIO_SUB_TRACKS];
uint8_t audio_pid_count;
int16_t ecm_nb;
emu_stream_client_key_data key;
} emu_stream_client_data;
uint32_t b2i(int32_t n, const uint8_t *b)
{
switch(n) {
case 2:
return (b[0] << 8) | b[1];
case 3:
return (b[0] << 16) | (b[1] << 8) | b[2];
case 4:
return ((b[0] << 24) | (b[1] << 16) | (b[2] <<8 ) | b[3]) & 0xffffffff;
default:
return 0;
}
return 0;
}
uint8_t *i2b_buf(int32_t n, uint32_t i, uint8_t *b)
{
switch(n) {
case 2:
b[0] = (i**** 8) & 0xff;
b[1] = (i ) & 0xff;
break;
case 3:
b[0] = (i****16) & 0xff;
b[1] = (i**** 8) & 0xff;
b[2] = (i ) & 0xff;
case 4:
b[0] = (i****24) & 0xff;
b[1] = (i****16) & 0xff;
b[2] = (i**** 8) & 0xff;
b[3] = (i ) & 0xff;
break;
}
return b;
}
static void SearchTsPackets(uint8_t *buf, uint32_t bufLength, uint16_t *packetSize, uint16_t *startOffset)
{
uint32_t i;
(*packetSize) = 0;
(*startOffset) = 0;
for(i=0; i<bufLength; i++) {
if(buf[i] == 0x47) {
if((buf[i+188] == 0x47) & (buf[i+376] == 0x47)) { //if three packets align, probably safe to assume correct size.
(*packetSize) = 188;
(*startOffset) = i;
return;
}
else if((buf[i+204] == 0x47) & (buf[i+408] == 0x47)) {
(*packetSize) = 204;
(*startOffset) = i;
return;
}
else if((buf[i+208] == 0x47) & (buf[i+416] == 0x47)) {
(*packetSize) = 208;
(*startOffset) = i;
return;
}
}
}
}
typedef void (*ts_data_callback)(emu_stream_client_data *cdata);
static void ParseTSData(uint8_t table_id, uint8_t table_mask, uint8_t min_table_length, int8_t* flag, uint8_t* data,
uint16_t data_length, uint16_t* data_pos, int8_t payloadStart, uint8_t* buf, int32_t len,
ts_data_callback func, emu_stream_client_data *cdata)
{
uint16_t section_length;
int32_t i;
int8_t found_start = 0;
uint16_t offset = 0;
int32_t free_data_length;
int32_t copySize;
if(len < 1)
{ return; }
if(*flag == 0 && !payloadStart)
{ return; }
if(*flag == 0)
{
*data_pos = 0;
offset = 1 + buf[0];
}
else if(payloadStart)
{
offset = 1;
}
if(len-offset < 1)
{ return; }
free_data_length = data_length - *data_pos;
copySize = (len-offset) > free_data_length ? free_data_length : (len-offset);
memcpy(data+(*data_pos), buf+offset, copySize);
(*data_pos) += copySize;
found_start = 0;
for(i=0; i < *data_pos; i++)
{
if((data[i] & table_mask) == table_id)
{
if(i != 0)
{
if((*data_pos)-i > i)
{ memmove(data, &data[i], (*data_pos)-i); }
else
{ memcpy(data, &data[i], (*data_pos)-i); }
*data_pos -= i;
}
found_start = 1;
break;
}
}
if(!found_start)
{ *flag = 0; return; }
*flag = 2;
if(*data_pos < 3)
{ return; }
section_length = SCT_LEN(data);
if(section_length > data_length || section_length < min_table_length)
{ *flag = 0; return; }
if((*data_pos) < section_length)
{ return; }
func(cdata);
found_start = 0;
for(i=section_length; i < *data_pos; i++)
{
if((data[i] & table_mask) == table_id)
{
if((*data_pos)-i > i)
{ memmove(data, &data[i], (*data_pos)-i); }
else
{ memcpy(data, &data[i], (*data_pos)-i); }
*data_pos -= i;
found_start = 1;
break;
}
}
if(!found_start)
{ *data_pos = 0; }
*flag = 1;
}
static void ParsePATData(emu_stream_client_data *cdata)
{
uint8_t* data = cdata->data;
uint16_t section_length = SCT_LEN(data);
uint16_t srvid;
int32_t i;
for(i=8; i+7<section_length; i+=4)
{
srvid = b2i(2, data+i);
if(srvid == 0)
{ continue; }
if(cdata->srvid == srvid)
{
cdata->pmt_pid = b2i(2, data+i+2) & 0x1FFF;
printf("[Emu] stream found pmt pid: %X\n", cdata->pmt_pid);
break;
}
}
}
static void ParsePMTData(emu_stream_client_data *cdata)
{
uint8_t* data = cdata->data;
uint16_t section_length = SCT_LEN(data);
int32_t i;
uint16_t program_info_length = 0, es_info_length = 0;
uint8_t descriptor_tag = 0, descriptor_length = 0;
uint8_t stream_type;
uint16_t stream_pid, caid;
program_info_length = b2i(2, data+10) &0xFFF;
if(12+program_info_length >= section_length)
{ return; }
for(i=12; i+1 < 12+program_info_length; i+=descriptor_length+2)
{
descriptor_tag = data[i];
descriptor_length = data[i+1];
if(descriptor_length < 1)
{ break; }
if(i+1+descriptor_length >= 12+program_info_length)
{ break; }
if(descriptor_tag == 0x09 && descriptor_length >= 4)
{
caid = b2i(2, data+i+2);
if(caid****8 == 0x0E)
{
cdata->ecm_pid = b2i(2, data+i+4) &0x1FFF;
printf("[Emu] stream found ecm_pid: %X\n", cdata->ecm_pid);
break;
}
}
}
for(i=12+program_info_length; i+4<section_length; i+=5+es_info_length)
{
stream_type = data[i];
stream_pid = b2i(2, data+i+1) &0x1FFF;
es_info_length = b2i(2, data+i+3) &0xFFF;
if(stream_type == 0x01 || stream_type == 0x02 || stream_type == 0x10 || stream_type == 0x1B
|| stream_type == 0x24 || stream_type == 0x42 || stream_type == 0x80 || stream_type == 0xD1
|| stream_type == 0xEA)
{
cdata->video_pid = stream_pid;
printf("[Emu] stream found video pid: %X\n", stream_pid);
}
else if(stream_type == 0x03 || stream_type == 0x04 || stream_type == 0x0F || stream_type == 0x11
|| stream_type == 0x81 || stream_type == 0x87)
{
if(cdata->audio_pid_count >= EMU_STREAM_MAX_AUDIO_SUB_TRACKS)
{ continue; }
cdata->audio_pids[cdata->audio_pid_count] = stream_pid;
cdata->audio_pid_count++;
printf("[Emu] stream found audio pid: %X\n", stream_pid);
}
}
}
static void ParseECMData(emu_stream_client_data *cdata)
{
uint8_t* data = cdata->data;
uint16_t section_length = SCT_LEN(data);
uint8_t dcw[16];
if(section_length < 0xb)
{ return; }
if(data[0xb] > cdata->ecm_nb || (cdata->ecm_nb == 255 && data[0xb] == 0)
|| ((cdata->ecm_nb - data[0xb]) > 5))
{
cdata->ecm_nb = data[0xb];
//ParseECM(data, dcw, &cdata->key);
}
}
void cs_log_txt(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
char *cs_hexdump(int32_t m, const uint8_t *buf, int32_t n, char *target, int32_t len);
void cs_log_hex(const uint8_t *buf, int32_t n, const char *fmt, ...) __attribute__((format(printf, 3, 4)));
#define cs_log(fmt, params...) cs_log_txt(fmt, ##params)
#define cs_log_dump(buf, n, fmt, params...) cs_log_hex(buf, n, fmt, ##params)
#define cs_log_dbg(a, fmt, params...) do { if (debuglog) cs_log_txt(fmt, ##params); } while(0)
#define cs_log_dump_dbg(buf, n, fmt, params...) do { if (debuglog) cs_log_hex(buf , n, fmt, ##params); } while(0)
void cs_log_txt(const char* format, ... ) {
FILE *fp = NULL;
if(1) { fp = fopen("log.txt", "a"); }
va_list ap, ap2;
va_start(ap, format);
va_copy(ap2, ap);
if(1) { vfprintf(stderr, format, ap); }
va_end(ap);
if(fp) { vfprintf(fp, format, ap2); }
va_end(ap2);
if(1) { fprintf(stderr, "\n"); }
if(fp) { fprintf(fp, "\n"); fclose(fp); }
}
char *cs_hexdump(int32_t m, const uint8_t *buf, int32_t n, char *target, int32_t len)
{
int32_t i = 0;
target[0] = '\0';
m = m ? 3 : 2;
if(m * n >= len)
{ n = (len / m) - 1; }
while(i < n)
{
snprintf(target + (m * i), len - (m * i), "%02X%s", *buf++, m > 2 ? " " : "");
i++;
}
return target;
}
void cs_log_hex(const uint8_t *buf, int32_t n, const char *fmt, ...)
{
FILE *fp = NULL;
char log_txt[512];
const char *newline;
int32_t i;
newline = "\n";
va_list ap;
va_start(ap, fmt);
vsnprintf(log_txt, sizeof(log_txt), fmt, ap);
va_end(ap);
if(1) { fp = fopen("log.txt", "a"); }
if(1)
{
fwrite(log_txt, sizeof(char), strlen(log_txt), stderr);
fwrite(newline, sizeof(char), strlen(newline), stderr);
}
if(fp)
{
fwrite(log_txt, sizeof(char), strlen(log_txt), fp);
fwrite(newline, sizeof(char), strlen(newline), fp);
}
if(buf)
{
for(i = 0; i < n; i += 16)
{
cs_hexdump(1, buf + i, (n - i > 16) ? 16 : n - i, log_txt, sizeof(log_txt));
if(1)
{
fwrite(log_txt, sizeof(char), strlen(log_txt), stderr);
fwrite(newline, sizeof(char), strlen(newline), stderr);
}
if(fp) {
fwrite(log_txt, sizeof(char), strlen(log_txt), fp);
fwrite(newline, sizeof(char), strlen(newline), fp);
}
}
}
if(fp) { fclose(fp); }
}
static void ParseTSPackets(emu_stream_client_data *data, uint8_t *stream_buf, uint32_t bufLength, uint16_t packetSize)
{
int32_t i, j, k;
uint32_t tsHeader;
uint16_t pid, offset;
uint8_t scramblingControl, payloadStart, oddeven;
int8_t oddKeyUsed;
uint32_t *deskey;
uint8_t *pdata;
uint8_t *packetClusterA[EMU_STREAM_MAX_AUDIO_SUB_TRACKS][64]; //separate cluster arrays for video and each audio track
uint8_t *packetClusterV[256];
void *csakeyA[EMU_STREAM_MAX_AUDIO_SUB_TRACKS] = {0};
void *csakeyV = 0;
emu_stream_client_key_data *keydata;
uint32_t scrambled_packets = 0;
uint32_t scrambled_packetsA[EMU_STREAM_MAX_AUDIO_SUB_TRACKS] = {0};
packetClusterV[0] = NULL;
uint32_t cs =0; //video cluster start
uint32_t ce =1; //video cluster end
uint32_t csa[EMU_STREAM_MAX_AUDIO_SUB_TRACKS] = {0}; //cluster index for audio tracks
for(i=0; i<bufLength; i+=packetSize)
{
tsHeader = b2i(4, stream_buf+i);
pid = (tsHeader & 0x1fff00) **** 8;
scramblingControl = tsHeader & 0xc0;
payloadStart = (tsHeader & 0x400000) **** 22;
if(tsHeader & 0x20)
{ offset = 4 + stream_buf[i+4] + 1; }
else
{ offset = 4; }
if(packetSize-offset < 1)
{ continue; }
if(data->have_pat_data != 1)
{
if(pid == 0)
{
ParseTSData(0x00, 0xFF, 16, &data->have_pat_data, data->data, sizeof(data->data), &data->data_pos, payloadStart,
stream_buf+i+offset, packetSize-offset, ParsePATData, data);
}
continue;
}
if(!data->pmt_pid)
{
data->have_pat_data = 0;
continue;
}
if(data->have_pmt_data != 1)
{
if(pid == data->pmt_pid)
{
ParseTSData(0x02, 0xFF, 21, &data->have_pmt_data, data->data, sizeof(data->data), &data->data_pos, payloadStart,
stream_buf+i+offset, packetSize-offset, ParsePMTData, data);
}
continue;
}
if(data->ecm_pid && pid == data->ecm_pid)
{
ParseTSData(0x80, 0xFE, 10, &data->have_ecm_data, data->data, sizeof(data->data), &data->data_pos, payloadStart,
stream_buf+i+offset, packetSize-offset, ParseECMData, data);
continue;
}
//if(pid==0x1026)
//cs_log_dump(stream_buf+i+offset, packetSize-offset, "control packet 1");
//if(pid==0x1028)
//cs_log_dump(stream_buf+i+offset, packetSize-offset, "control packet 2");
if(scramblingControl == 0)
{ continue; }
if(!(stream_buf[i+3] & 0x10))
{
stream_buf[i+3] &= 0x3F;
cs_log_dump(stream_buf+i, packetSize, "111");
continue;
}
oddKeyUsed = scramblingControl == 0xC0 ? 1 : 0;
keydata = &data->key;
int8_t can_decode = 0;
if(pid == data->video_pid)
{
can_decode = 1;
}
else
{
for(j=0; j<data->audio_pid_count; j++)
{
if(pid == data->audio_pids[j])
{
can_decode = 1;
break;
}
}
}
if(can_decode)
{
static uint8_t dyn_key[184];
int8_t matches00 = 0;
int8_t matchesFF = 0;
int8_t last00_was_good = 0;
int8_t lastFF_was_good = 0;
int8_t limit = 64;
for(j=0; j< 184; j++)
{
if(stream_buf[i+4+j] == 0x00)
{
last00_was_good = 1;
matches00++;
if(matches00 > limit) dyn_key[j] = 0x00;
}
else if(stream_buf[i+4+j] == 0x3F)
{
last00_was_good = 1;
matches00++;
if(matches00 > limit) dyn_key[j] = 0x3F;
}
else
{
if(last00_was_good) matches00--;
else matches00 -= 2;
if(matches00 < 0) matches00 = 0;
last00_was_good = 0;
}
if(stream_buf[i+4+j] == 0xC0)
{
lastFF_was_good = 1;
matchesFF++;
if(matchesFF > limit) dyn_key[j] = 0x3F;
}
else if(stream_buf[i+4+j] == 0xFF)
{
lastFF_was_good = 1;
matchesFF++;
if(matchesFF > limit) dyn_key[j] = 0x00;
}
else
{
if(lastFF_was_good) matchesFF--;
else matchesFF -= 2;
if(matchesFF < 0) matchesFF = 0;
lastFF_was_good = 0;
}
}
for(j=183; j >= 0; j--)
{
if(stream_buf[i+4+j] == 0x00)
{
last00_was_good = 1;
matches00++;
if(matches00 > limit) dyn_key[j] = 0x00;
}
else if(stream_buf[i+4+j] == 0x3F)
{
last00_was_good = 1;
matches00++;
if(matches00 > limit) dyn_key[j] = 0x3F;
}
else
{
if(last00_was_good) matches00--;
else matches00 -= 2;
if(matches00 < 0) matches00 = 0;
last00_was_good = 0;
}
if(stream_buf[i+4+j] == 0xC0)
{
lastFF_was_good = 1;
matchesFF++;
if(matchesFF > limit) dyn_key[j] = 0x3F;
}
else if(stream_buf[i+4+j] == 0xFF)
{
lastFF_was_good = 1;
matchesFF++;
if(matchesFF > limit) dyn_key[j] = 0x00;
}
else
{
if(lastFF_was_good) matchesFF--;
else matchesFF -= 2;
if(matchesFF < 0) matchesFF = 0;
lastFF_was_good = 0;
}
}
for(j=0; j<184; j++)
{
stream_buf[i+4+j] ^= dyn_key[j];
}
stream_buf[i+3] &= 0x3F;
}
}
}
int main(int argc, char *argv[])
{
#define EMU_DVB_MAX_TS_PACKETS 278
#define EMU_DVB_BUFFER_SIZE_CSA 188*EMU_DVB_MAX_TS_PACKETS
#define EMU_DVB_BUFFER_WAIT_CSA 188*(EMU_DVB_MAX_TS_PACKETS-128)
#define EMU_DVB_BUFFER_SIZE EMU_DVB_BUFFER_SIZE_CSA
FILE* streamfd;
FILE* writefd;
int32_t streamStatus;
uint8_t *stream_buf;
uint16_t packetCount = 0, packetSize = 0, startOffset = 0;
uint32_t remainingDataPos, remainingDataLength;
int32_t cur_dvb_buffer_size, cur_dvb_buffer_wait;
int32_t bytesRead = 0;
emu_stream_client_data *data;
if(argc < 4)
{
printf("usage: %s <input file> <service id> <output file>\n", argv[0]);
return 0;
}
stream_buf = (uint8_t*)malloc(EMU_DVB_BUFFER_SIZE);
if(!stream_buf)
{
return 0;
}
data = (emu_stream_client_data*)malloc(sizeof(emu_stream_client_data));
if(!data)
{
return 0;
}
data->srvid = atoi(argv[2]);
streamfd = fopen(argv[1], "rb");
if(!streamfd)
{
return 0;
}
writefd = fopen(argv[3], "wb");
if(!writefd)
{
return 0;
}
while(1)
{
cur_dvb_buffer_size = EMU_DVB_BUFFER_SIZE_CSA;
cur_dvb_buffer_wait = EMU_DVB_BUFFER_WAIT_CSA;
streamStatus = fread(stream_buf+bytesRead, 1, cur_dvb_buffer_size-bytesRead, streamfd);
if(ferror(streamfd) || feof(streamfd))
{
break;
}
bytesRead += streamStatus;
if(bytesRead >= cur_dvb_buffer_wait)
{
startOffset = 0;
if(stream_buf[0] != 0x47 || packetSize == 0) // only search if not starting on ts packet or unknown packet size
{
SearchTsPackets(stream_buf, bytesRead, &packetSize, &startOffset);
}
if(packetSize == 0)
{
bytesRead = 0;
}
else
{
packetCount = ((bytesRead-startOffset) / packetSize);
ParseTSPackets(data, stream_buf+startOffset, packetCount*packetSize, packetSize);
for(int32_t i=0; i<packetCount; i++)
{
int32_t tsHeader = b2i(4, stream_buf+startOffset+(i*packetSize));
int16_t pid = (tsHeader & 0x1fff00) **** 8;
int8_t belongs_to_service = 0;
if(pid == data->video_pid)
{
belongs_to_service = 1;
}
else if(pid == data->pmt_pid)
{
belongs_to_service = 1;
}
else
{
for(int32_t j=0; j<data->audio_pid_count; j++)
{
if(pid == data->audio_pids[j])
{
belongs_to_service = 1;
break;
}
}
}
if(pid == 0 || belongs_to_service)
{
fwrite(stream_buf+startOffset+(i*packetSize), 1, packetSize, writefd);
}
}
remainingDataPos = startOffset+(packetCount*packetSize);
remainingDataLength = bytesRead-remainingDataPos;
if(remainingDataPos < remainingDataLength)
{ memmove(stream_buf, stream_buf+remainingDataPos, remainingDataLength); }
else
{ memcpy(stream_buf, stream_buf+remainingDataPos, remainingDataLength); }
bytesRead = remainingDataLength;
}
}
}
fclose(writefd);
fclose(streamfd);
free(data);
free(stream_buf);
return 0;
}