diff options
author | oskwon <kos@dev3> | 2014-06-12 00:14:12 (GMT) |
---|---|---|
committer | oskwon <kos@dev3> | 2014-06-12 01:37:55 (GMT) |
commit | bcfacba238ee3e4e2f04c71293841734d0444311 (patch) | |
tree | 342b669c05327759712fc3be5edcc67cc8e5ec5d |
import sources.
-rw-r--r-- | .gitignore | 4 | ||||
-rw-r--r-- | src/Demuxer.cpp | 223 | ||||
-rw-r--r-- | src/Demuxer.h | 48 | ||||
-rw-r--r-- | src/Encoder.cpp | 85 | ||||
-rw-r--r-- | src/Encoder.h | 46 | ||||
-rw-r--r-- | src/Logger.cpp | 184 | ||||
-rw-r--r-- | src/Logger.h | 79 | ||||
-rw-r--r-- | src/Makefile | 72 | ||||
-rw-r--r-- | src/Source.h | 23 | ||||
-rw-r--r-- | src/UriDecoder.cpp | 277 | ||||
-rw-r--r-- | src/UriDecoder.h | 43 | ||||
-rw-r--r-- | src/Utils.cpp | 193 | ||||
-rw-r--r-- | src/Utils.h | 79 | ||||
-rw-r--r-- | src/main.cpp | 332 | ||||
-rw-r--r-- | src/openpli-streamproxy/mpegts.cpp | 734 | ||||
-rw-r--r-- | src/openpli-streamproxy/mpegts.h | 216 | ||||
-rw-r--r-- | src/openpli-streamproxy/trap.cpp | 42 | ||||
-rw-r--r-- | src/openpli-streamproxy/trap.h | 33 | ||||
-rwxr-xr-x | src/up.sh | 8 |
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 ++ + |