From 079f21f81abc6fd7230d6ea853d903c1af0b72e8 Mon Sep 17 00:00:00 2001 From: anon000111 <000111_anon@kobej.zzn.com> Date: Sun, 24 Jun 2012 09:46:12 +0900 Subject: [PATCH] initial commit --- Makefile | 8 + arib_std_b25.sln | 20 + arib_std_b25.vcproj | 259 ++++ src/Makefile | 51 + src/arib_std_b25.c | 2651 ++++++++++++++++++++++++++++++++++++ src/arib_std_b25.h | 60 + src/arib_std_b25_error_code.h | 25 + src/b_cas_card.c | 749 ++++++++++ src/b_cas_card.h | 75 + src/b_cas_card_error_code.h | 11 + src/makefile.win | 33 + src/multi2.c | 527 +++++++ src/multi2.h | 35 + src/multi2_error_code.h | 9 + src/portable.h | 38 + src/td.c | 432 ++++++ src/ts_common_types.h | 36 + src/ts_section_parser.c | 802 +++++++++++ src/ts_section_parser.h | 40 + src/ts_section_parser_error_code.h | 12 + 20 files changed, 5873 insertions(+) create mode 100644 Makefile create mode 100644 arib_std_b25.sln create mode 100644 arib_std_b25.vcproj create mode 100644 src/Makefile create mode 100644 src/arib_std_b25.c create mode 100644 src/arib_std_b25.h create mode 100644 src/arib_std_b25_error_code.h create mode 100644 src/b_cas_card.c create mode 100644 src/b_cas_card.h create mode 100644 src/b_cas_card_error_code.h create mode 100644 src/makefile.win create mode 100644 src/multi2.c create mode 100644 src/multi2.h create mode 100644 src/multi2_error_code.h create mode 100644 src/portable.h create mode 100644 src/td.c create mode 100644 src/ts_common_types.h create mode 100644 src/ts_section_parser.c create mode 100644 src/ts_section_parser.h create mode 100644 src/ts_section_parser_error_code.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5fdf66d --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ +all: + cd src; make all + +clean: + cd src; make clean + +install: + cd src; make install diff --git a/arib_std_b25.sln b/arib_std_b25.sln new file mode 100644 index 0000000..d52a7e2 --- /dev/null +++ b/arib_std_b25.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual C++ Express 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "arib_std_b25", "arib_std_b25.vcproj", "{6E77C1AC-A31A-49B9-9A52-9FE1E03B8FEC}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6E77C1AC-A31A-49B9-9A52-9FE1E03B8FEC}.Debug|Win32.ActiveCfg = Debug|Win32 + {6E77C1AC-A31A-49B9-9A52-9FE1E03B8FEC}.Debug|Win32.Build.0 = Debug|Win32 + {6E77C1AC-A31A-49B9-9A52-9FE1E03B8FEC}.Release|Win32.ActiveCfg = Release|Win32 + {6E77C1AC-A31A-49B9-9A52-9FE1E03B8FEC}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/arib_std_b25.vcproj b/arib_std_b25.vcproj new file mode 100644 index 0000000..9e78ea5 --- /dev/null +++ b/arib_std_b25.vcproj @@ -0,0 +1,259 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..5c167e6 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,51 @@ +PREFIX = /usr/local +MAJOR = 0 +MINOR = 2 +REVISION = 5 +VER = $(MAJOR).$(MINOR).$(REVISION) + +DEST_HEADER = $(PREFIX)/include/arib25 + +CC = gcc +PCSC_CFLAGS ?= `pkg-config libpcsclite --cflags` +CPPFLAGS = -Wall $(PCSC_CFLAGS) -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 +CFLAGS = -O2 -g -fPIC + +PCSC_LIBS ?= `pkg-config libpcsclite --libs` +LIBS = $(PCSC_LIBS) -lm +LDFLAGS ?= + +OBJS = arib_std_b25.o b_cas_card.o multi2.o ts_section_parser.o +HEADERS = arib_std_b25.h b_cas_card.h portable.h +TARGET_APP = b25 +TARGET_LIB = libarib25.so +TARGETS = $(TARGET_APP) $(TARGET_LIB) +DEPEND = Makefile.dep +SONAME = $(TARGET_LIB).$(MAJOR) + +all: $(TARGETS) + +clean: + rm -f $(OBJS) td.o $(TARGETS) $(DEPEND) + +$(TARGET_APP): $(OBJS) td.o + $(CC) $(LDFLAGS) -o $(TARGET_APP) $(OBJS) td.o $(LIBS) + +$(TARGET_LIB): $(OBJS) + $(CC) $(LDFLAGS) -shared -o $(TARGET_LIB) $(OBJS) $(LIBS) -Wl,-soname,$(SONAME) + +$(DEPEND): + $(CC) -MM $(OBJS:.o=.c) $(CPPFLAGS) > $@ + +install: $(TARGET) install-headers + install -m755 b25 $(PREFIX)/bin + install -m755 $(TARGET_LIB) $(PREFIX)/lib/$(TARGET_LIB).$(VER) + ln -sf $(PREFIX)/lib/$(TARGET_LIB).$(VER) $(PREFIX)/lib/$(TARGET_LIB).$(MAJOR) + ln -sf $(PREFIX)/lib/$(TARGET_LIB).$(MAJOR) $(PREFIX)/lib/$(TARGET_LIB) + ldconfig + +install-headers: + mkdir -p $(DEST_HEADER) + install -m644 $(HEADERS) $(DEST_HEADER) + +-include $(DEPEND) diff --git a/src/arib_std_b25.c b/src/arib_std_b25.c new file mode 100644 index 0000000..12bdf9f --- /dev/null +++ b/src/arib_std_b25.c @@ -0,0 +1,2651 @@ +#include +#include +#include + +#include "arib_std_b25.h" +#include "arib_std_b25_error_code.h" +#include "multi2.h" +#include "ts_common_types.h" +#include "ts_section_parser.h" + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + inner structures + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +typedef struct { + int32_t pid; + int32_t type; + void *prev; + void *next; +} TS_STREAM_ELEM; + +typedef struct { + TS_STREAM_ELEM *head; + TS_STREAM_ELEM *tail; + int32_t count; +} TS_STREAM_LIST; + +typedef struct { + + uint8_t *pool; + uint8_t *head; + uint8_t *tail; + int32_t max; + +} TS_WORK_BUFFER; + +typedef struct { + + int32_t phase; + + int32_t program_number; + + int32_t pmt_pid; + TS_SECTION_PARSER *pmt; + + int32_t pcr_pid; + + TS_STREAM_LIST streams; + TS_STREAM_LIST old_strm; + +} TS_PROGRAM; + +typedef struct { + + int32_t ref; + int32_t phase; + + int32_t locked; + + int32_t ecm_pid; + TS_SECTION_PARSER *ecm; + + MULTI2 *m2; + + int32_t unpurchased; + int32_t last_error; + + void *prev; + void *next; + +} DECRYPTOR_ELEM; + +typedef struct { + DECRYPTOR_ELEM *head; + DECRYPTOR_ELEM *tail; + int32_t count; +} DECRYPTOR_LIST; + +typedef struct { + uint32_t ref; + uint32_t type; + int64_t normal_packet; + int64_t undecrypted; + void *target; +} PID_MAP; + +typedef struct { + + int32_t multi2_round; + int32_t strip; + int32_t emm_proc_on; + + int32_t unit_size; + + int32_t sbuf_offset; + + TS_SECTION_PARSER *pat; + TS_SECTION_PARSER *cat; + + TS_STREAM_LIST strm_pool; + + int32_t p_count; + TS_PROGRAM *program; + + DECRYPTOR_LIST decrypt; + + PID_MAP map[0x2000]; + + B_CAS_CARD *bcas; + B_CAS_ID casid; + int32_t ca_system_id; + + int32_t emm_pid; + TS_SECTION_PARSER *emm; + + TS_WORK_BUFFER sbuf; + TS_WORK_BUFFER dbuf; + +} ARIB_STD_B25_PRIVATE_DATA; + +typedef struct { + int64_t card_id; + int32_t associated_information_length; + int32_t protocol_number; + int32_t broadcaster_group_id; + int32_t update_number; + int32_t expiration_date; +} EMM_FIXED_PART; + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + constant values + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +enum TS_STREAM_TYPE { + TS_STREAM_TYPE_11172_2_VIDEO = 0x01, + TS_STREAM_TYPE_13818_2_VIDEO = 0x02, + TS_STREAM_TYPE_11172_3_AUDIO = 0x03, + TS_STREAM_TYPE_13818_3_AUDIO = 0x04, + TS_STREAM_TYPE_13818_1_PRIVATE_SECTIONS = 0x05, + TS_STREAM_TYPE_13818_1_PES_PRIVATE_DATA = 0x06, + TS_STREAM_TYPE_13522_MHEG = 0x07, + TS_STREAM_TYPE_13818_1_DSM_CC = 0x08, + TS_STREAM_TYPE_H_222_1 = 0x09, + TS_STREAM_TYPE_13818_6_TYPE_A = 0x0a, + TS_STREAM_TYPE_13818_6_TYPE_B = 0x0b, + TS_STREAM_TYPE_13818_6_TYPE_C = 0x0c, + TS_STREAM_TYPE_13818_6_TYPE_D = 0x0d, + TS_STREAM_TYPE_13818_1_AUX = 0x0e, + TS_STREAM_TYPE_13818_7_AUDIO_ADTS = 0x0f, + TS_STREAM_TYPE_14496_2_VISUAL = 0x10, + TS_STREAM_TYPE_14496_3_AUDIO_LATM = 0x11, + TS_STREAM_TYPE_14496_1_PES_SL_PACKET = 0x12, + TS_STREAM_TYPE_14496_1_SECTIONS_SL_PACKET = 0x13, + TS_STREAM_TYPE_13818_6_SYNC_DWLOAD_PROTCOL = 0x14, +}; + +enum TS_SECTION_ID { + TS_SECTION_ID_PROGRAM_ASSOCIATION = 0x00, + TS_SECTION_ID_CONDITIONAL_ACCESS = 0x01, + TS_SECTION_ID_PROGRAM_MAP = 0x02, + TS_SECTION_ID_DESCRIPTION = 0x03, + TS_SECTION_ID_14496_SCENE_DESCRIPTION = 0x04, + TS_SECTION_ID_14496_OBJECT_DESCRIPTION = 0x05, + + /* ARIB STD-B10 stuff */ + TS_SECTION_ID_DSM_CC_HEAD = 0x3a, + TS_SECTION_ID_DSM_CC_TAIL = 0x3f, + TS_SECTION_ID_NIT_ACTUAL = 0x40, + TS_SECTION_ID_NIT_OTHER = 0x41, + TS_SECTION_ID_SDT_ACTUAL = 0x42, + TS_SECTION_ID_SDT_OTHER = 0x46, + TS_SECTION_ID_BAT = 0x4a, + TS_SECTION_ID_EIT_ACTUAL_CURRENT = 0x4e, + TS_SECTION_ID_EIT_OTHER_CURRENT = 0x4f, + TS_SECTION_ID_EIT_ACTUAL_SCHEDULE_HEAD = 0x50, + TS_SECTION_ID_EIT_ACTUAL_SCHEDULE_TAIL = 0x5f, + TS_SECTION_ID_EIT_OTHER_SCHEDULE_HEAD = 0x60, + TS_SECTION_ID_EIT_OTHER_SCHEDULE_TAIL = 0x6f, + TS_SECTION_ID_TDT = 0x70, + TS_SECTION_ID_RST = 0x71, + TS_SECTION_ID_ST = 0x72, + TS_SECTION_ID_TOT = 0x73, + TS_SECTION_ID_AIT = 0x74, + TS_SECTION_ID_DIT = 0x7e, + TS_SECTION_ID_SIT = 0x7f, + TS_SECTION_ID_ECM_S = 0x82, + TS_SECTION_ID_ECM = 0x83, + TS_SECTION_ID_EMM_S = 0x84, + TS_SECTION_ID_EMM_MESSAGE = 0x85, + TS_SECTION_ID_DCT = 0xc0, + TS_SECTION_ID_DLT = 0xc1, + TS_SECTION_ID_PCAT = 0xc2, + TS_SECTION_ID_SDTT = 0xc3, + TS_SECTION_ID_BIT = 0xc4, + TS_SECTION_ID_NBIT_BODY = 0xc5, + TS_SECTION_ID_NBIT_REFERENCE = 0xc6, + TS_SECTION_ID_LDT = 0xc7, + TS_SECTION_ID_CDT = 0xc8, + TS_SECTION_ID_LIT = 0xd0, + TS_SECTION_ID_ERT = 0xd1, + TS_SECTION_ID_ITT = 0xd2, +}; + +enum TS_DESCRIPTOR_TAG { + TS_DESCRIPTOR_TAG_VIDEO_STREAM = 0x02, + TS_DESCRIPTOR_TAG_AUDIO_STREAM = 0x03, + TS_DESCRIPTOR_TAG_HIERARCHY = 0x04, + TS_DESCRIPTOR_TAG_REGISTRATION = 0x05, + TS_DESCRIPTOR_TAG_DATA_STREAM_ALIGNMENT = 0x06, + TS_DESCRIPTOR_TAG_TARGET_BACKGROUND_GRID = 0x07, + TS_DESCRIPTOR_TAG_VIDEO_WINDOW = 0x08, + TS_DESCRIPTOR_TAG_CA = 0x09, + TS_DESCRIPTOR_TAG_ISO_639_LANGUAGE = 0x0a, + TS_DESCRIPTOR_TAG_SYSTEM_CLOCK = 0x0b, + TS_DESCRIPTOR_TAG_MULTIPLEX_BUF_UTILIZ = 0x0c, + TS_DESCRIPTOR_TAG_COPYRIGHT = 0x0d, + TS_DESCRIPTOR_TAG_MAXIMUM_BITRATE = 0x0e, + TS_DESCRIPTOR_TAG_PRIVATE_DATA_INDICATOR = 0x0f, + TS_DESCRIPTOR_TAG_SMOOTHING_BUFFER = 0x10, + TS_DESCRIPTOR_TAG_STD = 0x11, + TS_DESCRIPTOR_TAG_IBP = 0x12, + TS_DESCRIPTOR_TAG_MPEG4_VIDEO = 0x1b, + TS_DESCRIPTOR_TAG_MPEG4_AUDIO = 0x1c, + TS_DESCRIPTOR_TAG_IOD = 0x1d, + TS_DESCRIPTOR_TAG_SL = 0x1e, + TS_DESCRIPTOR_TAG_FMC = 0x1f, + TS_DESCRIPTOR_TAG_EXTERNAL_ES_ID = 0x20, + TS_DESCRIPTOR_TAG_MUXCODE = 0x21, + TS_DESCRIPTOR_TAG_FMX_BUFFER_SIZE = 0x22, + TS_DESCRIPTOR_TAG_MULTIPLEX_BUFFER = 0x23, + TS_DESCRIPTOR_TAG_AVC_VIDEO = 0x28, + TS_DESCRIPTOR_TAG_AVC_TIMING_HRD = 0x2a, + + /* ARIB STD-B10 stuff */ + TS_DESCRIPTOR_TAG_NETWORK_NAME = 0x40, + TS_DESCRIPTOR_TAG_SERVICE_LIST = 0x41, + TS_DESCRIPTOR_TAG_STUFFING = 0x42, + TS_DESCRIPTOR_TAG_SATELLITE_DELIVERY_SYS = 0x43, + TS_DESCRIPTOR_TAG_CABLE_DISTRIBUTION = 0x44, + TS_DESCRIPTOR_TAG_BOUNQUET_NAME = 0x47, + TS_DESCRIPTOR_TAG_SERVICE = 0x48, + TS_DESCRIPTOR_TAG_COUNTRY_AVAILABILITY = 0x49, + TS_DESCRIPTOR_TAG_LINKAGE = 0x4a, + TS_DESCRIPTOR_TAG_NVOD_REFERENCE = 0x4b, + TS_DESCRIPTOR_TAG_TIME_SHIFTED_SERVICE = 0x4c, + TS_DESCRIPTOR_TAG_SHORT_EVENT = 0x4d, + TS_DESCRIPTOR_TAG_EXTENDED_EVENT = 0x4e, + TS_DESCRIPTOR_TAG_TIME_SHIFTED_EVENT = 0x4f, + TS_DESCRIPTOR_TAG_COMPONENT = 0x50, + TS_DESCRIPTOR_TAG_MOSAIC = 0x51, + TS_DESCRIPTOR_TAG_STREAM_IDENTIFIER = 0x52, + TS_DESCRIPTOR_TAG_CA_IDENTIFIER = 0x53, + TS_DESCRIPTOR_TAG_CONTENT = 0x54, + TS_DESCRIPTOR_TAG_PARENTAL_RATING = 0x55, + TS_DESCRIPTOR_TAG_LOCAL_TIME_OFFSET = 0x58, + TS_DESCRIPTOR_TAG_PARTIAL_TRANSPORT_STREAM = 0x63, + TS_DESCRIPTOR_TAG_HIERARCHICAL_TRANSMISSION = 0xc0, + TS_DESCRIPTOR_TAG_DIGITAL_COPY_CONTROL = 0xc1, + TS_DESCRIPTOR_TAG_NETWORK_IDENTIFICATION = 0xc2, + TS_DESCRIPTOR_TAG_PARTIAL_TRANSPORT_TIME = 0xc3, + TS_DESCRIPTOR_TAG_AUDIO_COMPONENT = 0xc4, + TS_DESCRIPTOR_TAG_HYPERLINK = 0xc5, + TS_DESCRIPTOR_TAG_TARGET_REGION = 0xc6, + TS_DESCRIPTOR_TAG_DATA_COTENT = 0xc7, + TS_DESCRIPTOR_TAG_VIDEO_DECODE_CONTROL = 0xc8, + TS_DESCRIPTOR_TAG_DOWNLOAD_CONTENT = 0xc9, + TS_DESCRIPTOR_TAG_CA_EMM_TS = 0xca, + TS_DESCRIPTOR_TAG_CA_CONTRACT_INFORMATION = 0xcb, + TS_DESCRIPTOR_TAG_CA_SERVICE = 0xcc, + TS_DESCRIPTOR_TAG_TS_INFORMATION = 0xcd, + TS_DESCRIPTOR_TAG_EXTENDED_BROADCASTER = 0xce, + TS_DESCRIPTOR_TAG_LOGO_TRANSMISSION = 0xcf, + TS_DESCRIPTOR_TAG_BASIC_LOCAL_EVENT = 0xd0, + TS_DESCRIPTOR_TAG_REFERENCE = 0xd1, + TS_DESCRIPTOR_TAG_NODE_RELATION = 0xd2, + TS_DESCRIPTOR_TAG_SHORT_NODE_INFORMATION = 0xd3, + TS_DESCRIPTOR_TAG_STC_REFERENCE = 0xd4, + TS_DESCRIPTOR_TAG_SERIES = 0xd5, + TS_DESCRIPTOR_TAG_EVENT_GROUP = 0xd6, + TS_DESCRIPTOR_TAG_SI_PARAMETER = 0xd7, + TS_DESCRIPTOR_TAG_BROADCASTER_NAME = 0xd8, + TS_DESCRIPTOR_TAG_COMPONENT_GROUP = 0xd9, + TS_DESCRIPTOR_TAG_SI_PRIME_TS = 0xda, + TS_DESCRIPTOR_TAG_BOARD_INFORMATION = 0xdb, + TS_DESCRIPTOR_TAG_LDT_LINKAGE = 0xdc, + TS_DESCRIPTOR_TAG_CONNECTED_TRANSMISSION = 0xdd, + TS_DESCRIPTOR_TAG_CONTENT_AVAILABILITY = 0xde, + TS_DESCRIPTOR_TAG_VALUE_EXTENSION = 0xdf, + TS_DESCRIPTOR_TAG_SERVICE_GROUP = 0xe0, + TS_DESCRIPTOR_TAG_CARUSEL_COMPOSITE = 0xf7, + TS_DESCRIPTOR_TAG_CONDITIONAL_PLAYBACK = 0xf8, + TS_DESCRIPTOR_TAG_CABLE_TS_DIVISSION = 0xf9, + TS_DESCRIPTOR_TAG_TERRESTRIAL_DELIVERY_SYS = 0xfa, + TS_DESCRIPTOR_TAG_PARTIAL_RECEPTION = 0xfb, + TS_DESCRIPTOR_TAG_EMERGENCY_INFOMATION = 0xfc, + TS_DESCRIPTOR_TAG_DATA_COMPONENT = 0xfd, + TS_DESCRIPTOR_TAG_SYSTEM_MANAGEMENT = 0xfe, +}; + +enum PID_MAP_TYPE { + PID_MAP_TYPE_UNKNOWN = 0x0000, + PID_MAP_TYPE_PAT = 0x0100, + PID_MAP_TYPE_PMT = 0x0200, + PID_MAP_TYPE_NIT = 0x0300, + PID_MAP_TYPE_PCR = 0x0400, + PID_MAP_TYPE_ECM = 0x0500, + PID_MAP_TYPE_EMM = 0x0600, + PID_MAP_TYPE_EIT = 0x0700, + PID_MAP_TYPE_CAT = 0x0800, + PID_MAP_TYPE_OTHER = 0xff00, +}; + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + function prottypes (interface method) + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static void release_arib_std_b25(void *std_b25); +static int set_multi2_round_arib_std_b25(void *std_b25, int32_t round); +static int set_strip_arib_std_b25(void *std_b25, int32_t strip); +static int set_emm_proc_arib_std_b25(void *std_b25, int32_t on); +static int set_b_cas_card_arib_std_b25(void *std_b25, B_CAS_CARD *bcas); +static int reset_arib_std_b25(void *std_b25); +static int flush_arib_std_b25(void *std_b25); +static int put_arib_std_b25(void *std_b25, ARIB_STD_B25_BUFFER *buf); +static int get_arib_std_b25(void *std_b25, ARIB_STD_B25_BUFFER *buf); +static int get_program_count_arib_std_b25(void *std_b25); +static int get_program_info_arib_std_b25(void *std_b25, ARIB_STD_B25_PROGRAM_INFO *info, int idx); + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + global function implementation + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +ARIB_STD_B25 *create_arib_std_b25() +{ + int n; + + ARIB_STD_B25 *r; + ARIB_STD_B25_PRIVATE_DATA *prv; + + n = sizeof(ARIB_STD_B25_PRIVATE_DATA); + n += sizeof(ARIB_STD_B25); + + prv = (ARIB_STD_B25_PRIVATE_DATA *)calloc(1, n); + if(prv == NULL){ + return NULL; + } + + prv->multi2_round = 4; + + r = (ARIB_STD_B25 *)(prv+1); + r->private_data = prv; + + r->release = release_arib_std_b25; + r->set_multi2_round = set_multi2_round_arib_std_b25; + r->set_strip = set_strip_arib_std_b25; + r->set_emm_proc = set_emm_proc_arib_std_b25; + r->set_b_cas_card = set_b_cas_card_arib_std_b25; + r->reset = reset_arib_std_b25; + r->flush = flush_arib_std_b25; + r->put = put_arib_std_b25; + r->get = get_arib_std_b25; + r->get_program_count = get_program_count_arib_std_b25; + r->get_program_info = get_program_info_arib_std_b25; + + return r; +} + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + function prottypes (private method) + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static ARIB_STD_B25_PRIVATE_DATA *private_data(void *std_b25); +static void teardown(ARIB_STD_B25_PRIVATE_DATA *prv); +static int select_unit_size(ARIB_STD_B25_PRIVATE_DATA *prv); +static int find_pat(ARIB_STD_B25_PRIVATE_DATA *prv); +static int proc_pat(ARIB_STD_B25_PRIVATE_DATA *prv); +static int check_pmt_complete(ARIB_STD_B25_PRIVATE_DATA *prv); +static int find_pmt(ARIB_STD_B25_PRIVATE_DATA *prv); +static int proc_pmt(ARIB_STD_B25_PRIVATE_DATA *prv, TS_PROGRAM *pgrm); +static int32_t find_ca_descriptor_pid(uint8_t *head, uint8_t *tail, int32_t ca_system_id); +static int32_t add_ecm_stream(ARIB_STD_B25_PRIVATE_DATA *prv, TS_STREAM_LIST *list, int32_t ecm_pid); +static int check_ecm_complete(ARIB_STD_B25_PRIVATE_DATA *prv); +static int find_ecm(ARIB_STD_B25_PRIVATE_DATA *prv); +static int proc_ecm(DECRYPTOR_ELEM *dec, B_CAS_CARD *bcas, int32_t multi2_round); +static int proc_arib_std_b25(ARIB_STD_B25_PRIVATE_DATA *prv); + +static int proc_cat(ARIB_STD_B25_PRIVATE_DATA *prv); +static int proc_emm(ARIB_STD_B25_PRIVATE_DATA *prv); + +static void release_program(ARIB_STD_B25_PRIVATE_DATA *prv, TS_PROGRAM *pgrm); + +static void unref_stream(ARIB_STD_B25_PRIVATE_DATA *prv, int32_t pid); + +static DECRYPTOR_ELEM *set_decryptor(ARIB_STD_B25_PRIVATE_DATA *prv, int32_t pid); +static void remove_decryptor(ARIB_STD_B25_PRIVATE_DATA *prv, DECRYPTOR_ELEM *dec); +static DECRYPTOR_ELEM *select_active_decryptor(DECRYPTOR_ELEM *a, DECRYPTOR_ELEM *b, int32_t pid); +static void bind_stream_decryptor(ARIB_STD_B25_PRIVATE_DATA *prv, int32_t pid, DECRYPTOR_ELEM *dec); +static void unlock_all_decryptor(ARIB_STD_B25_PRIVATE_DATA *prv); + +static TS_STREAM_ELEM *get_stream_list_head(TS_STREAM_LIST *list); +static TS_STREAM_ELEM *find_stream_list_elem(TS_STREAM_LIST *list, int32_t pid); +static TS_STREAM_ELEM *create_stream_elem(int32_t pid, int32_t type); +static void put_stream_list_tail(TS_STREAM_LIST *list, TS_STREAM_ELEM *elem); +static void clear_stream_list(TS_STREAM_LIST *list); + +static int reserve_work_buffer(TS_WORK_BUFFER *buf, int32_t size); +static int append_work_buffer(TS_WORK_BUFFER *buf, uint8_t *data, int32_t size); +static void reset_work_buffer(TS_WORK_BUFFER *buf); +static void release_work_buffer(TS_WORK_BUFFER *buf); + +static void extract_ts_header(TS_HEADER *dst, uint8_t *src); +static void extract_emm_fixed_part(EMM_FIXED_PART *dst, uint8_t *src); + +static uint8_t *resync(uint8_t *head, uint8_t *tail, int32_t unit); +static uint8_t *resync_force(uint8_t *head, uint8_t *tail, int32_t unit); + +/* static uint32_t crc32(uint8_t *head, uint8_t *tail); */ + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + interface method implementation + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static void release_arib_std_b25(void *std_b25) +{ + ARIB_STD_B25_PRIVATE_DATA *prv; + + prv = private_data(std_b25); + if(prv == NULL){ + return; + } + + teardown(prv); + free(prv); +} + +static int set_multi2_round_arib_std_b25(void *std_b25, int32_t round) +{ + ARIB_STD_B25_PRIVATE_DATA *prv; + + prv = private_data(std_b25); + if(prv == NULL){ + return ARIB_STD_B25_ERROR_INVALID_PARAM; + } + + prv->multi2_round = round; + + return 0; +} + +static int set_strip_arib_std_b25(void *std_b25, int32_t strip) +{ + ARIB_STD_B25_PRIVATE_DATA *prv; + + prv = private_data(std_b25); + if(prv == NULL){ + return ARIB_STD_B25_ERROR_INVALID_PARAM; + } + + prv->strip = strip; + + return 0; +} + +static int set_emm_proc_arib_std_b25(void *std_b25, int32_t on) +{ + ARIB_STD_B25_PRIVATE_DATA *prv; + + prv = private_data(std_b25); + if(prv == NULL){ + return ARIB_STD_B25_ERROR_INVALID_PARAM; + } + + prv->emm_proc_on = on; + + return 0; +} + +static int set_b_cas_card_arib_std_b25(void *std_b25, B_CAS_CARD *bcas) +{ + int n; + B_CAS_INIT_STATUS is; + ARIB_STD_B25_PRIVATE_DATA *prv; + + prv = private_data(std_b25); + if(prv == NULL){ + return ARIB_STD_B25_ERROR_INVALID_PARAM; + } + + prv->bcas = bcas; + if(prv->bcas != NULL){ + n = prv->bcas->get_init_status(bcas, &is); + if(n < 0){ + return ARIB_STD_B25_ERROR_INVALID_B_CAS_STATUS; + } + prv->ca_system_id = is.ca_system_id; + n = prv->bcas->get_id(prv->bcas, &(prv->casid)); + if(n < 0){ + return ARIB_STD_B25_ERROR_INVALID_B_CAS_STATUS; + } + } + + return 0; +} + +static int reset_arib_std_b25(void *std_b25) +{ + ARIB_STD_B25_PRIVATE_DATA *prv; + + prv = private_data(std_b25); + if(prv == NULL){ + return ARIB_STD_B25_ERROR_INVALID_PARAM; + } + + teardown(prv); + + return 0; +} + +static int flush_arib_std_b25(void *std_b25) +{ + int r; + int m,n; + + int32_t crypt; + int32_t unit; + int32_t pid; + + uint8_t *p; + uint8_t *curr; + uint8_t *tail; + + TS_HEADER hdr; + DECRYPTOR_ELEM *dec; + TS_PROGRAM *pgrm; + + ARIB_STD_B25_PRIVATE_DATA *prv; + + prv = private_data(std_b25); + if(prv == NULL){ + return ARIB_STD_B25_ERROR_INVALID_PARAM; + } + + if(prv->unit_size < 188){ + r = select_unit_size(prv); + if(r < 0){ + return r; + } + } + + r = proc_arib_std_b25(prv); + if(r < 0){ + return r; + } + + unit = prv->unit_size; + curr = prv->sbuf.head; + tail = prv->sbuf.tail; + + m = prv->dbuf.tail - prv->dbuf.head; + n = tail - curr; + if(!reserve_work_buffer(&(prv->dbuf), m+n)){ + return ARIB_STD_B25_ERROR_NO_ENOUGH_MEMORY; + } + + r = 0; + + while( (curr+188) <= tail ){ + + if(curr[0] != 0x47){ + p = resync_force(curr, tail, unit); + if(p == NULL){ + goto LAST; + } + curr = p; + } + + extract_ts_header(&hdr, curr); + crypt = hdr.transport_scrambling_control; + pid = hdr.pid; + + if(hdr.transport_error_indicator != 0){ + /* bit error - append output buffer without parsing */ + if(!append_work_buffer(&(prv->dbuf), curr, 188)){ + r = ARIB_STD_B25_ERROR_NO_ENOUGH_MEMORY; + goto LAST; + } + goto NEXT; + } + + if( (pid == 0x1fff) && (prv->strip) ){ + goto NEXT; + } + + p = curr+4; + if(hdr.adaptation_field_control & 0x02){ + p += (p[0]+1); + } + n = 188 - (p-curr); + if( (n < 1) && ((n < 0) || (hdr.adaptation_field_control & 0x01)) ){ + /* broken packet */ + curr += 1; + continue; + } + + if( (crypt != 0) && + (hdr.adaptation_field_control & 0x01) ){ + + if(prv->map[pid].type == PID_MAP_TYPE_OTHER){ + dec = (DECRYPTOR_ELEM *)(prv->map[pid].target); + }else if( (prv->map[pid].type == 0) && + (prv->decrypt.count == 1) ){ + dec = prv->decrypt.head; + }else{ + dec = NULL; + } + + if( (dec != NULL) && (dec->m2 != NULL) ){ + m = dec->m2->decrypt(dec->m2, crypt, p, n); + if(m < 0){ + r = ARIB_STD_B25_ERROR_DECRYPT_FAILURE; + goto LAST; + } + curr[3] &= 0x3f; + prv->map[pid].normal_packet += 1; + }else{ + prv->map[pid].undecrypted += 1; + } + }else{ + prv->map[pid].normal_packet += 1; + } + + if(!append_work_buffer(&(prv->dbuf), curr, 188)){ + r = ARIB_STD_B25_ERROR_NO_ENOUGH_MEMORY; + goto LAST; + } + + if(prv->map[pid].type == PID_MAP_TYPE_ECM){ + dec = (DECRYPTOR_ELEM *)(prv->map[pid].target); + if( (dec == NULL) || (dec->ecm == NULL) ){ + /* this code will never execute */ + r = ARIB_STD_B25_ERROR_ECM_PARSE_FAILURE; + goto LAST; + } + m = dec->ecm->put(dec->ecm, &hdr, p, n); + if(m < 0){ + r = ARIB_STD_B25_ERROR_ECM_PARSE_FAILURE; + goto LAST; + } + m = dec->ecm->get_count(dec->ecm); + if(m < 0){ + r = ARIB_STD_B25_ERROR_ECM_PARSE_FAILURE; + goto LAST; + } + if(m == 0){ + goto NEXT; + } + r = proc_ecm(dec, prv->bcas, prv->multi2_round); + if(r < 0){ + goto LAST; + } + }else if(prv->map[pid].type == PID_MAP_TYPE_PMT){ + pgrm = (TS_PROGRAM *)(prv->map[pid].target); + if( (pgrm == NULL) || (pgrm->pmt == NULL) ){ + /* this code will never execute */ + r = ARIB_STD_B25_ERROR_PMT_PARSE_FAILURE; + goto LAST; + } + m = pgrm->pmt->put(pgrm->pmt, &hdr, p, n); + if(m < 0){ + r = ARIB_STD_B25_ERROR_PMT_PARSE_FAILURE; + goto LAST; + } + m = pgrm->pmt->get_count(pgrm->pmt); + if(m < 0){ + r = ARIB_STD_B25_ERROR_PMT_PARSE_FAILURE; + goto LAST; + } + if(m == 0){ + goto NEXT; + } + r = proc_pmt(prv, pgrm); + if(r < 0){ + goto LAST; + } + }else if(prv->map[pid].type == PID_MAP_TYPE_EMM){ + if( prv->emm_proc_on == 0){ + goto NEXT; + } + if( prv->emm == NULL ){ + prv->emm = create_ts_section_parser(); + if(prv->emm == NULL){ + r = ARIB_STD_B25_ERROR_EMM_PARSE_FAILURE; + goto LAST; + } + } + m = prv->emm->put(prv->emm, &hdr, p, n); + if(m < 0){ + r = ARIB_STD_B25_ERROR_EMM_PARSE_FAILURE; + goto LAST; + } + m = prv->emm->get_count(prv->emm); + if(m < 0){ + r = ARIB_STD_B25_ERROR_EMM_PARSE_FAILURE; + goto LAST; + } + if(m == 0){ + goto NEXT; + } + r = proc_emm(prv); + if(r < 0){ + goto LAST; + } + }else if(pid == 0x0001){ + if( prv->cat == NULL ){ + prv->cat = create_ts_section_parser(); + if(prv->cat == NULL){ + r = ARIB_STD_B25_ERROR_NO_ENOUGH_MEMORY; + goto LAST; + } + } + m = prv->cat->put(prv->cat, &hdr, p, n); + if(m < 0){ + r = ARIB_STD_B25_ERROR_CAT_PARSE_FAILURE; + goto LAST; + } + m = prv->cat->get_count(prv->cat); + if(m < 0){ + r = ARIB_STD_B25_ERROR_CAT_PARSE_FAILURE; + goto LAST; + } + if(m == 0){ + goto NEXT; + } + r = proc_cat(prv); + if(r < 0){ + goto LAST; + } + }else if(pid == 0x0000){ + if( prv->pat == NULL ){ + prv->pat = create_ts_section_parser(); + if(prv->pat == NULL){ + r = ARIB_STD_B25_ERROR_NO_ENOUGH_MEMORY; + goto LAST; + } + } + m = prv->pat->put(prv->pat, &hdr, p, n); + if(m < 0){ + r = ARIB_STD_B25_ERROR_PAT_PARSE_FAILURE; + goto LAST; + } + m = prv->pat->get_count(prv->pat); + if(m < 0){ + r = ARIB_STD_B25_ERROR_PAT_PARSE_FAILURE; + goto LAST; + } + if(m == 0){ + goto NEXT; + } + r = proc_pat(prv); + if(r < 0){ + goto LAST; + } + } + + NEXT: + curr += unit; + } + +LAST: + + m = curr - prv->sbuf.head; + n = tail - curr; + if( (n < 1024) || (m > (prv->sbuf.max/2) ) ){ + p = prv->sbuf.pool; + memcpy(p, curr, n); + prv->sbuf.head = p; + prv->sbuf.tail = p+n; + }else{ + prv->sbuf.head = curr; + } + + return r; +} + +static int put_arib_std_b25(void *std_b25, ARIB_STD_B25_BUFFER *buf) +{ + int32_t n; + + ARIB_STD_B25_PRIVATE_DATA *prv; + + prv = private_data(std_b25); + if( (prv == NULL) || (buf == NULL) ){ + return ARIB_STD_B25_ERROR_INVALID_PARAM; + } + + if(!append_work_buffer(&(prv->sbuf), buf->data, buf->size)){ + return ARIB_STD_B25_ERROR_NO_ENOUGH_MEMORY; + } + + if(prv->unit_size < 188){ + n = select_unit_size(prv); + if(n < 0){ + return n; + } + if(prv->unit_size < 188){ + /* need more data */ + return 0; + } + } + + if(prv->p_count < 1){ + n = find_pat(prv); + if(n < 0){ + return n; + } + if(prv->p_count < 1){ + if(prv->sbuf_offset < (16*1024*1024)){ + /* need more data */ + return 0; + }else{ + /* exceed sbuf limit */ + return ARIB_STD_B25_ERROR_NO_PAT_IN_HEAD_16M; + } + } + prv->sbuf_offset = 0; + } + + if(!check_pmt_complete(prv)){ + n = find_pmt(prv); + if(n < 0){ + return n; + } + if(!check_pmt_complete(prv)){ + if(prv->sbuf_offset < (32*1024*1024)){ + /* need more data */ + return 0; + }else{ + /* exceed sbuf limit */ + return ARIB_STD_B25_ERROR_NO_PMT_IN_HEAD_32M; + } + } + prv->sbuf_offset = 0; + } + + if(!check_ecm_complete(prv)){ + n = find_ecm(prv); + if(n < 0){ + return n; + } + if(!check_ecm_complete(prv)){ + if(prv->sbuf_offset < (32*1024*1024)){ + /* need more data */ + return 0; + }else{ + /* exceed sbuf limit */ + return ARIB_STD_B25_ERROR_NO_ECM_IN_HEAD_32M; + } + } + prv->sbuf_offset = 0; + } + + return proc_arib_std_b25(prv); +} + +static int get_arib_std_b25(void *std_b25, ARIB_STD_B25_BUFFER *buf) +{ + ARIB_STD_B25_PRIVATE_DATA *prv; + prv = private_data(std_b25); + if( (prv == NULL) || (buf == NULL) ){ + return ARIB_STD_B25_ERROR_INVALID_PARAM; + } + + buf->data = prv->dbuf.head; + buf->size = prv->dbuf.tail - prv->dbuf.head; + + reset_work_buffer(&(prv->dbuf)); + + return 0; +} + +static int get_program_count_arib_std_b25(void *std_b25) +{ + ARIB_STD_B25_PRIVATE_DATA *prv; + + prv = private_data(std_b25); + if(prv == NULL){ + return ARIB_STD_B25_ERROR_INVALID_PARAM; + } + + return prv->p_count; +} + +static int get_program_info_arib_std_b25(void *std_b25, ARIB_STD_B25_PROGRAM_INFO *info, int idx) +{ + ARIB_STD_B25_PRIVATE_DATA *prv; + + TS_PROGRAM *pgrm; + + TS_STREAM_ELEM *strm; + DECRYPTOR_ELEM *dec; + + int32_t pid; + + prv = private_data(std_b25); + if( (prv == NULL) || (info == NULL) || (idx < 0) || (idx >= prv->p_count) ){ + return ARIB_STD_B25_ERROR_INVALID_PARAM; + } + + pgrm = prv->program + idx; + + memset(info, 0, sizeof(ARIB_STD_B25_PROGRAM_INFO)); + + info->program_number = pgrm->program_number; + + pid = pgrm->pmt_pid; + info->total_packet_count += prv->map[pid].normal_packet; + info->total_packet_count += prv->map[pid].undecrypted; + info->undecrypted_packet_count += prv->map[pid].undecrypted; + + pid = pgrm->pcr_pid; + if( (pid != 0) && (pid != 0x1fff) ){ + info->total_packet_count += prv->map[pid].normal_packet; + info->total_packet_count += prv->map[pid].undecrypted; + info->undecrypted_packet_count += prv->map[pid].undecrypted; + } + + strm = pgrm->streams.head; + while(strm != NULL){ + pid = strm->pid; + if(prv->map[pid].type == PID_MAP_TYPE_ECM){ + dec = (DECRYPTOR_ELEM *)(prv->map[pid].target); + info->ecm_unpurchased_count += dec->unpurchased; + info->last_ecm_error_code = dec->last_error; + } + info->total_packet_count += prv->map[pid].normal_packet; + info->total_packet_count += prv->map[pid].undecrypted; + info->undecrypted_packet_count += prv->map[pid].undecrypted; + strm = (TS_STREAM_ELEM *)(strm->next); + } + + return 0; +} + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + private method implementation + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static ARIB_STD_B25_PRIVATE_DATA *private_data(void *std_b25) +{ + ARIB_STD_B25 *p; + ARIB_STD_B25_PRIVATE_DATA *r; + + p = (ARIB_STD_B25 *)std_b25; + if(p == NULL){ + return NULL; + } + + r = (ARIB_STD_B25_PRIVATE_DATA *)p->private_data; + if( ((void *)(r+1)) != ((void *)p) ){ + return NULL; + } + + return r; +} + +static void teardown(ARIB_STD_B25_PRIVATE_DATA *prv) +{ + int i; + + prv->unit_size = 0; + prv->sbuf_offset = 0; + + if(prv->pat != NULL){ + prv->pat->release(prv->pat); + prv->pat = NULL; + } + if(prv->cat != NULL){ + prv->cat->release(prv->cat); + prv->cat = NULL; + } + + if(prv->program != NULL){ + for(i=0;ip_count;i++){ + release_program(prv, prv->program+i); + } + free(prv->program); + prv->program = NULL; + } + prv->p_count = 0; + + clear_stream_list(&(prv->strm_pool)); + + while(prv->decrypt.head != NULL){ + remove_decryptor(prv, prv->decrypt.head); + } + + memset(prv->map, 0, sizeof(prv->map)); + + prv->emm_pid = 0; + if(prv->emm != NULL){ + prv->emm->release(prv->emm); + prv->emm = NULL; + } + + release_work_buffer(&(prv->sbuf)); + release_work_buffer(&(prv->dbuf)); +} + +static int select_unit_size(ARIB_STD_B25_PRIVATE_DATA *prv) +{ + int i; + int m,n,w; + int count[320-188]; + + unsigned char *head; + unsigned char *buf; + unsigned char *tail; + + head = prv->sbuf.head; + tail = prv->sbuf.tail; + + buf = head; + memset(count, 0, sizeof(count)); + + // 1st step, count up 0x47 interval + while( (buf+188) < tail ){ + if(buf[0] != 0x47){ + buf += 1; + continue; + } + m = 320; + if( buf+m > tail ){ + m = tail-buf; + } + for(i=188;iunit_size = n; + + return 0; +} + +static int find_pat(ARIB_STD_B25_PRIVATE_DATA *prv) +{ + int r; + int n,size; + + int32_t unit; + + uint8_t *p; + uint8_t *curr; + uint8_t *tail; + + TS_HEADER hdr; + + r = 0; + unit = prv->unit_size; + curr = prv->sbuf.head + prv->sbuf_offset; + tail = prv->sbuf.tail; + + while( (curr+unit) < tail ){ + if( (curr[0] != 0x47) || (curr[unit] != 0x47) ){ + p = resync(curr, tail, unit); + if(p == NULL){ + goto LAST; + } + curr = p; + } + extract_ts_header(&hdr, curr); + if(hdr.pid == 0x0000){ + + p = curr+4; + if(hdr.adaptation_field_control & 0x02){ + p += (p[0]+1); + } + size = 188 - (p-curr); + if(size < 1){ + goto NEXT; + } + + if(prv->pat == NULL){ + prv->pat = create_ts_section_parser(); + if(prv->pat == NULL){ + return ARIB_STD_B25_ERROR_NO_ENOUGH_MEMORY; + } + } + + n = prv->pat->put(prv->pat, &hdr, p, size); + if(n < 0){ + r = ARIB_STD_B25_ERROR_PAT_PARSE_FAILURE; + curr += unit; + goto LAST; + } + n = prv->pat->get_count(prv->pat); + if(n < 0){ + r = ARIB_STD_B25_ERROR_PAT_PARSE_FAILURE; + curr += unit; + goto LAST; + } + if(n > 0){ + curr += unit; + goto LAST; + } + } + NEXT: + curr += unit; + } + +LAST: + prv->sbuf_offset = curr - prv->sbuf.head; + + if( (prv->pat != NULL) && (prv->pat->get_count(prv->pat) > 0) ){ + r = proc_pat(prv); + } + + return r; +} + +static int proc_pat(ARIB_STD_B25_PRIVATE_DATA *prv) +{ + int r; + int i,n; + int len; + int count; + + int32_t program_number; + int32_t pid; + + uint8_t *head; + uint8_t *tail; + + TS_PROGRAM *work; + TS_SECTION sect; + + r = 0; + memset(§, 0, sizeof(sect)); + + n = prv->pat->get(prv->pat, §); + if(n < 0){ + r = ARIB_STD_B25_ERROR_PAT_PARSE_FAILURE; + goto LAST; + } + + if(sect.hdr.table_id != TS_SECTION_ID_PROGRAM_ASSOCIATION){ + r = ARIB_STD_B25_WARN_TS_SECTION_ID_MISSMATCH; + goto LAST; + } + + len = (sect.tail - sect.data) - 4; + + count = len / 4; + work = (TS_PROGRAM *)calloc(count, sizeof(TS_PROGRAM)); + if(work == NULL){ + r = ARIB_STD_B25_ERROR_NO_ENOUGH_MEMORY; + goto LAST; + } + + if(prv->program != NULL){ + for(i=0;ip_count;i++){ + release_program(prv, prv->program+i); + } + free(prv->program); + prv->program = NULL; + } + prv->p_count = 0; + memset(&(prv->map), 0, sizeof(prv->map)); + + head = sect.data; + tail = sect.tail-4; + + i = 0; + while( (head+4) <= tail ){ + program_number = ((head[0] << 8) | head[1]); + pid = ((head[2] << 8) | head[3]) & 0x1fff; + if(program_number != 0){ + work[i].program_number = program_number; + work[i].pmt_pid = pid; + work[i].pmt = create_ts_section_parser(); + if(work[i].pmt == NULL){ + r = ARIB_STD_B25_ERROR_NO_ENOUGH_MEMORY; + break; + } + prv->map[pid].type = PID_MAP_TYPE_PMT; + prv->map[pid].target = work+i; + i += 1; + } + head += 4; + } + + prv->program = work; + prv->p_count = i; + + prv->map[0x0000].ref = 1; + prv->map[0x0000].type = PID_MAP_TYPE_PAT; + prv->map[0x0000].target = NULL; + +LAST: + if(sect.raw != NULL){ + n = prv->pat->ret(prv->pat, §); + if( (n < 0) && (r == 0) ){ + r = ARIB_STD_B25_ERROR_PAT_PARSE_FAILURE; + } + } + + return r; +} + +static int check_pmt_complete(ARIB_STD_B25_PRIVATE_DATA *prv) +{ + int i,n; + int num[3]; + + memset(num, 0, sizeof(num)); + + for(i=0;ip_count;i++){ + n = prv->program[i].phase; + if(n < 0){ + n = 0; + }else if(n > 2){ + n = 2; + } + num[n] += 1; + } + + if(num[2] > 0){ + return 1; + } + + if(num[0] > 0){ + return 0; + } + + return 1; +} + +static int find_pmt(ARIB_STD_B25_PRIVATE_DATA *prv) +{ + int r; + int n,size; + + int32_t unit; + + uint8_t *p; + uint8_t *curr; + uint8_t *tail; + + TS_HEADER hdr; + TS_PROGRAM *pgrm; + + r = 0; + unit = prv->unit_size; + curr = prv->sbuf.head + prv->sbuf_offset; + tail = prv->sbuf.tail; + + while( (curr+unit) < tail ){ + + if( (curr[0] != 0x47) || (curr[unit] != 0x47) ){ + p = resync(curr, tail, unit); + if(p == NULL){ + goto LAST; + } + curr = p; + } + + extract_ts_header(&hdr, curr); + + if(prv->map[hdr.pid].type != PID_MAP_TYPE_PMT){ + goto NEXT; + } + pgrm = (TS_PROGRAM *)(prv->map[hdr.pid].target); + if(pgrm == NULL){ + goto NEXT; + } + + if(pgrm->phase == 0){ + + p = curr + 4; + if(hdr.adaptation_field_control & 0x02){ + p += (p[0]+1); + } + size = 188 - (p-curr); + if(size < 1){ + goto NEXT; + } + + if(pgrm->pmt == NULL){ + /* this code will never execute */ + r = ARIB_STD_B25_ERROR_PMT_PARSE_FAILURE; + curr += unit; + goto LAST; + } + + n = pgrm->pmt->put(pgrm->pmt, &hdr, p, size); + if(n < 0){ + r = ARIB_STD_B25_ERROR_PMT_PARSE_FAILURE; + curr += unit; + goto LAST; + } + n = pgrm->pmt->get_count(pgrm->pmt); + if(n < 0){ + r =ARIB_STD_B25_ERROR_PMT_PARSE_FAILURE; + curr += unit; + goto LAST; + } + if(n == 0){ + goto NEXT; + } + r = proc_pmt(prv, pgrm); + if(r < 0){ + curr += unit; + goto LAST; + } + if(r > 0){ + /* broken or unexpected section data */ + goto NEXT; + } + pgrm->phase = 1; + if(check_pmt_complete(prv)){ + curr += unit; + goto LAST; + } + }else{ + pgrm->phase = 2; + curr += unit; + goto LAST; + } + + NEXT: + curr += unit; + } + +LAST: + prv->sbuf_offset = curr - prv->sbuf.head; + + return r; +} + +static int proc_pmt(ARIB_STD_B25_PRIVATE_DATA *prv, TS_PROGRAM *pgrm) +{ + int r; + + int n; + int length; + + uint8_t *head; + uint8_t *tail; + + int32_t ecm_pid; + int32_t pid; + int32_t type; + + TS_SECTION sect; + + DECRYPTOR_ELEM *dec[2]; + DECRYPTOR_ELEM *dw; + + TS_STREAM_ELEM *strm; + + r = 0; + dec[0] = NULL; + memset(§, 0, sizeof(sect)); + + n = pgrm->pmt->get(pgrm->pmt, §); + if(n < 0){ + r = ARIB_STD_B25_ERROR_PMT_PARSE_FAILURE; + goto LAST; + } + if(sect.hdr.table_id != TS_SECTION_ID_PROGRAM_MAP){ + r = ARIB_STD_B25_WARN_TS_SECTION_ID_MISSMATCH; + goto LAST; + } + + head = sect.data; + tail = sect.tail-4; + + pgrm->pcr_pid = ((head[0] << 8) | head[1]) & 0x1fff; + length = ((head[2] << 8) | head[3]) & 0x0fff; + head += 4; + if(head+length > tail){ + r = ARIB_STD_B25_WARN_BROKEN_TS_SECTION; + goto LAST; + } + + /* find major ecm_pid and regist decryptor */ + ecm_pid = find_ca_descriptor_pid(head, head+length, prv->ca_system_id); + if( (ecm_pid != 0) && (ecm_pid != 0x1fff) ){ + dec[0] = set_decryptor(prv, ecm_pid); + if(dec[0] == NULL){ + r = ARIB_STD_B25_ERROR_NO_ENOUGH_MEMORY; + goto LAST; + } + dec[0]->ref += 1; + } else { + if (prv->decrypt.count == 1) { + dec[0] = prv->decrypt.head; + dec[0]->ref += 1; + } + } + head += length; + + + /* unref old stream entries */ + while( (strm = get_stream_list_head(&(pgrm->old_strm))) != NULL ){ + unref_stream(prv, strm->pid); + memset(strm, 0, sizeof(TS_STREAM_ELEM)); + put_stream_list_tail(&(prv->strm_pool), strm); + } + + /* save current streams */ + memcpy(&(pgrm->old_strm), &(pgrm->streams), sizeof(TS_STREAM_LIST)); + memset(&(pgrm->streams), 0, sizeof(TS_STREAM_LIST)); + + /* add current stream entries */ + if( (ecm_pid != 0) && (ecm_pid != 0x1fff) ){ + if(!add_ecm_stream(prv, &(pgrm->streams), ecm_pid)){ + r = ARIB_STD_B25_ERROR_NO_ENOUGH_MEMORY; + goto LAST; + } + } + + while( head+4 < tail ){ + + type = head[0]; + pid = ((head[1] << 8) | head[2]) & 0x1fff; + length = ((head[3] << 8) | head[4]) & 0x0fff; + head += 5; + ecm_pid = find_ca_descriptor_pid(head, head+length, prv->ca_system_id); + head += length; + + if( (ecm_pid != 0) && (ecm_pid != 0x1fff) ){ + dec[1] = set_decryptor(prv, ecm_pid); + if(dec[1] == NULL){ + r = ARIB_STD_B25_ERROR_NO_ENOUGH_MEMORY; + goto LAST; + } + if(!add_ecm_stream(prv, &(pgrm->streams), ecm_pid)){ + r = ARIB_STD_B25_ERROR_NO_ENOUGH_MEMORY; + goto LAST; + } + }else{ + dec[1] = NULL; + } + + strm = get_stream_list_head(&(prv->strm_pool)); + if( strm == NULL ){ + strm = create_stream_elem(pid, type); + if(strm == NULL){ + r = ARIB_STD_B25_ERROR_NO_ENOUGH_MEMORY; + goto LAST; + } + }else{ + strm->pid = pid; + strm->type = type; + } + + prv->map[pid].type = PID_MAP_TYPE_OTHER; + prv->map[pid].ref += 1; + + dw = select_active_decryptor(dec[0], dec[1], ecm_pid); + bind_stream_decryptor(prv, pid, dw); + + put_stream_list_tail(&(pgrm->streams), strm); + } + +LAST: + if( dec[0] != NULL ){ + dec[0]->ref -= 1; + if( dec[0]->ref < 1 ){ + remove_decryptor(prv, dec[0]); + dec[0] = NULL; + } + } + + if(sect.raw != NULL){ + n = pgrm->pmt->ret(pgrm->pmt, §); + if( (n < 0) && (r == 0) ){ + return ARIB_STD_B25_ERROR_PMT_PARSE_FAILURE; + } + } + + return r; +} + +static int32_t find_ca_descriptor_pid(uint8_t *head, uint8_t *tail, int32_t ca_system_id) +{ + uint32_t ca_pid; + uint32_t ca_sys_id; + + uint32_t tag; + uint32_t len; + + while(head+1 < tail){ + tag = head[0]; + len = head[1]; + head += 2; + if( (tag == 0x09) && /* CA_descriptor */ + (len >= 4) && + (head+len <= tail) ){ + ca_sys_id = ((head[0] << 8) | head[1]); + ca_pid = ((head[2] << 8) | head[3]) & 0x1fff; + if(ca_sys_id == ca_system_id){ + return ca_pid; + } + } + head += len; + } + + return 0; +} + +static int add_ecm_stream(ARIB_STD_B25_PRIVATE_DATA *prv, TS_STREAM_LIST *list, int32_t ecm_pid) +{ + TS_STREAM_ELEM *strm; + + strm = find_stream_list_elem(list, ecm_pid); + if(strm != NULL){ + // ECM is already registered + return 1; + } + + strm = get_stream_list_head(&(prv->strm_pool)); + if(strm == NULL){ + strm = create_stream_elem(ecm_pid, PID_MAP_TYPE_ECM); + if(strm == NULL){ + return 0; + } + }else{ + strm->pid = ecm_pid; + strm->type = PID_MAP_TYPE_ECM; + } + + put_stream_list_tail(list, strm); + prv->map[ecm_pid].ref += 1; + + return 1; +} + +static int check_ecm_complete(ARIB_STD_B25_PRIVATE_DATA *prv) +{ + int n,num[3]; + DECRYPTOR_ELEM *e; + + memset(num, 0, sizeof(num)); + + e = prv->decrypt.head; + while( e != NULL ){ + n = e->phase; + if(n < 0){ + n = 0; + }else if(n > 2){ + n = 2; + } + num[n] += 1; + e = (DECRYPTOR_ELEM *)(e->next); + } + + if(num[2] > 0){ + return 1; + } + + if(num[0] > 0){ + return 0; + } + + return 1; +} + +static int find_ecm(ARIB_STD_B25_PRIVATE_DATA *prv) +{ + int r; + int n,size; + + int32_t unit; + + uint8_t *p; + uint8_t *curr; + uint8_t *tail; + + TS_HEADER hdr; + DECRYPTOR_ELEM *dec; + + r = 0; + unit = prv->unit_size; + curr = prv->sbuf.head + prv->sbuf_offset; + tail = prv->sbuf.tail; + + while( (curr+unit) < tail ){ + if( (curr[0] != 0x47) || (curr[unit] != 0x47) ){ + p = resync(curr, tail, unit); + if(p == NULL){ + goto LAST; + } + curr = p; + } + extract_ts_header(&hdr, curr); + if(prv->map[hdr.pid].type != PID_MAP_TYPE_ECM){ + goto NEXT; + } + dec = (DECRYPTOR_ELEM *)(prv->map[hdr.pid].target); + if(dec == NULL){ + goto NEXT; + } + + if(dec->phase == 0){ + + p = curr + 4; + if(hdr.adaptation_field_control & 0x02){ + p += (p[0]+1); + } + size = 188 - (p-curr); + if(size < 1){ + goto NEXT; + } + + if(dec->ecm == NULL){ + /* this code will never execute */ + r = ARIB_STD_B25_ERROR_ECM_PARSE_FAILURE; + curr += unit; + goto LAST; + } + + n = dec->ecm->put(dec->ecm, &hdr, p, size); + if(n < 0){ + r = ARIB_STD_B25_ERROR_ECM_PARSE_FAILURE; + curr += unit; + goto LAST; + } + n = dec->ecm->get_count(dec->ecm); + if(n < 0){ + r = ARIB_STD_B25_ERROR_ECM_PARSE_FAILURE; + curr += unit; + goto LAST; + } + if(n == 0){ + goto NEXT; + } + + r = proc_ecm(dec, prv->bcas, prv->multi2_round); + if(r < 0){ + curr += unit; + goto LAST; + } + if( (r > 0) && (r != ARIB_STD_B25_WARN_UNPURCHASED_ECM) ){ + /* broken or unexpected section data */ + goto NEXT; + } + + dec->phase = 1; + if(check_ecm_complete(prv)){ + curr += unit; + goto LAST; + } + + }else{ + dec->phase = 2; + curr += unit; + goto LAST; + } + + NEXT: + curr += unit; + } + +LAST: + prv->sbuf_offset = curr - prv->sbuf.head; + + return r; +} + +static int proc_ecm(DECRYPTOR_ELEM *dec, B_CAS_CARD *bcas, int32_t multi2_round) +{ + int r,n; + int length; + + uint8_t *p; + + B_CAS_INIT_STATUS is; + B_CAS_ECM_RESULT res; + + TS_SECTION sect; + + r = 0; + memset(§, 0, sizeof(sect)); + + if(bcas == NULL){ + r = ARIB_STD_B25_ERROR_EMPTY_B_CAS_CARD; + goto LAST; + } + + n = dec->ecm->get(dec->ecm, §); + if(n < 0){ + r = ARIB_STD_B25_ERROR_ECM_PARSE_FAILURE; + goto LAST; + } + if(sect.hdr.table_id != TS_SECTION_ID_ECM_S){ + r = ARIB_STD_B25_WARN_TS_SECTION_ID_MISSMATCH; + goto LAST; + } + + if(dec->locked){ + /* previous ECM has returned unpurchased + skip this pid for B-CAS card load reduction */ + dec->unpurchased += 1; + r = ARIB_STD_B25_WARN_UNPURCHASED_ECM; + goto LAST; + } + + length = (sect.tail - sect.data) - 4; + p = sect.data; + + r = bcas->proc_ecm(bcas, &res, p, length); + if(r < 0){ + if(dec->m2 != NULL){ + dec->m2->clear_scramble_key(dec->m2); + } + r = ARIB_STD_B25_ERROR_ECM_PROC_FAILURE; + goto LAST; + } + + if( (res.return_code != 0x0800) && + (res.return_code != 0x0400) && + (res.return_code != 0x0200) ){ + /* return_code is not equal "purchased" */ + if(dec->m2 != NULL){ + dec->m2->release(dec->m2); + dec->m2 = NULL; + } + dec->unpurchased += 1; + dec->last_error = res.return_code; + dec->locked += 1; + r = ARIB_STD_B25_WARN_UNPURCHASED_ECM; + goto LAST; + } + + if(dec->m2 == NULL){ + dec->m2 = create_multi2(); + if(dec->m2 == NULL){ + return ARIB_STD_B25_ERROR_NO_ENOUGH_MEMORY; + } + r = bcas->get_init_status(bcas, &is); + if(r < 0){ + return ARIB_STD_B25_ERROR_INVALID_B_CAS_STATUS; + } + dec->m2->set_system_key(dec->m2, is.system_key); + dec->m2->set_init_cbc(dec->m2, is.init_cbc); + dec->m2->set_round(dec->m2, multi2_round); + } + + dec->m2->set_scramble_key(dec->m2, res.scramble_key); + + if (0) { + int i; + fprintf(stdout, "----\n"); + fprintf(stdout, "odd: "); + for(i=0;i<8;i++){ + fprintf(stdout, " %02x", res.scramble_key[i]); + } + fprintf(stdout, "\n"); + fprintf(stdout, "even:"); + for(i=8;i<16;i++){ + fprintf(stdout, " %02x", res.scramble_key[i]); + } + fprintf(stdout, "\n"); + fflush(stdout); + } + +LAST: + if(sect.raw != NULL){ + n = dec->ecm->ret(dec->ecm, §); + if( (n < 0) && (r == 0) ){ + r = ARIB_STD_B25_ERROR_ECM_PARSE_FAILURE; + } + } + + return r; +} + +static void dump_pts(uint8_t *src, int32_t crypt) +{ + int32_t pts_dts_flag; + int64_t pts,dts; + + src += 4; // TS ヘッダ部 + src += 4; // start_code_prefix + stream_id 部 + src += 2; // packet_length 部 + + pts_dts_flag = (src[1] >> 6) & 3; + + src += 3; + if(pts_dts_flag & 2){ + // PTS + pts = (src[0] >> 1) & 0x07; + pts <<= 15; + pts += ((src[1] << 8) + src[2]) >> 1; + pts <<= 15; + pts += ((src[3] << 8) + src[4]) >> 1; + src += 5; + } + if(pts_dts_flag & 1){ + // DTS + dts = (src[0] >> 1) & 0x07; + dts <<= 15; + dts += ((src[1] << 8) + src[2]) >> 1; + dts <<= 15; + dts += ((src[3] << 8) + src[4]) >> 1; + } + + if(pts_dts_flag == 2){ + fprintf(stdout, " key=%d, pts=%"PRId64"\n", crypt, pts/90); + fflush(stdout); + } +} + +static int proc_arib_std_b25(ARIB_STD_B25_PRIVATE_DATA *prv) +{ + int r; + int m,n; + + int32_t crypt; + int32_t unit; + int32_t pid; + + uint8_t *p; + uint8_t *curr; + uint8_t *tail; + + TS_HEADER hdr; + DECRYPTOR_ELEM *dec; + TS_PROGRAM *pgrm; + + unit = prv->unit_size; + curr = prv->sbuf.head; + tail = prv->sbuf.tail; + + m = prv->dbuf.tail - prv->dbuf.head; + n = tail - curr; + if(!reserve_work_buffer(&(prv->dbuf), m+n)){ + return ARIB_STD_B25_ERROR_NO_ENOUGH_MEMORY; + } + + r = 0; + + while( (curr+unit) < tail ){ + + if( (curr[0] != 0x47) || (curr[unit] != 0x47) ){ + p = resync(curr, tail, unit); + if(p == NULL){ + goto LAST; + } + curr = p; + } + + extract_ts_header(&hdr, curr); + crypt = hdr.transport_scrambling_control; + pid = hdr.pid; + + if(hdr.transport_error_indicator != 0){ + /* bit error - append output buffer without parsing */ + if(!append_work_buffer(&(prv->dbuf), curr, 188)){ + r = ARIB_STD_B25_ERROR_NO_ENOUGH_MEMORY; + goto LAST; + } + goto NEXT; + } + + if( (pid == 0x1fff) && (prv->strip) ){ + /* strip null(padding) stream */ + goto NEXT; + } + + p = curr+4; + if(hdr.adaptation_field_control & 0x02){ + p += (p[0]+1); + } + n = 188 - (p-curr); + if( (n < 1) && ((n < 0) || (hdr.adaptation_field_control & 0x01)) ){ + /* broken packet */ + curr += 1; + continue; + } + + if( (crypt != 0) && + (hdr.adaptation_field_control & 0x01) ){ + + if(prv->map[pid].type == PID_MAP_TYPE_OTHER){ + dec = (DECRYPTOR_ELEM *)(prv->map[pid].target); + }else if( (prv->map[pid].type == 0) && + (prv->decrypt.count == 1) ){ + dec = prv->decrypt.head; + }else{ + dec = NULL; + } + + if( (dec != NULL) && (dec->m2 != NULL) ){ + m = dec->m2->decrypt(dec->m2, crypt, p, n); + if(m < 0){ + r = ARIB_STD_B25_ERROR_DECRYPT_FAILURE; + goto LAST; + } + curr[3] &= 0x3f; + prv->map[pid].normal_packet += 1; + }else{ + prv->map[pid].undecrypted += 1; + } + }else{ + prv->map[pid].normal_packet += 1; + } +#if 0 + if( (hdr.payload_unit_start_indicator != 0) && (pid == 0x111) ){ + dump_pts(curr, crypt); + } +#endif + if(!append_work_buffer(&(prv->dbuf), curr, 188)){ + r = ARIB_STD_B25_ERROR_NO_ENOUGH_MEMORY; + goto LAST; + } + + if(prv->map[pid].type == PID_MAP_TYPE_ECM){ + dec = (DECRYPTOR_ELEM *)(prv->map[pid].target); + if( (dec == NULL) || (dec->ecm == NULL) ){ + /* this code will never execute */ + r = ARIB_STD_B25_ERROR_ECM_PARSE_FAILURE; + goto LAST; + } + m = dec->ecm->put(dec->ecm, &hdr, p, n); + if(m < 0){ + r = ARIB_STD_B25_ERROR_ECM_PARSE_FAILURE; + goto LAST; + } + m = dec->ecm->get_count(dec->ecm); + if(m < 0){ + r = ARIB_STD_B25_ERROR_ECM_PARSE_FAILURE; + goto LAST; + } + if(m == 0){ + goto NEXT; + } + r = proc_ecm(dec, prv->bcas, prv->multi2_round); + if(r < 0){ + goto LAST; + } + }else if(prv->map[pid].type == PID_MAP_TYPE_PMT){ + pgrm = (TS_PROGRAM *)(prv->map[pid].target); + if( (pgrm == NULL) || (pgrm->pmt == NULL) ){ + /* this code will never execute */ + r = ARIB_STD_B25_ERROR_PMT_PARSE_FAILURE; + goto LAST; + } + m = pgrm->pmt->put(pgrm->pmt, &hdr, p, n); + if(m < 0){ + r = ARIB_STD_B25_ERROR_PMT_PARSE_FAILURE; + goto LAST; + } + m = pgrm->pmt->get_count(pgrm->pmt); + if(m < 0){ + r = ARIB_STD_B25_ERROR_PMT_PARSE_FAILURE; + goto LAST; + } + if(m == 0){ + goto NEXT; + } + r = proc_pmt(prv, pgrm); + if(r < 0){ + goto LAST; + } + }else if(prv->map[pid].type == PID_MAP_TYPE_EMM){ + if( prv->emm_proc_on == 0){ + goto NEXT; + } + if( prv->emm == NULL ){ + prv->emm = create_ts_section_parser(); + if(prv->emm == NULL){ + r = ARIB_STD_B25_ERROR_EMM_PARSE_FAILURE; + goto LAST; + } + } + m = prv->emm->put(prv->emm, &hdr, p, n); + if(m < 0){ + r = ARIB_STD_B25_ERROR_EMM_PARSE_FAILURE; + goto LAST; + } + m = prv->emm->get_count(prv->emm); + if(m < 0){ + r = ARIB_STD_B25_ERROR_EMM_PARSE_FAILURE; + goto LAST; + } + if(m == 0){ + goto NEXT; + } + r = proc_emm(prv); + if(r < 0){ + goto LAST; + } + }else if(pid == 0x0001){ + if( prv->cat == NULL ){ + prv->cat = create_ts_section_parser(); + if(prv->cat == NULL){ + r = ARIB_STD_B25_ERROR_NO_ENOUGH_MEMORY; + goto LAST; + } + } + m = prv->cat->put(prv->cat, &hdr, p, n); + if(m < 0){ + r = ARIB_STD_B25_ERROR_CAT_PARSE_FAILURE; + goto LAST; + } + m = prv->cat->get_count(prv->cat); + if(m < 0){ + r = ARIB_STD_B25_ERROR_CAT_PARSE_FAILURE; + goto LAST; + } + if(m == 0){ + goto NEXT; + } + r = proc_cat(prv); + if(r < 0){ + goto LAST; + } + }else if(pid == 0x0000){ + if( prv->pat == NULL ){ + prv->pat = create_ts_section_parser(); + if(prv->pat == NULL){ + r = ARIB_STD_B25_ERROR_NO_ENOUGH_MEMORY; + goto LAST; + } + } + m = prv->pat->put(prv->pat, &hdr, p, n); + if(m < 0){ + r = ARIB_STD_B25_ERROR_PAT_PARSE_FAILURE; + goto LAST; + } + m = prv->pat->get_count(prv->pat); + if(m < 0){ + r = ARIB_STD_B25_ERROR_PAT_PARSE_FAILURE; + goto LAST; + } + if(m == 0){ + goto NEXT; + } + r = proc_pat(prv); + goto LAST; + } + + NEXT: + curr += unit; + } + +LAST: + m = curr - prv->sbuf.head; + n = tail - curr; + if( (n < 1024) || (m > (prv->sbuf.max/2) ) ){ + p = prv->sbuf.pool; + memcpy(p, curr, n); + prv->sbuf.head = p; + prv->sbuf.tail = p+n; + }else{ + prv->sbuf.head = curr; + } + + return r; +} + +static int proc_cat(ARIB_STD_B25_PRIVATE_DATA *prv) +{ + int r; + int n; + int emm_pid; + + TS_SECTION sect; + + r = 0; + memset(§, 0, sizeof(sect)); + + n = prv->cat->get(prv->cat, §); + if(n < 0){ + r = ARIB_STD_B25_ERROR_CAT_PARSE_FAILURE; + goto LAST; + } + + if(sect.hdr.table_id != TS_SECTION_ID_CONDITIONAL_ACCESS){ + r = ARIB_STD_B25_WARN_TS_SECTION_ID_MISSMATCH; + goto LAST; + } + + emm_pid = find_ca_descriptor_pid(sect.data, sect.tail-4, prv->ca_system_id); + if( (emm_pid != 0x0000) && (emm_pid != 0x1fff) ){ + if( (prv->map[emm_pid].target != NULL) && + (prv->map[emm_pid].type == PID_MAP_TYPE_OTHER) ){ + DECRYPTOR_ELEM *dec; + dec = (DECRYPTOR_ELEM *)(prv->map[emm_pid].target); + dec->ref -= 1; + if(dec->ref < 1){ + remove_decryptor(prv, dec); + } + } + prv->emm_pid = emm_pid; + prv->map[emm_pid].ref = 1; + prv->map[emm_pid].type = PID_MAP_TYPE_EMM; + prv->map[emm_pid].target = NULL; + } + + prv->map[0x0001].ref = 1; + prv->map[0x0001].type = PID_MAP_TYPE_CAT; + prv->map[0x0001].target = NULL; + +LAST: + + if(sect.raw != NULL){ + n = prv->cat->ret(prv->cat, §); + if( (n < 0) && (r == 0) ){ + r = ARIB_STD_B25_ERROR_CAT_PARSE_FAILURE; + } + } + + return r; +} + +static int proc_emm(ARIB_STD_B25_PRIVATE_DATA *prv) +{ + int r; + int j,n; + + int len; + + uint8_t *head; + uint8_t *tail; + + TS_SECTION sect; + EMM_FIXED_PART emm_hdr; + + r = 0; + memset(§, 0, sizeof(sect)); + + if(prv->bcas == NULL){ + r = ARIB_STD_B25_ERROR_EMPTY_B_CAS_CARD; + goto LAST; + } + + while( (n = prv->emm->get_count(prv->emm)) > 0 ){ + + n = prv->emm->get(prv->emm, §); + if(n < 0){ + r = ARIB_STD_B25_ERROR_CAT_PARSE_FAILURE; + goto LAST; + } + + if(sect.hdr.table_id == TS_SECTION_ID_EMM_MESSAGE){ + /* EMM_MESSAGE is not supported */ + goto NEXT; + }else if(sect.hdr.table_id != TS_SECTION_ID_EMM_S){ + r = ARIB_STD_B25_WARN_TS_SECTION_ID_MISSMATCH; + goto LAST; + } + + head = sect.data; + tail = sect.tail - 4; + + while( (head+13) <= tail ){ + + extract_emm_fixed_part(&emm_hdr, head); + len = emm_hdr.associated_information_length+7; + if( (head+len) > tail ){ + /* broken EMM element */ + goto NEXT; + } + + for(j=0;jcasid.count;j++){ + if(prv->casid.data[j] == emm_hdr.card_id){ + n = prv->bcas->proc_emm(prv->bcas, head, len); + if(n < 0){ + r = ARIB_STD_B25_ERROR_EMM_PROC_FAILURE; + goto LAST; + } + unlock_all_decryptor(prv); + } + } + + head += len; + } + + NEXT: + if(sect.raw != NULL){ + n = prv->emm->ret(prv->emm, §); + if( (n < 0) && (r == 0) ){ + r = ARIB_STD_B25_ERROR_EMM_PARSE_FAILURE; + goto LAST; + } + memset(§, 0, sizeof(sect)); + } + } + +LAST: + if(sect.raw != NULL){ + n = prv->emm->ret(prv->emm, §); + if( (n < 0) && (r == 0) ){ + r = ARIB_STD_B25_ERROR_EMM_PARSE_FAILURE; + } + } + + return r; +} + +static void release_program(ARIB_STD_B25_PRIVATE_DATA *prv, TS_PROGRAM *pgrm) +{ + int32_t pid; + TS_STREAM_ELEM *strm; + + pid = pgrm->pmt_pid; + + if(pgrm->pmt != NULL){ + pgrm->pmt->release(pgrm->pmt); + pgrm->pmt = NULL; + } + + while( (strm = get_stream_list_head(&(pgrm->old_strm))) != NULL ){ + unref_stream(prv, strm->pid); + memset(strm, 0, sizeof(TS_STREAM_ELEM)); + put_stream_list_tail(&(prv->strm_pool), strm); + } + + while( (strm = get_stream_list_head(&(pgrm->streams))) != NULL ){ + unref_stream(prv, strm->pid); + memset(strm, 0, sizeof(TS_STREAM_ELEM)); + put_stream_list_tail(&(prv->strm_pool), strm); + } + + prv->map[pid].type = PID_MAP_TYPE_UNKNOWN; + prv->map[pid].ref = 0; + prv->map[pid].target = NULL; +} + +static void unref_stream(ARIB_STD_B25_PRIVATE_DATA *prv, int32_t pid) +{ + DECRYPTOR_ELEM *dec; + + prv->map[pid].ref -= 1; + if( prv->map[pid].ref < 1 ){ + if( (prv->map[pid].target != NULL) && + (prv->map[pid].type == PID_MAP_TYPE_OTHER) ){ + dec = (DECRYPTOR_ELEM *)(prv->map[pid].target); + dec->ref -= 1; + if(dec->ref < 1){ + remove_decryptor(prv, dec); + } + } + prv->map[pid].type = PID_MAP_TYPE_UNKNOWN; + prv->map[pid].ref = 0; + prv->map[pid].target = NULL; + } +} + +static DECRYPTOR_ELEM *set_decryptor(ARIB_STD_B25_PRIVATE_DATA *prv, int32_t pid) +{ + DECRYPTOR_ELEM *r; + + r = NULL; + if(prv->map[pid].type == PID_MAP_TYPE_ECM){ + r = (DECRYPTOR_ELEM *)(prv->map[pid].target); + if(r != NULL){ + return r; + } + } + r = (DECRYPTOR_ELEM *)calloc(1, sizeof(DECRYPTOR_ELEM)); + if(r == NULL){ + return NULL; + } + r->ecm_pid = pid; + r->ecm = create_ts_section_parser(); + if(r->ecm == NULL){ + free(r); + return NULL; + } + + if(prv->decrypt.tail != NULL){ + r->prev = prv->decrypt.tail; + r->next = NULL; + prv->decrypt.tail->next = r; + prv->decrypt.tail = r; + prv->decrypt.count += 1; + }else{ + r->prev = NULL; + r->next = NULL; + prv->decrypt.head = r; + prv->decrypt.tail = r; + prv->decrypt.count = 1; + } + + if( (prv->map[pid].type == PID_MAP_TYPE_OTHER) && + (prv->map[pid].target != NULL) ){ + DECRYPTOR_ELEM *dec; + dec = (DECRYPTOR_ELEM *)(prv->map[pid].target); + dec->ref -= 1; + if(dec->ref < 1){ + remove_decryptor(prv, dec); + } + } + + prv->map[pid].type = PID_MAP_TYPE_ECM; + prv->map[pid].target = r; + + return r; +} + +static void remove_decryptor(ARIB_STD_B25_PRIVATE_DATA *prv, DECRYPTOR_ELEM *dec) +{ + int32_t pid; + + DECRYPTOR_ELEM *prev; + DECRYPTOR_ELEM *next; + + pid = dec->ecm_pid; + if( (prv->map[pid].type == PID_MAP_TYPE_ECM) && + (prv->map[pid].target == ((void *)dec)) ){ + prv->map[pid].type = PID_MAP_TYPE_UNKNOWN; + prv->map[pid].target = NULL; + } + + prev = (DECRYPTOR_ELEM *)(dec->prev); + next = (DECRYPTOR_ELEM *)(dec->next); + if(prev != NULL){ + prev->next = next; + }else{ + prv->decrypt.head = next; + } + if(next != NULL){ + next->prev = prev; + }else{ + prv->decrypt.tail = prev; + } + prv->decrypt.count -= 1; + + if(dec->ecm != NULL){ + dec->ecm->release(dec->ecm); + dec->ecm = NULL; + } + + if(dec->m2 != NULL){ + dec->m2->release(dec->m2); + dec->m2 = NULL; + } + + free(dec); +} + +static DECRYPTOR_ELEM *select_active_decryptor(DECRYPTOR_ELEM *a, DECRYPTOR_ELEM *b, int32_t pid) +{ + if( b != NULL ){ + return b; + } + if( pid == 0x1fff ){ + return NULL; + } + return a; +} + +static void bind_stream_decryptor(ARIB_STD_B25_PRIVATE_DATA *prv, int32_t pid, DECRYPTOR_ELEM *dec) +{ + DECRYPTOR_ELEM *old; + + old = (DECRYPTOR_ELEM *)(prv->map[pid].target); + if(old == dec){ + /* already binded - do nothing */ + return; + } + + if(old != NULL){ + old->ref -= 1; + if(old->ref == 0){ + remove_decryptor(prv, old); + } + prv->map[pid].target = NULL; + } + + if(dec != NULL){ + prv->map[pid].target = dec; + dec->ref += 1; + } +} + +static void unlock_all_decryptor(ARIB_STD_B25_PRIVATE_DATA *prv) +{ + DECRYPTOR_ELEM *e; + + e = prv->decrypt.head; + while(e != NULL){ + e->locked = 0; + e = (DECRYPTOR_ELEM *)(e->next); + } +} + +static TS_STREAM_ELEM *get_stream_list_head(TS_STREAM_LIST *list) +{ + TS_STREAM_ELEM *r; + + r = list->head; + if(r == NULL){ + return NULL; + } + + list->head = (TS_STREAM_ELEM *)(r->next); + if(list->head == NULL){ + list->tail = NULL; + list->count = 0; + }else{ + list->head->prev = NULL; + list->count -= 1; + } + + r->prev = NULL; + r->next = NULL; + + return r; +} + +static TS_STREAM_ELEM *find_stream_list_elem(TS_STREAM_LIST *list, int32_t pid) +{ + TS_STREAM_ELEM *r; + + r = list->head; + while(r != NULL){ + if(r->pid == pid){ + break; + } + r = (TS_STREAM_ELEM *)(r->next); + } + + return r; +} + +static TS_STREAM_ELEM *create_stream_elem(int32_t pid, int32_t type) +{ + TS_STREAM_ELEM *r; + + r = (TS_STREAM_ELEM *)calloc(1, sizeof(TS_STREAM_ELEM)); + if(r == NULL){ + return NULL; + } + + r->pid = pid; + r->type = type; + + return r; +} + +static void put_stream_list_tail(TS_STREAM_LIST *list, TS_STREAM_ELEM *elem) +{ + if(list->tail != NULL){ + elem->prev = list->tail; + elem->next = NULL; + list->tail->next = elem; + list->tail = elem; + list->count += 1; + }else{ + elem->prev = NULL; + elem->next = NULL; + list->head = elem; + list->tail = elem; + list->count = 1; + } +} + +static void clear_stream_list(TS_STREAM_LIST *list) +{ + TS_STREAM_ELEM *p,*n; + + p = list->head; + while(p != NULL){ + n = (TS_STREAM_ELEM *)(p->next); + free(p); + p = n; + } + + list->head = NULL; + list->tail = NULL; + list->count = 0; +} + +static int reserve_work_buffer(TS_WORK_BUFFER *buf, int32_t size) +{ + int m,n; + uint8_t *p; + + if(buf->max >= size){ + return 1; + } + + if(buf->max < 512){ + n = 512; + }else{ + n = buf->max * 2; + } + + while(n < size){ + n += n; + } + + p = (uint8_t *)malloc(n); + if(p == NULL){ + return 0; + } + + m = 0; + if(buf->pool != NULL){ + m = buf->tail - buf->head; + if(m > 0){ + memcpy(p, buf->head, m); + } + free(buf->pool); + buf->pool = NULL; + } + + buf->pool = p; + buf->head = p; + buf->tail = p+m; + buf->max = n; + + return 1; +} + +static int append_work_buffer(TS_WORK_BUFFER *buf, uint8_t *data, int32_t size) +{ + int m; + + if(size < 1){ + /* ignore - do nothing */ + return 1; + } + + m = buf->tail - buf->pool; + + if( (m+size) > buf->max ){ + if(!reserve_work_buffer(buf, m+size)){ + return 0; + } + } + + memcpy(buf->tail, data, size); + buf->tail += size; + + return 1; +} + +static void reset_work_buffer(TS_WORK_BUFFER *buf) +{ + buf->head = buf->pool; + buf->tail = buf->pool; +} + +static void release_work_buffer(TS_WORK_BUFFER *buf) +{ + if(buf->pool != NULL){ + free(buf->pool); + } + buf->pool = NULL; + buf->head = NULL; + buf->tail = NULL; + buf->max = 0; +} + +static void extract_ts_header(TS_HEADER *dst, uint8_t *src) +{ + dst->sync = src[0]; + dst->transport_error_indicator = (src[1] >> 7) & 0x01; + dst->payload_unit_start_indicator = (src[1] >> 6) & 0x01; + dst->transport_priority = (src[1] >> 5) & 0x01; + dst->pid = ((src[1] & 0x1f) << 8) | src[2]; + dst->transport_scrambling_control = (src[3] >> 6) & 0x03; + dst->adaptation_field_control = (src[3] >> 4) & 0x03; + dst->continuity_counter = src[3] & 0x0f; +} + +static void extract_emm_fixed_part(EMM_FIXED_PART *dst, uint8_t *src) +{ + int i; + + dst->card_id = 0; + for(i=0;i<6;i++){ + dst->card_id = (dst->card_id << 8) | src[i]; + } + + dst->associated_information_length = src[ 6]; + dst->protocol_number = src[ 7]; + dst->broadcaster_group_id = src[ 8]; + dst->update_number = (src[ 9]<<8)|src[10]; + dst->expiration_date = (src[11]<<8)|src[12]; +} + +static uint8_t *resync(uint8_t *head, uint8_t *tail, int32_t unit_size) +{ + int i; + unsigned char *buf; + + buf = head; + tail -= unit_size * 8; + while( buf <= tail ){ + if(buf[0] == 0x47){ + for(i=1;i<8;i++){ + if(buf[unit_size*i] != 0x47){ + break; + } + } + if(i == 8){ + return buf; + } + } + buf += 1; + } + + return NULL; +} + +static uint8_t *resync_force(uint8_t *head, uint8_t *tail, int32_t unit_size) +{ + int i,n; + unsigned char *buf; + + buf = head; + while( buf <= (tail-188) ){ + if(buf[0] == 0x47){ + n = (tail - buf) / unit_size; + if(n == 0){ + return buf; + } + for(i=1;i +#include + +#include + +#if defined(WIN32) + #include +#endif +#include + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + inner structures + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +typedef struct { + + SCARDCONTEXT mng; + SCARDHANDLE card; + + uint8_t *pool; + char *reader; + + uint8_t *sbuf; + uint8_t *rbuf; + + B_CAS_INIT_STATUS stat; + + B_CAS_ID id; + int32_t id_max; + + B_CAS_PWR_ON_CTRL_INFO pwc; + int32_t pwc_max; + +} B_CAS_CARD_PRIVATE_DATA; + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + constant values + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static const uint8_t INITIAL_SETTING_CONDITIONS_CMD[] = { + 0x90, 0x30, 0x00, 0x00, 0x00, +}; + +static const uint8_t CARD_ID_INFORMATION_ACQUIRE_CMD[] = { + 0x90, 0x32, 0x00, 0x00, 0x00, +}; + +static const uint8_t POWER_ON_CONTROL_INFORMATION_REQUEST_CMD[] = { + 0x90, 0x80, 0x00, 0x00, 0x01, 0x00, 0x00, +}; + +static const uint8_t ECM_RECEIVE_CMD_HEADER[] = { + 0x90, 0x34, 0x00, 0x00, +}; + +static const uint8_t EMM_RECEIVE_CMD_HEADER[] = { + 0x90, 0x36, 0x00, 0x00, +}; + +#define B_CAS_BUFFER_MAX (4*1024) + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + function prottypes (interface method) + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static void release_b_cas_card(void *bcas); +static int init_b_cas_card(void *bcas); +static int get_init_status_b_cas_card(void *bcas, B_CAS_INIT_STATUS *stat); +static int get_id_b_cas_card(void *bcas, B_CAS_ID *dst); +static int get_pwr_on_ctrl_b_cas_card(void *bcas, B_CAS_PWR_ON_CTRL_INFO *dst); +static int proc_ecm_b_cas_card(void *bcas, B_CAS_ECM_RESULT *dst, uint8_t *src, int len); +static int proc_emm_b_cas_card(void *bcas, uint8_t *src, int len); + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + global function implementation + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +B_CAS_CARD *create_b_cas_card() +{ + int n; + + B_CAS_CARD *r; + B_CAS_CARD_PRIVATE_DATA *prv; + + n = sizeof(B_CAS_CARD) + sizeof(B_CAS_CARD_PRIVATE_DATA); + prv = (B_CAS_CARD_PRIVATE_DATA *)calloc(1, n); + if(prv == NULL){ + return NULL; + } + + r = (B_CAS_CARD *)(prv+1); + + r->private_data = prv; + + r->release = release_b_cas_card; + r->init = init_b_cas_card; + r->get_init_status = get_init_status_b_cas_card; + r->get_id = get_id_b_cas_card; + r->get_pwr_on_ctrl = get_pwr_on_ctrl_b_cas_card; + r->proc_ecm = proc_ecm_b_cas_card; + r->proc_emm = proc_emm_b_cas_card; + + return r; +} + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + function prottypes (private method) + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static B_CAS_CARD_PRIVATE_DATA *private_data(void *bcas); +static void teardown(B_CAS_CARD_PRIVATE_DATA *prv); +static int change_id_max(B_CAS_CARD_PRIVATE_DATA *prv, int max); +static int change_pwc_max(B_CAS_CARD_PRIVATE_DATA *prv, int max); +static int connect_card(B_CAS_CARD_PRIVATE_DATA *prv, const char *reader_name); +static void extract_power_on_ctrl_response(B_CAS_PWR_ON_CTRL *dst, uint8_t *src); +static void extract_mjd(int *yy, int *mm, int *dd, int mjd); +static int setup_ecm_receive_command(uint8_t *dst, uint8_t *src, int len); +static int setup_emm_receive_command(uint8_t *dst, uint8_t *src, int len); +static int32_t load_be_uint16(uint8_t *p); +static int64_t load_be_uint48(uint8_t *p); + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + interface method implementation + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static void release_b_cas_card(void *bcas) +{ + B_CAS_CARD_PRIVATE_DATA *prv; + + prv = private_data(bcas); + if(prv == NULL){ + /* do nothing */ + return; + } + + teardown(prv); + free(prv); +} + +static int init_b_cas_card(void *bcas) +{ + int m; + LONG ret; + DWORD len; + + B_CAS_CARD_PRIVATE_DATA *prv; + + prv = private_data(bcas); + if(prv == NULL){ + return B_CAS_CARD_ERROR_INVALID_PARAMETER; + } + + teardown(prv); + + ret = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &(prv->mng)); + if(ret != SCARD_S_SUCCESS){ + return B_CAS_CARD_ERROR_NO_SMART_CARD_READER; + } + + ret = SCardListReaders(prv->mng, NULL, NULL, &len); + if(ret != SCARD_S_SUCCESS){ + return B_CAS_CARD_ERROR_NO_SMART_CARD_READER; + } + len += 256; + + m = len + (2*B_CAS_BUFFER_MAX) + (sizeof(int64_t)*16) + (sizeof(B_CAS_PWR_ON_CTRL)*16); + prv->pool = (uint8_t *)malloc(m); + if(prv->pool == NULL){ + return B_CAS_CARD_ERROR_NO_ENOUGH_MEMORY; + } + + prv->reader = (char *)(prv->pool); + prv->sbuf = prv->pool + len; + prv->rbuf = prv->sbuf + B_CAS_BUFFER_MAX; + prv->id.data = (int64_t *)(prv->rbuf + B_CAS_BUFFER_MAX); + prv->id_max = 16; + prv->pwc.data = (B_CAS_PWR_ON_CTRL *)(prv->id.data + prv->id_max); + prv->pwc_max = 16; + + ret = SCardListReaders(prv->mng, NULL, prv->reader, &len); + if(ret != SCARD_S_SUCCESS){ + return B_CAS_CARD_ERROR_NO_SMART_CARD_READER; + } + + while( prv->reader[0] != 0 ){ + if(connect_card(prv, prv->reader)){ + break; + } + prv->reader += (strlen(prv->reader) + 1); + } + + if(prv->card == 0){ + return B_CAS_CARD_ERROR_ALL_READERS_CONNECTION_FAILED; + } + + return 0; +} + +static int get_init_status_b_cas_card(void *bcas, B_CAS_INIT_STATUS *stat) +{ + B_CAS_CARD_PRIVATE_DATA *prv; + + prv = private_data(bcas); + if( (prv == NULL) || (stat == NULL) ){ + return B_CAS_CARD_ERROR_INVALID_PARAMETER; + } + + if(prv->card == 0){ + return B_CAS_CARD_ERROR_NOT_INITIALIZED; + } + + memcpy(stat, &(prv->stat), sizeof(B_CAS_INIT_STATUS)); + + return 0; +} + +static int get_id_b_cas_card(void *bcas, B_CAS_ID *dst) +{ + LONG ret; + + DWORD slen; + DWORD rlen; + + int i,num; + + uint8_t *p; + uint8_t *tail; + + B_CAS_CARD_PRIVATE_DATA *prv; + SCARD_IO_REQUEST sir; + + prv = private_data(bcas); + if( (prv == NULL) || (dst == NULL) ){ + return B_CAS_CARD_ERROR_INVALID_PARAMETER; + } + + if(prv->card == 0){ + return B_CAS_CARD_ERROR_NOT_INITIALIZED; + } + + slen = sizeof(CARD_ID_INFORMATION_ACQUIRE_CMD); + memcpy(prv->sbuf, CARD_ID_INFORMATION_ACQUIRE_CMD, slen); + memcpy(&sir, SCARD_PCI_T1, sizeof(sir)); + rlen = B_CAS_BUFFER_MAX; + + ret = SCardTransmit(prv->card, SCARD_PCI_T1, prv->sbuf, slen, &sir, prv->rbuf, &rlen); + if( (ret != SCARD_S_SUCCESS) || (rlen < 19) ){ + return B_CAS_CARD_ERROR_TRANSMIT_FAILED; + } + + p = prv->rbuf + 6; + tail = prv->rbuf + rlen; + if( p+1 > tail ){ + return B_CAS_CARD_ERROR_TRANSMIT_FAILED; + } + + num = p[0]; + if(num > prv->id_max){ + if(change_id_max(prv, num+4) < 0){ + return B_CAS_CARD_ERROR_NO_ENOUGH_MEMORY; + } + } + + p += 1; + for(i=0;i tail ){ + return B_CAS_CARD_ERROR_TRANSMIT_FAILED; + } +#if 0 + { + int maker_id; + int version; + int check_code; + + maker_id = p[0]; + version = p[1]; + prv->id.data[i] = load_be_uint48(p+2); + check_code = load_be_uint16(p+8); + } +#endif + p += 10; + } + + prv->id.count = num; + + memcpy(dst, &(prv->id), sizeof(B_CAS_ID)); + + return 0; +} + +static int get_pwr_on_ctrl_b_cas_card(void *bcas, B_CAS_PWR_ON_CTRL_INFO *dst) +{ + LONG ret; + + DWORD slen; + DWORD rlen; + + int i,num,code; + + B_CAS_CARD_PRIVATE_DATA *prv; + SCARD_IO_REQUEST sir; + + memset(dst, 0, sizeof(B_CAS_PWR_ON_CTRL_INFO)); + + prv = private_data(bcas); + if( (prv == NULL) || (dst == NULL) ){ + return B_CAS_CARD_ERROR_INVALID_PARAMETER; + } + + if(prv->card == 0){ + return B_CAS_CARD_ERROR_NOT_INITIALIZED; + } + + slen = sizeof(POWER_ON_CONTROL_INFORMATION_REQUEST_CMD); + memcpy(prv->sbuf, POWER_ON_CONTROL_INFORMATION_REQUEST_CMD, slen); + prv->sbuf[5] = 0; + memcpy(&sir, SCARD_PCI_T1, sizeof(sir)); + rlen = B_CAS_BUFFER_MAX; + + ret = SCardTransmit(prv->card, SCARD_PCI_T1, prv->sbuf, slen, &sir, prv->rbuf, &rlen); + if( (ret != SCARD_S_SUCCESS) || (rlen < 18) || (prv->rbuf[6] != 0) ){ + return B_CAS_CARD_ERROR_TRANSMIT_FAILED; + } + + code = load_be_uint16(prv->rbuf+4); + if(code == 0xa101){ + /* no data */ + return 0; + }else if(code != 0x2100){ + return B_CAS_CARD_ERROR_TRANSMIT_FAILED; + } + + num = (prv->rbuf[7] + 1); + if(prv->pwc_max < num){ + if(change_pwc_max(prv, num+4) < 0){ + return B_CAS_CARD_ERROR_NO_ENOUGH_MEMORY; + } + } + + extract_power_on_ctrl_response(prv->pwc.data+0, prv->rbuf); + + for(i=1;isbuf[5] = i; + rlen = B_CAS_BUFFER_MAX; + + ret = SCardTransmit(prv->card, SCARD_PCI_T1, prv->sbuf, slen, &sir, prv->rbuf, &rlen); + if( (ret != SCARD_S_SUCCESS) || (rlen < 18) || (prv->rbuf[6] != i) ){ + return B_CAS_CARD_ERROR_TRANSMIT_FAILED; + } + + extract_power_on_ctrl_response(prv->pwc.data+i, prv->rbuf); + } + + prv->pwc.count = num; + + memcpy(dst, &(prv->pwc), sizeof(B_CAS_PWR_ON_CTRL_INFO)); + + return 0; +} + +static int proc_ecm_b_cas_card(void *bcas, B_CAS_ECM_RESULT *dst, uint8_t *src, int len) +{ + int retry_count; + + LONG ret; + DWORD slen; + DWORD rlen; + + B_CAS_CARD_PRIVATE_DATA *prv; + + SCARD_IO_REQUEST sir; + + prv = private_data(bcas); + if( (prv == NULL) || + (dst == NULL) || + (src == NULL) || + (len < 1) ){ + return B_CAS_CARD_ERROR_INVALID_PARAMETER; + } + + if(prv->card == 0){ + return B_CAS_CARD_ERROR_NOT_INITIALIZED; + } + + slen = setup_ecm_receive_command(prv->sbuf, src, len); + memcpy(&sir, SCARD_PCI_T1, sizeof(sir)); + rlen = B_CAS_BUFFER_MAX; + + retry_count = 0; + ret = SCardTransmit(prv->card, SCARD_PCI_T1, prv->sbuf, slen, &sir, prv->rbuf, &rlen); + while( ((ret != SCARD_S_SUCCESS) || (rlen < 25)) && (retry_count < 10) ){ + retry_count += 1; + if(!connect_card(prv, prv->reader)){ + continue; + } + slen = setup_ecm_receive_command(prv->sbuf, src, len); + memcpy(&sir, SCARD_PCI_T1, sizeof(sir)); + rlen = B_CAS_BUFFER_MAX; + + ret = SCardTransmit(prv->card, SCARD_PCI_T1, prv->sbuf, slen, &sir, prv->rbuf, &rlen); + } + + if( (ret != SCARD_S_SUCCESS) || (rlen < 25) ){ + return B_CAS_CARD_ERROR_TRANSMIT_FAILED; + } + + memcpy(dst->scramble_key, prv->rbuf+6, 16); + dst->return_code = load_be_uint16(prv->rbuf+4); + + return 0; +} + +static int proc_emm_b_cas_card(void *bcas, uint8_t *src, int len) +{ + int retry_count; + + LONG ret; + DWORD slen; + DWORD rlen; + + B_CAS_CARD_PRIVATE_DATA *prv; + + SCARD_IO_REQUEST sir; + + prv = private_data(bcas); + if( (prv == NULL) || + (src == NULL) || + (len < 1) ){ + return B_CAS_CARD_ERROR_INVALID_PARAMETER; + } + + if(prv->card == 0){ + return B_CAS_CARD_ERROR_NOT_INITIALIZED; + } + + slen = setup_emm_receive_command(prv->sbuf, src, len); + memcpy(&sir, SCARD_PCI_T1, sizeof(sir)); + rlen = B_CAS_BUFFER_MAX; + + retry_count = 0; + ret = SCardTransmit(prv->card, SCARD_PCI_T1, prv->sbuf, slen, &sir, prv->rbuf, &rlen); + while( ((ret != SCARD_S_SUCCESS) || (rlen < 6)) && (retry_count < 2) ){ + retry_count += 1; + if(!connect_card(prv, prv->reader)){ + continue; + } + slen = setup_emm_receive_command(prv->sbuf, src, len); + memcpy(&sir, SCARD_PCI_T1, sizeof(sir)); + rlen = B_CAS_BUFFER_MAX; + + ret = SCardTransmit(prv->card, SCARD_PCI_T1, prv->sbuf, slen, &sir, prv->rbuf, &rlen); + } + + if( (ret != SCARD_S_SUCCESS) || (rlen < 6) ){ + return B_CAS_CARD_ERROR_TRANSMIT_FAILED; + } + + return 0; +} + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + private method implementation + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static B_CAS_CARD_PRIVATE_DATA *private_data(void *bcas) +{ + B_CAS_CARD_PRIVATE_DATA *r; + B_CAS_CARD *p; + + p = (B_CAS_CARD *)bcas; + if(p == NULL){ + return NULL; + } + + r = (B_CAS_CARD_PRIVATE_DATA *)(p->private_data); + if( ((void *)(r+1)) != ((void *)p) ){ + return NULL; + } + + return r; +} + +static void teardown(B_CAS_CARD_PRIVATE_DATA *prv) +{ + if(prv->card != 0){ + SCardDisconnect(prv->card, SCARD_LEAVE_CARD); + prv->card = 0; + } + + if(prv->mng != 0){ + SCardReleaseContext(prv->mng); + prv->mng = 0; + } + + if(prv->pool != NULL){ + free(prv->pool); + prv->pool = NULL; + } + + prv->reader = NULL; + prv->sbuf = NULL; + prv->rbuf = NULL; + prv->id.data = NULL; + prv->id_max = 0; +} + +static int change_id_max(B_CAS_CARD_PRIVATE_DATA *prv, int max) +{ + int m; + int reader_size; + int pwctrl_size; + + uint8_t *p; + uint8_t *old_reader; + uint8_t *old_pwctrl; + + reader_size = prv->sbuf - prv->pool; + pwctrl_size = prv->pwc.count * sizeof(B_CAS_PWR_ON_CTRL); + + m = reader_size; + m += (2*B_CAS_BUFFER_MAX); + m += (max*sizeof(int64_t)); + m += (prv->pwc_max*sizeof(B_CAS_PWR_ON_CTRL)); + p = (uint8_t *)malloc(m); + if(p == NULL){ + return B_CAS_CARD_ERROR_NO_ENOUGH_MEMORY; + } + + old_reader = (uint8_t *)(prv->reader); + old_pwctrl = (uint8_t *)(prv->pwc.data); + + prv->reader = (char *)p; + prv->sbuf = prv->pool + reader_size; + prv->rbuf = prv->sbuf + B_CAS_BUFFER_MAX; + prv->id.data = (int64_t *)(prv->rbuf + B_CAS_BUFFER_MAX); + prv->id_max = max; + prv->pwc.data = (B_CAS_PWR_ON_CTRL *)(prv->id.data + prv->id_max); + + memcpy(prv->reader, old_reader, reader_size); + memcpy(prv->pwc.data, old_pwctrl, pwctrl_size); + + free(prv->pool); + prv->pool = p; + + return 0; +} + +static int change_pwc_max(B_CAS_CARD_PRIVATE_DATA *prv, int max) +{ + int m; + int reader_size; + int cardid_size; + + uint8_t *p; + uint8_t *old_reader; + uint8_t *old_cardid; + + reader_size = prv->sbuf - prv->pool; + cardid_size = prv->id.count * sizeof(int64_t); + + m = reader_size; + m += (2*B_CAS_BUFFER_MAX); + m += (prv->id_max*sizeof(int64_t)); + m += (max*sizeof(B_CAS_PWR_ON_CTRL)); + p = (uint8_t *)malloc(m); + if(p == NULL){ + return B_CAS_CARD_ERROR_NO_ENOUGH_MEMORY; + } + + old_reader = (uint8_t *)(prv->reader); + old_cardid = (uint8_t *)(prv->id.data); + + prv->reader = (char *)p; + prv->sbuf = prv->pool + reader_size; + prv->rbuf = prv->sbuf + B_CAS_BUFFER_MAX; + prv->id.data = (int64_t *)(prv->rbuf + B_CAS_BUFFER_MAX); + prv->pwc.data = (B_CAS_PWR_ON_CTRL *)(prv->id.data + prv->id_max); + prv->pwc_max = max; + + memcpy(prv->reader, old_reader, reader_size); + memcpy(prv->id.data, old_cardid, cardid_size); + + free(prv->pool); + prv->pool = p; + + return 0; +} + +static int connect_card(B_CAS_CARD_PRIVATE_DATA *prv, const char *reader_name) +{ + int m,n; + + LONG ret; + DWORD rlen,protocol; + + uint8_t *p; + + SCARD_IO_REQUEST sir; + + if(prv->card != 0){ + SCardDisconnect(prv->card, SCARD_RESET_CARD); + prv->card = 0; + } + + ret = SCardConnect(prv->mng, reader_name, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T1, &(prv->card), &protocol); + if(ret != SCARD_S_SUCCESS){ + return 0; + } + + m = sizeof(INITIAL_SETTING_CONDITIONS_CMD); + memcpy(prv->sbuf, INITIAL_SETTING_CONDITIONS_CMD, m); + memcpy(&sir, SCARD_PCI_T1, sizeof(sir)); + rlen = B_CAS_BUFFER_MAX; + ret = SCardTransmit(prv->card, SCARD_PCI_T1, prv->sbuf, m, &sir, prv->rbuf, &rlen); + if(ret != SCARD_S_SUCCESS){ + return 0; + } + + if(rlen < 57){ + return 0; + } + + p = prv->rbuf; + + n = load_be_uint16(p+4); + if(n != 0x2100){ // return code missmatch + return 0; + } + + memcpy(prv->stat.system_key, p+16, 32); + memcpy(prv->stat.init_cbc, p+48, 8); + prv->stat.bcas_card_id = load_be_uint48(p+8); + prv->stat.card_status = load_be_uint16(p+2); + prv->stat.ca_system_id = load_be_uint16(p+6); + + return 1; +} + +static void extract_power_on_ctrl_response(B_CAS_PWR_ON_CTRL *dst, uint8_t *src) +{ + int referrence; + int start; + int limit; + + + dst->broadcaster_group_id = src[8]; + referrence = (src[9]<<8)|src[10]; + start = referrence - src[11]; + limit = start + (src[12]-1); + + extract_mjd(&(dst->s_yy), &(dst->s_mm), &(dst->s_dd), start); + extract_mjd(&(dst->l_yy), &(dst->l_mm), &(dst->l_dd), limit); + + dst->hold_time = src[13]; + dst->network_id = (src[14]<<8)|src[15]; + dst->transport_id = (src[16]<<8)|src[17]; + +} + +static void extract_mjd(int *yy, int *mm, int *dd, int mjd) +{ + int a1,m1; + int a2,m2; + int a3,m3; + int a4,m4; + int mw; + int dw; + int yw; + + mjd -= 51604; // 2000,3/1 + if(mjd < 0){ + mjd += 0x10000; + } + + a1 = mjd / 146097; + m1 = mjd % 146097; + a2 = m1 / 36524; + m2 = m1 - (a2 * 36524); + a3 = m2 / 1461; + m3 = m2 - (a3 * 1461); + a4 = m3 / 365; + if(a4 > 3){ + a4 = 3; + } + m4 = m3 - (a4 * 365); + + mw = (1071*m4+450) >> 15; + dw = m4 - ((979*mw+16) >> 5); + + yw = a1*400 + a2*100 + a3*4 + a4 + 2000; + mw += 3; + if(mw > 12){ + mw -= 12; + yw += 1; + } + dw += 1; + + *yy = yw; + *mm = mw; + *dd = dw; +} + +static int setup_ecm_receive_command(uint8_t *dst, uint8_t *src, int len) +{ + int r; + + r = sizeof(ECM_RECEIVE_CMD_HEADER); + memcpy(dst+0, ECM_RECEIVE_CMD_HEADER, r); + dst[r] = (uint8_t)(len & 0xff); + r += 1; + memcpy(dst+r, src, len); + r += len; + dst[r] = 0; + r += 1; + + return r; +} + +static int setup_emm_receive_command(uint8_t *dst, uint8_t *src, int len) +{ + int r; + + r = sizeof(EMM_RECEIVE_CMD_HEADER); + memcpy(dst+0, EMM_RECEIVE_CMD_HEADER, r); + dst[r] = (uint8_t)(len & 0xff); + r += 1; + memcpy(dst+r, src, len); + r += len; + dst[r] = 0; + r += 1; + + return r; +} + +static int32_t load_be_uint16(uint8_t *p) +{ + return ((p[0]<<8)|p[1]); +} + +static int64_t load_be_uint48(uint8_t *p) +{ + int i; + int64_t r; + + r = p[0]; + for(i=1;i<6;i++){ + r <<= 8; + r |= p[i]; + } + + return r; +} + diff --git a/src/b_cas_card.h b/src/b_cas_card.h new file mode 100644 index 0000000..5c65c72 --- /dev/null +++ b/src/b_cas_card.h @@ -0,0 +1,75 @@ +#ifndef B_CAS_CARD_H +#define B_CAS_CARD_H + +#include "portable.h" + +typedef struct { + uint8_t system_key[32]; + uint8_t init_cbc[8]; + int64_t bcas_card_id; + int32_t card_status; + int32_t ca_system_id; +} B_CAS_INIT_STATUS; + +typedef struct { + int64_t *data; + int32_t count; +} B_CAS_ID; + +typedef struct { + + int32_t s_yy; /* start date : year */ + int32_t s_mm; /* start date : month */ + int32_t s_dd; /* start date : day */ + + int32_t l_yy; /* limit date : year */ + int32_t l_mm; /* limit date : month */ + int32_t l_dd; /* limit date : day */ + + int32_t hold_time; /* in hour unit */ + + int32_t broadcaster_group_id; + + int32_t network_id; + int32_t transport_id; + +} B_CAS_PWR_ON_CTRL; + +typedef struct { + B_CAS_PWR_ON_CTRL *data; + int32_t count; +} B_CAS_PWR_ON_CTRL_INFO; + +typedef struct { + uint8_t scramble_key[16]; + uint32_t return_code; +} B_CAS_ECM_RESULT; + +typedef struct { + + void *private_data; + + void (* release)(void *bcas); + + int (* init)(void *bcas); + + int (* get_init_status)(void *bcas, B_CAS_INIT_STATUS *stat); + int (* get_id)(void *bcas, B_CAS_ID *dst); + int (* get_pwr_on_ctrl)(void *bcas, B_CAS_PWR_ON_CTRL_INFO *dst); + + int (* proc_ecm)(void *bcas, B_CAS_ECM_RESULT *dst, uint8_t *src, int len); + int (* proc_emm)(void *bcas, uint8_t *src, int len); + +} B_CAS_CARD; + +#ifdef __cplusplus +extern "C" { +#endif + +extern B_CAS_CARD *create_b_cas_card(); + +#ifdef __cplusplus +} +#endif + +#endif /* B_CAS_CARD_H */ diff --git a/src/b_cas_card_error_code.h b/src/b_cas_card_error_code.h new file mode 100644 index 0000000..116085a --- /dev/null +++ b/src/b_cas_card_error_code.h @@ -0,0 +1,11 @@ +#ifndef B_CAS_CARD_ERROR_CODE_H +#define B_CAS_CARD_ERROR_CODE_H + +#define B_CAS_CARD_ERROR_INVALID_PARAMETER -1 +#define B_CAS_CARD_ERROR_NOT_INITIALIZED -2 +#define B_CAS_CARD_ERROR_NO_SMART_CARD_READER -3 +#define B_CAS_CARD_ERROR_ALL_READERS_CONNECTION_FAILED -4 +#define B_CAS_CARD_ERROR_NO_ENOUGH_MEMORY -5 +#define B_CAS_CARD_ERROR_TRANSMIT_FAILED -6 + +#endif /* B_CAS_CARD_ERROR_CODE_H */ diff --git a/src/makefile.win b/src/makefile.win new file mode 100644 index 0000000..e5d83a3 --- /dev/null +++ b/src/makefile.win @@ -0,0 +1,33 @@ +CC = cl +CFLAG = /c /MT /W4 /O2 +LINK = link +LFLAG = /nologo +LIBS = winscard.lib + +ALL: b25.exe + +arib_std_b25.obj: arib_std_b25.c arib_std_b25.h portable.h b_cas_card.h arib_std_b25_error_code.h multi2.h ts_section_parser.h ts_common_types.h + $(CC) $(CFLAG) arib_std_b25.c + +b_cas_card.obj: b_cas_card.c b_cas_card.h portable.h b_cas_card_error_code.h + $(CC) $(CFLAG) b_cas_card.c + +multi2.obj: multi2.c multi2.h portable.h multi2_error_code.h + $(CC) $(CFLAG) multi2.c + +td.obj: td.c arib_std_b25.h portable.h b_cas_card.h + $(CC) $(CFLAG) td.c + +ts_section_parser.obj: ts_section_parser.c ts_section_parser.h ts_common_types.h portable.h ts_section_parser_error_code.h + $(CC) $(CFLAG) ts_section_parser.c + +OBJ = arib_std_b25.obj b_cas_card.obj multi2.obj td.obj ts_section_parser.obj + +b25.exe: $(OBJ) + $(LINK) $(LFLAG) $(LIBS) /OUT:b25.exe $(OBJ) + +clean: + DEL *.obj + DEL *.exe + + diff --git a/src/multi2.c b/src/multi2.c new file mode 100644 index 0000000..dcff361 --- /dev/null +++ b/src/multi2.c @@ -0,0 +1,527 @@ +#include +#include + +#include "multi2.h" +#include "multi2_error_code.h" + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + inline functions + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static __inline uint8_t *load_be_uint32(uint32_t *dst, uint8_t *src) +{ + *dst = ((src[0]<<24)|(src[1]<<16)|(src[2]<<8)|src[3]); + return src+4; +} + +static __inline uint8_t *save_be_uint32(uint8_t *dst, uint32_t src) +{ + dst[0] = (uint8_t)((src>>24) & 0xff); + dst[1] = (uint8_t)((src>>16) & 0xff); + dst[2] = (uint8_t)((src>> 8) & 0xff); + dst[3] = (uint8_t)( src & 0xff); + return dst+4; +} + +static __inline uint32_t left_rotate_uint32(uint32_t val, uint32_t count) +{ + return ((val << count) | (val >> (32-count))); +} + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + inner structures + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +typedef struct { + uint32_t key[8]; +} CORE_PARAM; + +typedef struct { + uint32_t l; + uint32_t r; +} CORE_DATA; + +typedef struct { + + int32_t ref_count; + + CORE_DATA cbc_init; + + CORE_PARAM sys; + CORE_DATA scr[2]; /* 0: odd, 1: even */ + CORE_PARAM wrk[2]; /* 0: odd, 1: even */ + + uint32_t round; + uint32_t state; + +} MULTI2_PRIVATE_DATA; + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + constant values + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define MULTI2_STATE_CBC_INIT_SET (0x0001) +#define MULTI2_STATE_SYSTEM_KEY_SET (0x0002) +#define MULTI2_STATE_SCRAMBLE_KEY_SET (0x0004) + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + function prottypes (interface method) + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static void release_multi2(void *m2); +static int add_ref_multi2(void *m2); +static int set_round_multi2(void *m2, int32_t val); +static int set_system_key_multi2(void *m2, uint8_t *val); +static int set_init_cbc_multi2(void *m2, uint8_t *val); +static int set_scramble_key_multi2(void *m2, uint8_t *val); +static int clear_scramble_key_multi2(void *m2); +static int encrypt_multi2(void *m2, int32_t type, uint8_t *buf, int32_t size); +static int decrypt_multi2(void *m2, int32_t type, uint8_t *buf, int32_t size); + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + global function implementation + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +MULTI2 *create_multi2() +{ + int n; + + MULTI2 *r; + MULTI2_PRIVATE_DATA *prv; + + n = sizeof(MULTI2_PRIVATE_DATA); + n += sizeof(MULTI2); + + prv = (MULTI2_PRIVATE_DATA *)calloc(1, n); + if(prv == NULL){ + return NULL; + } + + r = (MULTI2 *)(prv+1); + r->private_data = prv; + + prv->ref_count = 1; + prv->round = 4; + + r->release = release_multi2; + r->add_ref = add_ref_multi2; + r->set_round = set_round_multi2; + r->set_system_key = set_system_key_multi2; + r->set_init_cbc = set_init_cbc_multi2; + r->set_scramble_key = set_scramble_key_multi2; + r->clear_scramble_key = clear_scramble_key_multi2; + r->encrypt = encrypt_multi2; + r->decrypt = decrypt_multi2; + + return r; +} + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + function prottypes (private method) + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static MULTI2_PRIVATE_DATA *private_data(void *m2); + +static void core_schedule(CORE_PARAM *work, CORE_PARAM *skey, CORE_DATA *dkey); + +static void core_encrypt(CORE_DATA *dst, CORE_DATA *src, CORE_PARAM *w, int32_t round); +static void core_decrypt(CORE_DATA *dst, CORE_DATA *src, CORE_PARAM *w, int32_t round); + +static void core_pi1(CORE_DATA *dst, CORE_DATA *src); +static void core_pi2(CORE_DATA *dst, CORE_DATA *src, uint32_t a); +static void core_pi3(CORE_DATA *dst, CORE_DATA *src, uint32_t a, uint32_t b); +static void core_pi4(CORE_DATA *dst, CORE_DATA *src, uint32_t a); + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + interface method implementation + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static void release_multi2(void *m2) +{ + MULTI2_PRIVATE_DATA *prv; + + prv = private_data(m2); + if(prv == NULL){ + /* do nothing */ + return; + } + + prv->ref_count -= 1; + if(prv->ref_count == 0){ + free(prv); + } +} + +static int add_ref_multi2(void *m2) +{ + MULTI2_PRIVATE_DATA *prv; + + prv = private_data(m2); + if(prv == NULL){ + return MULTI2_ERROR_INVALID_PARAMETER; + } + + prv->ref_count += 1; + + return 0; +} + +static int set_round_multi2(void *m2, int32_t val) +{ + MULTI2_PRIVATE_DATA *prv; + + prv = private_data(m2); + if(prv == NULL){ + /* do nothing */ + return MULTI2_ERROR_INVALID_PARAMETER; + } + + prv->round = val; + + return 0; +} + +static int set_system_key_multi2(void *m2, uint8_t *val) +{ + int i; + uint8_t *p; + + MULTI2_PRIVATE_DATA *prv; + + prv = private_data(m2); + if( (prv == NULL) || (val == NULL) ){ + return MULTI2_ERROR_INVALID_PARAMETER; + } + + p = val; + for(i=0;i<8;i++){ + p = load_be_uint32(prv->sys.key+i, p); + } + + prv->state |= MULTI2_STATE_SYSTEM_KEY_SET; + + return 0; +} + +static int set_init_cbc_multi2(void *m2, uint8_t *val) +{ + uint8_t *p; + + MULTI2_PRIVATE_DATA *prv; + + prv = private_data(m2); + if( (prv == NULL) || (val == NULL) ){ + return MULTI2_ERROR_INVALID_PARAMETER; + } + + p = val; + + p = load_be_uint32(&(prv->cbc_init.l), p); + p = load_be_uint32(&(prv->cbc_init.r), p); + + prv->state |= MULTI2_STATE_CBC_INIT_SET; + + return 0; +} + +static int set_scramble_key_multi2(void *m2, uint8_t *val) +{ + uint8_t *p; + + MULTI2_PRIVATE_DATA *prv; + + prv = private_data(m2); + if( (prv == NULL) || (val == NULL) ){ + return MULTI2_ERROR_INVALID_PARAMETER; + } + + p = val; + + p = load_be_uint32(&(prv->scr[0].l), p); + p = load_be_uint32(&(prv->scr[0].r), p); + p = load_be_uint32(&(prv->scr[1].l), p); + p = load_be_uint32(&(prv->scr[1].r), p); + + core_schedule(prv->wrk+0, &(prv->sys), prv->scr+0); + core_schedule(prv->wrk+1, &(prv->sys), prv->scr+1); + + prv->state |= MULTI2_STATE_SCRAMBLE_KEY_SET; + + return 0; +} + +static int clear_scramble_key_multi2(void *m2) +{ + MULTI2_PRIVATE_DATA *prv; + + prv = private_data(m2); + if(prv == NULL){ + return MULTI2_ERROR_INVALID_PARAMETER; + } + + memset(prv->scr, 0, sizeof(prv->scr)); + memset(prv->wrk, 0, sizeof(prv->wrk)); + + prv->state &= (~MULTI2_STATE_SCRAMBLE_KEY_SET); + + return 0; +} + +static int encrypt_multi2(void *m2, int32_t type, uint8_t *buf, int32_t size) +{ + CORE_DATA src,dst; + CORE_PARAM *prm; + + uint8_t *p; + + MULTI2_PRIVATE_DATA *prv; + + prv = private_data(m2); + if( (prv == NULL) || (buf == NULL) || (size < 1) ){ + return MULTI2_ERROR_INVALID_PARAMETER; + } + + if(prv->state != (MULTI2_STATE_CBC_INIT_SET|MULTI2_STATE_SYSTEM_KEY_SET|MULTI2_STATE_SCRAMBLE_KEY_SET)){ + if( (prv->state & MULTI2_STATE_CBC_INIT_SET) == 0 ){ + return MULTI2_ERROR_UNSET_CBC_INIT; + } + if( (prv->state & MULTI2_STATE_SYSTEM_KEY_SET) == 0 ){ + return MULTI2_ERROR_UNSET_SYSTEM_KEY; + } + if( (prv->state & MULTI2_STATE_SCRAMBLE_KEY_SET) == 0 ){ + return MULTI2_ERROR_UNSET_SCRAMBLE_KEY; + } + } + + if(type == 0x02){ + prm = prv->wrk+1; + }else{ + prm = prv->wrk+0; + } + + dst.l = prv->cbc_init.l; + dst.r = prv->cbc_init.r; + + p = buf; + while(size >= 8){ + load_be_uint32(&(src.l), p+0); + load_be_uint32(&(src.r), p+4); + src.l = src.l ^ dst.l; + src.r = src.r ^ dst.r; + core_encrypt(&dst, &src, prm, prv->round); + p = save_be_uint32(p, dst.l); + p = save_be_uint32(p, dst.r); + size -= 8; + } + + if(size > 0){ + int i; + uint8_t tmp[8]; + + src.l = dst.l; + src.r = dst.r; + core_encrypt(&dst, &src, prm, prv->round); + save_be_uint32(tmp+0, dst.l); + save_be_uint32(tmp+4, dst.r); + + for(i=0;istate != (MULTI2_STATE_CBC_INIT_SET|MULTI2_STATE_SYSTEM_KEY_SET|MULTI2_STATE_SCRAMBLE_KEY_SET)){ + if( (prv->state & MULTI2_STATE_CBC_INIT_SET) == 0 ){ + return MULTI2_ERROR_UNSET_CBC_INIT; + } + if( (prv->state & MULTI2_STATE_SYSTEM_KEY_SET) == 0 ){ + return MULTI2_ERROR_UNSET_SYSTEM_KEY; + } + if( (prv->state & MULTI2_STATE_SCRAMBLE_KEY_SET) == 0 ){ + return MULTI2_ERROR_UNSET_SCRAMBLE_KEY; + } + } + + if(type == 0x02){ + prm = prv->wrk+1; + }else{ + prm = prv->wrk+0; + } + + cbc.l = prv->cbc_init.l; + cbc.r = prv->cbc_init.r; + + p = buf; + while(size >= 8){ + load_be_uint32(&(src.l), p+0); + load_be_uint32(&(src.r), p+4); + core_decrypt(&dst, &src, prm, prv->round); + dst.l = dst.l ^ cbc.l; + dst.r = dst.r ^ cbc.r; + cbc.l = src.l; + cbc.r = src.r; + p = save_be_uint32(p, dst.l); + p = save_be_uint32(p, dst.r); + size -= 8; + } + + if(size > 0){ + int i; + uint8_t tmp[8]; + + core_encrypt(&dst, &cbc, prm, prv->round); + save_be_uint32(tmp+0, dst.l); + save_be_uint32(tmp+4, dst.r); + + for(i=0;iprivate_data); + if( ((void *)(r+1)) != ((void *)p) ){ + return NULL; + } + + return r; +} + +static void core_schedule(CORE_PARAM *work, CORE_PARAM *skey, CORE_DATA *dkey) +{ + CORE_DATA b1,b2,b3,b4,b5,b6,b7,b8,b9; + + core_pi1(&b1, dkey); + + core_pi2(&b2, &b1, skey->key[0]); + work->key[0] = b2.l; + + core_pi3(&b3, &b2, skey->key[1], skey->key[2]); + work->key[1] = b3.r; + + core_pi4(&b4, &b3, skey->key[3]); + work->key[2] = b4.l; + + core_pi1(&b5, &b4); + work->key[3] = b5.r; + + core_pi2(&b6, &b5, skey->key[4]); + work->key[4] = b6.l; + + core_pi3(&b7, &b6, skey->key[5], skey->key[6]); + work->key[5] = b7.r; + + core_pi4(&b8, &b7, skey->key[7]); + work->key[6] = b8.l; + + core_pi1(&b9, &b8); + work->key[7] = b9.r; +} + +static void core_encrypt(CORE_DATA *dst, CORE_DATA *src, CORE_PARAM *w, int32_t round) +{ + int32_t i; + + CORE_DATA tmp; + + dst->l = src->l; + dst->r = src->r; + for(i=0;ikey[0]); + core_pi3(&tmp, dst, w->key[1], w->key[2]); + core_pi4( dst, &tmp, w->key[3]); + core_pi1(&tmp, dst); + core_pi2( dst, &tmp, w->key[4]); + core_pi3(&tmp, dst, w->key[5], w->key[6]); + core_pi4( dst, &tmp, w->key[7]); + } +} + +static void core_decrypt(CORE_DATA *dst, CORE_DATA *src, CORE_PARAM *w, int32_t round) +{ + int32_t i; + + CORE_DATA tmp; + + dst->l = src->l; + dst->r = src->r; + for(i=0;ikey[7]); + core_pi3( dst, &tmp, w->key[5], w->key[6]); + core_pi2(&tmp, dst, w->key[4]); + core_pi1( dst, &tmp); + core_pi4(&tmp, dst, w->key[3]); + core_pi3( dst, &tmp, w->key[1], w->key[2]); + core_pi2(&tmp, dst, w->key[0]); + core_pi1( dst, &tmp); + } +} + +static void core_pi1(CORE_DATA *dst, CORE_DATA *src) +{ + dst->l = src->l; + dst->r = src->r ^ src->l; +} + +static void core_pi2(CORE_DATA *dst, CORE_DATA *src, uint32_t a) +{ + uint32_t t0,t1,t2; + + t0 = src->r + a; + t1 = left_rotate_uint32(t0, 1) + t0 - 1; + t2 = left_rotate_uint32(t1, 4) ^ t1; + + dst->l = src->l ^ t2; + dst->r = src->r; +} + +static void core_pi3(CORE_DATA *dst, CORE_DATA *src, uint32_t a, uint32_t b) +{ + uint32_t t0,t1,t2,t3,t4,t5; + + t0 = src->l + a; + t1 = left_rotate_uint32(t0, 2) + t0 + 1; + t2 = left_rotate_uint32(t1, 8) ^ t1; + t3 = t2 + b; + t4 = left_rotate_uint32(t3, 1) - t3; + t5 = left_rotate_uint32(t4, 16) ^ (t4 | src->l); + + dst->l = src->l; + dst->r = src->r ^ t5; +} + +static void core_pi4(CORE_DATA *dst, CORE_DATA *src, uint32_t a) +{ + uint32_t t0,t1; + + t0 = src->r + a; + t1 = left_rotate_uint32(t0, 2) + t0 + 1; + + dst->l = src->l ^ t1; + dst->r = src->r; +} diff --git a/src/multi2.h b/src/multi2.h new file mode 100644 index 0000000..65798e3 --- /dev/null +++ b/src/multi2.h @@ -0,0 +1,35 @@ +#ifndef MULTI2_H +#define MULTI2_H + +#include "portable.h" + +typedef struct { + + void *private_data; + + void (* release)(void *m2); + int (* add_ref)(void *m2); + + int (* set_round)(void *m2, int32_t val); + + int (* set_system_key)(void *m2, uint8_t *val); + int (* set_init_cbc)(void *m2, uint8_t *val); + int (* set_scramble_key)(void *m2, uint8_t *val); + int (* clear_scramble_key)(void *m2); + + int (* encrypt)(void *m2, int32_t type, uint8_t *buf, int32_t size); + int (* decrypt)(void *m2, int32_t type, uint8_t *buf, int32_t size); + +} MULTI2; + +#ifdef __cplusplus +extern "C" { +#endif + +extern MULTI2 *create_multi2(); + +#ifdef __cplusplus +} +#endif + +#endif /* MULTI2_H */ diff --git a/src/multi2_error_code.h b/src/multi2_error_code.h new file mode 100644 index 0000000..7004737 --- /dev/null +++ b/src/multi2_error_code.h @@ -0,0 +1,9 @@ +#ifndef MULTI2_ERROR_CODE_H +#define MULTI2_ERROR_CODE_H + +#define MULTI2_ERROR_INVALID_PARAMETER -1 +#define MULTI2_ERROR_UNSET_SYSTEM_KEY -2 +#define MULTI2_ERROR_UNSET_CBC_INIT -3 +#define MULTI2_ERROR_UNSET_SCRAMBLE_KEY -4 + +#endif /* MULTI2_ERROR_CODE_H */ diff --git a/src/portable.h b/src/portable.h new file mode 100644 index 0000000..9a28a2b --- /dev/null +++ b/src/portable.h @@ -0,0 +1,38 @@ +#ifndef PORTABLE_H +#define PORTABLE_H + +#if (defined(WIN32) && MSC_VER < 1300) + +typedef unsigned char uint8_t; +typedef signed char int8_t; +typedef unsigned short uint16_t; +typedef signed short int16_t; +typedef unsigned int uint32_t; +typedef signed int int32_t; +typedef unsigned __int64 uint64_t; +typedef signed __int64 int64_t; + +#else + +#include + +#endif + +#if !defined(WIN32) + #define _open open + #define _close close + #define _read read + #define _write write + #define _lseeki64 lseek + #define _telli64(fd) (lseek(fd,0,SEEK_CUR)) + #define _O_BINARY (0) + #define _O_RDONLY (O_RDONLY) + #define _O_WRONLY (O_WRONLY) + #define _O_SEQUENTIAL (0) + #define _O_CREAT (O_CREAT) + #define _O_TRUNC (O_TRUNC) + #define _S_IREAD (S_IRUSR|S_IRGRP|S_IROTH) + #define _S_IWRITE (S_IWUSR|S_IWGRP|S_IWOTH) +#endif + +#endif /* PORTABLE_H */ diff --git a/src/td.c b/src/td.c new file mode 100644 index 0000000..e703b87 --- /dev/null +++ b/src/td.c @@ -0,0 +1,432 @@ +#include +#include +#include + +#include +#include +#include + +#if defined(WIN32) + #include + #include + #include +#else + #define __STDC_FORMAT_MACROS + #include + #include + #include +#endif + +#include "arib_std_b25.h" +#include "b_cas_card.h" + +typedef struct { + int32_t round; + int32_t strip; + int32_t emm; + int32_t verbose; + int32_t power_ctrl; +} OPTION; + +static void show_usage(); +static int parse_arg(OPTION *dst, int argc, char **argv); +static void test_arib_std_b25(const char *src, const char *dst, OPTION *opt); +static void show_bcas_power_on_control_info(B_CAS_CARD *bcas); + +int main(int argc, char **argv) +{ + int n; + OPTION opt; + + #if defined(WIN32) + _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDOUT ); + _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDOUT ); + _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDOUT ); + _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_DELAY_FREE_MEM_DF|_CRTDBG_CHECK_ALWAYS_DF|_CRTDBG_LEAK_CHECK_DF); + #endif + + n = parse_arg(&opt, argc, argv); + if(n+2 > argc){ + show_usage(); + exit(EXIT_FAILURE); + } + + for(;n<=(argc-2);n+=2){ + test_arib_std_b25(argv[n+0], argv[n+1], &opt); + } + + #if defined(WIN32) + _CrtDumpMemoryLeaks(); + #endif + + return EXIT_SUCCESS; +} + +static void show_usage() +{ + fprintf(stderr, "b25 - ARIB STD-B25 test program ver. 0.2.5 (2012, 2/13)\n"); + fprintf(stderr, "usage: b25 [options] src.m2t dst.m2t [more pair ..]\n"); + fprintf(stderr, "options:\n"); + fprintf(stderr, " -r round (integer, default=4)\n"); + fprintf(stderr, " -s strip\n"); + fprintf(stderr, " 0: keep null(padding) stream (default)\n"); + fprintf(stderr, " 1: strip null stream\n"); + fprintf(stderr, " -m EMM\n"); + fprintf(stderr, " 0: ignore EMM (default)\n"); + fprintf(stderr, " 1: send EMM to B-CAS card\n"); + fprintf(stderr, " -p power_on_control_info\n"); + fprintf(stderr, " 0: do nothing additionaly\n"); + fprintf(stderr, " 1: show B-CAS EMM receiving request (default)\n"); + fprintf(stderr, " -v verbose\n"); + fprintf(stderr, " 0: silent\n"); + fprintf(stderr, " 1: show processing status (default)\n"); + fprintf(stderr, "\n"); +} + +static int parse_arg(OPTION *dst, int argc, char **argv) +{ + int i; + + dst->round = 4; + dst->strip = 0; + dst->emm = 0; + dst->power_ctrl = 1; + dst->verbose = 1; + + for(i=1;iemm = atoi(argv[i]+2); + }else{ + dst->emm = atoi(argv[i+1]); + i += 1; + } + break; + case 'p': + if(argv[i][2]){ + dst->power_ctrl = atoi(argv[i]+2); + }else{ + dst->power_ctrl = atoi(argv[i+1]); + i += 1; + } + break; + case 'r': + if(argv[i][2]){ + dst->round = atoi(argv[i]+2); + }else{ + dst->round = atoi(argv[i+1]); + i += 1; + } + break; + case 's': + if(argv[i][2]){ + dst->strip = atoi(argv[i]+2); + }else{ + dst->strip = atoi(argv[i+1]); + i += 1; + } + break; + case 'v': + if(argv[i][2]){ + dst->verbose = atoi(argv[i]+2); + }else{ + dst->verbose = atoi(argv[i+1]); + i += 1; + } + break; + default: + fprintf(stderr, "error - unknown option '-%c'\n", argv[i][1]); + return argc; + } + } + + return i; +} + +static void test_arib_std_b25(const char *src, const char *dst, OPTION *opt) +{ + int code,i,n,m; + int sfd,dfd; + + int64_t total; + int64_t offset; +#if defined(WIN32) + unsigned long tick,tock; +#else + struct timeval tick,tock; + double millisec; +#endif + double mbps; + + ARIB_STD_B25 *b25; + B_CAS_CARD *bcas; + + ARIB_STD_B25_PROGRAM_INFO pgrm; + + uint8_t data[64*1024]; + + ARIB_STD_B25_BUFFER sbuf; + ARIB_STD_B25_BUFFER dbuf; + + sfd = -1; + dfd = -1; + b25 = NULL; + bcas = NULL; + + sfd = _open(src, _O_BINARY|_O_RDONLY|_O_SEQUENTIAL); + if(sfd < 0){ + fprintf(stderr, "error - failed on _open(%s) [src]\n", src); + goto LAST; + } + + _lseeki64(sfd, 0, SEEK_END); + total = _telli64(sfd); + _lseeki64(sfd, 0, SEEK_SET); + + b25 = create_arib_std_b25(); + if(b25 == NULL){ + fprintf(stderr, "error - failed on create_arib_std_b25()\n"); + goto LAST; + } + + code = b25->set_multi2_round(b25, opt->round); + if(code < 0){ + fprintf(stderr, "error - failed on ARIB_STD_B25::set_multi2_round() : code=%d\n", code); + goto LAST; + } + + code = b25->set_strip(b25, opt->strip); + if(code < 0){ + fprintf(stderr, "error - failed on ARIB_STD_B25::set_strip() : code=%d\n", code); + goto LAST; + } + + code = b25->set_emm_proc(b25, opt->emm); + if(code < 0){ + fprintf(stderr, "error - failed on ARIB_STD_B25::set_emm_proc() : code=%d\n", code); + goto LAST; + } + + bcas = create_b_cas_card(); + if(bcas == NULL){ + fprintf(stderr, "error - failed on create_b_cas_card()\n"); + goto LAST; + } + + code = bcas->init(bcas); + if(code < 0){ + fprintf(stderr, "error - failed on B_CAS_CARD::init() : code=%d\n", code); + goto LAST; + } + + code = b25->set_b_cas_card(b25, bcas); + if(code < 0){ + fprintf(stderr, "error - failed on ARIB_STD_B25::set_b_cas_card() : code=%d\n", code); + goto LAST; + } + + dfd = _open(dst, _O_BINARY|_O_WRONLY|_O_SEQUENTIAL|_O_CREAT|_O_TRUNC, _S_IREAD|_S_IWRITE); + if(dfd < 0){ + fprintf(stderr, "error - failed on _open(%s) [dst]\n", dst); + goto LAST; + } + + offset = 0; +#if defined(WIN32) + tock = GetTickCount(); +#else + gettimeofday(&tock, NULL); +#endif + while( (n = _read(sfd, data, sizeof(data))) > 0 ){ + sbuf.data = data; + sbuf.size = n; + + code = b25->put(b25, &sbuf); + if(code < 0){ + fprintf(stderr, "error - failed on ARIB_STD_B25::put() : code=%d\n", code); + goto LAST; + } + + code = b25->get(b25, &dbuf); + if(code < 0){ + fprintf(stderr, "error - failed on ARIB_STD_B25::get() : code=%d\n", code); + goto LAST; + } + + if(dbuf.size > 0){ + n = _write(dfd, dbuf.data, dbuf.size); + if(n != dbuf.size){ + fprintf(stderr, "error failed on _write(%d)\n", dbuf.size); + goto LAST; + } + } + + offset += sbuf.size; + if(opt->verbose != 0){ + m = (int)(10000*offset/total); + mbps = 0.0; +#if defined(WIN32) + tick = GetTickCount(); + if (tick-tock > 100) { + mbps = offset; + mbps /= 1024; + mbps /= (tick-tock); + } +#else + gettimeofday(&tick, NULL); + millisec = (tick.tv_sec - tock.tv_sec) * 1000; + millisec += (tick.tv_usec - tock.tv_usec) / 1000; + if(millisec > 100.0) { + mbps = offset; + mbps /= 1024; + mbps /= millisec; + } +#endif + fprintf(stderr, "\rprocessing: %2d.%02d%% [%6.2f MB/sec]", m/100, m%100, mbps); + } + } + + code = b25->flush(b25); + if(code < 0){ + fprintf(stderr, "error - failed on ARIB_STD_B25::flush() : code=%d\n", code); + goto LAST; + } + + code = b25->get(b25, &dbuf); + if(code < 0){ + fprintf(stderr, "error - failed on ARIB_STD_B25::get() : code=%d\n", code); + goto LAST; + } + + if(dbuf.size > 0){ + n = _write(dfd, dbuf.data, dbuf.size); + if(n != dbuf.size){ + fprintf(stderr, "error - failed on _write(%d)\n", dbuf.size); + goto LAST; + } + } + + if(opt->verbose != 0){ + mbps = 0.0; +#if defined(WIN32) + tick = GetTickCount(); + if (tick-tock > 100) { + mbps = offset; + mbps /= 1024; + mbps /= (tick-tock); + } +#else + gettimeofday(&tick, NULL); + millisec = (tick.tv_sec - tock.tv_sec) * 1000; + millisec += (tick.tv_usec - tock.tv_usec) / 1000; + if(millisec > 100.0) { + mbps = offset; + mbps /= 1024; + mbps /= millisec; + } +#endif + fprintf(stderr, "\rprocessing: finish [%6.2f MB/sec]\n", mbps); + fflush(stderr); + fflush(stdout); + } + + n = b25->get_program_count(b25); + if(n < 0){ + fprintf(stderr, "error - failed on ARIB_STD_B25::get_program_count() : code=%d\n", code); + goto LAST; + } + for(i=0;iget_program_info(b25, &pgrm, i); + if(code < 0){ + fprintf(stderr, "error - failed on ARIB_STD_B25::get_program_info(%d) : code=%d\n", i, code); + goto LAST; + } + if(pgrm.ecm_unpurchased_count > 0){ + fprintf(stderr, "warning - unpurchased ECM is detected\n"); + fprintf(stderr, " channel: %d\n", pgrm.program_number); + fprintf(stderr, " unpurchased ECM count: %d\n", pgrm.ecm_unpurchased_count); + fprintf(stderr, " last ECM error code: %04x\n", pgrm.last_ecm_error_code); + #if defined(WIN32) + fprintf(stderr, " undecrypted TS packet: %d\n", pgrm.undecrypted_packet_count); + fprintf(stderr, " total TS packet: %d\n", pgrm.total_packet_count); + #else + fprintf(stderr, " undecrypted TS packet: %"PRId64"\n", pgrm.undecrypted_packet_count); + fprintf(stderr, " total TS packet: %"PRId64"\n", pgrm.total_packet_count); + #endif + } + } + + if(opt->power_ctrl != 0){ + show_bcas_power_on_control_info(bcas); + } + +LAST: + + if(sfd >= 0){ + _close(sfd); + sfd = -1; + } + + if(dfd >= 0){ + _close(dfd); + dfd = -1; + } + + if(b25 != NULL){ + b25->release(b25); + b25 = NULL; + } + + if(bcas != NULL){ + bcas->release(bcas); + bcas = NULL; + } +} + +static void show_bcas_power_on_control_info(B_CAS_CARD *bcas) +{ + int code; + int i,w; + B_CAS_PWR_ON_CTRL_INFO pwc; + + code = bcas->get_pwr_on_ctrl(bcas, &pwc); + if(code < 0){ + fprintf(stderr, "error - failed on B_CAS_CARD::get_pwr_on_ctrl() : code=%d\n", code); + return; + } + + if(pwc.count == 0){ + fprintf(stdout, "no EMM receiving request\n"); + return; + } + + fprintf(stdout, "total %d EMM receiving request\n", pwc.count); + for(i=0;i> 4) & 0x1f), (w & 7)); + break; + case 6: + case 7: + w = pwc.data[i].transport_id; + fprintf(stdout, "ND-%d/TS-%d ", ((w >> 4) & 0x1f), (w & 7)); + break; + default: + fprintf(stdout, "unknown(b:0x%02x,n:0x%04x,t:0x%04x) ", pwc.data[i].broadcaster_group_id, pwc.data[i].network_id, pwc.data[i].transport_id); + break; + } + fprintf(stdout, "between %04d %02d/%02d ", pwc.data[i].s_yy, pwc.data[i].s_mm, pwc.data[i].s_dd); + fprintf(stdout, "to %04d %02d/%02d ", pwc.data[i].l_yy, pwc.data[i].l_mm, pwc.data[i].l_dd); + fprintf(stdout, "least %d hours\n", pwc.data[i].hold_time); + } +} + diff --git a/src/ts_common_types.h b/src/ts_common_types.h new file mode 100644 index 0000000..f9b07b9 --- /dev/null +++ b/src/ts_common_types.h @@ -0,0 +1,36 @@ +#ifndef TS_COMMON_TYPES_H +#define TS_COMMON_TYPES_H + +#include "portable.h" + +typedef struct { + int32_t sync; /* 0- 7 : 8 bits */ + int32_t transport_error_indicator; /* 8- 8 : 1 bit */ + int32_t payload_unit_start_indicator; /* 9- 9 : 1 bit */ + int32_t transport_priority; /* 10-10 : 1 bits */ + int32_t pid; /* 11-23 : 13 bits */ + int32_t transport_scrambling_control; /* 24-25 : 2 bits */ + int32_t adaptation_field_control; /* 26-27 : 2 bits */ + int32_t continuity_counter; /* 28-31 : 4 bits */ +} TS_HEADER; + +typedef struct { + int32_t table_id; /* 0- 7 : 8 bits */ + int32_t section_syntax_indicator; /* 8- 8 : 1 bit */ + int32_t private_indicator; /* 9- 9 : 1 bit */ + int32_t section_length; /* 12-23 : 12 bits */ + int32_t table_id_extension; /* 24-39 : 16 bits */ + int32_t version_number; /* 42-46 : 5 bits */ + int32_t current_next_indicator; /* 47-57 : 1 bit */ + int32_t section_number; /* 48-55 : 8 bits */ + int32_t last_section_number; /* 56-63 : 8 bits */ +} TS_SECTION_HEADER; + +typedef struct { + TS_SECTION_HEADER hdr; + uint8_t *raw; + uint8_t *data; + uint8_t *tail; +} TS_SECTION; + +#endif /* TS_COMMON_TYPES_H */ diff --git a/src/ts_section_parser.c b/src/ts_section_parser.c new file mode 100644 index 0000000..8791f73 --- /dev/null +++ b/src/ts_section_parser.c @@ -0,0 +1,802 @@ +#include +#include + +#include "ts_section_parser.h" +#include "ts_section_parser_error_code.h" + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + inner structures + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +typedef struct { + void *prev; + void *next; + TS_SECTION sect; + int32_t ref; +} TS_SECTION_ELEM; + +typedef struct { + TS_SECTION_ELEM *head; + TS_SECTION_ELEM *tail; + int32_t count; +} TS_SECTION_LIST; + +typedef struct { + + int32_t pid; + + TS_SECTION_ELEM *work; + TS_SECTION_ELEM *last; + + TS_SECTION_LIST pool; + TS_SECTION_LIST buff; + + TS_SECTION_PARSER_STAT stat; + +} TS_SECTION_PARSER_PRIVATE_DATA; + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + constant values + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define MAX_RAW_SECTION_SIZE 4100 + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + function prottypes (interface method) + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static void release_ts_section_parser(void *parser); +static int reset_ts_section_parser(void *parser); +static int put_ts_section_parser(void *parser, TS_HEADER *hdr, uint8_t *data, int size); +static int get_ts_section_parser(void *parser, TS_SECTION *sect); +static int ret_ts_section_parser(void *parser, TS_SECTION *sect); +static int get_count_ts_section_parser(void *parser); +static int get_stat_ts_section_parser(void *parser, TS_SECTION_PARSER_STAT *stat); + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + global function implementation (factory method) + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +TS_SECTION_PARSER *create_ts_section_parser() +{ + TS_SECTION_PARSER *r; + TS_SECTION_PARSER_PRIVATE_DATA *prv; + + int n; + + n = sizeof(TS_SECTION_PARSER_PRIVATE_DATA); + n += sizeof(TS_SECTION_PARSER); + + prv = (TS_SECTION_PARSER_PRIVATE_DATA *)calloc(1, n); + if(prv == NULL){ + /* failed on malloc() - no enough memory */ + return NULL; + } + + prv->pid = -1; + + r = (TS_SECTION_PARSER *)(prv+1); + r->private_data = prv; + + r->release = release_ts_section_parser; + r->reset = reset_ts_section_parser; + + r->put = put_ts_section_parser; + r->get = get_ts_section_parser; + r->ret = ret_ts_section_parser; + + r->get_count = get_count_ts_section_parser; + + r->get_stat = get_stat_ts_section_parser; + + return r; +} + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + function prottypes (private method) + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static TS_SECTION_PARSER_PRIVATE_DATA *private_data(void *parser); +static void teardown(TS_SECTION_PARSER_PRIVATE_DATA *prv); + +static int put_exclude_section_start(TS_SECTION_PARSER_PRIVATE_DATA *prv, uint8_t *data, int size); +static int put_include_section_start(TS_SECTION_PARSER_PRIVATE_DATA *prv, uint8_t *data, int size); + +static void reset_section(TS_SECTION *sect); +static void append_section_data(TS_SECTION *sect, uint8_t *data, int size); +static int check_section_complete(TS_SECTION *sect); + +static int compare_elem_section(TS_SECTION_ELEM *a, TS_SECTION_ELEM *b); + +static void cancel_elem_empty(TS_SECTION_PARSER_PRIVATE_DATA *prv, TS_SECTION_ELEM *elem); +static void cancel_elem_error(TS_SECTION_PARSER_PRIVATE_DATA *prv, TS_SECTION_ELEM *elem); +static void cancel_elem_same(TS_SECTION_PARSER_PRIVATE_DATA *prv, TS_SECTION_ELEM *elem); +static void commit_elem_updated(TS_SECTION_PARSER_PRIVATE_DATA *prv, TS_SECTION_ELEM *elem); + +static TS_SECTION_ELEM *query_work_elem(TS_SECTION_PARSER_PRIVATE_DATA *prv); + +static void extract_ts_section_header(TS_SECTION *sect); + +static TS_SECTION_ELEM *create_ts_section_elem(); +static TS_SECTION_ELEM *get_ts_section_list_head(TS_SECTION_LIST *list); +static void put_ts_section_list_tail(TS_SECTION_LIST *list, TS_SECTION_ELEM *elem); +static void unlink_ts_section_list(TS_SECTION_LIST *list, TS_SECTION_ELEM *elem); +static void clear_ts_section_list(TS_SECTION_LIST *list); + +static uint32_t crc32(uint8_t *head, uint8_t *tail); + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + function implementation (interface method) + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static void release_ts_section_parser(void *parser) +{ + TS_SECTION_PARSER_PRIVATE_DATA *prv; + + prv = private_data(parser); + if(prv == NULL){ + return; + } + + teardown(prv); + + memset(parser, 0, sizeof(TS_SECTION_PARSER)); + free(prv); +} + +static int reset_ts_section_parser(void *parser) +{ + TS_SECTION_PARSER_PRIVATE_DATA *prv; + + prv = private_data(parser); + if(prv == NULL){ + return TS_SECTION_PARSER_ERROR_INVALID_PARAM; + } + + teardown(prv); + + return 0; +} + +static int put_ts_section_parser(void *parser, TS_HEADER *hdr, uint8_t *data, int size) +{ + TS_SECTION_PARSER_PRIVATE_DATA *prv; + + prv = private_data(parser); + if( (prv == NULL) || (hdr == NULL) || (data == NULL) || (size < 1) ){ + return TS_SECTION_PARSER_ERROR_INVALID_PARAM; + } + + if( (prv->pid >= 0) && (prv->pid != hdr->pid) ){ + return TS_SECTION_PARSER_ERROR_INVALID_TS_PID; + } + + prv->pid = hdr->pid; + + if(hdr->payload_unit_start_indicator == 0){ + /* exclude section start */ + return put_exclude_section_start(prv, data, size); + }else{ + /* include section start */ + return put_include_section_start(prv, data, size); + } +} + +static int get_ts_section_parser(void *parser, TS_SECTION *sect) +{ + TS_SECTION_PARSER_PRIVATE_DATA *prv; + TS_SECTION_ELEM *w; + + prv = private_data(parser); + if( (prv == NULL) || (sect == NULL) ){ + return TS_SECTION_PARSER_ERROR_INVALID_PARAM; + } + + w = get_ts_section_list_head(&(prv->buff)); + if(w == NULL){ + memset(sect, 0, sizeof(TS_SECTION)); + return TS_SECTION_PARSER_ERROR_NO_SECTION_DATA; + } + + memcpy(sect, &(w->sect), sizeof(TS_SECTION)); + put_ts_section_list_tail(&(prv->pool), w); + + return 0; +} + +static int ret_ts_section_parser(void *parser, TS_SECTION *sect) +{ + TS_SECTION_PARSER_PRIVATE_DATA *prv; + TS_SECTION_ELEM *w; + + prv = private_data(parser); + if( (prv == NULL) || (sect == NULL) ){ + return TS_SECTION_PARSER_ERROR_INVALID_PARAM; + } + + w = prv->pool.tail; + while(w != NULL){ + if(w->sect.data == sect->data){ + break; + } + w = (TS_SECTION_ELEM *)(w->prev); + } + + if( (w != NULL) && (w->ref > 0) ){ + w->ref -= 1; + } + + return 0; +} + +static int get_count_ts_section_parser(void *parser) +{ + TS_SECTION_PARSER_PRIVATE_DATA *prv; + + prv = private_data(parser); + if(prv == NULL){ + return TS_SECTION_PARSER_ERROR_INVALID_PARAM; + } + + return prv->buff.count; +} + +static int get_stat_ts_section_parser(void *parser, TS_SECTION_PARSER_STAT *stat) +{ + TS_SECTION_PARSER_PRIVATE_DATA *prv; + + prv = private_data(parser); + if( (prv == NULL) || (stat == NULL) ){ + return TS_SECTION_PARSER_ERROR_INVALID_PARAM; + } + + memcpy(stat, &(prv->stat), sizeof(TS_SECTION_PARSER_STAT)); + + return 0; +} + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + function implementation (private method) + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static TS_SECTION_PARSER_PRIVATE_DATA *private_data(void *parser) +{ + TS_SECTION_PARSER_PRIVATE_DATA *r; + TS_SECTION_PARSER *p; + + p = (TS_SECTION_PARSER *)parser; + if(p == NULL){ + return NULL; + } + + r = (TS_SECTION_PARSER_PRIVATE_DATA *)(p->private_data); + if( ((void *)(r+1)) != parser ){ + return NULL; + } + + return r; +} + +static void teardown(TS_SECTION_PARSER_PRIVATE_DATA *prv) +{ + prv->pid = -1; + + if(prv->work != NULL){ + free(prv->work); + prv->work = NULL; + } + + prv->last = NULL; + + clear_ts_section_list(&(prv->pool)); + clear_ts_section_list(&(prv->buff)); + + memset(&(prv->stat), 0, sizeof(TS_SECTION_PARSER_STAT)); +} + +static int put_exclude_section_start(TS_SECTION_PARSER_PRIVATE_DATA *prv, uint8_t *data, int size) +{ + TS_SECTION_ELEM *w; + + w = prv->work; + if( (w == NULL) || (w->sect.raw == w->sect.tail) ){ + /* no previous data */ + return 0; + } + + append_section_data(&(w->sect), data, size); + if(check_section_complete(&(w->sect)) == 0){ + /* need more data */ + return 0; + } + + prv->work = NULL; + + if( (w->sect.hdr.section_syntax_indicator != 0) && + (crc32(w->sect.raw, w->sect.tail) != 0) ){ + cancel_elem_error(prv, w); + return TS_SECTION_PARSER_WARN_CRC_MISSMATCH; + } + + if(compare_elem_section(w, prv->last) == 0){ + /* same section data */ + cancel_elem_same(prv, w); + return 0; + } + + commit_elem_updated(prv, w); + return 0; +} + +static int put_include_section_start(TS_SECTION_PARSER_PRIVATE_DATA *prv, uint8_t *data, int size) +{ + TS_SECTION_ELEM *w; + + int pointer_field; + + uint8_t *p; + uint8_t *tail; + + int r; + int length; + + p = data; + tail = p + size; + + r = 0; + + pointer_field = p[0]; + p += 1; + + if( (p+pointer_field) >= tail ){ + /* input data is probably broken */ + w = prv->work; + prv->work = NULL; + if(w != NULL) { + if(w->sect.raw != w->sect.tail){ + cancel_elem_error(prv, w); + return TS_SECTION_PARSER_WARN_LENGTH_MISSMATCH; + } + cancel_elem_empty(prv, w); + } + return 0; + } + + if(pointer_field > 0){ + r = put_exclude_section_start(prv, p, pointer_field); + if(r < 0){ + return r; + } + p += pointer_field; + } + + w = prv->work; + prv->work = NULL; + + if(w != NULL){ + if(w->sect.raw != w->sect.tail){ + cancel_elem_error(prv, w); + r = TS_SECTION_PARSER_WARN_LENGTH_MISSMATCH; + }else{ + cancel_elem_empty(prv, w); + } + w = NULL; + } + + do { + + w = query_work_elem(prv); + if(w == NULL){ + return TS_SECTION_PARSER_ERROR_NO_ENOUGH_MEMORY; + } + + append_section_data(&(w->sect), p, tail-p); + if(check_section_complete(&(w->sect)) == 0){ + /* need more data */ + prv->work = w; + return 0; + } + length = (w->sect.tail - w->sect.raw); + + if( (w->sect.hdr.section_syntax_indicator != 0) && + (crc32(w->sect.raw, w->sect.tail) != 0) ){ + cancel_elem_error(prv, w); + r = TS_SECTION_PARSER_WARN_CRC_MISSMATCH; + }else if(compare_elem_section(w, prv->last) == 0){ + cancel_elem_same(prv, w); + }else{ + commit_elem_updated(prv, w); + } + + p += length; + + } while ( (p < tail) && (p[0] != 0xff) ); + + return r; +} + +static void reset_section(TS_SECTION *sect) +{ + memset(&(sect->hdr), 0, sizeof(TS_SECTION_HEADER)); + sect->tail = sect->raw; + sect->data = NULL; +} + +static void append_section_data(TS_SECTION *sect, uint8_t *data, int size) +{ + int m,n; + + m = sect->tail - sect->raw; + n = MAX_RAW_SECTION_SIZE - m; + + if(size < n){ + n = size; + } + memcpy(sect->tail, data, n); + sect->tail += n; + m += n; + + if(sect->data == NULL){ + extract_ts_section_header(sect); + } + + if(sect->data == NULL){ + /* need more data */ + return; + } + + n = sect->hdr.section_length + 3; + if(m > n){ + sect->tail = sect->raw + n; + } + + return; +} + +static int check_section_complete(TS_SECTION *sect) +{ + int m,n; + + if(sect->data == NULL){ + return 0; + } + + m = sect->tail - sect->raw; + n = sect->hdr.section_length + 3; + + if(n > m){ + return 0; + } + + return 1; +} + +static int compare_elem_section(TS_SECTION_ELEM *a, TS_SECTION_ELEM *b) +{ + int m,n; + + if( (a == NULL) || (b == NULL) ){ + return 1; + } + + m = a->sect.tail - a->sect.raw; + n = b->sect.tail - b->sect.raw; + if( m != n ){ + return 1; + } + + if(memcmp(a->sect.raw, b->sect.raw, m) != 0){ + return 1; + } + + return 0; +} + +static void cancel_elem_empty(TS_SECTION_PARSER_PRIVATE_DATA *prv, TS_SECTION_ELEM *elem) +{ + reset_section(&(elem->sect)); + elem->ref = 0; + put_ts_section_list_tail(&(prv->pool), elem); +} + +static void cancel_elem_error(TS_SECTION_PARSER_PRIVATE_DATA *prv, TS_SECTION_ELEM *elem) +{ + reset_section(&(elem->sect)); + elem->ref = 0; + put_ts_section_list_tail(&(prv->pool), elem); + prv->stat.total += 1; + prv->stat.error += 1; +} + +static void cancel_elem_same(TS_SECTION_PARSER_PRIVATE_DATA *prv, TS_SECTION_ELEM *elem) +{ + reset_section(&(elem->sect)); + elem->ref = 0; + put_ts_section_list_tail(&(prv->pool), elem); + prv->stat.total +=1; +} + +static void commit_elem_updated(TS_SECTION_PARSER_PRIVATE_DATA *prv, TS_SECTION_ELEM *elem) +{ + if( (prv->last != NULL) && (prv->last->ref > 0) ){ + prv->last->ref -= 1; + } + + elem->ref = 2; + prv->last = elem; + put_ts_section_list_tail(&(prv->buff), elem); + prv->stat.total += 1; + prv->stat.unique += 1; +} + +static TS_SECTION_ELEM *query_work_elem(TS_SECTION_PARSER_PRIVATE_DATA *prv) +{ + TS_SECTION_ELEM *r; + + r = prv->pool.head; + while(r != NULL){ + if(r->ref < 1){ + break; + } + r = (TS_SECTION_ELEM *)(r->next); + } + + if(r != NULL){ + unlink_ts_section_list(&(prv->pool), r); + reset_section(&(r->sect)); + r->ref = 0; + return r; + } + + return create_ts_section_elem(); +} + +static void extract_ts_section_header(TS_SECTION *sect) +{ + int size; + uint8_t *p; + + sect->data = NULL; + + size = sect->tail - sect->raw; + if(size < 3){ + /* need more data */ + return; + } + + p = sect->raw; + + sect->hdr.table_id = p[0]; + sect->hdr.section_syntax_indicator = (p[1] >> 7) & 0x01; + sect->hdr.private_indicator = (p[1] >> 6) & 0x01; + sect->hdr.section_length =((p[1] << 8) | p[2]) & 0x0fff; + + if(sect->hdr.section_syntax_indicator == 0){ + /* short format section header */ + sect->data = p+3; + return; + } + + /* long format section header */ + + if(size < 8){ + /* need more data */ + return; + } + + sect->hdr.table_id_extension =((p[3] << 8) | p[4]); + sect->hdr.version_number = (p[5] >> 1) & 0x1f; + sect->hdr.current_next_indicator = p[5] & 0x01; + sect->hdr.section_number = p[6]; + sect->hdr.last_section_number = p[7]; + + sect->data = p+8; + + return; +} + +static TS_SECTION_ELEM *create_ts_section_elem() +{ + TS_SECTION_ELEM *r; + int n; + + n = sizeof(TS_SECTION_ELEM) + MAX_RAW_SECTION_SIZE; + r = (TS_SECTION_ELEM *)calloc(1, n); + if(r == NULL){ + /* failed on malloc() */ + return NULL; + } + + r->sect.raw = (uint8_t *)(r+1); + r->sect.tail = r->sect.raw; + + return r; +} + +static TS_SECTION_ELEM *get_ts_section_list_head(TS_SECTION_LIST *list) +{ + TS_SECTION_ELEM *r; + + if(list == NULL){/* invalid param */ + return NULL; + } + + r = list->head; + if(r == NULL){ + return NULL; + } + + list->head = (TS_SECTION_ELEM *)(r->next); + if(list->head != NULL){ + list->head->prev = NULL; + list->count -= 1; + }else{ + list->tail = NULL; + list->count = 0; + } + + r->next = NULL; + + return r; +} + +static void put_ts_section_list_tail(TS_SECTION_LIST *list, TS_SECTION_ELEM *elem) +{ + if( (list == NULL) || (elem == NULL) ){ + /* invalid param */ + return; + } + + if(list->tail != NULL){ + elem->prev = list->tail; + elem->next = NULL; + list->tail->next = elem; + list->tail = elem; + list->count += 1; + }else{ + elem->prev = NULL; + elem->next = NULL; + list->head = elem; + list->tail = elem; + list->count = 1; + } +} + +static void unlink_ts_section_list(TS_SECTION_LIST *list, TS_SECTION_ELEM *elem) +{ + TS_SECTION_ELEM *prev; + TS_SECTION_ELEM *next; + + if( (list == NULL) || (elem == NULL) ){ + /* invalid param */ + return; + } + + prev = (TS_SECTION_ELEM *)(elem->prev); + next = (TS_SECTION_ELEM *)(elem->next); + + if(prev != NULL){ + prev->next = next; + }else{ + list->head = next; + } + if(next != NULL){ + next->prev = prev; + }else{ + list->tail = prev; + } + list->count -= 1; +} + +static void clear_ts_section_list(TS_SECTION_LIST *list) +{ + TS_SECTION_ELEM *e; + TS_SECTION_ELEM *n; + + if(list == NULL){ /* invalid param */ + return; + } + + e = list->head; + while(e != NULL){ + n = (TS_SECTION_ELEM *)(e->next); + free(e); + e = n; + } + + list->head = NULL; + list->tail = NULL; + list->count = 0; +} + +static uint32_t crc32(uint8_t *head, uint8_t *tail) +{ + uint32_t crc; + uint8_t *p; + + static const uint32_t table[256] = { + 0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9, + 0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005, + 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, 0x2B4BCB61, + 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD, + + 0x4C11DB70, 0x48D0C6C7, 0x4593E01E, 0x4152FDA9, + 0x5F15ADAC, 0x5BD4B01B, 0x569796C2, 0x52568B75, + 0x6A1936C8, 0x6ED82B7F, 0x639B0DA6, 0x675A1011, + 0x791D4014, 0x7DDC5DA3, 0x709F7B7A, 0x745E66CD, + + 0x9823B6E0, 0x9CE2AB57, 0x91A18D8E, 0x95609039, + 0x8B27C03C, 0x8FE6DD8B, 0x82A5FB52, 0x8664E6E5, + 0xBE2B5B58, 0xBAEA46EF, 0xB7A96036, 0xB3687D81, + 0xAD2F2D84, 0xA9EE3033, 0xA4AD16EA, 0xA06C0B5D, + + 0xD4326D90, 0xD0F37027, 0xDDB056FE, 0xD9714B49, + 0xC7361B4C, 0xC3F706FB, 0xCEB42022, 0xCA753D95, + 0xF23A8028, 0xF6FB9D9F, 0xFBB8BB46, 0xFF79A6F1, + 0xE13EF6F4, 0xE5FFEB43, 0xE8BCCD9A, 0xEC7DD02D, + + 0x34867077, 0x30476DC0, 0x3D044B19, 0x39C556AE, + 0x278206AB, 0x23431B1C, 0x2E003DC5, 0x2AC12072, + 0x128E9DCF, 0x164F8078, 0x1B0CA6A1, 0x1FCDBB16, + 0x018AEB13, 0x054BF6A4, 0x0808D07D, 0x0CC9CDCA, + + 0x7897AB07, 0x7C56B6B0, 0x71159069, 0x75D48DDE, + 0x6B93DDDB, 0x6F52C06C, 0x6211E6B5, 0x66D0FB02, + 0x5E9F46BF, 0x5A5E5B08, 0x571D7DD1, 0x53DC6066, + 0x4D9B3063, 0x495A2DD4, 0x44190B0D, 0x40D816BA, + + 0xACA5C697, 0xA864DB20, 0xA527FDF9, 0xA1E6E04E, + 0xBFA1B04B, 0xBB60ADFC, 0xB6238B25, 0xB2E29692, + 0x8AAD2B2F, 0x8E6C3698, 0x832F1041, 0x87EE0DF6, + 0x99A95DF3, 0x9D684044, 0x902B669D, 0x94EA7B2A, + + 0xE0B41DE7, 0xE4750050, 0xE9362689, 0xEDF73B3E, + 0xF3B06B3B, 0xF771768C, 0xFA325055, 0xFEF34DE2, + 0xC6BCF05F, 0xC27DEDE8, 0xCF3ECB31, 0xCBFFD686, + 0xD5B88683, 0xD1799B34, 0xDC3ABDED, 0xD8FBA05A, + + 0x690CE0EE, 0x6DCDFD59, 0x608EDB80, 0x644FC637, + 0x7A089632, 0x7EC98B85, 0x738AAD5C, 0x774BB0EB, + 0x4F040D56, 0x4BC510E1, 0x46863638, 0x42472B8F, + 0x5C007B8A, 0x58C1663D, 0x558240E4, 0x51435D53, + + 0x251D3B9E, 0x21DC2629, 0x2C9F00F0, 0x285E1D47, + 0x36194D42, 0x32D850F5, 0x3F9B762C, 0x3B5A6B9B, + 0x0315D626, 0x07D4CB91, 0x0A97ED48, 0x0E56F0FF, + 0x1011A0FA, 0x14D0BD4D, 0x19939B94, 0x1D528623, + + 0xF12F560E, 0xF5EE4BB9, 0xF8AD6D60, 0xFC6C70D7, + 0xE22B20D2, 0xE6EA3D65, 0xEBA91BBC, 0xEF68060B, + 0xD727BBB6, 0xD3E6A601, 0xDEA580D8, 0xDA649D6F, + 0xC423CD6A, 0xC0E2D0DD, 0xCDA1F604, 0xC960EBB3, + + 0xBD3E8D7E, 0xB9FF90C9, 0xB4BCB610, 0xB07DABA7, + 0xAE3AFBA2, 0xAAFBE615, 0xA7B8C0CC, 0xA379DD7B, + 0x9B3660C6, 0x9FF77D71, 0x92B45BA8, 0x9675461F, + 0x8832161A, 0x8CF30BAD, 0x81B02D74, 0x857130C3, + + 0x5D8A9099, 0x594B8D2E, 0x5408ABF7, 0x50C9B640, + 0x4E8EE645, 0x4A4FFBF2, 0x470CDD2B, 0x43CDC09C, + 0x7B827D21, 0x7F436096, 0x7200464F, 0x76C15BF8, + 0x68860BFD, 0x6C47164A, 0x61043093, 0x65C52D24, + + 0x119B4BE9, 0x155A565E, 0x18197087, 0x1CD86D30, + 0x029F3D35, 0x065E2082, 0x0B1D065B, 0x0FDC1BEC, + 0x3793A651, 0x3352BBE6, 0x3E119D3F, 0x3AD08088, + 0x2497D08D, 0x2056CD3A, 0x2D15EBE3, 0x29D4F654, + + 0xC5A92679, 0xC1683BCE, 0xCC2B1D17, 0xC8EA00A0, + 0xD6AD50A5, 0xD26C4D12, 0xDF2F6BCB, 0xDBEE767C, + 0xE3A1CBC1, 0xE760D676, 0xEA23F0AF, 0xEEE2ED18, + 0xF0A5BD1D, 0xF464A0AA, 0xF9278673, 0xFDE69BC4, + + 0x89B8FD09, 0x8D79E0BE, 0x803AC667, 0x84FBDBD0, + 0x9ABC8BD5, 0x9E7D9662, 0x933EB0BB, 0x97FFAD0C, + 0xAFB010B1, 0xAB710D06, 0xA6322BDF, 0xA2F33668, + 0xBCB4666D, 0xB8757BDA, 0xB5365D03, 0xB1F740B4, + }; + + crc = 0xffffffff; + + p = head; + while(p < tail){ + crc = (crc << 8) ^ table[ ((crc >> 24) ^ p[0]) & 0xff ]; + p += 1; + } + + return crc; +} + diff --git a/src/ts_section_parser.h b/src/ts_section_parser.h new file mode 100644 index 0000000..617c37d --- /dev/null +++ b/src/ts_section_parser.h @@ -0,0 +1,40 @@ +#ifndef TS_SECTION_PARSER_H +#define TS_SECTION_PARSER_H + +#include "ts_common_types.h" + +typedef struct { + int64_t total; /* total received section count */ + int64_t unique; /* unique section count */ + int64_t error; /* crc and other error section count */ +} TS_SECTION_PARSER_STAT; + +typedef struct { + + void *private_data; + + void (* release)(void *parser); + + int (* reset)(void *parser); + + int (* put)(void *parser, TS_HEADER *hdr, uint8_t *data, int size); + int (* get)(void *parser, TS_SECTION *sect); + int (* ret)(void *parser, TS_SECTION *sect); + + int (* get_count)(void *parser); + + int (* get_stat)(void *parser, TS_SECTION_PARSER_STAT *stat); + +} TS_SECTION_PARSER; + +#ifdef __cplusplus +extern "C" { +#endif + +extern TS_SECTION_PARSER *create_ts_section_parser(); + +#ifdef __cplusplus +} +#endif + +#endif /* TS_SECTION_PARSER_H */ diff --git a/src/ts_section_parser_error_code.h b/src/ts_section_parser_error_code.h new file mode 100644 index 0000000..7042b76 --- /dev/null +++ b/src/ts_section_parser_error_code.h @@ -0,0 +1,12 @@ +#ifndef TS_SECTION_PARSER_ERROR_CODE_H +#define TS_SECTION_PARESR_ERROR_CODE_H + +#define TS_SECTION_PARSER_ERROR_INVALID_PARAM -1 +#define TS_SECTION_PARSER_ERROR_NO_ENOUGH_MEMORY -2 +#define TS_SECTION_PARSER_ERROR_INVALID_TS_PID -3 +#define TS_SECTION_PARSER_ERROR_NO_SECTION_DATA -4 + +#define TS_SECTION_PARSER_WARN_CRC_MISSMATCH 1 +#define TS_SECTION_PARSER_WARN_LENGTH_MISSMATCH 2 + +#endif /* TS_SECTION_PARSER_ERROR_CODE_H */ -- 2.11.4.GIT