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