summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoroskwon <kos@dev3>2014-06-12 00:14:12 (GMT)
committeroskwon <kos@dev3>2014-06-12 01:37:55 (GMT)
commitbcfacba238ee3e4e2f04c71293841734d0444311 (patch)
tree342b669c05327759712fc3be5edcc67cc8e5ec5d
import sources.
-rw-r--r--.gitignore4
-rw-r--r--src/Demuxer.cpp223
-rw-r--r--src/Demuxer.h48
-rw-r--r--src/Encoder.cpp85
-rw-r--r--src/Encoder.h46
-rw-r--r--src/Logger.cpp184
-rw-r--r--src/Logger.h79
-rw-r--r--src/Makefile72
-rw-r--r--src/Source.h23
-rw-r--r--src/UriDecoder.cpp277
-rw-r--r--src/UriDecoder.h43
-rw-r--r--src/Utils.cpp193
-rw-r--r--src/Utils.h79
-rw-r--r--src/main.cpp332
-rw-r--r--src/openpli-streamproxy/mpegts.cpp734
-rw-r--r--src/openpli-streamproxy/mpegts.h216
-rw-r--r--src/openpli-streamproxy/trap.cpp42
-rw-r--r--src/openpli-streamproxy/trap.h33
-rwxr-xr-xsrc/up.sh8
19 files changed, 2721 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..79f27fc
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+*.o
+transtreamproxy
+.cproject
+.project
diff --git a/src/Demuxer.cpp b/src/Demuxer.cpp
new file mode 100644
index 0000000..a943bab
--- /dev/null
+++ b/src/Demuxer.cpp
@@ -0,0 +1,223 @@
+/*
+ * Demux.h
+ *
+ * Created on: 2014. 6. 11.
+ * Author: oskwon
+ */
+
+#include <poll.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <netinet/ip.h>
+#include <linux/dvb/dmx.h>
+#include <linux/dvb/version.h>
+
+#include "Utils.h"
+#include "Logger.h"
+#include "Demuxer.h"
+
+using namespace std;
+//-------------------------------------------------------------------------------
+
+std::string Demuxer::webif_reauest(std::string service, std::string auth) throw(trap)
+{
+ if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0)
+ throw(trap("webif create socket fail."));
+
+ struct sockaddr_in sock_addr;
+ sock_addr.sin_family = AF_INET;
+ sock_addr.sin_port = htons(80);
+ sock_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+ if (connect(sock, (struct sockaddr*)&sock_addr, sizeof(struct sockaddr_in)))
+ throw(trap("webif connect fail."));
+
+ std::string request = string("GET /web/stream?StreamService=") + service + " HTTP/1.0\r\n";
+ if (auth != "")
+ request += "Authorization: " + auth + "\r\n";
+ request += "\r\n";
+
+ if (write(sock, request.c_str(), request.length()) != request.length())
+ throw(trap("webif send(request) fail."));
+ DEBUG("webif request :\n", request.c_str());
+
+ std::string response = "";
+ struct pollfd pollevt[2];
+ pollevt[0].fd = sock;
+ pollevt[0].events = POLLIN;
+ for (;;) {
+ char buffer[1024] = {0};
+
+ pollevt[0].revents = 0;
+ int poll_state = poll(pollevt, 1, 1000);
+ if (poll_state == 0)
+ break;
+ else if (poll_state < 0) {
+ ERROR("webif receive poll error : %s (%d)", strerror(errno), errno);
+ throw(trap("webif receive response error."));
+ }
+ if (pollevt[0].revents & POLLIN) {
+ if (read(sock, buffer, 1024) <= 0) {
+ break;
+ }
+ response += buffer;
+ }
+ }
+ return response;
+}
+//-------------------------------------------------------------------------------
+
+bool Demuxer::already_exist(std::vector<unsigned long> &pidlist, int pid)
+{
+ for(int i = 0; i < pidlist.size(); ++i) {
+ if(pidlist[i] == pid)
+ return true;
+ }
+ return false;
+}
+//-------------------------------------------------------------------------------
+
+void Demuxer::set_filter(std::vector<unsigned long> &new_pids) throw(trap)
+{
+ struct dmx_pes_filter_params filter;
+ ioctl(fd, DMX_SET_BUFFER_SIZE, 1024*1024);
+
+ filter.pid = new_pids[0];
+ filter.input = DMX_IN_FRONTEND;
+#if DVB_API_VERSION > 3
+ filter.output = DMX_OUT_TSDEMUX_TAP;
+ filter.pes_type = DMX_PES_OTHER;
+#else
+ filter.output = DMX_OUT_TAP;
+ filter.pes_type = DMX_TAP_TS;
+#endif
+ filter.flags = DMX_IMMEDIATE_START;
+
+ if (::ioctl(fd, DMX_SET_PES_FILTER, &filter) < 0)
+ throw(trap("demux filter setting failed."));
+ DEBUG("demux filter setting ok.");
+
+ for(int i = 0; i < new_pids.size(); ++i) {
+ uint16_t pid = new_pids[i];
+
+ if(already_exist(pids, pid))
+ continue;
+ LOG("demux add pid (%x).", pid);
+
+#if DVB_API_VERSION > 3
+ if (::ioctl(fd, DMX_ADD_PID, &pid) < 0)
+ throw(trap("demux add pid failed."));
+#else
+ if (::ioctl(fd, DMX_ADD_PID, pid) < 0)
+ throw(trap("demux add pid failed."));
+#endif
+ }
+
+ for(int i = 0; i < pids.size(); ++i) {
+ uint16_t pid = pids[i];
+ if(already_exist(new_pids, pid))
+ continue;
+ if(i == 4) break;
+
+ LOG("demux remove pid (%x).", pid);
+#if DVB_API_VERSION > 3
+ ::ioctl(fd, DMX_REMOVE_PID, &pid);
+#else
+ ::ioctl(fd, DMX_REMOVE_PID, pid);
+#endif
+ }
+ DEBUG("demux setting PID ok.");
+ pids = new_pids;
+}
+//-------------------------------------------------------------------------------
+
+bool Demuxer::parse_webif_response(std::string& response, std::vector<unsigned long> &new_pids)
+{
+ int start_idx, end_idx;
+ if ((start_idx = response.rfind('+')) == string::npos)
+ return false;
+ if ((end_idx = response.find('\n', start_idx)) == string::npos)
+ return false;
+
+ std::string line = response.substr(start_idx, end_idx - start_idx);
+ if (line.length() < 3 || line.at(0) != '+')
+ return false;
+
+ /*+0:0:pat,17d4:pmt,17de:video,17e8:audio,17e9:audio,17eb:audio,17ea:audio,17f3:subtitle,17de:pcr,17f2:text*/
+ demux_id = atoi(line.substr(1,1).c_str());
+
+ std::vector<std::string> pidtokens;
+ if (split(line.c_str() + 3, ',', pidtokens)) {
+ for (int i = 0; i < pidtokens.size(); ++i) {
+ std::string pidstr, pidtype;
+ std::string toekn = pidtokens[i];
+ if (!split_key_value(toekn, ":", pidstr, pidtype))
+ continue;
+
+ unsigned long pid = strtoul(pidstr.c_str(), 0, 0x10);
+ if (pid == -1) continue;
+
+ if (!video_pid || !audio_pid || !pmt_pid) {
+ if (pidtype == "pat") {
+ pat_pid = pid;
+ }
+ else if (pidtype == "pmt") {
+ pmt_pid = pid;
+ }
+ else if (pidtype == "video") {
+ video_pid = pid;
+ }
+ else if (pidtype == "audio") {
+ audio_pid = pid;
+ }
+ }
+ if (!already_exist(new_pids, pid)) {
+ new_pids.push_back(pid);
+ }
+ DEBUG("find pid : %s - %04X", toekn.c_str(), pid);
+ }
+ }
+ return true;
+}
+//-------------------------------------------------------------------------------
+
+Demuxer::Demuxer(RequestHeader *header) throw(trap)
+{
+ demux_id = pat_pid = fd = sock = -1;
+ pmt_pid = audio_pid = video_pid = 0;
+
+ std::string auth = header->params["WWW-Authenticate"];
+ std::string service = header->path.substr(1);
+ std::string webif_response = webif_reauest(service, auth);
+ DEBUG("webif response :\n%s", webif_response.c_str());
+
+ std::vector<unsigned long> new_pids;
+ if (!parse_webif_response(webif_response, new_pids))
+ throw(trap("webif response parsing fail."));
+
+ std::string demuxpath = "/dev/dvb/adapter0/demux" + ultostr(demux_id);
+ if ((fd = open(demuxpath.c_str(), O_RDWR | O_NONBLOCK)) < 0) {
+ throw(trap(std::string("demux open fail : ") + demuxpath));
+ }
+ INFO("demux open success : %s", demuxpath.c_str());
+
+ set_filter(new_pids);
+}
+//-------------------------------------------------------------------------------
+
+Demuxer::~Demuxer() throw()
+{
+ if (fd != -1) close(fd);
+ if (sock != -1) close(sock);
+
+ fd = sock = -1;
+}
+//-------------------------------------------------------------------------------
+
+int Demuxer::get_fd() const throw()
+{
+ return fd;
+}
+//-------------------------------------------------------------------------------
diff --git a/src/Demuxer.h b/src/Demuxer.h
new file mode 100644
index 0000000..2b8e636
--- /dev/null
+++ b/src/Demuxer.h
@@ -0,0 +1,48 @@
+/*
+ * Demux.h
+ *
+ * Created on: 2014. 6. 11.
+ * Author: oskwon
+ */
+
+#ifndef DEMUX_H_
+#define DEMUX_H_
+
+#include <vector>
+#include <string>
+
+#include "trap.h"
+
+#include "Utils.h"
+#include "Source.h"
+//----------------------------------------------------------------------
+
+class Demuxer : public Source
+{
+public:
+ int pmt_pid;
+ int video_pid;
+ int audio_pid;
+
+private:
+ int fd;
+ int sock;
+
+ int demux_id;
+ int pat_pid;
+ std::vector<unsigned long> pids;
+
+protected:
+ std::string webif_reauest(std::string service, std::string auth) throw(trap);
+ bool already_exist(std::vector<unsigned long> &pidlist, int pid);
+ void set_filter(std::vector<unsigned long> &new_pids) throw(trap);
+ bool parse_webif_response(std::string& response, std::vector<unsigned long> &new_pids);
+
+public:
+ Demuxer(RequestHeader *header) throw(trap);
+ virtual ~Demuxer() throw();
+ int get_fd() const throw();
+};
+//----------------------------------------------------------------------
+
+#endif /* DEMUX_H_ */
diff --git a/src/Encoder.cpp b/src/Encoder.cpp
new file mode 100644
index 0000000..23f66e0
--- /dev/null
+++ b/src/Encoder.cpp
@@ -0,0 +1,85 @@
+/*
+ * Encoder.cpp
+ *
+ * Created on: 2014. 6. 12.
+ * Author: oskwon
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include "Utils.h"
+#include "Logger.h"
+#include "Encoder.h"
+
+using namespace std;
+//----------------------------------------------------------------------
+
+Encoder::Encoder()
+{
+ fd = -1;
+ state = ENCODER_STAT_INIT;
+}
+//----------------------------------------------------------------------
+
+Encoder::~Encoder()
+{
+ if (fd != -1) {
+ if (state == ENCODER_STAT_STARTED) {
+ DEBUG("stop transcoding..");
+ ioctl(IOCTL_STOP_TRANSCODING, 0);
+ }
+ close(fd);
+ fd = -1;
+ }
+}
+//----------------------------------------------------------------------
+
+bool Encoder::open(int encoder_id)
+{
+ std::string path = "/dev/bcm_enc" + ultostr(encoder_id);
+ fd = ::open(path.c_str(), O_RDWR, 0);
+ if (fd >= 0) {
+ state = ENCODER_STAT_OPENED;
+ }
+ DEBUG("open encoder : %s, fd : %d", path.c_str(), fd);
+ return (state == ENCODER_STAT_OPENED) ? true : false;
+}
+//----------------------------------------------------------------------
+
+bool Encoder::retry_open(int encoder_id, int retry_count, int sleep_time)
+{
+ for (int i = 0; i < retry_count; ++i) {
+ if (open(encoder_id)) {
+ DEBUG("encoder-%d open success..", encoder_id);
+ return true;
+ }
+ WARNING("encoder%d open fail, retry count : %d/%d", encoder_id, i, retry_count);
+ sleep(sleep_time);
+ }
+ return false;
+}
+
+bool Encoder::ioctl(int cmd, int value)
+{
+ int result = ::ioctl(fd, cmd, value);
+ DEBUG("ioctl command : %d -> %x, result : %d", cmd, value, result);
+
+ if (result == 0) {
+ switch (cmd) {
+ case IOCTL_START_TRANSCODING: state = ENCODER_STAT_STARTED; break;
+ case IOCTL_STOP_TRANSCODING: state = ENCODER_STAT_STOPED; break;
+ }
+ }
+
+ return (result == 0) ? true : false;
+}
+//----------------------------------------------------------------------
+
+int Encoder::get_fd()
+{
+ return fd;
+}
+//----------------------------------------------------------------------
diff --git a/src/Encoder.h b/src/Encoder.h
new file mode 100644
index 0000000..3ffea57
--- /dev/null
+++ b/src/Encoder.h
@@ -0,0 +1,46 @@
+/*
+ * Encoder.h
+ *
+ * Created on: 2014. 6. 10.
+ * Author: oskwon
+ */
+
+#ifndef ENCODER_H_
+#define ENCODER_H_
+
+class Encoder
+{
+private:
+ int fd;
+
+public:
+ enum {
+ IOCTL_SET_VPID = 1,
+ IOCTL_SET_APID = 2,
+ IOCTL_SET_PMTPID = 3,
+ IOCTL_START_TRANSCODING = 100,
+ IOCTL_STOP_TRANSCODING = 200
+ };
+
+ enum {
+ ENCODER_STAT_INIT = 0,
+ ENCODER_STAT_OPENED,
+ ENCODER_STAT_STARTED,
+ ENCODER_STAT_STOPED,
+ };
+
+ int state;
+protected:
+ bool open(int encoder_id);
+
+public:
+ Encoder();
+ virtual ~Encoder();
+
+ int get_fd();
+ bool ioctl(int cmd, int value);
+ bool retry_open(int encoder_id, int retry_count, int sleep_time);
+};
+//----------------------------------------------------------------------
+
+#endif /* ENCODER_H_ */
diff --git a/src/Logger.cpp b/src/Logger.cpp
new file mode 100644
index 0000000..8d06b88
--- /dev/null
+++ b/src/Logger.cpp
@@ -0,0 +1,184 @@
+/*
+ * logger.cpp
+ *
+ * Created on: 2014. 2. 7.
+ * Author: kos
+ */
+
+#include <time.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "Logger.h"
+
+#define USE_COLOR_LOG 1
+
+static char log_data_buffer[MAX_PRINT_LEN] = {0};
+#ifdef USE_COLOR_LOG
+static const char* LOG_LV_STR[] = {
+ "[ NONE]",
+ "\e[1;31m[ ERROR]\e[00m",
+ "\e[1;33m[WARNING]\e[00m",
+ "\e[1;32m[ INFO]\e[00m",
+ "\e[1;36m[ DEBUG]\e[00m",
+ "[ LOG]"
+};
+#else
+static const char* LOG_LV_STR[] = {
+ "[ NONE]",
+ "[ ERROR]",
+ "[WARNING]",
+ "[ INFO]",
+ "[ DEBUG]",
+ "[ LOG]"
+};
+#endif
+//----------------------------------------------------------------------
+
+char* timestamp(const char* aFormat)
+{
+ time_t t;
+ time(&t);
+
+ struct tm* m = localtime(&t);
+ static char sz_timestamp[32] = {0};
+ strftime(sz_timestamp, sizeof(sz_timestamp), aFormat, m);
+ return sz_timestamp;
+}
+//----------------------------------------------------------------------
+
+Logger::Logger()
+ : mLogLevel(0), mLogHandle(0)
+{
+ mPid = getpid();
+}
+//----------------------------------------------------------------------
+
+Logger::~Logger()
+{
+ if (mLogHandle) {
+ fclose(mLogHandle);
+ mLogHandle = 0;
+ }
+}
+//----------------------------------------------------------------------
+
+void Logger::set_pid()
+{
+ mPid = getpid();
+}
+//----------------------------------------------------------------------
+
+Logger* Logger::instance()
+{
+ if (mInstHandle == 0) {
+ mInstHandle = new Logger();
+ atexit(logger_release);
+ }
+ return mInstHandle;
+}
+//----------------------------------------------------------------------
+
+bool Logger::init(const char* aName, int aLogLevel, bool aWithTimestamp, const char* aVersion)
+{
+ mLogLevel = aLogLevel;
+ if (aName == NULL) {
+ mLogHandle = stdout;
+ INFO("logger initialized.");
+ return true;
+ }
+ char path[256] = {0};
+ if (aWithTimestamp)
+ sprintf(path, "%s_%s.log", aName, timestamp("%Y%m%d"));
+ else sprintf(path, "%s.log", aName);
+ if (!(mLogHandle = fopen(path, "a+"))) {
+ mLogHandle = 0;
+ printf("fail to open logger [%s].", path);
+ return false;
+ }
+ DUMMY("Logger initialized. (Ver %s)", aVersion);
+ return true;
+}
+//----------------------------------------------------------------------
+
+void Logger::hexlog(const char *header, const char *buffer, const int length, const char *aFormat, ...)
+{
+ int offset = 0, i = 0, ll = 0;
+
+ FILE* output = mLogHandle;
+
+ memset(log_data_buffer, 0, MAX_PRINT_LEN);
+
+ va_list args;
+ va_start(args, aFormat);
+ ll = vsnprintf(log_data_buffer, MAX_PRINT_LEN-1, aFormat, args);
+ va_end(args);
+
+ if (ll > MAX_PRINT_LEN - 1) {
+ ll = MAX_PRINT_LEN - 1;
+ }
+ fprintf(output, "%s\n", log_data_buffer);
+
+ fprintf(output, "HEX DUMP : [%s]-[%d]\n", header, length);
+ fprintf(output, "-----------------------------------------------------------------------------\n");
+ while (offset < length) {
+ char *tmp = (char*) (buffer + offset);
+ int tmp_len = (offset + 16 < length) ? 16 : (length - offset);
+
+ fprintf(output, "%08X: ", offset);
+ for (i = 0; i < tmp_len; i++) {
+ if (i == 8) fprintf(output, " ");
+ fprintf(output, "%02X ", (unsigned char) tmp[i]);
+ }
+
+ for (i = 0; i <= (16 - tmp_len) * 3; i++)
+ fprintf(output, " ");
+ if (tmp_len < 9) fprintf(output, " ");
+
+ for (i = 0; i < tmp_len; i++)
+ fprintf(output, "%c", (tmp[i] >= 0x20 && tmp[i] <= 0x7E) ? tmp[i] : '.');
+ offset += 16; fprintf(output, "\n");
+ }
+ if (offset == 0) fprintf(output, "%08X: ", offset);
+ fprintf(output, "-----------------------------------------------------------------------------\n");
+ fflush(output);
+}
+//----------------------------------------------------------------------
+
+void Logger::log(const char* aFormat, ...)
+{
+ memset(log_data_buffer, 0, MAX_PRINT_LEN);
+
+ va_list args;
+ va_start(args, aFormat);
+ vsnprintf(log_data_buffer, MAX_PRINT_LEN-1, aFormat, args);
+ va_end(args);
+
+ fprintf(mLogHandle, "%s\n", log_data_buffer);
+ fflush(mLogHandle);
+}
+//----------------------------------------------------------------------
+
+void Logger::log(int aLogLevel, const char* aFormat, ...)
+{
+#ifndef _DISABLE_LOGGER
+ if (aLogLevel > mLogLevel || mLogHandle == 0) {
+ //printf("mLogHandle : %p, mLogLevel : %d, aLogLevel : %d\n", mLogHandle, mLogLevel, aLogLevel);
+ return;
+ }
+
+ memset(log_data_buffer, 0, MAX_PRINT_LEN);
+ va_list args;
+ va_start(args, aFormat);
+ vsnprintf(log_data_buffer, MAX_PRINT_LEN-1, aFormat, args);
+ va_end(args);
+
+ fprintf(mLogHandle, "[%s]%s[%d] %s\n", timestamp(DEFAULT_TIMESTAMP_FORMAT), LOG_LV_STR[aLogLevel], mPid, log_data_buffer);
+ fflush(mLogHandle);
+#endif
+}
+//----------------------------------------------------------------------
+
+Logger* Logger::mInstHandle = 0;
+//----------------------------------------------------------------------
diff --git a/src/Logger.h b/src/Logger.h
new file mode 100644
index 0000000..eaa77c5
--- /dev/null
+++ b/src/Logger.h
@@ -0,0 +1,79 @@
+/*
+ * Logger.h
+ *
+ * Created on: 2014. 2. 7.
+ * Author: oskwon
+ */
+
+#ifndef LOGGER_H_
+#define LOGGER_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdint.h>
+//----------------------------------------------------------------------
+
+#define MAX_PRINT_LEN 2048
+#define DEFAULT_TIMESTAMP_FORMAT "%Y%m%d-%H%M%S"
+
+#ifdef _DISABLE_LOGGER
+# define ERROR(fmt,...) {}
+# define WARNING(fmt,...) {}
+# define INFO(fmt,...) {}
+# define DEBUG(fmt,...) {}
+# define LOG(fmt,...) {}
+# define LINESTAMP(fmt,...) {}
+# define HEXLOG(fmt,...) {}
+# define DUMMY(fmt,...) {}
+#else
+# define ERROR(fmt,...) { Logger::instance()->log(Logger::ERROR, fmt" (%s, %s:%d)", ##__VA_ARGS__, __FILE__, __FUNCTION__, __LINE__); }
+# define WARNING(fmt,...){ Logger::instance()->log(Logger::WARNING, fmt" (%s, %s:%d)", ##__VA_ARGS__, __FILE__, __FUNCTION__, __LINE__); }
+# define INFO(fmt,...) { Logger::instance()->log(Logger::INFO, fmt" (%s, %s:%d)", ##__VA_ARGS__, __FILE__, __FUNCTION__, __LINE__); }
+# define DEBUG(fmt,...) { Logger::instance()->log(Logger::DEBUG, fmt" (%s, %s:%d)", ##__VA_ARGS__, __FILE__, __FUNCTION__, __LINE__); }
+# define LOG(fmt,...) { Logger::instance()->log(Logger::LOG, fmt" (%s, %s:%d)", ##__VA_ARGS__, __FILE__, __FUNCTION__, __LINE__); }
+# define LINESTAMP(fmt,...) { Logger::instance()->log(fmt" (%s, %s:%d)", ##__VA_ARGS__, __FILE__, __FUNCTION__, __LINE__); }
+# define HEXLOG(header, buffer, length) { Logger::instance()->hexlog(header, buffer, length, " (%s, %s:%d)", __FILE__, __FUNCTION__, __LINE__); }
+# define DUMMY(fmt,...) { Logger::instance()->log(fmt, ##__VA_ARGS__); }
+#endif /* USE_DEBUG */
+//----------------------------------------------------------------------
+
+class Logger
+{
+private:
+ int mLogLevel, mPid;
+ FILE* mLogHandle;
+
+ static Logger* mInstHandle;
+
+private:
+ Logger();
+ virtual ~Logger();
+
+ static void logger_release()
+ {
+ if (mInstHandle) {
+ DUMMY("Logger Released.");
+ delete mInstHandle;
+ }
+ };
+
+public:
+ enum { NONE = 0, ERROR, WARNING, INFO, DEBUG, LOG };
+
+#ifndef _DISABLE_LOGGER
+ bool init(const char* aFileName = 0, int aLogLevel = Logger::ERROR, bool aWithTimestamp = true, const char* aVersion = 0);
+
+ void log(const char* aFormat, ...);
+ void log(int aLogLevel, const char* aFormat, ...);
+ void hexlog(const char *header, const char *buffer, const int length, const char *aFormat, ...);
+
+ static Logger* instance();
+#endif
+
+ void set_pid();
+ int get_level() { return mLogLevel; }
+};
+//----------------------------------------------------------------------
+
+#endif /* ULOGGER_H_ */
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..397fde2
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,72 @@
+#============================================================================
+# Name : Makefile
+# Author : oskwon(kos@dev3)
+# Version :
+# Copyright : Copyright(c)2013 Vu+ Team. All right reserved.
+# Description :
+#============================================================================
+
+VER = v3.0
+
+
+Q=@
+IP=100.214
+MODEL=vuduo2
+OETOP=/home/oskwon/works/workrounds/openembedded
+CROSS=$(OETOP)/$(MODEL)/build/tmp/cross/mipsel/bin/mipsel-oe-linux-
+SYSROOT=$(OETOP)/$(MODEL)/build/tmp/staging/mipsel-oe-linux
+
+BIN = transtreamproxy
+SRCS = $(shell find . -name "*.cpp")
+
+CFLAGS ?=
+LDFLAGS ?=
+
+#CFLAGS += -Wno-unused-result
+
+CFLAGS += -O2
+
+CXX=$(CROSS)g++
+LD=$(CROSS)ld
+STRIP=$(CROSS)strip
+RM=rm -f
+
+CFLAGS += -I. -I./openpli-streamproxy -I$(SYSROOT)/usr/include
+LDFLAGS += -L$(SYSROOT)/usr/lib -lpthread -lrt
+
+OBJS=$(SRCS:.cpp=.o)
+
+.SUFFIXES : .cpp .o
+.PHONY : all clean install .showinfo
+
+.cpp.o:
+ $(Q)echo "Compile... "$<
+ $(Q)$(CXX) $(CFLAGS) -c $< -o $(subst .cpp,.o,$<)
+
+all: .showinfo $(BIN)
+
+$(BIN):$(OBJS)
+ $(Q)echo "Linking... "$@
+ $(Q)$(CXX) -o $@ $(OBJS) $(LDFLAGS)
+ $(Q)$(STRIP) $@
+
+install:
+ $(Q)./up.sh $(IP)
+
+clean:
+ $(Q)$(RM) $(BIN) $(OBJS) *.log
+
+.showinfo:
+ @echo "-----------------------------------------------------"
+ @echo " [ BUILD ENVIRONMENT ] "
+ @echo "-----------------------------------------------------"
+ @echo "OUTPUT : "$(STREAMER_BIN) $(TRANSTREAMPROXY_BIN)
+ @echo "VERSION : "$(VER)
+ @echo ""
+ @echo "CXX : "$(CXX)
+ @echo "LD : "$(LD)
+ @echo "STRIP : "$(STRIP)
+ @echo "CFLAGS : "$(CFLAGS)
+ @echo "LDFLAGS : "$(LDFLAGS)
+ @echo "-----------------------------------------------------"
+ @echo
diff --git a/src/Source.h b/src/Source.h
new file mode 100644
index 0000000..003fdeb
--- /dev/null
+++ b/src/Source.h
@@ -0,0 +1,23 @@
+/*
+ * Source.h
+ *
+ * Created on: 2014. 6. 12.
+ * Author: oskwon
+ */
+
+#ifndef SOURCE_H_
+#define SOURCE_H_
+
+#include "trap.h"
+//----------------------------------------------------------------------
+
+class Source
+{
+public:
+ Source(){}
+ virtual ~Source(){}
+ virtual int get_fd() const throw() = 0;
+};
+//----------------------------------------------------------------------
+
+#endif /* SOURCE_H_ */
diff --git a/src/UriDecoder.cpp b/src/UriDecoder.cpp
new file mode 100644
index 0000000..55b62bb
--- /dev/null
+++ b/src/UriDecoder.cpp
@@ -0,0 +1,277 @@
+/*
+ * UriDecoder.h
+ *
+ * Created on: 2014. 6. 10.
+ * Author: oskwon
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "UriDecoder.h"
+//-------------------------------------------------------------------------------
+
+unsigned char UriDecoder::h2i(wchar_t aHexDigit)
+{
+ switch (aHexDigit) {
+ case _UL_('0'):
+ case _UL_('1'):
+ case _UL_('2'):
+ case _UL_('3'):
+ case _UL_('4'):
+ case _UL_('5'):
+ case _UL_('6'):
+ case _UL_('7'):
+ case _UL_('8'):
+ case _UL_('9'):
+ return (unsigned char)(9 + aHexDigit - _UL_('9'));
+ case _UL_('a'):
+ case _UL_('b'):
+ case _UL_('c'):
+ case _UL_('d'):
+ case _UL_('e'):
+ case _UL_('f'):
+ return (unsigned char)(15 + aHexDigit - _UL_('f'));
+ case _UL_('A'):
+ case _UL_('B'):
+ case _UL_('C'):
+ case _UL_('D'):
+ case _UL_('E'):
+ case _UL_('F'):
+ return (unsigned char)(15 + aHexDigit - _UL_('F'));
+ default:
+ return 0;
+ }
+}
+//-------------------------------------------------------------------------------
+
+const wchar_t* UriDecoder::decode_uri(wchar_t* aData, int aBreakCond)
+{
+ wchar_t* read = aData;
+ wchar_t* write = aData;
+ bool prevWasCr = false;
+
+ if (aData == NULL) {
+ return NULL;
+ }
+
+ for (;;) {
+ switch (read[0]) {
+ case _UL_('\0'):
+ if (read > write) {
+ write[0] = _UL_('\0');
+ }
+ return write;
+
+ case _UL_('%'):
+ switch (read[1]) {
+ case _UL_('0'):
+ case _UL_('1'):
+ case _UL_('2'):
+ case _UL_('3'):
+ case _UL_('4'):
+ case _UL_('5'):
+ case _UL_('6'):
+ case _UL_('7'):
+ case _UL_('8'):
+ case _UL_('9'):
+ case _UL_('a'):
+ case _UL_('b'):
+ case _UL_('c'):
+ case _UL_('d'):
+ case _UL_('e'):
+ case _UL_('f'):
+ case _UL_('A'):
+ case _UL_('B'):
+ case _UL_('C'):
+ case _UL_('D'):
+ case _UL_('E'):
+ case _UL_('F'):
+ switch (read[2]) {
+ case _UL_('0'):
+ case _UL_('1'):
+ case _UL_('2'):
+ case _UL_('3'):
+ case _UL_('4'):
+ case _UL_('5'):
+ case _UL_('6'):
+ case _UL_('7'):
+ case _UL_('8'):
+ case _UL_('9'):
+ case _UL_('a'):
+ case _UL_('b'):
+ case _UL_('c'):
+ case _UL_('d'):
+ case _UL_('e'):
+ case _UL_('f'):
+ case _UL_('A'):
+ case _UL_('B'):
+ case _UL_('C'):
+ case _UL_('D'):
+ case _UL_('E'):
+ case _UL_('F'): {
+ const unsigned char left = h2i(read[1]);
+ const unsigned char right = h2i(read[2]);
+ const int code = 16 * left + right;
+ switch (code) {
+ case 10:
+ switch (aBreakCond) {
+ case BR_TO_LF:
+ if (!prevWasCr) {
+ write[0] = (wchar_t)10;
+ write++;
+ }
+ break;
+
+ case BR_TO_CRLF:
+ if (!prevWasCr) {
+ write[0] = (wchar_t)13;
+ write[1] = (wchar_t)10;
+ write += 2;
+ }
+ break;
+
+ case BR_TO_CR:
+ if (!prevWasCr) {
+ write[0] = (wchar_t)13;
+ write++;
+ }
+ break;
+
+ case BR_DONT_TOUCH:
+ default:
+ write[0] = (wchar_t)10;
+ write++;
+
+ }
+ prevWasCr = false;
+ break;
+
+ case 13:
+ switch (aBreakCond) {
+ case BR_TO_LF:
+ write[0] = (wchar_t)10;
+ write++;
+ break;
+
+ case BR_TO_CRLF:
+ write[0] = (wchar_t)13;
+ write[1] = (wchar_t)10;
+ write += 2;
+ break;
+
+ case BR_TO_CR:
+ write[0] = (wchar_t)13;
+ write++;
+ break;
+
+ case BR_DONT_TOUCH:
+ default:
+ write[0] = (wchar_t)13;
+ write++;
+
+ }
+ prevWasCr = true;
+ break;
+
+ default:
+ write[0] = (wchar_t)(code);
+ write++;
+
+ prevWasCr = false;
+
+ }
+ read += 3;
+ }
+ break;
+
+ default:
+ if (read > write) {
+ write[0] = read[0];
+ write[1] = read[1];
+ }
+ read += 2;
+ write += 2;
+
+ prevWasCr = false;
+ break;
+ }
+ break;
+
+ default:
+ if (read > write) {
+ write[0] = read[0];
+ }
+ read++;
+ write++;
+
+ prevWasCr = false;
+ break;
+ }
+ break;
+
+ case _UL_('+'):
+ if (read > write) {
+ write[0] = _UL_(' ');
+ }
+ read++;
+ write++;
+
+ prevWasCr = false;
+ break;
+
+ default:
+ if (read > write) {
+ write[0] = read[0];
+ }
+ read++;
+ write++;
+
+ prevWasCr = false;
+ break;
+ }
+ }
+ return NULL;
+}
+//-------------------------------------------------------------------------------
+
+std::wstring UriDecoder::decode64(const wchar_t* aInput)
+{
+ wchar_t working[1024] = {0};
+
+ wcscpy(working, aInput);
+ decode_uri(working, BR_DONT_TOUCH);
+
+ return std::wstring(working);
+}
+//-------------------------------------------------------------------------------
+
+std::string UriDecoder::decode(const char* aInput)
+{
+ std::string tmp = aInput;
+ std::wstring in = L"";
+ in.assign(tmp.begin(), tmp.end());
+
+ std::wstring decode = decode64(in.c_str());
+
+ tmp.assign(decode.begin(), decode.end());
+
+ return tmp;
+}
+//-------------------------------------------------------------------------------
+
+#ifdef UNIT_TEST
+#include <iostream>
+int main()
+{
+ std::string in = "/home/kos/work/workspace/tsstreamproxy/test/20130528%201415%20-%20ZDF%20-%20Die%20K%C3%BCchenschlacht.ts";
+ std::string out = UriDecoder().decode(in.c_str());
+
+ cout << out << endl;
+
+ FILE* fp = fopen(out.c_str(), "rb");
+
+ cout << (fp == NULL) ? "OPEN FAIL!!" : "OPEN OK" << endl;
+}
+
+#endif
diff --git a/src/UriDecoder.h b/src/UriDecoder.h
new file mode 100644
index 0000000..2e5a0d5
--- /dev/null
+++ b/src/UriDecoder.h
@@ -0,0 +1,43 @@
+/*
+ * UriDecoder.h
+ *
+ * Created on: 2014. 6. 10.
+ * Author: oskwon
+ */
+
+#ifndef URIDECODER_H_
+#define URIDECODER_H_
+
+#include <memory>
+#include <string>
+
+#include <wchar.h>
+//-------------------------------------------------------------------------------
+
+#define BR_TO_LF 0
+#define BR_TO_CRLF 1
+#define BR_TO_CR 2
+#define BR_TO_UNIX BR_TO_LF
+#define BR_TO_WINDOWS BR_TO_CRLF
+#define BR_TO_MAC BR_TO_CR
+#define BR_DONT_TOUCH 6
+
+#define _UL_(x) L##x
+//-------------------------------------------------------------------------------
+
+class UriDecoder
+{
+protected:
+ unsigned char h2i(wchar_t aHexDigit);
+ const wchar_t* decode_uri(wchar_t* aData, int aBreakCond);
+
+public:
+ UriDecoder(){};
+ virtual ~UriDecoder(){};
+
+ std::string decode(const char* aInput);
+ std::wstring decode64(const wchar_t* aInput);
+};
+//-------------------------------------------------------------------------------
+
+#endif /* URIDECODER_H_ */
diff --git a/src/Utils.cpp b/src/Utils.cpp
new file mode 100644
index 0000000..9345e59
--- /dev/null
+++ b/src/Utils.cpp
@@ -0,0 +1,193 @@
+/*
+ * Utils.cpp
+ *
+ * Created on: 2014. 6. 10.
+ * Author: oskwon
+ */
+
+#include <errno.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include <sstream>
+
+#include "mpegts.h"
+
+#include "Utils.h"
+#include "Logger.h"
+#include "UriDecoder.h"
+
+using namespace std;
+//----------------------------------------------------------------------
+
+std::string ultostr(int64_t data)
+{
+ std::stringstream ss;
+ ss << data;
+ return ss.str();
+}
+//----------------------------------------------------------------------
+
+int strtoi(std::string data)
+{
+ int retval;
+ std::stringstream ss;
+ try {
+ ss.str(data);
+ ss >> retval;
+ }
+ catch(...) {
+ return -1;
+ }
+ return retval;
+}
+//----------------------------------------------------------------------
+
+std::string trim(std::string& s, const std::string& drop)
+{
+ std::string r = s.erase(s.find_last_not_of(drop) + 1);
+ return r.erase(0, r.find_first_not_of(drop));
+}
+//----------------------------------------------------------------------
+
+int split(std::string data, const char delimiter, std::vector<string>& tokens)
+{
+ std::stringstream data_stream(data);
+ for(std::string token; std::getline(data_stream, token, delimiter); tokens.push_back(trim(token)));
+ return tokens.size();
+}
+//----------------------------------------------------------------------
+
+bool split_key_value(std::string data, std::string delimiter, std::string &key, std::string &value)
+{
+ int idx = data.find(delimiter);
+ if (idx == string::npos) {
+ WARNING("split key & value (data : %s, delimiter : %s)", data.c_str(), delimiter.c_str());
+ return false;
+ }
+ key = data.substr(0, idx);
+ value = data.substr(idx+1, data.length()-idx);
+ return true;
+}
+//----------------------------------------------------------------------
+
+std::string read_request()
+{
+ std::string request = "";
+ while (true) {
+ char buffer[128] = {0};
+ fgets(buffer, 127, stdin);
+
+ request += buffer;
+ if(request.find("\r\n\r\n") != string::npos)
+ break;
+ }
+ return request;
+}
+//----------------------------------------------------------------------
+
+bool RequestHeader::parse_header(std::string header)
+{
+ std::vector<string> lines;
+ split(header, '\n', lines);
+
+ DEBUG("header lines count : %d", lines.size());
+ std::vector<string>::iterator iter = lines.begin();
+ std::vector<string> infos;
+ if (split(*iter, ' ', infos) != 3) {
+ ERROR("fail to parse info : %d", infos.size());
+ return false;
+ }
+
+ type = REQ_TYPE_TRANSCODING_LIVE;
+ method = infos[0];
+ path = infos[1];
+ version = infos[2];
+ decoded_path = path;
+
+ if (strncmp(path.c_str(), "/file", 5) == 0) {
+ std::string key = "", value = "";
+ if (!split_key_value(path, "=", key, value)) {
+ ERROR("fail to parse path(file) : %s", path.c_str());
+ return false;
+ }
+ if (key != "/file?file") {
+ ERROR("unknown request file path (key : %s, value : %s)", key.c_str(), value.c_str());
+ return false;
+ }
+ type = REQ_TYPE_TRANSCODING_FILE;
+ decoded_path = UriDecoder().decode(value.c_str());
+ }
+
+ DEBUG("info (%d) -> type : [%s], path : [%s], version : [%s]", infos.size(), method.c_str(), path.c_str(), version.c_str());
+
+ for (++iter; iter != lines.end(); ++iter) {
+ std::string key = "", value = "";
+ if (!split_key_value(*iter, ":", key, value))
+ continue;
+ if (key == "")
+ continue;
+ key = trim(key);
+ value = trim(value);
+
+ if (key.length() > 0) {
+ params[key] = value;
+ DEBUG("add params : %s -> %s", key.c_str(), value.c_str());
+ }
+ }
+ return true;
+}
+//----------------------------------------------------------------------
+
+off_t make_response(ThreadParams *params, std::string& response)
+{
+ response = "";
+
+ LINESTAMP();
+
+ off_t byte_offset = 0;
+ RequestHeader *header = ((ThreadParams*) params)->request;
+ switch(header->type) {
+ case REQ_TYPE_TRANSCODING_FILE: {
+ MpegTS *source = (MpegTS*)((ThreadParams*) params)->source;
+
+ std::string range = header->params["Range"];
+ if((range.length() > 7) && (range.substr(0, 6) == "bytes=")) {
+ range = range.substr(6);
+ if(range.find('-') == (range.length() - 1)) {
+ byte_offset = strtoi(range);
+ }
+ }
+
+ response += (byte_offset > 0) ? HTTP_PARTIAL : HTTP_OK;
+ response += HTTP_PARAMS;
+ response += "Accept-Ranges: bytes\r\n"
+ "Content-Length: " + ultostr(source->stream_length) + "\r\n";
+ response += HTTP_DONE;
+ }
+ break;
+ case REQ_TYPE_TRANSCODING_LIVE: {
+ response += HTTP_OK;
+ response += HTTP_PARAMS;
+ response += HTTP_DONE;
+ }
+ break;
+ default: return -1;
+ }
+ return byte_offset;
+}
+//----------------------------------------------------------------------
+
+void Util::vlog(const char * format, ...) throw()
+{
+ static char vlog_buffer[MAX_PRINT_LEN];
+ memset(vlog_buffer, 0, MAX_PRINT_LEN);
+
+ va_list args;
+ va_start(args, format);
+ vsnprintf(vlog_buffer, MAX_PRINT_LEN-1, format, args);
+ va_end(args);
+
+ WARNING("%s", vlog_buffer);
+}
+//----------------------------------------------------------------------
diff --git a/src/Utils.h b/src/Utils.h
new file mode 100644
index 0000000..bfeee16
--- /dev/null
+++ b/src/Utils.h
@@ -0,0 +1,79 @@
+/*
+ * Utils.h
+ *
+ * Created on: 2014. 6. 10.
+ * Author: oskwon
+ */
+
+#ifndef UTILS_H_
+#define UTILS_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <stdint.h>
+
+#include "Source.h"
+#include "Encoder.h"
+//----------------------------------------------------------------------
+
+int strtoi(std::string data);
+std::string ultostr(int64_t data);
+std::string trim(std::string& s, const std::string& drop = " \t\n\v\r");
+int split(std::string data, const char delimiter, std::vector<std::string>& tokens);
+bool split_key_value(std::string data, std::string delimiter, std::string &key, std::string &value);
+
+std::string read_request();
+//----------------------------------------------------------------------
+
+typedef enum {
+ REQ_TYPE_UNKNOWN = 0,
+ REQ_TYPE_LIVE,
+ REQ_TYPE_TRANSCODING_LIVE,
+ REQ_TYPE_FILE,
+ REQ_TYPE_TRANSCODING_FILE,
+} RequestType;
+//----------------------------------------------------------------------
+
+class RequestHeader
+{
+public:
+ RequestType type;
+ std::string method;
+ std::string path;
+ std::string decoded_path;
+ std::string version;
+ std::map<std::string, std::string> params;
+
+public:
+ bool parse_header(std::string header);
+};
+//----------------------------------------------------------------------
+
+#define HTTP_OK "HTTP/1.1 200 OK\r\n"
+#define HTTP_PARTIAL "HTTP/1.1 206 Partial Content\r\n"
+#define HTTP_PARAMS "Connection: Close\r\n" \
+ "Content-Type: video/mpeg\r\n" \
+ "Server: transtreamproxy\r\n"
+#define HTTP_DONE "\r\n"
+//----------------------------------------------------------------------
+
+typedef struct _thread_params_t {
+ Source *source;
+ Encoder *encoder;
+ RequestHeader *request;
+} ThreadParams;
+//----------------------------------------------------------------------
+
+off_t make_response(ThreadParams *params, std::string& response);
+//----------------------------------------------------------------------
+
+class Util
+{
+public:
+ static void vlog(const char * format, ...) throw();
+};
+//----------------------------------------------------------------------
+
+#endif /* UTILS_H_ */
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..b1b8ea9
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,332 @@
+/*
+ * main.cpp
+ *
+ * Created on: 2014. 6. 10.
+ * Author: oskwon
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <pthread.h>
+#include <poll.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <string>
+
+#include "trap.h"
+#include "mpegts.h"
+
+#include "Utils.h"
+#include "Logger.h"
+
+#include "Demuxer.h"
+#include "Encoder.h"
+
+using namespace std;
+//----------------------------------------------------------------------
+
+#define RESPONSE_FD (1)
+#define BUFFFER_SIZE (188 * 256)
+
+void signal_setting();
+void signal_handler(int sig_no);
+
+void do_exit(const char *message);
+
+static bool is_terminated = true;
+//----------------------------------------------------------------------
+
+void *streaming_thread_main(void *params)
+{
+ if (is_terminated) return 0;
+
+ INFO("streaming thread start.");
+ Encoder *encoder = ((ThreadParams*) params)->encoder;
+ RequestHeader *header = ((ThreadParams*) params)->request;
+
+ try {
+ int poll_state, rc, wc;
+ struct pollfd poll_fd[2];
+ unsigned char buffer[BUFFFER_SIZE];
+
+ poll_fd[0].fd = encoder->get_fd();
+ poll_fd[0].events = POLLIN | POLLHUP;
+
+ while(!is_terminated) {
+ poll_state = poll(poll_fd, 1, 1000);
+ if (poll_state == -1) {
+ throw(trap("poll error."));
+ }
+ else if (poll_state == 0) {
+ continue;
+ }
+ if (poll_fd[0].revents & POLLIN) {
+ rc = wc = 0;
+ rc = read(encoder->get_fd(), buffer, BUFFFER_SIZE - 1);
+ if (rc <= 0) {
+ break;
+ }
+ else if (rc > 0) {
+ wc = write(RESPONSE_FD, buffer, rc);
+ if (wc < rc) {
+ //DEBUG("need rewrite.. remain (%d)", rc - wc);
+ int retry_wc = 0;
+ for (int remain_len = rc - wc; rc != wc; remain_len -= retry_wc) {
+ poll_fd[0].revents = 0;
+
+ retry_wc = write(RESPONSE_FD, (buffer + rc - remain_len), remain_len);
+ wc += retry_wc;
+ }
+ DEBUG("re-write result : %d - %d", wc, rc);
+ }
+ }
+ }
+ else if (poll_fd[0].revents & POLLHUP)
+ {
+ if (encoder->state == Encoder::ENCODER_STAT_STARTED) {
+ DEBUG("stop transcoding..");
+ encoder->ioctl(Encoder::IOCTL_STOP_TRANSCODING, 0);
+ }
+ break;
+ }
+ }
+ }
+ catch (const trap &e) {
+ ERROR("%s %s (%d)", e.what(), strerror(errno), errno);
+ }
+ do_exit(0);
+ INFO("streaming thread stop.");
+
+ if (encoder->state == Encoder::ENCODER_STAT_STARTED) {
+ DEBUG("stop transcoding..");
+ encoder->ioctl(Encoder::IOCTL_STOP_TRANSCODING, 0);
+ }
+
+ pthread_exit(0);
+
+ return 0;
+}
+//----------------------------------------------------------------------
+
+void *source_thread_main(void *params)
+{
+ Source *source = ((ThreadParams*) params)->source;
+ Encoder *encoder = ((ThreadParams*) params)->encoder;
+ RequestHeader *header = ((ThreadParams*) params)->request;
+
+ INFO("source thread start.");
+
+ try {
+ int poll_state, rc, wc;
+ struct pollfd poll_fd[2];
+ unsigned char buffer[BUFFFER_SIZE];
+
+ poll_fd[0].fd = encoder->get_fd();
+ poll_fd[0].events = POLLOUT;
+
+ poll_fd[1].fd = source->get_fd();
+ poll_fd[1].events = POLLIN;
+
+ while(!is_terminated) {
+ poll_state = poll(poll_fd, 2, 1000);
+ if (poll_state == -1) {
+ throw(trap("poll error."));
+ }
+ else if (poll_state == 0) {
+ continue;
+ }
+
+ if (poll_fd[0].revents & POLLOUT) {
+ rc = wc = 0;
+ if (poll_fd[1].revents & POLLIN) {
+ rc = read(source->get_fd(), buffer, BUFFFER_SIZE - 1);
+ if (rc == 0) {
+ break;
+ }
+ else if (rc > 0) {
+ wc = write(encoder->get_fd(), buffer, rc);
+
+ if (wc < rc) {
+ //DEBUG("need rewrite.. remain (%d)", rc - wc);
+ int retry_wc = 0;
+ for (int remain_len = rc - wc; rc != wc; remain_len -= retry_wc) {
+ poll_fd[0].revents = 0;
+
+ poll_state = poll(poll_fd, 1, 1000);
+ if (poll_fd[0].revents & POLLOUT) {
+ retry_wc = write(encoder->get_fd(), (buffer + rc - remain_len), remain_len);
+ wc += retry_wc;
+ }
+ }
+ DEBUG("re-write result : %d - %d", wc, rc);
+ usleep(500000);
+ }
+ }
+ }
+ }
+ }
+ }
+ catch (const trap &e) {
+ ERROR("%s %s (%d)", e.what(), strerror(errno), errno);
+ }
+ INFO("source thread stop.");
+
+ pthread_exit(0);
+
+ return 0;
+}
+//----------------------------------------------------------------------
+
+int main(int argc, char **argv)
+{
+ if (access("/tmp/.debug_on", F_OK) == 0) {
+ Logger::instance()->init("/tmp/transtreamproxy", Logger::DEBUG, false, "3.0");
+ }
+ else {
+ Logger::instance()->init("/tmp/transtreamproxy", Logger::WARNING, false, "3.0");
+ }
+ signal_setting();
+
+ RequestHeader header;
+
+ int source_thread_id, stream_thread_id;
+ pthread_t source_thread_handle, stream_thread_handle;
+
+ std::string req = read_request();
+
+ DEBUG("request head :\n%s", req.c_str());
+ if (header.parse_header(req)) {
+ Encoder encoder;
+ Source *source = 0;
+
+ ThreadParams thread_params = { 0, &encoder, &header };
+
+ int video_pid = 0, audio_pid = 0, pmt_pid = 0;
+
+ switch(header.type) {
+ case REQ_TYPE_TRANSCODING_FILE:
+ try {
+ MpegTS *ts = new MpegTS(header.decoded_path, false);
+ pmt_pid = ts->pmt_pid;
+ video_pid = ts->video_pid;
+ audio_pid = ts->audio_pid;
+ source = ts;
+ }
+ catch (const trap &e) {
+ ERROR("fail to create source : %s", e.what());
+ exit(-1);
+ }
+ break;
+ case REQ_TYPE_TRANSCODING_LIVE:
+ try {
+ Demuxer *dmx = new Demuxer(&header);
+ pmt_pid = dmx->pmt_pid;
+ video_pid = dmx->video_pid;
+ audio_pid = dmx->audio_pid;
+ source = dmx;
+ }
+ catch (const trap &e) {
+ ERROR("fail to create source : %s", e.what());
+ exit(-1);
+ }
+ break;
+ default:
+ ERROR("not support source type (type : %d)", header.type);
+ exit(-1);
+ }
+ thread_params.source = source;
+
+ if (!encoder.retry_open(0, 2, 3)) {
+ exit(-1);
+ }
+
+ if (encoder.state == Encoder::ENCODER_STAT_OPENED) {
+ std::string response;
+ off_t byte_offset = 0;
+ if ((byte_offset = make_response((ThreadParams*) &thread_params, response)) < 0) {
+ do_exit(0);
+ return 0;
+ }
+
+ write(RESPONSE_FD, response.c_str(), response.length());
+ DEBUG("response data :\n%s", response.c_str());
+
+ if (header.type == REQ_TYPE_TRANSCODING_FILE) {
+ ((MpegTS*)source)->seek_absolute(byte_offset);
+ DEBUG("seek to %ld", byte_offset);
+ }
+
+ if (!encoder.ioctl(Encoder::IOCTL_SET_VPID, video_pid)) {
+ do_exit("fail to set video pid.");
+ exit(-1);
+ }
+ if (!encoder.ioctl(Encoder::IOCTL_SET_APID, audio_pid)) {
+ do_exit("fail to set audio pid.");
+ exit(-1);
+ }
+ if (!encoder.ioctl(Encoder::IOCTL_SET_PMTPID, pmt_pid)) {
+ do_exit("fail to set pmtid.");
+ exit(-1);
+ }
+ }
+
+ is_terminated = false;
+ source_thread_id = pthread_create(&source_thread_handle, 0, source_thread_main, (void *)&thread_params);
+ if (source_thread_id < 0) {
+ do_exit("fail to create source thread.");
+ }
+ else {
+ pthread_detach(source_thread_handle);
+ sleep(1);
+ if (!encoder.ioctl(Encoder::IOCTL_START_TRANSCODING, 0)) {
+ do_exit("fail to start transcoding.");
+ }
+ else {
+ stream_thread_id = pthread_create(&stream_thread_handle, 0, streaming_thread_main, (void *)&thread_params);
+ if (stream_thread_id < 0) {
+ do_exit("fail to create stream thread.");
+ }
+ }
+ }
+ pthread_join(stream_thread_handle, 0);
+
+ if (source != 0) {
+ delete source;
+ source = 0;
+ }
+ }
+ return 0;
+}
+//----------------------------------------------------------------------
+
+void do_exit(const char *message)
+{
+ is_terminated = true;
+ if (message) {
+ ERROR("%s", message);
+ }
+}
+//----------------------------------------------------------------------
+
+void signal_setting()
+{
+ signal(SIGHUP, signal_handler);
+ signal(SIGINT, signal_handler);
+ signal(SIGQUIT, signal_handler);
+ signal(SIGILL, signal_handler);
+ signal(SIGABRT, signal_handler);
+ signal(SIGKILL, signal_handler);
+ signal(SIGBUS, signal_handler);
+ signal(SIGSEGV, signal_handler);
+ signal(SIGTERM, signal_handler);
+}
+//----------------------------------------------------------------------
+
+void signal_handler(int sig_no)
+{
+ INFO("signal no : %d", sig_no);
+ do_exit("signal detected..");
+}
+//----------------------------------------------------------------------
diff --git a/src/openpli-streamproxy/mpegts.cpp b/src/openpli-streamproxy/mpegts.cpp
new file mode 100644
index 0000000..1f0752b
--- /dev/null
+++ b/src/openpli-streamproxy/mpegts.cpp
@@ -0,0 +1,734 @@
+//#include "config.h" // oskwon
+#include "trap.h"
+
+#include "mpegts.h"
+//#include "util.h" // oskwon
+#include "Utils.h" // oskwon
+
+#include <unistd.h>
+#include <stddef.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include <boost/crc.hpp>
+
+#include <string>
+using std::string;
+
+#include <boost/algorithm/string.hpp>
+
+MpegTS::MpegTS(int fd_in, bool request_time_seek_in) throw(trap)
+ : private_fd(false),
+ fd(fd_in),
+ request_time_seek(request_time_seek_in)
+{
+ init();
+}
+
+MpegTS::MpegTS(string filename, bool request_time_seek_in) throw(trap)
+ : private_fd(true),
+ request_time_seek(request_time_seek_in)
+{
+ if((fd = open(filename.c_str(), O_RDONLY, 0)) < 0)
+ throw(trap("MpegTS::MpegTS: cannot open file"));
+
+ init();
+}
+
+MpegTS::~MpegTS() throw()
+{
+ if(private_fd)
+ close(fd);
+}
+
+void MpegTS::init() throw(trap)
+{
+ mpegts_pat_t::const_iterator it;
+ struct stat filestat;
+
+ if(fstat(fd, &filestat))
+ throw(trap("MpegTS::init: cannot stat"));
+
+ Util::vlog("MpegTS::init: file length: %lld Mb", filestat.st_size >> 20);
+
+ eof_offset = stream_length = filestat.st_size;
+ eof_offset = (eof_offset / sizeof(ts_packet_t)) * sizeof(ts_packet_t);
+
+ if(!read_pat())
+ throw(trap("MpegTS::init: invalid transport stream (no suitable pat)"));
+
+ for(it = pat.begin(); it != pat.end(); it++)
+ if(read_pmt(it->second))
+ break;
+
+ if(it == pat.end())
+ throw(trap("MpegTS::init: invalid transport stream (no suitable pmt)"));
+
+ pmt_pid = it->second;
+ is_time_seekable = false;
+
+ if(request_time_seek)
+ {
+ Util::vlog("MpegTS: start find pcr");
+
+ if(lseek(fd, 0, SEEK_SET) == (off_t)-1)
+ Util::vlog("MpegTS::init seek to sof fails");
+ else
+ {
+ Util::vlog("MpegTS: start find first pcr");
+
+ first_pcr_ms = find_pcr_ms(direction_forward);
+
+ if(lseek(fd, eof_offset, SEEK_SET) == (off_t)-1)
+ Util::vlog("MpegTS::init: seek to eof fails");
+ else
+ {
+ Util::vlog("MpegTS: start find last pcr");
+ last_pcr_ms = find_pcr_ms(direction_backward);
+
+ if(last_pcr_ms < first_pcr_ms)
+ Util::vlog("MpegTS::init: pcr wraparound, cannot seek this stream, first pcr: %d, last pcr: %d", first_pcr_ms / 1000, last_pcr_ms / 1000);
+ else
+ if((first_pcr_ms >= 0) && (last_pcr_ms >= 0))
+ is_time_seekable = true;
+ }
+ }
+
+ Util::vlog("MpegTS: find pcr done");
+ }
+
+ if(!is_time_seekable)
+ {
+ first_pcr_ms = -1;
+ last_pcr_ms = -1;
+ }
+
+ if(lseek(fd, 0, SEEK_SET) == (off_t)-1)
+ Util::vlog("MpegTS::init: seek to sof fails");
+
+ //Util::vlog("first_pcr_ms = %d", first_pcr_ms);
+ //Util::vlog("last_pcr_ms = %d", last_pcr_ms);
+ //Util::vlog("eof_offset is at %lld", eof_offset);
+}
+
+bool MpegTS::read_table(int filter_pid, int filter_table) throw(trap)
+{
+ typedef boost::crc_optimal<32, 0x04c11db7, 0xffffffff, 0x0, false, false> boost_mpeg_crc_t;
+
+ boost_mpeg_crc_t my_crc;
+ mpeg_crc_t mpeg_crc;
+ uint32_t their_crc;
+
+ ts_packet_t packet;
+ const section_table_header_t *table;
+ const section_table_syntax_t *syntax;
+ const uint8_t *payload_data;
+
+ int timeout;
+ int section_length = -1;
+ int section_length_remaining = -1;
+ int pid;
+ int cc = -1;
+ int packet_payload_offset;
+ int payload_length;
+
+ raw_table_data.clear();
+ table_data.clear();
+
+ for(timeout = 0; timeout < 2000; timeout++)
+ {
+ if(read(fd, (void *)&packet, sizeof(packet)) != sizeof(packet))
+ throw(trap("MpegTS::read_table: read error"));
+
+ if(packet.header.sync_byte != MpegTS::sync_byte_value)
+ throw(trap("MpegTS::read_table: no sync byte found"));
+
+ pid = (packet.header.pid_high << 8) | (packet.header.pid_low);
+
+ if(pid != filter_pid)
+ continue;
+
+ if(packet.header.tei)
+ continue;
+
+ if(!packet.header.payload_present)
+ continue;
+
+ if((cc != -1) && (cc != packet.header.cc))
+ {
+ //Util::vlog("MpegTS::read_table discontinuity: %d/%d", cc, packet.header.cc);
+ goto retry;
+ }
+
+ cc = (packet.header.cc + 1) & 0x0f;
+
+ if(packet.header.pusi)
+ {
+ if(table_data.length() > 0)
+ {
+ //Util::vlog("MpegTS::read_table: start payload upexpected");
+ goto retry;
+ }
+ }
+ else
+ {
+ if(table_data.length() <= 0)
+ {
+ //Util::vlog("MpegTS::read_table: continue payload unexpected");
+ goto retry;
+ }
+ }
+
+ //Util::vlog("MpegTS::read_table: correct packet with pid: %x, %s", pid, packet.header.pusi ? "start" : "continuation");
+
+ packet_payload_offset = offsetof(ts_packet_t, header.payload);
+
+ //Util::vlog("MpegTS::read_table: payload offset: %d", packet_payload_offset);
+
+ if(packet.header.af)
+ packet_payload_offset = offsetof(ts_packet_t, header.afield) + packet_payload_offset;
+
+ //Util::vlog("MpegTS::read_table: payload offset after adaptation field: %d", packet_payload_offset);
+
+ if(packet.header.pusi)
+ packet_payload_offset += packet.byte[packet_payload_offset] + 1; // psi payload pointer byte
+
+ //Util::vlog("MpegTS::read_table: payload offset after section pointer field: %d", packet_payload_offset);
+
+ if(table_data.length() == 0)
+ {
+ table = (const section_table_header_t *)&packet.byte[packet_payload_offset];
+
+ raw_table_data.assign((const uint8_t *)table, offsetof(section_table_header_t, payload));
+
+ //Util::vlog("MpegTS::read_table: table id: %d", table->table_id);
+
+ if(table->table_id != filter_table)
+ {
+ Util::vlog("MpegTS::read_table: table %d != %d", table->table_id, filter_table);
+ goto retry;
+ }
+
+ if(table->private_bit)
+ {
+ Util::vlog("MpegTS::read_table: private != 0: %d", table->private_bit);
+ goto retry;
+ }
+
+ if(!table->section_syntax)
+ {
+ Util::vlog("MpegTS::read_table: section_syntax == 0: %d", table->section_syntax);
+ goto retry;
+ }
+
+ if(table->reserved != 0x03)
+ {
+ Util::vlog("MpegTS::read_table: reserved != 0x03: %x", table->reserved);
+ goto retry;
+ }
+
+ if(table->section_length_unused != 0x00)
+ {
+ Util::vlog("MpegTS::read_table: section length unused != 0x00: %x", table->section_length_unused);
+ goto retry;
+ }
+
+ section_length = ((table->section_length_high << 8) | (table->section_length_low)) - offsetof(section_table_syntax_t, data);
+ //Util::vlog("MpegTS::read_table: section length: %d", section_length);
+
+ if(section_length < 0)
+ {
+ Util::vlog("MpegTS::read_table: section length < 0: %d", section_length);
+ goto retry;
+ }
+
+ if(section_length_remaining < 0)
+ section_length_remaining = section_length;
+
+ syntax = (const section_table_syntax_t *)&table->payload;
+
+ raw_table_data.append((const uint8_t *)syntax, offsetof(section_table_syntax_t, data));
+
+ //Util::vlog("MpegTS::read_table: tide: 0x%x", (syntax->tide_high << 8) | syntax->tide_low);
+
+ if(syntax->reserved != 0x03)
+ {
+ Util::vlog("MpegTS::read_table: syntax reserved != 0x03: %d", syntax->reserved);
+ goto retry;
+ }
+
+ //Util::vlog("MpegTS::read_table: currnext: %d", syntax->currnext);
+ //Util::vlog("MpegTS::read_table: version: %d", syntax->version);
+ //Util::vlog("MpegTS::read_table: ordinal: %d", syntax->ordinal);
+ //Util::vlog("MpegTS::read_table: last: %d", syntax->last);
+
+ payload_length = sizeof(ts_packet_t) - ((const uint8_t *)&syntax->data - &packet.byte[0]);
+ //Util::vlog("MpegTS::read_table: payload length: %d", payload_length);
+
+ if(payload_length > section_length_remaining)
+ payload_length = section_length_remaining;
+
+ //Util::vlog("MpegTS::read_table: payload length after trimming: %d", payload_length);
+
+ raw_table_data.append((const uint8_t *)&syntax->data, payload_length);
+ table_data.append((const uint8_t *)&syntax->data, payload_length);
+ }
+ else
+ {
+ payload_data = (const uint8_t *)&packet.byte[packet_payload_offset];
+ payload_length = sizeof(ts_packet_t) - packet_payload_offset;
+
+ if(payload_length > section_length_remaining)
+ payload_length = section_length_remaining;
+
+ raw_table_data.append((const uint8_t *)payload_data, payload_length);
+ table_data.append((const uint8_t *)payload_data, payload_length);
+ }
+
+ section_length_remaining -= payload_length;
+
+ if((section_length > 0) && (section_length_remaining == 0))
+ break;
+
+ continue;
+
+retry:
+ section_length = -1;
+ section_length_remaining = -1;
+ raw_table_data.clear();
+ table_data.clear();
+ }
+
+ if(table_data.length() == 0)
+ {
+ //Util::vlog("MpegTS::read_table: timeout");
+ return(false);
+ }
+
+ if(section_length < (int)sizeof(mpeg_crc_t))
+ {
+ Util::vlog("MpegTS::read_table: table too small");
+ return(false);
+ }
+
+ my_crc.process_bytes(raw_table_data.data(), raw_table_data.length() - sizeof(mpeg_crc_t));
+
+ mpeg_crc = *(const mpeg_crc_t *)(&raw_table_data.data()[raw_table_data.length() - sizeof(mpeg_crc_t)]);
+ their_crc = (mpeg_crc.byte[0] << 24) | (mpeg_crc.byte[1] << 16) | (mpeg_crc.byte[2] << 8) | mpeg_crc.byte[3];
+
+ if(my_crc.checksum() != their_crc)
+ {
+ Util::vlog("MpegTS::read_table: crc mismatch: my crc: %x, their crc: %x", my_crc.checksum(), their_crc);
+ return(false);
+ }
+
+ return(true);
+}
+
+bool MpegTS::read_pat() throw(trap)
+{
+ int attempt;
+ int current, entries, program, pid;
+ const pat_entry_t *entry;
+
+ for(attempt = 0; attempt < 16; attempt++)
+ {
+ if(!read_table(0, 0))
+ continue;
+
+ entries = (table_data.length() - sizeof(mpeg_crc_t)) / sizeof(*entry);
+ entry = (const pat_entry_t *)table_data.data();
+
+ for(current = 0; current < entries; current++)
+ {
+ program = (entry[current].program_high << 8) | (entry[current].program_low);
+ pid = (entry[current].pmt_pid_high << 8) | (entry[current].pmt_pid_low);
+ //Util::vlog("MpegTS::read_pat > program: %d -> pid %x", program, pid);
+
+ if(entry[current].reserved != 0x07)
+ {
+ Util::vlog("MpegTS::read_pat > reserved != 0x07: 0x%x", entry[current].reserved);
+ goto next_pat_entry;
+ }
+
+ pat[program] = pid;
+ }
+
+ return(true);
+
+next_pat_entry:
+ (void)0;
+ }
+
+ return(false);
+}
+
+bool MpegTS::read_pmt(int filter_pid) throw(trap)
+{
+ int attempt, programinfo_length, esinfo_length;
+ int es_pid, es_data_length, es_data_skip, es_data_offset;
+ int ds_data_skip, ds_data_offset;
+ bool private_stream_is_ac3;
+ string stream_language;
+
+ const uint8_t *es_data;
+ const pmt_header_t *pmt_header;
+ const pmt_es_entry_t *es_entry;
+ const pmt_ds_entry_t *ds_entry;
+ const pmt_ds_a_t *ds_a;
+
+ pcr_pid = video_pid = audio_pid = -1;
+
+ for(attempt = 0; attempt < 16; attempt++)
+ {
+ if(!read_table(filter_pid, table_pmt))
+ break;
+
+ pmt_header = (const pmt_header_t *)table_data.data();
+ pcr_pid = (pmt_header->pcrpid_high << 8) | pmt_header->pcrpid_low;
+ programinfo_length = (pmt_header->programinfo_length_high << 8) |
+ pmt_header->programinfo_length_low;
+
+ if(pmt_header->reserved_1 != 0x07)
+ {
+ Util::vlog("MpegTS::read_pmt > reserved_1: %x", pmt_header->reserved_1);
+ continue;
+ }
+
+ //Util::vlog("MpegTS::read_pmt: > pcr_pid: %x", pcr_pid);
+ //Util::vlog("MpegTS::read_pmt: > program info length: %d", programinfo_length);
+
+ if(pmt_header->unused != 0x00)
+ {
+ Util::vlog("MpegTS::read_pmt: > unused: %x", pmt_header->unused);
+ continue;
+ }
+
+ if(pmt_header->reserved_2 != 0x0f)
+ {
+ Util::vlog("MpegTS::read_pmt: > reserved_2: %x", pmt_header->reserved_2);
+ continue;
+ }
+
+ es_data = &(table_data.data()[offsetof(pmt_header_t, data) + programinfo_length]);
+ es_data_length = table_data.length() - offsetof(pmt_header_t, data);
+ es_data_skip = offsetof(pmt_es_entry_t, descriptors);
+
+ for(es_data_offset = 0; (es_data_offset + es_data_skip + (int)sizeof(uint32_t)) < es_data_length; )
+ {
+ es_entry = (const pmt_es_entry_t *)&es_data[es_data_offset];
+ esinfo_length = (es_entry->es_length_high << 8) | es_entry->es_length_low;
+ es_pid = (es_entry->es_pid_high << 8) | es_entry->es_pid_low;
+
+ if(es_entry->reserved_1 != 0x07)
+ {
+ Util::vlog("MpegTS::read_pmt: reserved 1: %x", es_entry->reserved_1);
+ goto next_descriptor_entry;
+ }
+
+ //Util::vlog("MpegTS::read_pmt: >> pid: %x", es_pid);
+
+ if(es_entry->reserved_2 != 0x0f)
+ {
+ Util::vlog("MpegTS::read_pmt: reserved 2: %x", es_entry->reserved_2);
+ goto next_descriptor_entry;
+ }
+
+ if(es_entry->unused != 0x00)
+ {
+ Util::vlog("MpegTS::read_pmt: unused: %x", es_entry->unused);
+ goto next_descriptor_entry;
+ }
+
+ //Util::vlog("MpegTS::read_pmt: esinfo_length: %d", esinfo_length);
+
+ switch(es_entry->stream_type)
+ {
+ case(mpeg_streamtype_video_mpeg1):
+ case(mpeg_streamtype_video_mpeg2):
+ case(mpeg_streamtype_video_h264):
+ {
+ if(video_pid < 0)
+ video_pid = es_pid;
+ break;
+ }
+
+ case(mpeg_streamtype_audio_mpeg1):
+ case(mpeg_streamtype_audio_mpeg2):
+ case(mpeg_streamtype_private_pes): // ac3
+ {
+ private_stream_is_ac3 = false;
+
+ ds_data_skip = es_data_offset + offsetof(pmt_es_entry_t, descriptors);
+
+ for(ds_data_offset = 0; (ds_data_offset + 2) < esinfo_length; )
+ {
+ ds_entry = (const pmt_ds_entry_t *)&es_data[ds_data_skip + ds_data_offset];
+
+ //Util::vlog("MpegTS::read_pmt: >>> offset: %d", ds_data_offset);
+ //Util::vlog("MpegTS::read_pmt: >>> descriptor id: %x", ds_entry->id);
+ //Util::vlog("MpegTS::read_pmt: >>> length: %d", ds_entry->length);
+
+ switch(ds_entry->id)
+ {
+ case(pmt_desc_language):
+ {
+ ds_a = (const pmt_ds_a_t *)&ds_entry->data;
+ //Util::vlog("MpegTS::read_pmt: >>>> lang: %c%c%c [%d]", ds_a->lang[0],
+ // ds_a->lang[1], ds_a->lang[2], ds_a->code);
+
+ stream_language.assign((const char *)&ds_a->lang, offsetof(pmt_ds_a_t, code));
+
+ break;
+ }
+
+ case(pmt_desc_ac3):
+ {
+ private_stream_is_ac3 = true;
+ break;
+ }
+ }
+
+ ds_data_offset += ds_entry->length + offsetof(pmt_ds_entry_t, data);
+ }
+
+ if(!boost::iequals(stream_language, "nar"))
+ {
+ if(private_stream_is_ac3 || (audio_pid < 0)) // ac3 stream has preference
+ audio_pid = es_pid;
+ }
+ }
+ }
+
+next_descriptor_entry:
+ es_data_offset += es_data_skip + esinfo_length;
+ }
+
+ return(true);
+ }
+
+ return(false);
+}
+
+int MpegTS::get_fd() const throw()
+{
+ return(fd);
+}
+
+void MpegTS::parse_pts_ms(int pts_ms, int &h, int &m, int &s, int &ms) throw()
+{
+ h = pts_ms / (60 * 60 * 1000);
+ pts_ms -= h * (60 * 60 * 1000);
+ m = pts_ms / (60 * 1000);
+ pts_ms -= m * (60 * 1000);
+ s = pts_ms / (1000);
+ pts_ms -= s * (1000);
+ ms = pts_ms;
+}
+
+int MpegTS::find_pcr_ms(seek_direction_t direction) const throw()
+{
+ ts_packet_t packet;
+ ts_adaptation_field_t *afield;
+ int attempt, pcr_ms = -1, pid;
+ int ms, h, m, s;
+ off_t offset;
+
+ for(attempt = 0; (pcr_ms < 0) && (attempt < find_pcr_max_probe); attempt++)
+ {
+ if(direction == direction_backward)
+ {
+ offset = (off_t)sizeof(ts_packet_t) * 2 * -1;
+
+ if(lseek(fd, offset, SEEK_CUR) == (off_t)-1)
+ {
+ Util::vlog("MpegTS::find_pcr_ms: lseek failed");
+ return(-1);
+ }
+ }
+
+ if(read(fd, &packet, sizeof(packet)) != sizeof(packet))
+ {
+ Util::vlog("MpegTS::find_pcr_ms: read error");
+ return(-1);
+ }
+
+ if(packet.header.sync_byte != sync_byte_value)
+ {
+ Util::vlog("MpegTS::find_pcr_ms: no sync byte");
+ return(-1);
+ }
+
+ pid = (packet.header.pid_high << 8) | packet.header.pid_low;
+
+ //Util::vlog("MpegTS::find_pcr_ms: pid: %d", pid);
+
+ if(pid != pcr_pid)
+ continue;
+
+ if(!packet.header.af)
+ {
+ //Util::vlog("MpegTS::find_pcr_ms: no adaptation field");
+ continue;
+ }
+
+ afield = (ts_adaptation_field_t *)&packet.header.afield;
+
+ if(!afield->contains_pcr)
+ {
+ //Util::vlog("MpegTS::find_pcr_ms: attempt: %d, adaptation field does not have pcr field", attempt);
+ continue;
+ }
+
+ // read 32 bits of the total 42 bits of clock precision, which is
+ // enough for seeking, one tick = 1/45th second
+
+ pcr_ms = uint32_t(afield->pcr_0 << 24) | uint32_t(afield->pcr_1 << 16) |
+ uint32_t(afield->pcr_2 << 8) | uint32_t(afield->pcr_3 << 0);
+ pcr_ms /= 90;
+ pcr_ms <<= 1;
+ }
+
+ if(attempt >= find_pcr_max_probe)
+ {
+ Util::vlog("MpegTS::find_pcr_ms: no pcr found");
+ return(-1);
+ }
+
+ parse_pts_ms(pcr_ms, h, m, s, ms);
+
+ //Util::vlog("PCR found after %d packets", attempt);
+ //Util::vlog("PCR = %d ms (%02d:%02d:%02d:%03d)", pcr_ms, h, m, s, ms);
+
+ return(pcr_ms);
+}
+
+off_t MpegTS::seek(int whence, off_t offset) const throw(trap)
+{
+ ts_packet_t packet;
+ off_t actual_offset;
+ off_t new_offset;
+
+ offset = ((offset / (off_t)sizeof(ts_packet_t)) * (off_t)sizeof(ts_packet_t));
+
+ if(lseek(fd, offset, whence) < 0)
+ throw(trap("MpegTS::seek: lseek (1)"));
+
+ if(read(fd, &packet, sizeof(packet)) != sizeof(packet))
+ throw(trap("MpegTS::seek: read error"));
+
+ if(packet.header.sync_byte != sync_byte_value)
+ throw(trap("MpegTS::seek: no sync byte"));
+
+ new_offset = (off_t)0 - (off_t)sizeof(packet);
+
+ if((actual_offset = lseek(fd, new_offset, SEEK_CUR)) < 0)
+ throw(trap("MpegTS::seek: lseek (2)"));
+
+ return(actual_offset);
+}
+
+off_t MpegTS::seek_absolute(off_t offset) const throw(trap)
+{
+ return(seek(SEEK_SET, offset));
+}
+
+off_t MpegTS::seek_relative(off_t roffset, off_t limit) const throw(trap)
+{
+ off_t offset;
+
+ if((roffset < 0) || (roffset > limit))
+ throw(trap("MpegTS::seek_relative: value out of range"));
+
+ offset = (eof_offset / limit) * roffset;
+
+ return(seek(SEEK_SET, offset));
+}
+
+off_t MpegTS::seek_time(int pts_ms) const throw(trap)
+{
+ int h, m, s, ms;
+ int attempt;
+ off_t lower_bound_offset = 0;
+ int lower_bound_pts_ms = first_pcr_ms;
+ off_t upper_bound_offset = eof_offset;
+ int upper_bound_pts_ms = last_pcr_ms;
+ off_t disect_offset;
+ int disect_pts_ms;
+ off_t current_offset;
+
+ if(!is_time_seekable)
+ throw(trap("MpegTS::seek: stream is not seekable"));
+
+ if(pts_ms < first_pcr_ms)
+ {
+ Util::vlog("MpegTS::seek: seek pts beyond start of file");
+ return(-1);
+ }
+
+ if(pts_ms > last_pcr_ms)
+ {
+ Util::vlog("MpegTS::seek: pts beyond end of file");
+ return(-1);
+ }
+
+ for(attempt = 0; attempt < seek_max_attempts; attempt++)
+ {
+ disect_offset = (lower_bound_offset + upper_bound_offset) / 2;
+
+ parse_pts_ms(pts_ms, h, m, s, ms);
+ //Util::vlog("MpegTS::seek: seek for [%02d:%02d:%02d.%03d] between ", h, m, s, ms);
+ parse_pts_ms(lower_bound_pts_ms, h, m, s, ms);
+ //Util::vlog("MpegTS::seek: [%02d:%02d:%02d.%03d", h, m, s, ms);
+ parse_pts_ms(upper_bound_pts_ms, h, m, s, ms);
+ //Util::vlog("MpegTS::seek: -%02d:%02d:%02d.%03d], ", h, m, s, ms);
+ //Util::vlog("MpegTS::seek: offset = %lld [%lld-%lld], ", disect_offset, lower_bound_offset, upper_bound_offset);
+
+ if((current_offset = seek(SEEK_SET, disect_offset)) == 1)
+ {
+ Util::vlog("MpegTS::seek: seek fails");
+ return(-1);
+ }
+
+ //Util::vlog("MpegTS::seek: current offset = %lld (%lld%%)", current_offset, (current_offset * 100) / eof_offset);
+
+ if((disect_pts_ms = find_pcr_ms(direction_forward)) < 0)
+ {
+ Util::vlog("MpegTS::seek: eof");
+ return(-1);
+ }
+
+ parse_pts_ms(disect_pts_ms, h, m, s, ms);
+ //Util::vlog("MpegTS::seek: disect=[%02d:%02d:%02d.%03d]", h, m, s, ms);
+
+ if(disect_pts_ms < 0)
+ {
+ Util::vlog("MpegTS::seek failed to find pts");
+ return(-1);
+ }
+
+ if(((disect_pts_ms > pts_ms) && ((disect_pts_ms - pts_ms) < 8000)) ||
+ ((pts_ms >= disect_pts_ms) && ((pts_ms - disect_pts_ms) < 8000)))
+ {
+ //Util::vlog("MpegTS::seek: found");
+ return(current_offset);
+ }
+
+ if(disect_pts_ms < pts_ms) // seek higher
+ {
+ lower_bound_offset = disect_offset;
+ lower_bound_pts_ms = disect_pts_ms;
+
+ //Util::vlog("MpegTS::seek: not found: change lower bound");
+ }
+ else
+ {
+ upper_bound_offset = disect_offset;
+ upper_bound_pts_ms = disect_pts_ms;
+
+ //Util::vlog("MpegTS::seek: not found: change upper bound");
+ }
+ }
+
+ return(-1);
+}
diff --git a/src/openpli-streamproxy/mpegts.h b/src/openpli-streamproxy/mpegts.h
new file mode 100644
index 0000000..8bbf17e
--- /dev/null
+++ b/src/openpli-streamproxy/mpegts.h
@@ -0,0 +1,216 @@
+#ifndef _mpegts_h_
+#define _mpegts_h_
+
+//#include "config.h" // oskwon
+#include "trap.h"
+#include "Source.h" // oskwon
+
+#include <map>
+#include <string>
+
+#include <stdint.h>
+#include <sys/types.h>
+
+class MpegTS : public Source // oskwon
+{
+ private:
+
+ typedef std::basic_string<uint8_t> u8string;
+ typedef unsigned int bf;
+
+ enum
+ {
+ sync_byte_value = 0x47,
+ };
+
+ enum
+ {
+ table_pmt = 0x02,
+ };
+
+ enum
+ {
+ find_pcr_max_probe = 1000000,
+ seek_max_attempts = 32,
+ };
+
+ enum
+ {
+ pmt_desc_language = 0x0a,
+ pmt_desc_ac3 = 0x6a,
+ };
+
+ enum
+ {
+ mpeg_streamtype_video_mpeg1 = 0x01,
+ mpeg_streamtype_video_mpeg2 = 0x02,
+ mpeg_streamtype_video_h264 = 0x1b,
+ mpeg_streamtype_audio_mpeg1 = 0x03,
+ mpeg_streamtype_audio_mpeg2 = 0x04,
+ mpeg_streamtype_private_pes = 0x06,
+ };
+
+ typedef struct
+ {
+ uint8_t byte[4];
+ } mpeg_crc_t;
+
+ typedef union
+ {
+ struct
+ {
+ uint8_t sync_byte;
+ bf pid_high:5;
+ bf tp:1;
+ bf pusi:1;
+ bf tei:1;
+ uint8_t pid_low;
+ bf cc:4;
+ bf payload_present:1;
+ bf af:1;
+ bf sc:2;
+ uint8_t payload[0];
+ uint8_t afield_length;
+ uint8_t afield[0];
+ } header;
+ uint8_t byte[188];
+ } ts_packet_t;
+
+ typedef struct
+ {
+ bf field_ext:1;
+ bf private_data:1;
+ bf splice_point:1;
+ bf contains_opcr:1;
+ bf contains_pcr:1;
+ bf priority:1;
+ bf gop_start:1;
+ bf discont:1;
+ uint8_t pcr_0; // 32 bits of the total 42 bits
+ uint8_t pcr_1; // of clock precision, which is
+ uint8_t pcr_2; // enough for seeking
+ uint8_t pcr_3; // tick = 1/45th second
+ } ts_adaptation_field_t;
+
+ typedef struct
+ {
+ uint8_t table_id;
+ bf section_length_high:2;
+ bf section_length_unused:2;
+ bf reserved:2;
+ bf private_bit:1;
+ bf section_syntax:1;
+ uint8_t section_length_low;
+ uint8_t payload[0];
+ } section_table_header_t;
+
+ typedef struct
+ {
+ uint8_t tide_high;
+ uint8_t tide_low;
+ bf currnext:1;
+ bf version:5;
+ bf reserved:2;
+ uint8_t ordinal;
+ uint8_t last;
+ uint8_t data[0];
+ } section_table_syntax_t;
+
+ typedef struct
+ {
+ uint8_t program_high;
+ uint8_t program_low;
+ bf pmt_pid_high:5;
+ bf reserved:3;
+ uint8_t pmt_pid_low;
+ } pat_entry_t;
+
+ typedef struct
+ {
+ bf pcrpid_high:5;
+ bf reserved_1:3;
+ uint8_t pcrpid_low;
+ uint programinfo_length_high:2;
+ bf unused:2;
+ bf reserved_2:4;
+ uint8_t programinfo_length_low;
+ uint8_t data[0];
+ } pmt_header_t;
+
+ typedef struct
+ {
+ uint8_t stream_type;
+ bf es_pid_high:5;
+ bf reserved_1:3;
+ uint8_t es_pid_low;
+ bf es_length_high:2;
+ bf unused:2;
+ bf reserved_2:4;
+ uint8_t es_length_low;
+ uint8_t descriptors[0];
+ } pmt_es_entry_t;
+
+ typedef struct
+ {
+ uint8_t id;
+ uint8_t length;
+ uint8_t data[0];
+ } pmt_ds_entry_t;
+
+ typedef struct
+ {
+ uint8_t lang[3];
+ uint8_t code;
+ } pmt_ds_a_t;
+
+ typedef enum
+ {
+ direction_forward,
+ direction_backward,
+ } seek_direction_t;
+
+ typedef std::map<int, int> mpegts_pat_t;
+
+ MpegTS();
+ MpegTS(const MpegTS &);
+
+ bool private_fd;
+ int fd;
+ mpegts_pat_t pat;
+ u8string raw_table_data;
+ u8string table_data;
+
+ static void parse_pts_ms(int pts_ms, int &h, int &m, int &s, int &ms) throw();
+
+ void init() throw(trap);
+ bool read_table(int filter_pid, int filter_table) throw(trap);
+ bool read_pat() throw(trap);
+ bool read_pmt(int filter_pid) throw(trap);
+ int find_pcr_ms(seek_direction_t direction) const throw();
+ off_t seek(int whence, off_t offset) const throw(trap);
+
+ public:
+
+ int pmt_pid;
+ int pcr_pid;
+ int video_pid;
+ int audio_pid;
+
+ bool request_time_seek;
+ bool is_time_seekable;
+
+ int first_pcr_ms;
+ int last_pcr_ms;
+ off_t eof_offset;
+ off_t stream_length;
+
+ MpegTS(int fd, bool request_time_seek) throw(trap);
+ MpegTS(std::string file, bool request_time_seek) throw(trap);
+ ~MpegTS() throw();
+
+ int get_fd() const throw();
+ off_t seek_absolute(off_t offset) const throw(trap);
+ off_t seek_relative(off_t offset, off_t limit) const throw(trap);
+ off_t seek_time(int pts_ms) const throw(trap);
+};
+#endif
diff --git a/src/openpli-streamproxy/trap.cpp b/src/openpli-streamproxy/trap.cpp
new file mode 100644
index 0000000..b57b23e
--- /dev/null
+++ b/src/openpli-streamproxy/trap.cpp
@@ -0,0 +1,42 @@
+//#include "config.h" // oskwon
+#include "trap.h"
+
+#include <string>
+using std::string;
+
+#include <exception>
+using std::exception;
+
+trap::trap(string message_in) throw()
+ :
+ message(message_in)
+{
+ message += string(" (") + exception::what() + ")";
+}
+
+const char *trap::what() const throw()
+{
+ return(message.c_str());
+}
+
+trap::~trap() throw()
+{
+}
+
+http_trap::http_trap(string message_in,
+ int http_error_in, string http_header_in) throw()
+ :
+ trap(message_in),
+ http_error(http_error_in),
+ http_header(http_header_in)
+{
+}
+
+const char *http_trap::what() const throw()
+{
+ return(message.c_str());
+}
+
+http_trap::~http_trap() throw()
+{
+}
diff --git a/src/openpli-streamproxy/trap.h b/src/openpli-streamproxy/trap.h
new file mode 100644
index 0000000..ed33328
--- /dev/null
+++ b/src/openpli-streamproxy/trap.h
@@ -0,0 +1,33 @@
+#ifndef _trap_h_
+#define _trap_h_
+
+#include <string>
+#include <exception>
+
+class trap : public std::exception
+{
+ public:
+
+ std::string message;
+
+ trap(std::string msg) throw();
+ virtual const char *what() const throw();
+ virtual ~trap() throw();
+};
+
+class http_trap : public trap
+{
+ public:
+
+ int http_error;
+ std::string http_header;
+
+ http_trap(std::string msg,
+ int http_error, std::string http_header) throw();
+
+ virtual const char *what() const throw();
+ virtual ~http_trap() throw();
+
+};
+
+#endif
diff --git a/src/up.sh b/src/up.sh
new file mode 100755
index 0000000..0d52858
--- /dev/null
+++ b/src/up.sh
@@ -0,0 +1,8 @@
+ftp -n 192.168.$1 << +
+user root a
+prompt off
+cd /usr/bin
+put transtreamproxy
+bye
++
+