summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile20
-rw-r--r--input_pps.c338
-rw-r--r--pps-fixed.h12
-rw-r--r--pps.h372
4 files changed, 742 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..d384d03
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,20 @@
+plugindir = `xine-config --plugindir`
+CFLAGS = -Wall -g -O0 -fPIC -shared `xine-config --cflags`
+LIBS = -lppswrapper `xine-config --libs`
+LDFLAGS = -shared
+plugin = xineplug_inp_pps.so
+
+all: $(plugin)
+
+.c.o:
+ $(CC) -o $@ $< $(CFLAGS)
+
+$(plugin): input_pps.o
+ $(CC) -o $@ $< $(LIBS) $(LDFLAGS)
+
+install: $(plugin)
+ install $< $(plugindir)
+
+clean:
+ -rm *.o
+ -rm *.so
diff --git a/input_pps.c b/input_pps.c
new file mode 100644
index 0000000..7de012d
--- /dev/null
+++ b/input_pps.c
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2009 Luo Jinghua<sunmoon1997@gmail.com>
+ *
+ * PPStream plugin for xine
+ *
+ * xine is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * xine is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * pps input plugin
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#define LOG_MODULE "input_pps"
+#define LOG_VERBOSE
+/*
+#define LOG
+*/
+
+#include <xine/xine_internal.h>
+#include <xine/xineutils.h>
+#include <xine/input_plugin.h>
+
+#include "pps-fixed.h"
+
+#ifndef EXPORTED
+#define EXPORTED
+#endif
+
+typedef struct {
+
+ input_class_t input_class;
+
+ xine_t *xine;
+} pps_input_class_t;
+
+typedef struct {
+ input_plugin_t input_plugin;
+
+ int fd;
+ int size;
+
+ xine_stream_t *stream;
+
+ char *mrl;
+
+ off_t curpos;
+} pps_input_plugin_t;
+
+
+static off_t pps_plugin_read (input_plugin_t *this_gen,
+ char *buf, off_t len) {
+ pps_input_plugin_t *this = (pps_input_plugin_t *) this_gen;
+ int ret;
+
+ lprintf ("pps_plugin_read: %"PRId64" bytes ...\n", len);
+
+ while (1) {
+ struct timeval tv;
+ int error;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 500000;
+ ret = ppsvod_read(this->fd, buf, len,
+ this->curpos, &tv);
+ error = errno;
+ if (ret >= 0)
+ break;
+ if (error != EAGAIN)
+ return -1;
+ usleep (5000);
+ }
+
+ if (ret > 0)
+ this->curpos += ret;
+
+ return ret;
+}
+
+static buf_element_t *pps_plugin_read_block (input_plugin_t *this_gen,
+ fifo_buffer_t *fifo, off_t todo) {
+ /*pps_input_plugin_t *this = (pps_input_plugin_t *) this_gen; */
+ buf_element_t *buf = fifo->buffer_pool_alloc (fifo);
+ int total_bytes;
+
+ lprintf ("pps_plugin_read_block: %"PRId64" bytes...\n", todo);
+
+ if (todo > buf->max_size)
+ todo = buf->max_size;
+ if (todo < 0) {
+ buf->free_buffer (buf);
+ return NULL;
+ }
+
+ buf->content = buf->mem;
+ buf->type = BUF_DEMUX_BLOCK;
+
+ total_bytes = pps_plugin_read (this_gen, (char*)buf->content, todo);
+
+ if (total_bytes != todo) {
+ buf->free_buffer (buf);
+ return NULL;
+ }
+
+ buf->size = total_bytes;
+
+ return buf;
+}
+
+static off_t pps_plugin_seek (input_plugin_t *this_gen, off_t offset, int origin) {
+
+ pps_input_plugin_t *this = (pps_input_plugin_t *) this_gen;
+ int ret;
+
+ lprintf ("seek %"PRId64" bytes, origin %d\n", offset, origin);
+
+ /* only realtive forward-seeking is implemented */
+
+ if (origin == SEEK_CUR) {
+ offset += this->curpos;
+ } else if (origin == SEEK_END) {
+ if (this->size == -1)
+ return -1;
+ offset = this->size - offset;
+ }
+ ret = ppsvod_seek (this->fd, offset);
+ if (ret < 0) {
+ return -1;
+ }
+
+ this->curpos = offset;
+ return this->curpos;
+}
+
+static off_t pps_plugin_get_length (input_plugin_t *this_gen) {
+
+ pps_input_plugin_t *this = (pps_input_plugin_t *) this_gen;
+
+ if (this->size == -1)
+ this->size = ppsvod_get_file_length(this->fd);
+ return this->size;
+}
+
+static uint32_t pps_plugin_get_capabilities (input_plugin_t *this_gen) {
+ return INPUT_CAP_SEEKABLE;
+}
+
+static uint32_t pps_plugin_get_blocksize (input_plugin_t *this_gen) {
+ return 0;
+}
+
+static off_t pps_plugin_get_current_pos (input_plugin_t *this_gen){
+ pps_input_plugin_t *this = (pps_input_plugin_t *) this_gen;
+
+ /*
+ printf ("current pos is %"PRId64"\n", this->curpos);
+ */
+
+ return this->curpos;
+}
+
+static int pps_plugin_event_callback (int index, pps_event event)
+{
+ return 0;
+}
+
+static void pps_plugin_dispose (input_plugin_t *this_gen) {
+ pps_input_plugin_t *this = (pps_input_plugin_t *) this_gen;
+
+ if (this->fd >= 0) {
+ ppsvod_stop_item(this->fd);
+ ppsvod_remove_item(this->fd);
+ this->fd = -1;
+ }
+
+ if(this->mrl)
+ free(this->mrl);
+
+ ppsvod_destroy();
+
+ free (this);
+}
+
+static const char* pps_plugin_get_mrl (input_plugin_t *this_gen) {
+ pps_input_plugin_t *this = (pps_input_plugin_t *) this_gen;
+
+ return this->mrl;
+}
+
+static int pps_plugin_get_optional_data (input_plugin_t *this_gen,
+ void *data, int data_type) {
+#if 0
+ pps_input_plugin_t *this = (pps_input_plugin_t *) this_gen;
+
+ switch (data_type) {
+ case INPUT_OPTIONAL_DATA_PREVIEW:
+ break;
+ }
+#endif
+
+ return INPUT_OPTIONAL_UNSUPPORTED;
+}
+
+static int pps_plugin_open (input_plugin_t *this_gen) {
+ pps_input_plugin_t *this = (pps_input_plugin_t *) this_gen;
+
+ int ret;
+
+ lprintf ("trying to open '%s'\n", this->mrl);
+
+ if (ppsvod_create("ppstream", "demo", "ppsdemo001",
+ pps_plugin_event_callback) < 0) {
+ lprintf ("Failed to initialize ppsvod.\n");
+ return 0;
+ }
+
+ ret = ppsvod_add_item(this->mrl, 0);
+ if (ret < 0) {
+ lprintf ("Failed to open url.\n");
+
+ ppsvod_destroy();
+ return 0;
+ }
+ this->fd = ret;
+
+ ret = ppsvod_play_item(this->fd, NULL);
+ if (ret < 0) {
+ lprintf ("Failed to play url.\n");
+
+ ppsvod_remove_item(this->fd);
+ ppsvod_destroy();
+ return 0;
+ }
+ this->size = ppsvod_get_file_length(this->fd);
+
+ return 1;
+}
+
+static input_plugin_t *pps_class_get_instance (input_class_t *cls_gen, xine_stream_t *stream,
+ const char *mrl) {
+
+ /* pps_input_class_t *cls = (pps_input_class_t *) cls_gen; */
+ pps_input_plugin_t *this;
+
+ if (strncasecmp (mrl, "pps://", 6) &&
+ strncasecmp (mrl, "tvod://", 7))
+ return NULL;
+
+ this = calloc(1, sizeof (pps_input_plugin_t));
+
+ this->stream = stream;
+ this->fd = -1;
+ this->mrl = strdup (mrl);
+
+ this->input_plugin.open = pps_plugin_open;
+ this->input_plugin.get_capabilities = pps_plugin_get_capabilities;
+ this->input_plugin.read = pps_plugin_read;
+ this->input_plugin.read_block = pps_plugin_read_block;
+ this->input_plugin.seek = pps_plugin_seek;
+ this->input_plugin.get_current_pos = pps_plugin_get_current_pos;
+ this->input_plugin.get_length = pps_plugin_get_length;
+ this->input_plugin.get_blocksize = pps_plugin_get_blocksize;
+ this->input_plugin.get_mrl = pps_plugin_get_mrl;
+ this->input_plugin.dispose = pps_plugin_dispose;
+ this->input_plugin.get_optional_data = pps_plugin_get_optional_data;
+ this->input_plugin.input_class = cls_gen;
+
+ return &this->input_plugin;
+}
+
+/*
+ * pps input plugin class stuff
+ */
+
+static const char *pps_class_get_description (input_class_t *this_gen) {
+ return _("pps streaming input plugin");
+}
+
+static const char *pps_class_get_identifier (input_class_t *this_gen) {
+ return "pps";
+}
+
+static void pps_class_dispose (input_class_t *this_gen) {
+ pps_input_class_t *this = (pps_input_class_t *) this_gen;
+
+ free (this);
+}
+
+static void *init_class (xine_t *xine, void *data) {
+
+ pps_input_class_t *this;
+
+ this = calloc(1, sizeof (pps_input_class_t));
+
+ this->xine = xine;
+
+ this->input_class.get_instance = pps_class_get_instance;
+ this->input_class.get_identifier = pps_class_get_identifier;
+ this->input_class.get_description = pps_class_get_description;
+ this->input_class.get_dir = NULL;
+ this->input_class.get_autoplay_list = NULL;
+ this->input_class.dispose = pps_class_dispose;
+ this->input_class.eject_media = NULL;
+
+ return this;
+}
+
+/*
+ * exported plugin catalog entry
+ */
+
+const plugin_info_t xine_plugin_info[] EXPORTED = {
+ /* type, API, "name", version, special_info, init_function */
+ { PLUGIN_INPUT, 17, "pps", XINE_VERSION_CODE, NULL, init_class },
+ { PLUGIN_NONE, 0, "", 0, NULL, NULL }
+};
+
diff --git a/pps-fixed.h b/pps-fixed.h
new file mode 100644
index 0000000..80014a8
--- /dev/null
+++ b/pps-fixed.h
@@ -0,0 +1,12 @@
+#ifndef PPS_FIXED_H
+#define PPS_FIXED_H
+
+#define uint64_t pps_uint64_t
+#define int64_t pps_int64_t
+
+#include "pps.h"
+
+#undef uint64_t
+#undef int64_t
+
+#endif
diff --git a/pps.h b/pps.h
new file mode 100644
index 0000000..06a6642
--- /dev/null
+++ b/pps.h
@@ -0,0 +1,372 @@
+#ifndef __PPS_H__
+#define __PPS_H__
+
+
+#ifndef HASH_VALUE_LENGTH
+#define HASH_VALUE_LENGTH 20
+#endif
+
+#define PPS_EXTRA_MSG_LEN 64
+#define PPS_MAX_MAP_SIZE 65536
+
+typedef unsigned long long uint64_t;
+typedef long long int64_t;
+
+typedef enum pps_event_id
+{
+ EPPS_SERVER_TIMEOUT, // 0, 连接服务器超时,停止播放
+ EPPS_NO_SERVER, // 1, 找不到服务器,停止播放
+ EPPS_SERVER_NO_RESPONSE, // 2, 服务器未响应,停止播放
+ EPPS_SERVER_INVALID, // 3, 解析服务器域名失败.
+ EPPS_VERSION_LOW, // 4, 客户端版本号太低,退出网络
+ EPPS_CLIENT_NAME, // 5, 客户端名称或引用页不正确,退出播放
+ EPPS_URL_INVALID, // 6, 播放的文件不存在或网址错误
+ EPPS_NO_SERVICE, // 7, 服务不存在,停止播放
+ EPPS_OUT_OF_SERVICE, // 8, 不在服务区.
+ EPPS_MEDIA_INFO_ERROR, // 9, 媒体信息加载时错误..
+ EPPS_INDEX_INFO_ERROR, // 10, 媒体索引信息错误
+ EPPS_NO_INDEX, // 11, 没有获取到媒体索引信息,不能拖动播放位置
+ EPPS_NO_MEIDIA, // 12, 找不到媒体
+ EPPS_MULTI_INSTANCE, // 13, 请确定是否有另一个实例正在播放此文件
+ EPPS_PREPARE_MEDIA_INFO, // 14, 正在准备媒体信息
+ EPPS_GETTING_MEDIA_INFO, // 15, 正在获取媒体信息
+ EPPS_GETTING_INDEX_INFO, // 16, 正在获取媒体索引信息
+ EPPS_PLAYING, // 17, 正在播放
+ EPPS_BUFFERING, // 18, 正在缓冲数据
+ EPPS_CONNECTING, // 19, 正在连入网络
+ EPPS_MEDIA_READY, // 20, 准备媒体就绪
+ EPPS_PARSING_SERVER, // 21, 正在解析服务器域名..
+ EPPS_GET_MEDIA_INFO, // 22, 已成功获取到媒体信息.
+ EPPS_BUFFERED, // 23, 数据缓冲完毕.
+ EPPS_PREPARE_MEDIA, // 24, 准备媒体
+ EPPS_STORAGE_ERROR, // 25. 访问存储器失败
+ EPPS_QUIT, // 26. 程序退出
+
+ EPPS_DATA_TIMEOUT, // 27, 等待数据超时
+
+ EPPS_UPDATING, // 28, 正在升级
+ EPPS_UPDATE_FAILED, // 29, 升级失败
+ EPPS_UPDATE_OK, // 30, 升级成功
+}pps_event_id;
+
+typedef struct pps_event
+{
+ pps_event_id id;
+ unsigned int wparam;
+ unsigned int lparam;
+ char extra[PPS_EXTRA_MSG_LEN];
+}pps_event;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int (*pps_callback)(int index, pps_event event);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+typedef struct ppsvod_iteminfo
+{
+ int index;
+ char hash_id[HASH_VALUE_LENGTH]; // task hash id
+ char *file_name; // save name
+ unsigned int file_size; // total file size
+ int progress; // download percent
+ int down_speed; // download speed (byte/s)
+ int up_speed; // upload speed (byte/s)
+ int peer_count; // peer count has connected
+ int status; // has the task been started?
+}ppsvod_iteminfo;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Description: ppsvod initialization, must be called before all.
+ * param: Datatype Param name In/Out Comment
+ * const char * oem_name in
+ * const char * terminal_type in
+ * const char * device_id in
+ * pps_callback pcallback in callback function pointer
+ * return: int return 0 if success; -1 if failed.
+ */
+int ppsvod_create(const char *oem_name,
+ const char * terminal_type,
+ const char *device_id,
+ pps_callback pcallback);
+
+/*
+ * Description: uninitialize ppsvod, call this fuction when you want to exit ppsvod.
+ * param: Datatype Param name In/Out Comment
+ * void
+ * return: int return 0 if success; -1 if failed
+ */
+int ppsvod_destroy(void);
+
+/*
+ * Description: Add an item into ppsvod internal list.
+ * param: Datatype Param name In/Out Comment
+ * char * url In url to add
+ * int vip_flag In flag for vip items
+ * return: int return a file descriptor if success; -1 if failed.
+ */
+int ppsvod_add_item(char *url, int vip_flag);
+
+/*
+ * Description: Remove an item from ppsvod internal list.
+ * param: Datatype Param name In/Out Comment
+ * int fd In file descriptor
+ * return: int return 0 if success; -1 if failed.
+ */
+int ppsvod_remove_item(int fd);
+
+/*
+ * Description: start to play a file.
+ * param: Datatype Param name In/Out Comment
+ * int fd In file descriptor
+ * const char* validcode In
+ * return: int return 0 if success; -1 if failed.
+ */
+int ppsvod_play_item(int fd, const char * validcode);
+
+/*
+ * Description: stop the playing url.
+ * param: Datatype Param name In/Out Comment
+ * int fd In file descriptor
+ * return: int return 0 if success; -1 if failed; -3 means validcode error.
+ */
+int ppsvod_stop_item(int fd);
+
+/*
+ * Description: read data.
+ * param: Datatype Param name In/Out Comment
+ * int fd In file descriptor
+ * char * buffer Out buffer to store data
+ * unsigned int length In data length
+ * unsigned int position In from where to read.
+ * struct timeval* wait_time In time length that read() can block
+ * return: int return length of data if success; -1 if failed(errno
+ * is set to EAGAIN if data is temporarily unavailable; etherwise,
+ * some error happens or EOF reaches).
+ */
+int ppsvod_read(int fd, char *buffer, unsigned int length, unsigned int position, struct timeval *wait_time);
+
+/*
+ * Description: Drop read operation
+ * param: Datatype Param name In/Out Comment
+ * int fd In file descriptor
+ */
+void ppsvod_drop_read(int fd);
+
+/*
+ * Description: seeking within a playing url.
+ * param: Datatype Param name In/Out Comment
+ * int fd In file descriptor
+ * unsigned int position In seeking destination
+ * return: int return 0 if success; -1 if failed.
+ */
+int ppsvod_seek(int fd, unsigned int position);
+
+/*
+ * Description: get total file length
+ * param: Datatype Param name In/Out Comment
+ * int fd In file descriptor
+ * return: int return file length if success; -1 if failed.
+ */
+int ppsvod_get_file_length(int fd);
+
+/*
+ * Description: validate url.
+ * param: Datatype Param name In/Out Comment
+ * const char* url In url to check
+ * return: int return 0 if success; -1 if failed.
+ */
+int ppsvod_check_url(const char* url);
+
+/*
+ * Description: Get information for a playing file.
+ * param: Datatype Param name In/Out Comment
+ * int fd In file descriptor
+ * return: ppsvod_iteminfo* return a pointer to ppsvod_iteminfo if success;
+ * NULL if failed.
+ */
+ppsvod_iteminfo *ppsvod_get_item_info(int fd);
+
+/*
+ * Description: Free memory of a ppsvod_iteminfo* pointer
+ * param: Datatype Param name In/Out Comment
+ * ppsvod_iteminfo* piteminfo In pointer returned by ppsvod_get_item_info
+ * return: void
+ */
+void ppsvod_free_item_info(ppsvod_iteminfo *piteminfo);
+
+/*
+ * APIs for playlist operation
+ */
+
+/*
+ * Define download, unzip and parse vodlist file status.
+ * vodlist status use highest 4 bits to express stage, use
+ * lowest 8 bits to express every stage process
+ */
+#define VODLIST_IDLE 0x0
+#define VODLIST_DOWNLOADING 0x10000000
+#define VODLIST_UNCOMPRESSING 0x20000000
+#define VODLIST_PARSING 0x40000000
+#define VODLIST_UPDATE_SUCCESS 0x80000000
+#define VODLIST_UPDATE_FAIL 0xff000000
+
+typedef struct __ppscategory
+{
+ int id; /* category or subcategory id */
+ char *name; /* category or subcategory num */
+ int type; /* subcategory type. 0: movie, 1: teleplay */
+ int subcatnum; /* subcategory number under this category */
+ struct __ppscategory *psubcat; /* subcategory array */
+
+ struct __ppscategory *next;
+}ppscategory;
+
+
+typedef struct __ppsitem
+{
+ int id; /* Item id */
+ int index; /* Item index */
+ int size; /* Item size (MB) */
+ int duration; /* Item duration (Minute)*/
+ char *format; /* file format (rm/rmvb/wmv) */
+ int bitrate; /* bitrate */
+ char *ppsurl; /* pps vod url */
+ int vipflag; /* vip flags */
+ struct __ppsitem *next;
+}ppsitem;
+
+typedef struct __ppschannel
+{
+ char *name; /* channel name */
+ char *director;
+ char *actor;
+ char *area;
+ int size; /* all items' total size under this channel (MB) */
+ char *pubtime; /* publish time */
+ int duration; /* all items' total duration time under this channel (Min) */
+ char *lang; /* channel language */
+ char *desc; /* channel description */
+ char *BImgUrl; /* Big Image Url */
+ char *SImgUrl; /* Small Image Url */
+ int itemnum; /* num of items */
+ struct __ppsitem *pitems; /* point to items list */
+
+ struct __ppschannel *next;
+}ppschannel;
+
+
+/*
+ * description: vod list initialization, must be called before all.
+ * param: datatype param name in/out comment
+ * void
+ * return: int return 0 if success; -1 if failed.
+*/
+int pps_vodlist_init(void);
+
+/*
+ * description: vod list uninitialize, call this fuction when you want to exit pps vodlist.
+ * param: datatype param name in/out comment
+ * void
+ * return: int return 0 if success; -1 if failed.
+*/
+int pps_vodlist_uninit(void);
+
+/*
+ * description: get all categories and sub categories list.
+ * param: datatype param name in/out comment
+ * void
+ * return: ppscategory* pointer of all categories, null if failed.
+*/
+ppscategory *pps_vodlist_getcat(int *catnum);
+
+/*
+ * description: get channels under pointed catogory and sub category.
+ * param: datatype param name in/out comment
+ * int catid in class id
+ * int subcatid in sub class id
+ * int page_size in channels one page have
+ * int page_num in page num which want to query
+ * return: ppschannel pointer of channels, null if failed.
+*/
+ppschannel *pps_vodlist_getchannel(int catid, int subcatid, int page_size, int page_num);
+
+/*
+ * Description: configure directory for store cache, config, data and logs.
+ * param: Datatype Param name In/Out Comment
+ * const char* root_path In root path used to store
+ * return: int return 0 if success, -1 if failed.
+ * comment:
+ */
+int ppsvod_save_config_dir(const char * root_path);
+
+/*
+ * Description: load configure directory for store cache, config, data and logs.
+ * param: Datatype Param name In/Out Comment
+ * void
+ * return: char* return root path if success, NULL if failed.
+ * comment:
+ */
+char *ppsvod_get_config_dir(void);
+
+/*
+ * Description: load default configure directory for store cache, config, data and logs.
+ * param: Datatype Param name In/Out Comment
+ * void
+ * return: int return 0 if success, -1 if failed.
+ * comment:
+ */
+int ppsvod_default_config_dir(void);
+
+/*
+ * Description: Deposit money for PPS VIP account
+ * param: Datatype Param name In/Out Comment
+ * const char* password In 17 numbers sold by pps
+ * return: int return 0 if success, -1 if failed.
+ * comment:
+ */
+int ppsvod_charge(const char* password);
+
+/*
+ * Description: Get account type of current user
+ * param: Datatype Param name In/Out Comment
+ * void
+ * return: int return -1 if failed;
+ * return 0: Non-VIP user
+ * return 1: Payment VIP
+ * comment:
+ */
+int ppsvod_get_account_type(void);
+
+/*
+ * Description: Get expire time of vip qualification
+ * param: Datatype Param name In/Out Comment
+ * char* time Out memory allocated by caller
+ * return: int -1: failed
+ * 0: OK
+ */
+int ppsvod_get_vip_expire_time(char *time);
+
+
+/*
+ * Description: Get time from network
+ * param: Datatype Param name In/Out Comment
+ * struct timeval tv Out time since 1970
+ * return: int return 0 if success, -1 if failed.
+ * comment:
+ */
+int ppsvod_get_time(struct timeval *tv);
+
+#ifdef __cplusplus
+}
+#endif
+#endif // __PPS_H__