From 0d5d66cdee3921eba4c11859fb4edc296339e746 Mon Sep 17 00:00:00 2001 From: oskwon Date: Wed, 18 Jun 2014 17:17:14 +0900 Subject: Add request type(/m3u) for Vu+PlayerHD (IOS). Remove up.sh and add some scripts. Some sources refactoring. --- .gitignore | 6 +- script/script.config | 42 +++++++ script/script.upload | 33 ++++++ src/Demuxer.cpp | 10 +- src/Demuxer.h | 5 +- src/Encoder.cpp | 22 ++-- src/Http.cpp | 174 +++++++++++++++++++++++++++++ src/Http.h | 49 +++++++++ src/Logger.cpp | 30 ++++- src/Logger.h | 6 +- src/Makefile | 57 +++++----- src/Mpeg.cpp | 46 ++++++++ src/Mpeg.h | 29 +++++ src/Util.cpp | 145 ++++++++++++++++++++++++ src/Util.h | 49 +++++++++ src/Utils.cpp | 278 ---------------------------------------------- src/Utils.h | 84 -------------- src/main.cpp | 305 +++++++++++++++++++++++++++------------------------ src/up.sh | 8 -- 19 files changed, 810 insertions(+), 568 deletions(-) create mode 100755 script/script.config create mode 100755 script/script.upload create mode 100644 src/Http.cpp create mode 100644 src/Http.h create mode 100644 src/Mpeg.cpp create mode 100644 src/Mpeg.h create mode 100644 src/Util.cpp create mode 100644 src/Util.h delete mode 100644 src/Utils.cpp delete mode 100644 src/Utils.h delete mode 100755 src/up.sh diff --git a/.gitignore b/.gitignore index 79f27fc..1ffb871 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ -*.o -transtreamproxy .cproject .project +config.mk +config.mk.bak +*.o +transtreamproxy diff --git a/script/script.config b/script/script.config new file mode 100755 index 0000000..3f85083 --- /dev/null +++ b/script/script.config @@ -0,0 +1,42 @@ +#!/bin/sh + +IP=192.168.0.1 +MODEL=vusolo2 +OETOP=/openembedded/path/here + +CONFIG_PATH=$PWD/../config.mk + +function getValue() { + IN=$1 + set -- "$IN" + IFS="="; declare -a Array=($*) + echo "${Array[1]}" +} + +# set default config value from old config. +if [ -e $CONFIG_PATH ]; then + IP="$(getValue `cat $CONFIG_PATH | grep 'IP='`)" + MODEL="$(getValue `cat $CONFIG_PATH | grep 'MODEL='`)" + OETOP="$(getValue `cat $CONFIG_PATH | grep 'OETOP='`)" +fi + +# input new config. +read -p "Please, input target ip [$IP] : " NEW_IP +read -p "Please, input model name [$MODEL] : " NEW_MODEL +read -p "Please, input openembeded root path [$OETOP] : " NEW_OETOP + +# check new config value. +if [ ! -z $NEW_IP ]; then IP=$NEW_IP; fi +if [ ! -z $NEW_MODEL ]; then MODEL=$NEW_MODEL; fi +if [ ! -z $NEW_OETOP ]; then OETOP=$NEW_OETOP; fi + +# backup config file. +if [ -e $CONFIG_PATH ]; then + cp -a $CONFIG_PATH $CONFIG_PATH.bak +fi + +# write config file. +echo "MODEL=$MODEL" > $CONFIG_PATH +echo "OETOP=$OETOP" >> $CONFIG_PATH +echo "" >> $CONFIG_PATH +echo "IP=$IP" >> $CONFIG_PATH diff --git a/script/script.upload b/script/script.upload new file mode 100755 index 0000000..841a5e1 --- /dev/null +++ b/script/script.upload @@ -0,0 +1,33 @@ +#!/bin/sh + +IP=$1 +LOC=$2 +BIN=$3 +TAR=$4 + +usage() { + echo "" + echo "usage : $0 [IP] [LOCAL] [BIN] [TARGET]" + echo " - IP : target ip" + echo " - BIN : binary name" + echo " - LOCAL : binary path" + echo " - TARGET : target path to install" + exit +} + +if [ -z $IP ]; then echo "[!] IP is not set."; usage; fi +if [ -z $BIN ]; then echo "[!] BIN is not set."; usage; fi +if [ -z $LOC ]; then echo "[!] LOCAL is not set."; usage; fi +if [ -z $TAR ]; then echo "[!] TARGET is not set."; usage; fi + +echo "[*] upload... $LOC/$BIN to $IP:$TAR" +cd $LOC +ftp -n $IP << + +user root a +prompt off +bi +cd $TAR +put $BIN +bye ++ + diff --git a/src/Demuxer.cpp b/src/Demuxer.cpp index a943bab..04cd35b 100644 --- a/src/Demuxer.cpp +++ b/src/Demuxer.cpp @@ -15,7 +15,7 @@ #include #include -#include "Utils.h" +#include "Util.h" #include "Logger.h" #include "Demuxer.h" @@ -149,11 +149,11 @@ bool Demuxer::parse_webif_response(std::string& response, std::vector pidtokens; - if (split(line.c_str() + 3, ',', pidtokens)) { + if (Util::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)) + if (!Util::split_key_value(toekn, ":", pidstr, pidtype)) continue; unsigned long pid = strtoul(pidstr.c_str(), 0, 0x10); @@ -183,7 +183,7 @@ bool Demuxer::parse_webif_response(std::string& response, std::vector &new_pids); public: - Demuxer(RequestHeader *header) throw(trap); + Demuxer(HttpHeader *header) throw(trap); virtual ~Demuxer() throw(); int get_fd() const throw(); }; diff --git a/src/Encoder.cpp b/src/Encoder.cpp index 3b59b07..d6f817f 100644 --- a/src/Encoder.cpp +++ b/src/Encoder.cpp @@ -14,7 +14,7 @@ #include #include -#include "Utils.h" +#include "Util.h" #include "Logger.h" #include "Encoder.h" @@ -50,7 +50,7 @@ Encoder::Encoder() throw(trap) DEBUG("shm-info : fd [%d], name [%s], size [%d], data [%p]", mShmFd, mShmName.c_str(), mShmSize, mShmData); DEBUG("sem-info : id [%p], name [%s]", mSemId, mSemName.c_str()); - std::vector pidlist = find_process_by_name("transtreamproxy", 0); + std::vector pidlist = Util::find_process_by_name("transtreamproxy", 0); session_dump("before init."); @@ -66,7 +66,7 @@ Encoder::Encoder() throw(trap) Post(); int mypid = getpid(); - std::string ipaddr = get_host_addr(); + std::string ipaddr = Util::host_addr(); if (session_already_exist(ipaddr) > 0) { encoder_id = session_update(ipaddr, mypid); } @@ -92,7 +92,7 @@ Encoder::~Encoder() bool Encoder::encoder_open() { - std::string path = "/dev/bcm_enc" + ultostr(encoder_id); + std::string path = "/dev/bcm_enc" + Util::ultostr(encoder_id); fd = ::open(path.c_str(), O_RDWR, 0); if (fd >= 0) { state = ENCODER_STAT_OPENED; @@ -141,12 +141,14 @@ int Encoder::get_fd() void Encoder::session_dump(const char* aMessage) { - DUMMY(" >> %s", aMessage); - DUMMY("-------- [ DUMP HOST INFO ] ---------"); - for (int i = 0; i < max_encodr_count; i++) { - DUMMY("%d : ip [%s], pid [%d]", i, mShmData[i].ip, mShmData[i].pid); + if (Logger::instance()->get_level() >= Logger::INFO) { + DUMMY(" >> %s", aMessage); + DUMMY("-------- [ DUMP HOST INFO ] ---------"); + for (int i = 0; i < max_encodr_count; i++) { + DUMMY("%d : ip [%s], pid [%d]", i, mShmData[i].ip, mShmData[i].pid); + } + DUMMY("-------------------------------------"); } - DUMMY("-------------------------------------"); } //---------------------------------------------------------------------- @@ -220,7 +222,7 @@ int Encoder::session_update(std::string aIpAddr, int aPid) for (; i < max_encodr_count; i++) { if (strcmp(mShmData[i].ip, aIpAddr.c_str()) == 0) { result = true; - kill_process(mShmData[i].pid); + Util::kill_process(mShmData[i].pid); memset(mShmData[i].ip, 0, 16); mShmData[i].pid = 0; break; diff --git a/src/Http.cpp b/src/Http.cpp new file mode 100644 index 0000000..7b736c6 --- /dev/null +++ b/src/Http.cpp @@ -0,0 +1,174 @@ +/* + * Http.cpp + * + * Created on: 2014. 6. 18. + * Author: oskwon + */ + +#include + +#include + +#include "Util.h" +#include "Logger.h" + +#include "Http.h" +#include "UriDecoder.h" + +using namespace std; +//---------------------------------------------------------------------- + +bool HttpHeader::parse_request(std::string header) +{ + std::string line, key, value; + std::istringstream request_stream; + request_stream.str(header); + + request_stream >> method; + request_stream >> path; + request_stream >> version; + std::getline(request_stream, line); + + while(std::getline(request_stream, line)) { + if ((line = Util::trim(line)) != "") { + Util::split_key_value(line, ":", key, value); + + key = Util::trim(key); + value = Util::trim(value); + + params[key] = value; + DEBUG("add param : [%s] - [%s]", key.c_str(), value.c_str()); + } + } + + int idx = path.find("?"); + // page + if (idx != std::string::npos) { + page = path.substr(0,idx); + std::string page_param = path.substr(idx + 1); + + DEBUG("request url : [%s] - [%s]", page.c_str(), page_param.c_str()); + std::istringstream request_params_stream; + request_params_stream.str(page_param); + while(std::getline(request_params_stream, line, '&')) { + if ((line = Util::trim(line)) != "") { + Util::split_key_value(line, "=", key, value); + + key = Util::trim(key); + value = Util::trim(value); + + page_params[key] = value; + DEBUG("add page param : [%s] - [%s]", key.c_str(), value.c_str()); + } + } + + if (page == "/file") { + type = HttpHeader::TRANSCODING_FILE; + } + else if (page == "/m3u") { + type = HttpHeader::M3U; + } + } + // live + else { + type = HttpHeader::TRANSCODING_LIVE; + } + return true; +} +//---------------------------------------------------------------------- + +static const char *http_ok = "HTTP/1.1 200 OK\r\n"; +static const char *http_partial = "HTTP/1.1 206 Partial Content\r\n"; +static const char *http_connection = "Connection: Close\r\n"; +static const char *http_server = "Server: transtreamproxy\r\n"; +static const char *http_done = "\r\n"; +std::string HttpHeader::build_response(Mpeg *source) +{ + std::ostringstream oss; + + switch(type) { + case HttpHeader::TRANSCODING_FILE: { + std::string range = params["Range"]; + off_t seek_offset = 0, content_length = 0; + + if((range.length() > 7) && (range.substr(0, 6) == "bytes=")) { + range = range.substr(6); + if(range.find('-') == (range.length() - 1)) { + seek_offset = Util::strtollu(range); + } + } + + content_length = source->stream_length - seek_offset; + if (seek_offset > 0) { + content_length += 1; + oss << http_partial; + } + else { + oss << http_ok; + } + oss << http_connection; + oss << "Content-Type: video/mpeg\r\n"; + oss << http_server; + oss << "Accept-Ranges: bytes\r\n"; + oss << "Content-Length: " << Util::ultostr(content_length) << "\r\n"; + oss << "Content-Range: bytes " << + Util::ultostr(seek_offset) << "-" << + Util::ultostr(source->stream_length - 1) << "/" << + Util::ultostr(source->stream_length) << "\r\n"; + oss << http_done; + } + break; + case HttpHeader::TRANSCODING_LIVE: { + oss << http_ok; + oss << http_connection; + oss << "Content-Type: video/mpeg\r\n"; + oss << http_server; + oss << http_done; + } + break; + case HttpHeader::M3U: { + std::ostringstream m3u_oss; + m3u_oss << "#EXTM3U\n"; + m3u_oss << "#EXTVLCOPT--http-reconnect=true\n"; + m3u_oss << "http://" << params["Host"] << "/file?file=" << page_params["file"]; + if (page_params["position"] != "") { + m3u_oss << "&position=" << page_params["position"]; + } + m3u_oss << "\n"; + m3u_oss << http_done; + + std::string m3u_content = m3u_oss.str(); + + oss << http_partial; + oss << "Content-Type: audio/x-mpegurl\r\n"; + oss << "Accept-Ranges: bytes\r\n"; + oss << http_connection; + oss << http_server; + oss << "Content-Length: " << Util::ultostr(m3u_content.length()) << "\r\n"; + oss << "Content-Range: bytes 0-" << + Util::ultostr(m3u_content.length() - 1) << "/" << + Util::ultostr(m3u_content.length()) << "\r\n"; + oss << http_done; + oss << m3u_content; + } + break; + default: return ""; + } + return oss.str(); +} +//---------------------------------------------------------------------- + +std::string HttpHeader::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; +} +//---------------------------------------------------------------------- diff --git a/src/Http.h b/src/Http.h new file mode 100644 index 0000000..31246bc --- /dev/null +++ b/src/Http.h @@ -0,0 +1,49 @@ +/* + * Http.h + * + * Created on: 2014. 6. 18. + * Author: oskwon + */ + +#ifndef HTTP_H_ +#define HTTP_H_ + +#include +#include + +#include "Mpeg.h" +//---------------------------------------------------------------------- + +class HttpHeader +{ +public: + enum { + UNKNOWN = 0, + TRANSCODING_LIVE, + TRANSCODING_FILE, + M3U + }; + + int type; + std::string method; + std::string path; + std::string version; + std::map params; + + std::string page; + std::map page_params; + +private: + +public: + HttpHeader() : type(UNKNOWN) {} + virtual ~HttpHeader() {} + + bool parse_request(std::string header); + std::string build_response(Mpeg *source); + + static std::string read_request(); +}; +//---------------------------------------------------------------------- + +#endif /* HTTP_H_ */ diff --git a/src/Logger.cpp b/src/Logger.cpp index 8d06b88..963a783 100644 --- a/src/Logger.cpp +++ b/src/Logger.cpp @@ -11,6 +11,7 @@ #include #include "Logger.h" +//---------------------------------------------------------------------- #define USE_COLOR_LOG 1 @@ -80,9 +81,25 @@ Logger* Logger::instance() } //---------------------------------------------------------------------- -bool Logger::init(const char* aName, int aLogLevel, bool aWithTimestamp, const char* aVersion) +bool Logger::init(const char* aName, int aLogLevel, bool aWithTimestamp) { - mLogLevel = aLogLevel; + if (access("/tmp/.debug_on", F_OK) == 0) { + FILE *fp = fopen("/tmp/.debug_on", "r"); + + int lv = 0; + fscanf(fp, "%d", &lv); + if (Logger::NONE < lv && lv <= Logger::LOG) { + mLogLevel = lv; + } + else { + mLogLevel = aLogLevel; + } + fclose(fp); + } + else { + mLogLevel = aLogLevel; + } + if (aName == NULL) { mLogHandle = stdout; INFO("logger initialized."); @@ -97,7 +114,14 @@ bool Logger::init(const char* aName, int aLogLevel, bool aWithTimestamp, const c printf("fail to open logger [%s].", path); return false; } - DUMMY("Logger initialized. (Ver %s)", aVersion); + + if (mLogLevel >= Logger::INFO) { +#if defined(_MAJOR) && defined(_MINOR) + DUMMY("Logger initialized. (Ver %d.%d)", _MAJOR, _MINOR); +#else + DUMMY("Logger initialized."); +#endif + } return true; } //---------------------------------------------------------------------- diff --git a/src/Logger.h b/src/Logger.h index eaa77c5..e9bcb23 100644 --- a/src/Logger.h +++ b/src/Logger.h @@ -53,7 +53,9 @@ private: static void logger_release() { if (mInstHandle) { - DUMMY("Logger Released."); + if (Logger::instance()->get_level() >= Logger::INFO) { + DUMMY("Logger Released."); + } delete mInstHandle; } }; @@ -62,7 +64,7 @@ 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); + bool init(const char* aFileName = 0, int aLogLevel = Logger::ERROR, bool aWithTimestamp = false); void log(const char* aFormat, ...); void log(int aLogLevel, const char* aFormat, ...); diff --git a/src/Makefile b/src/Makefile index a5a32d8..9b6cd84 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,67 +1,62 @@ #============================================================================ -# Name : Makefile +# Name : Makefile (transtreamproxy) # Author : oskwon(kos@dev3) # Version : # Copyright : Copyright(c)2013 Vu+ Team. All right reserved. # Description : #============================================================================ -VER = v3.0 +-include ../config.mk +ifeq ($(MODEL),) +$(error config.mk is not set. please run script.config before make.) +endif -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") +MAJOR = 3 +MINOR = 0 +PROJECT = transtreamproxy -CFLAGS ?= -LDFLAGS ?= +TOP=$(PWD)/.. -#CFLAGS += -Wno-unused-result - -CFLAGS += -O2 -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 +CROSS=$(OETOP)/$(MODEL)/build/tmp/cross/mipsel/bin/mipsel-oe-linux- +SYSROOT=$(OETOP)/$(MODEL)/build/tmp/staging/mipsel-oe-linux +RM=rm -f CXX=$(CROSS)g++ LD=$(CROSS)ld STRIP=$(CROSS)strip -RM=rm -f - -CFLAGS += -I. -I./external -I$(SYSROOT)/usr/include -LDFLAGS += -L$(SYSROOT)/usr/lib -lpthread -lrt +UPLOAD=$(TOP)/script/script.upload +SRCS = $(shell find . -name "*.cpp") OBJS=$(SRCS:.cpp=.o) +CFLAGS += -D_MAJOR=$(MAJOR) -D_MINOR=$(MINOR) +CFLAGS += -O2 -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -I. -I./external -I$(SYSROOT)/usr/include +LDFLAGS += -L$(SYSROOT)/usr/lib -lpthread -lrt + .SUFFIXES : .cpp .o .PHONY : all clean install .showinfo .cpp.o: - $(Q)echo "Compile... "$< - $(Q)$(CXX) -c $(CFLAGS) -o $@ $< + $(CXX) -c $(CFLAGS) -o $@ $< -all: .showinfo $(BIN) +all: .showinfo $(PROJECT) -$(BIN):$(OBJS) - $(Q)echo "Linking... "$@ - $(Q)$(CXX) -o $@ $(OBJS) $(LDFLAGS) - $(Q)$(STRIP) $@ +$(PROJECT):$(OBJS) + $(CXX) -o $@ $(OBJS) $(LDFLAGS) + $(STRIP) $@ install: - $(Q)./up.sh $(IP) + @$(UPLOAD) $(IP) . $(PROJECT) /usr/bin clean: - $(Q)$(RM) $(BIN) $(OBJS) *.log + $(RM) $(PROJECT) $(OBJS) *.log .showinfo: @echo "-----------------------------------------------------" @echo " [ BUILD ENVIRONMENT ] " @echo "-----------------------------------------------------" - @echo "OUTPUT : "$(STREAMER_BIN) $(TRANSTREAMPROXY_BIN) - @echo "VERSION : "$(VER) + @echo "PROJECT : "$(PROJECT)" (v"$(MAJOR)"."$(MINOR)")" @echo "" @echo "CXX : "$(CXX) @echo "LD : "$(LD) diff --git a/src/Mpeg.cpp b/src/Mpeg.cpp new file mode 100644 index 0000000..bbcc935 --- /dev/null +++ b/src/Mpeg.cpp @@ -0,0 +1,46 @@ +/* + * Mpeg.cpp + * + * Created on: 2014. 6. 18. + * Author: oskwon + */ + +#include "Mpeg.h" +#include "Http.h" +#include "Util.h" +#include "Logger.h" +//---------------------------------------------------------------------- + +void Mpeg::seek(HttpHeader &header) +{ + try { + std::string position = header.page_params["position"]; + if (position == "") { + off_t byte_offset = 0; + 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 = Util::strtollu(range); + } + } + if (is_time_seekable && byte_offset > 0) { + DEBUG("seek to byte_offset %llu", byte_offset); + seek_absolute(byte_offset); + DEBUG("seek ok"); + } + } + else { + unsigned int position_offset = Util::strtollu(position); + if (is_time_seekable && position_offset > 0) { + DEBUG("seek to position_offset %ds", position_offset); + seek_time((position_offset * 1000) + first_pcr_ms); + DEBUG("seek ok"); + } + } + } + catch (const trap &e) { + WARNING("Exception : %s", e.what()); + } +} +//---------------------------------------------------------------------- diff --git a/src/Mpeg.h b/src/Mpeg.h new file mode 100644 index 0000000..780d080 --- /dev/null +++ b/src/Mpeg.h @@ -0,0 +1,29 @@ +/* + * Mpeg.h + * + * Created on: 2014. 6. 18. + * Author: oskwon + */ + +#ifndef MPEG_H_ +#define MPEG_H_ + +#include "trap.h" +#include "mpegts.h" +//---------------------------------------------------------------------- + +class HttpHeader; + +class Mpeg : public MpegTS +{ +public: + Mpeg(std::string file, bool request_time_seek) throw (trap) + : MpegTS(file, request_time_seek) + {} + virtual ~Mpeg() throw () {} + + void seek(HttpHeader &header); +}; +//---------------------------------------------------------------------- + +#endif /* MPEG_H_ */ diff --git a/src/Util.cpp b/src/Util.cpp new file mode 100644 index 0000000..0473d08 --- /dev/null +++ b/src/Util.cpp @@ -0,0 +1,145 @@ +/* + * Utils.cpp + * + * Created on: 2014. 6. 10. + * Author: oskwon + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "Util.h" +#include "Logger.h" + +using namespace std; +//---------------------------------------------------------------------- + +std::string Util::ultostr(int64_t data) +{ + std::stringstream ss; + ss << data; + return ss.str(); +} +//---------------------------------------------------------------------- + +int Util::strtollu(std::string data) +{ + long long retval; + std::stringstream ss; + try { + ss.str(data); + ss >> retval; + } + catch(...) { + return -1; + } + return retval; +} +//---------------------------------------------------------------------- + +std::string Util::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 Util::split(std::string data, const char delimiter, std::vector& 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 Util::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; +} +//---------------------------------------------------------------------- + +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); +} +//---------------------------------------------------------------------- + +std::string Util::host_addr() +{ + std::stringstream ss; + struct sockaddr_in addr; + socklen_t addrlen = sizeof(addr); + + getpeername(0, (struct sockaddr*)&addr, &addrlen); + ss << inet_ntoa(addr.sin_addr); + + return ss.str(); +} +//------------------------------------------------------------------------------- + +std::vector Util::find_process_by_name(std::string name, int mypid) +{ + std::vector pidlist; + char cmdlinepath[256] = {0}; + DIR* d = opendir("/proc"); + if (d != 0) { + struct dirent* de; + while ((de = readdir(d)) != 0) { + int pid = atoi(de->d_name); + if (pid > 0) { + sprintf(cmdlinepath, "/proc/%s/cmdline", de->d_name); + + std::string cmdline; + std::ifstream cmdlinefile(cmdlinepath); + std::getline(cmdlinefile, cmdline); + if (!cmdline.empty()) { + size_t pos = cmdline.find('\0'); + if (pos != string::npos) + cmdline = cmdline.substr(0, pos); + pos = cmdline.rfind('/'); + if (pos != string::npos) + cmdline = cmdline.substr(pos + 1); + if ((name == cmdline) && ((mypid != pid) || (mypid == 0))) { + pidlist.push_back(pid); + } + } + } + } + closedir(d); + } + return pidlist; +} +//------------------------------------------------------------------------------- + +void Util::kill_process(int pid) +{ + int result = kill(pid, SIGINT); + DEBUG("SEND SIGINT to %d, result : %d", pid, result); + sleep(1); +} +//---------------------------------------------------------------------- diff --git a/src/Util.h b/src/Util.h new file mode 100644 index 0000000..089ffb5 --- /dev/null +++ b/src/Util.h @@ -0,0 +1,49 @@ +/* + * Utils.h + * + * Created on: 2014. 6. 10. + * Author: oskwon + */ + +#ifndef UTILS_H_ +#define UTILS_H_ + +#include +#include +#include + +#include + +#include "Http.h" +#include "Source.h" +#include "Encoder.h" +//---------------------------------------------------------------------- + +class Util { +public: + static void vlog(const char * format, ...) throw(); + + static int strtollu(std::string data); + static std::string ultostr(int64_t data); + + static std::string trim(std::string& s, const std::string& drop = " \t\n\v\r"); + + static int split(std::string data, const char delimiter, std::vector& tokens); + static bool split_key_value(std::string data, std::string delimiter, std::string &key, std::string &value); + + static void kill_process(int pid); + + static std::string host_addr(); + + static std::vector find_process_by_name(std::string name, int mypid); +}; +//---------------------------------------------------------------------- + +typedef struct _thread_params_t { + Source *source; + Encoder *encoder; + HttpHeader *request; +} ThreadParams; +//---------------------------------------------------------------------- + +#endif /* UTILS_H_ */ diff --git a/src/Utils.cpp b/src/Utils.cpp deleted file mode 100644 index af7ac4b..0000000 --- a/src/Utils.cpp +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Utils.cpp - * - * Created on: 2014. 6. 10. - * Author: oskwon - */ - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#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 strtollu(std::string data) -{ - long long 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& 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 lines; - split(header, '\n', lines); - - DEBUG("header lines count : %d", lines.size()); - std::vector::iterator iter = lines.begin(); - std::vector 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]; - - if (strncmp(path.c_str(), "/file", 5) == 0) { - std::vector tokens; - if (split(path.substr(6), '&', tokens) > 0) { - for (int i = 0; i < tokens.size(); ++i) { - std::string data = tokens[i]; - std::string key = "", value = ""; - if (!split_key_value(data, "=", key, value)) { - ERROR("fail to request : %s", data.c_str()); - continue; - } - if (key == "file") { - extension[key] = UriDecoder().decode(value.c_str());; - continue; - } - extension[key] = value; - } - } - type = REQ_TYPE_TRANSCODING_FILE; - -// DEBUG(":: HEADER :: %s", extension["file"].c_str()); -// std::map::iterator iter = extension.begin(); -// for (; iter != extension.end(); ++iter) { -// std::string key = iter->first; -// std::string value = iter->second; -// DEBUG("[%s] -> [%s]", key.c_str(), 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 = strtollu(range); - } - } - - off_t content_length = source->stream_length - byte_offset; - if (byte_offset > 0) { - content_length += 1; - response += HTTP_PARTIAL; - } - else { - response += HTTP_OK; - } - response += HTTP_PARAMS; - response += "Accept-Ranges: bytes\r\n" - "Content-Length: " + ultostr(content_length) + "\r\n"; - response += string("Content-Range: bytes ") + - ultostr(byte_offset) + "-" + - ultostr(source->stream_length - 1) + "/" + - 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); -} -//---------------------------------------------------------------------- - -std::string get_host_addr() -{ - std::stringstream ss; - struct sockaddr_in addr; - socklen_t addrlen = sizeof(addr); - - getpeername(0, (struct sockaddr*)&addr, &addrlen); - ss << inet_ntoa(addr.sin_addr); - - return ss.str(); -} -//------------------------------------------------------------------------------- - -std::vector find_process_by_name(std::string name, int mypid) -{ - std::vector pidlist; - char cmdlinepath[256] = {0}; - DIR* d = opendir("/proc"); - if (d != 0) { - struct dirent* de; - while ((de = readdir(d)) != 0) { - int pid = atoi(de->d_name); - if (pid > 0) { - sprintf(cmdlinepath, "/proc/%s/cmdline", de->d_name); - - std::string cmdline; - std::ifstream cmdlinefile(cmdlinepath); - std::getline(cmdlinefile, cmdline); - if (!cmdline.empty()) { - size_t pos = cmdline.find('\0'); - if (pos != string::npos) - cmdline = cmdline.substr(0, pos); - pos = cmdline.rfind('/'); - if (pos != string::npos) - cmdline = cmdline.substr(pos + 1); - if ((name == cmdline) && ((mypid != pid) || (mypid == 0))) { - pidlist.push_back(pid); - } - } - } - } - closedir(d); - } - return pidlist; -} -//------------------------------------------------------------------------------- - -void kill_process(int pid) -{ - int result = kill(pid, SIGINT); - DEBUG("SEND SIGINT to %d, result : %d", pid, result); - sleep(1); -} -//---------------------------------------------------------------------- diff --git a/src/Utils.h b/src/Utils.h deleted file mode 100644 index a64a780..0000000 --- a/src/Utils.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Utils.h - * - * Created on: 2014. 6. 10. - * Author: oskwon - */ - -#ifndef UTILS_H_ -#define UTILS_H_ - -#include -#include -#include - -#include - -#include "Source.h" -#include "Encoder.h" -//---------------------------------------------------------------------- - -int strtollu(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& 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 version; - std::map params; - std::map extension; - -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(); -}; -//---------------------------------------------------------------------- - -void kill_process(int pid); -std::string get_host_addr(); -std::vector find_process_by_name(std::string name, int mypid); -//---------------------------------------------------------------------- - -#endif /* UTILS_H_ */ diff --git a/src/main.cpp b/src/main.cpp index 42bed62..9121be6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -15,34 +15,172 @@ #include -#include "trap.h" -#include "mpegts.h" - -#include "Utils.h" +#include "Util.h" #include "Logger.h" +#include "Http.h" +#include "Mpeg.h" + #include "Demuxer.h" #include "Encoder.h" +#include "UriDecoder.h" using namespace std; //---------------------------------------------------------------------- -#define RESPONSE_FD (1) #define BUFFFER_SIZE (188 * 256) +void show_help(); + void signal_handler(int sig_no); void do_exit(const char *message); +void *source_thread_main(void *params); +void *streaming_thread_main(void *params); + +int streaming_write(const char *buffer, size_t buffer_len, bool enable_log = false); +//---------------------------------------------------------------------- + static bool is_terminated = true; //---------------------------------------------------------------------- +int main(int argc, char **argv) +{ + if (argc > 1) { + if (strcmp(argv[1], "-h") == 0) + show_help(); + exit(0); + } + Logger::instance()->init("/tmp/transtreamproxy", Logger::WARNING); + + signal(SIGINT, signal_handler); + + HttpHeader header; + + int source_thread_id, stream_thread_id; + pthread_t source_thread_handle, stream_thread_handle; + + std::string req = HttpHeader::read_request(); + + DEBUG("request head :\n%s", req.c_str()); + if (header.parse_request(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 HttpHeader::TRANSCODING_FILE: + try { + std::string uri = UriDecoder().decode(header.page_params["file"].c_str()); + Mpeg *ts = new Mpeg(uri, true); + 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 HttpHeader::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; + case HttpHeader::M3U: + try { + std::string response = header.build_response((Mpeg*) source); + if (response != "") { + streaming_write(response.c_str(), response.length(), true); + } + } + catch (...) { + } + exit(0); + default: + ERROR("not support source type (type : %d)", header.type); + exit(-1); + } + thread_params.source = source; + + if (!encoder.retry_open(2, 3)) { + exit(-1); + } + + if (encoder.state == Encoder::ENCODER_STAT_OPENED) { + std::string response = header.build_response((Mpeg*) source); + if (response == "") { + do_exit(0); + return 0; + } + + streaming_write(response.c_str(), response.length(), true); + + if (header.type == HttpHeader::TRANSCODING_FILE) { + ((Mpeg*) source)->seek(header); + } + + 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 *streaming_thread_main(void *params) { if (is_terminated) return 0; INFO("streaming thread start."); Encoder *encoder = ((ThreadParams*) params)->encoder; - RequestHeader *header = ((ThreadParams*) params)->request; + HttpHeader *header = ((ThreadParams*) params)->request; try { int poll_state, rc, wc; @@ -67,15 +205,14 @@ void *streaming_thread_main(void *params) break; } else if (rc > 0) { - wc = write(RESPONSE_FD, buffer, rc); - //DEBUG("write : %d", wc); + wc = streaming_write((const char*) 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); + retry_wc = streaming_write((const char*) (buffer + rc - remain_len), remain_len); wc += retry_wc; } LOG("re-write result : %d - %d", wc, rc); @@ -113,7 +250,7 @@ void *source_thread_main(void *params) { Source *source = ((ThreadParams*) params)->source; Encoder *encoder = ((ThreadParams*) params)->encoder; - RequestHeader *header = ((ThreadParams*) params)->request; + HttpHeader *header = ((ThreadParams*) params)->request; INFO("source thread start."); @@ -178,141 +315,12 @@ void *source_thread_main(void *params) } //---------------------------------------------------------------------- -int main(int argc, char **argv) +int streaming_write(const char *buffer, size_t buffer_len, bool enable_log) { - 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(SIGINT, signal_handler); - - 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.extension["file"], true); - 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(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) { - try { - std::string position = header.extension["position"]; - if (position == "") { - DEBUG("seek to byte_offset %llu", byte_offset); - ((MpegTS*)source)->seek_absolute(byte_offset); - DEBUG("seek ok"); - } - else { - unsigned int position_offset = strtollu(position); - if(((MpegTS*)source)->is_time_seekable && (position_offset > 0)) { - DEBUG("seek to position_offset %ds", position_offset); - ((MpegTS*)source)->seek_time((position_offset * 1000) + ((MpegTS*)source)->first_pcr_ms); - DEBUG("seek ok"); - } - } - } - catch (const trap &e) { - WARNING("Exception : %s", e.what()); - } - } - - 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; - } + if (enable_log) { + DEBUG("response data :\n%s", buffer); } - return 0; + return write(1, buffer, buffer_len); } //---------------------------------------------------------------------- @@ -331,3 +339,14 @@ void signal_handler(int sig_no) do_exit("signal detected.."); } //---------------------------------------------------------------------- + +void show_help() +{ + printf("usage : transtreamproxy [-h]\n"); + printf("\n"); + printf(" * To active debug mode, input NUMBER on /tmp/debug_on file. (default : warning)\n"); + printf(" NUMBER : error(1), warning(2), info(3), debug(4), log(5)\n"); + printf("\n"); + printf(" ex > echo \"4\" > /tmp/debug_on\n"); +} +//---------------------------------------------------------------------- diff --git a/src/up.sh b/src/up.sh deleted file mode 100755 index 0d52858..0000000 --- a/src/up.sh +++ /dev/null @@ -1,8 +0,0 @@ -ftp -n 192.168.$1 << + -user root a -prompt off -cd /usr/bin -put transtreamproxy -bye -+ - -- cgit