Commit be058841 authored by Mikhail Karpenko's avatar Mikhail Karpenko

Add unmodified sources from elphel353 project

parents
html/*
attic/*
/.project
/.cproject
/.pydevproject
/.externalToolBuilders
/.settings
.directory
generated*
sysroots
bitbake-logs
/temp/
*.o
*.cgi
bitbake-logs
.depend
/image
This diff is collapsed.
# Runs 'make', 'make install', and 'make clean' in specified subdirectories
SUBDIRS := src
INSTALLDIRS = $(SUBDIRS:%=install-%)
CLEANDIRS = $(SUBDIRS:%=clean-%)
#TARGETDIR=$(DESTDIR)/www/pages
all: $(SUBDIRS)
@echo "make all top"
$(SUBDIRS):
$(MAKE) -C $@
install: $(INSTALLDIRS)
@echo "make install top"
$(INSTALLDIRS):
$(MAKE) -C $(@:install-%=%) install
clean: $(CLEANDIRS)
@echo "make clean top"
$(CLEANDIRS):
$(MAKE) -C $(@:clean-%=%) clean
.PHONY: all install clean $(SUBDIRS) $(INSTALLDIRS) $(CLEANDIRS)
# elphel-apps-astreamer
#!/bin/bash
args="$@"
while (( "$#" )); do
shift
done
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
echo "Launching bitbake $args"
cd $DIR/../../poky
. ./oe-init-build-env
bitbake $args | sed -u 's@| @@'
exit 0
AXIS_USABLE_LIBS = UCLIBC GLIBC
AXIS_AUTO_DEPEND = yes
-include $(AXIS_TOP_DIR)/tools/build/Rules.axis
INSTALL ?= install_elinux
INSTALL_PATH = $(prefix)/usr/local
INCDIR = $(AXIS_KERNEL_DIR)/include
LDFLAGS = -Wl -s -L$(prefix)/lib -lpthread -lasound
CXXFLAGS = -Wall -O2 -I$(INCDIR)
STREAMER_OBJS = main.o streamer.o audio.o video.o socket.o rtsp.o rtp_stream.o helpers.o parameters.o
SRCS = main.cpp streamer.cpp audio.cpp video.cpp socket.cpp rtsp.cpp rtp_stream.cpp helpers.cpp parameters.cpp
PROG = str
all: dependency $(PROG)
clean:
rm -f *.o *.ver *.mp *~ core* $(PROG)
$(PROG): $(STREAMER_OBJS)
$(CXX) $(LDFLAGS) -o $@ $(STREAMER_OBJS) -lpthread -asound
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
#install: $(PROG)
install: clean $(PROG)
$(INSTALL) $(PROG) $(INSTALL_PATH)/bin/$(PROG)
configsubs:
This diff is collapsed.
#ifndef _AUDIO__H_
#define _AUDIO__H_
#include <string>
#define __cplusplus 1
#include <alsa/asoundlib.h>
#include <pthread.h>
#include "rtp_stream.h"
using namespace std;
#define SAMPLE_RATE 44100
#define SAMPLE_CHANNELS 2
class Audio : public RTP_Stream {
public:
Audio(bool enable, int sample_rate = SAMPLE_RATE, int channels = SAMPLE_CHANNELS);
virtual ~Audio(void);
long sample_rate(void) { return _sample_rate; };
long channels(void) { return _channels; };
bool present(void) { return _present; };
long volume(void) { return _volume; };
void set_volume(long volume) { set_capture_volume(volume); }
void Start(string ip, long port, int ttl = -1);
void Stop(void);
protected:
int fd;
snd_pcm_t *capture_handle;
short *sbuffer;
long sbuffer_len;
bool _present;
long _sample_rate;
long _channels;
long _volume;
long capture(void);
long process(void);
long process_send(long sample_len);
void set_capture_volume(int volume);
uint64_t timestamp_rtcp;
long delta_fpga_sys; // A/V clocks delta for RTCP
bool is_first;
bool is_first2;
};
//extern Audio *audio;
#endif // _AUDIO__H_
#ifndef __H_HELPER__
#define __H_HELPER__
#include <sys/time.h>
inline long time_delta_us(const struct timeval &tv_1, const struct timeval &tv_2) {
long tu = tv_1.tv_usec;
if(tv_1.tv_sec != tv_2.tv_sec)
tu += (tv_1.tv_sec - tv_2.tv_sec) * 1000000;
tu -= tv_2.tv_usec;
return tu;
}
inline struct timeval time_plus(struct timeval tv_1, const struct timeval &tv_2) {
tv_1.tv_sec += tv_2.tv_sec;
tv_1.tv_usec += tv_2.tv_usec;
if(tv_1.tv_usec >= 1000000) {
tv_1.tv_sec++;
tv_1.tv_usec -= 1000000;
}
return tv_1;
}
inline struct timeval time_minus(struct timeval tv_1, const struct timeval &tv_2) {
tv_1.tv_sec -= tv_2.tv_sec;
tv_1.tv_usec += 1000000;
tv_1.tv_usec -= tv_2.tv_usec;
if(tv_1.tv_usec >= 1000000) {
tv_1.tv_usec -= 1000000;
} else {
tv_1.tv_sec--;
}
return tv_1;
}
#endif
#include "helpers.h"
#include <iostream>
using namespace std;
bool String::split(const string &str, char delimiter, string &left, string &right) {
left = "";
right = "";
int i = str.find(delimiter);
//cerr << "_______" << endl;
//cerr << "delimiter == |" << delimiter << "|, i == " << i << endl;
if(i > 0 && i < (int)str.length()) {
const char *c = str.c_str();
//cerr <<
left.insert(0, c, i);
right.insert(0, &c[i + 1], str.length() - i - 1);
return true;
} else
left = str;
return false;
}
list<string> String::split_to_list(string str, char delimiter) {
list<string> l;
if((str.c_str())[str.length() - 1] != delimiter)
str += delimiter;
const char *c = str.c_str();
int j = 0;
for(int i = 0; c[i]; i++) {
if(c[i] == delimiter) {
string a;
a.insert(0, &c[j], i - j);
j = i + 1;
l.push_back(a);
}
}
return l;
}
map<string, string> String::split_list_to_map(const list<string> &l, char delimiter) {
map<string, string> m;
for(list<string>::const_iterator it = l.begin(); it != l.end(); it++) {
int i = (*it).find(delimiter);
if(i > 0 && i < (int)(*it).length()) {
const char *c = (*it).c_str();
string n, v;
n.insert(0, c, i);
v.insert(0, &c[i + 1], (*it).length() - i - 1);
m[n] = v;
}
}
return m;
}
#ifndef _HELPERS__H_
#define _HELPERS__H_
#include <string>
#include <list>
#include <map>
using namespace std;
class String {
public:
static bool split(const string &str, char delimiter, string &left, string &right);
static list<string> split_to_list(string str, char delimiter);
static map<string, string> split_list_to_map(const list<string> &l, char delimiter);
};
#endif // _HELPERS__H_
#include <iostream>
#include <string>
#include <map>
#include "streamer.h"
using namespace std;
#include <unistd.h>
#include <linux/sysctl.h>
int main(int argc, char *argv[]) {
string opt;
map<string, string> args;
for(int i = 1; i < argc; i++) {
if(argv[i][0] == '-' && argv[i][1] != '\0') {
if(opt != "")
args[opt] = "";
opt = &argv[i][1];
continue;
} else {
if(opt != "") {
args[opt] = argv[i];
opt = "";
}
}
}
if(opt != "")
args[opt] = "";
for(map<string, string>::iterator it = args.begin(); it != args.end(); it++) {
cerr << "|" << (*it).first << "| == |" << (*it).second << "|" << endl;
}
Streamer *streamer = new Streamer(args);
streamer->Main();
return 0;
}
#include "parameters.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <asm/elphel/c313a.h>
#include <iostream>
#include "streamer.h"
using namespace std;
#undef PARAMETERS_DEBUG
//#define PARAMETERS_DEBUG
#ifdef PARAMETERS_DEBUG
#define D(a) a
#else
#define D(a)
#endif
Parameters *Parameters::_parameters = NULL;
Parameters::Parameters(void) {
fd_fparmsall = open("/dev/frameparsall", O_RDWR);
if(fd_fparmsall < 0)
throw("can't open /dev/frameparsall");
//! now try to mmap
frameParsAll = (struct framepars_all_t *) mmap(0, sizeof (struct framepars_all_t) , PROT_READ | PROT_WRITE , MAP_SHARED, fd_fparmsall, 0);
if((int)frameParsAll == -1)
throw("Error in mmap /dev/frameparsall");
framePars = frameParsAll->framePars;
globalPars = frameParsAll->globalPars;
}
Parameters::~Parameters(void) {
if(fd_fparmsall > 0)
close(fd_fparmsall);
}
/**
* @brief Read either G_* parameter (these are 'regular' values defined by number) or P_* parameter
* (it can be read for up to 6 frames ahead, but current interface only allows to read last/current value)
* @param GPNumber parameter number (as defined in c313a.h), G_* parameters have numbers above FRAMEPAR_GLOBALS, P_* - below)
* @return parameter value
*/
unsigned long Parameters::getGPValue(unsigned long GPNumber) {
return (GPNumber >= FRAMEPAR_GLOBALS) ? GLOBALPARS(GPNumber) : framePars[GLOBALPARS(G_THIS_FRAME) & PARS_FRAMES_MASK].pars[GPNumber];
}
/**
* @brief Set value of the specified global (G_*) parameter
* @param GNumber - parameter number (as defined in c313a.h)
* @param value - value to set
*/
void Parameters::setGValue(unsigned long GNumber, unsigned long value) {
GLOBALPARS(GNumber) = value;
}
unsigned long Parameters::getFrameValue(unsigned long FPNumber) {
return framePars[GLOBALPARS(G_THIS_FRAME) & PARS_FRAMES_MASK].pars[FPNumber];
}
bool Parameters::daemon_enabled(void) {
return((getFrameValue(P_DAEMON_EN) & (1 << DAEMON_BIT_STREAMER)) != 0);
}
void Parameters::setPValue(unsigned long *val_array, int count) {
/*
long target_frame = params->getGPValue(G_THIS_FRAME) + FRAMES_AHEAD_FPS;
write_data[0] = FRAMEPARS_SETFRAME;
write_data[1] = target_frame; /// wait then for that frame to be available on the output plus 2 frames for fps to be stable
write_data[2] = P_FP1000SLIM;
write_data[3] = (unsigned long)fps * 1000;
write_data[4] = P_FPSFLAGS;
write_data[5] = 3;
*/
// long rslt = write(fd_fparmsall, write_data, sizeof(write_data));
this->write(val_array, sizeof(unsigned long) * count);
}
//------------------------------------------------------------------------------
#ifndef _PARS__H_
#define _PARS__H_
#include <sys/types.h>
#include <unistd.h>
#include <string>
using namespace std;
#include <asm/elphel/c313a.h>
//#define FRAMES_AHEAD_FPS 3 /// number of video frames ahead of current to frite FPS limit
//#define FRAMES_SKIP_FPS 3 /// number of video frames to wait after target so circbuf will have at least 2 frames with new fps for calculation
class Parameters {
public:
/*
enum vevent {
VEVENT0,
DAEMON_DISABLED,
FPS_CHANGE,
SIZE_CHANGE
};
*/
inline static Parameters *instance(void) {
if(_parameters == NULL)
_parameters = new Parameters();
return _parameters;
}
~Parameters(void);
// unsigned long get
/// interface to global camera parameters
unsigned long getGPValue(unsigned long GPNumber);
void setGValue(unsigned long GNumber, unsigned long value);
unsigned long getFrameValue(unsigned long FPNumber);
int write(unsigned long *data, int length) { return ::write(fd_fparmsall, (void *)data, length); }
off_t lseek(off_t offset, int whence) { return ::lseek(fd_fparmsall, offset, whence); }
bool daemon_enabled(void);
void setPValue(unsigned long *val_array, int count);
protected:
static Parameters *_parameters;
Parameters(void);
struct framepars_all_t *frameParsAll;
struct framepars_t *framePars;
unsigned long *globalPars;
int fd_fparmsall;
};
//extern Video *video;
#endif // _VIDEO__H_
#include <arpa/inet.h>
#include <iostream>
#include "rtp_stream.h"
#include "helper.h"
using namespace std;
#define CNAME "elphel353"
#undef RTP_DEBUG
//#define RTP_DEBUG
#ifdef RTP_DEBUG
#define D(a) a
#else
#define D(a)
#endif
RTP_Stream::RTP_Stream(void) {
_play = false;
pthread_mutex_init(&pthm_flow, NULL);
stream_name = "unknown";
rtp_socket = NULL;
rtcp_socket = NULL;
sem_init(&sem_play, 0, 0);
pth_id = -1;
}
RTP_Stream::~RTP_Stream() {
//cerr << "RTP_Stream::~RTP_Stream() for stream " << stream_name << endl;
if(pth_id >= 0)
pthread_cancel(pth);
if(rtp_socket != NULL)
delete rtp_socket;
if(rtcp_socket != NULL)
delete rtcp_socket;
sem_destroy(&sem_play);
pthread_mutex_destroy(&pthm_flow);
}
void RTP_Stream::init_pthread(void *__this) {
pthread_attr_t tattr;
pthread_attr_init(&tattr);
pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
pth_id = pthread_create(&pth, &tattr, RTP_Stream::pthread_f, (void *)__this);
}
void *RTP_Stream::pthread_f(void *_this) {
RTP_Stream *__this = (RTP_Stream *)_this;
return __this->thread();
}
void RTP_Stream::Start(string ip, int port, int ttl) {
D( cerr << " new " << stream_name << " UDP socket at port: " << port << endl;)
pthread_mutex_lock(&pthm_flow);
if(!_play) {
rtp_socket = new Socket(ip, port, Socket::TYPE_UDP, ttl);
rtcp_socket = new Socket(ip, port + 1, Socket::TYPE_UDP, ttl);
rtp_packets = 0;
rtp_octets = 0;
rtcp_tv.tv_sec = 0;
rtcp_tv.tv_usec = 0;
// rtcp_delay = 2500000; // in usec
// rtcp_delay = 1250000; // in usec
rtcp_delay = 2000000; // in usec
_play = true;
/// unlock semaphore - 'play' event
sem_post(&sem_play);
}
pthread_mutex_unlock(&pthm_flow);
}
void RTP_Stream::Stop(void) {
D( cerr << __FILE__<< ":"<< __FUNCTION__ << ":" <<__LINE__ <<endl;)
//cerr << "RTP_Stream::Stop() for stream " << stream_name << " - begin" << endl;
pthread_mutex_lock(&pthm_flow);
if(_play) {
//cerr << "RTP_Stream::Stop() for stream " << stream_name << " - in progress" << endl;
/// reset semaphore
sem_init(&sem_play, 0, 0);
_play = false;
// delete rtcp_socket;
if(rtp_socket != NULL) {
delete rtp_socket;
rtp_socket = NULL;
}
if(rtcp_socket != NULL) {
delete rtcp_socket;
rtcp_socket = NULL;
}
}
pthread_mutex_unlock(&pthm_flow);
//cerr << "RTP_Stream::Stop() for stream " << stream_name << " - end" << endl;
}
/**
* @brief Thread that invokes the video (and audio too) frame acquisition/ transmission
* In the current implementation video process() is blocking, but may be made
* non-blocking again later (through poll()). In addition to bool result
* (false if no frames are available, there are now long process(), with
* additional <0 result for the video frame change/stream shut down detected
* in Video class.
* @return never
*/
void *RTP_Stream::thread(void) {
D( cerr << "RTP_Stream::thread(void)" <<endl;)
for(;;) {
pthread_mutex_lock(&pthm_flow);
if(_play) {
long f = process();
if(f > 0)
rtcp();
// process() and rtcp() use sockets
pthread_mutex_unlock(&pthm_flow);
if(f < 0) {
D( cerr << __FILE__<< ":"<< __FUNCTION__ << ":" <<__LINE__<< "process exception detected: " << f << endl;)
// cerr << "Stop() from thread for stream " << stream_name << endl;
Stop();
}
} else { /// wait for 'play' event semaphore
pthread_mutex_unlock(&pthm_flow);
sem_wait(&sem_play);
}
}
return NULL;
}
void RTP_Stream::rtcp(void) {
// check time for next one RTCP...
if(f_tv.tv_sec == 0 && f_tv.tv_usec == 0)
return;
long td = time_delta_us(f_tv, rtcp_tv);
if(td < 0) {
rtcp_tv = f_tv;
return;
}
if(td < rtcp_delay)
return;
rtcp_tv = f_tv;
rtcp_send_sdes();
rtcp_send_sr();
}
void RTP_Stream::rtcp_send_sr(void) {
char packet[8 + 20]; // by RTP RFC 3550, for SR RTCP packet needed 8 + 20 bytes
int packet_len = sizeof(packet);
uint16_t us;
uint32_t ul;
// RTCP header
packet[0] = 0x81;
packet[1] = 200; // SR
us = htons(((packet_len) / 4) - 1);
memcpy((void *)&packet[2], (void *)&us, 2);
memcpy((void *)&packet[4], (void *)&SSRC, 4);
// NTP timestamp is a fixed point 32.32 format time
ul = htonl(f_tv.tv_sec);
memcpy((void *)&packet[8], (void *)&ul, 4);
double d = f_tv.tv_usec;
d /= 1000000.0;
d *= 65536.0;
d *= 4096.0;
uint32_t f = (uint32_t)d;
if(f > 0x0FFFFFFF)
f = 0x0FFFFFFF;
f <<= 4;
ul = htonl(f);
memcpy((void *)&packet[12], (void *)&ul, 4);
ul = htonl(timestamp);
memcpy((void *)&packet[16], (void *)&ul, 4);
ul = htonl(rtp_packets);
memcpy((void *)&packet[20], (void *)&ul, 4);
ul = htonl(rtp_octets);
memcpy((void *)&packet[24], (void *)&ul, 4);
rtcp_socket->send(packet, packet_len);
}
void RTP_Stream::rtcp_send_sdes(void) {
char packet[8 + 4 + 128]; // by RTP RFC 3550, for SDES RTCP packet needed 8 + 4 + ... bytes,
// so get additional 128 bytes for 126 CNAME field
int packet_len = 0;
int padding=0;
uint16_t us;
const char *cname = CNAME;
int cname_len = strlen(cname);
bzero((void *)packet, 140); //8+4+128
// RTCP header
packet[0] = 0x81;
packet[1] = 202;
memcpy((void *)&packet[4], (void *)&SSRC, 4);
packet_len += 8;
// SDES fields
packet[8] = 0x01;
memcpy((void *)&packet[10], (void *)cname, cname_len);
packet_len += 2; // + cname_len;
// calculate common length SDES
padding=(cname_len+2)%4;
if(padding)
cname_len += (4-padding);
packet[9] = cname_len;
// each chunk MUST be terminated by one or more null octets(RFC3350)
packet_len += (cname_len+4);
us = htons((packet_len / 4) - 1);
memcpy((void *)&packet[2], (void *)&us, 2);
rtcp_socket->send(packet, packet_len);
}
#ifndef __H_RTP_STREAM__
#define __H_RTP_STREAM__
#include <sys/time.h>
#include <string>
#include <pthread.h>
#include <semaphore.h>
#include "socket.h"
#include <stdint.h>
using namespace std;
extern int fd_stream;
class RTP_Stream {
public:
RTP_Stream(void);
virtual ~RTP_Stream();
int ptype(void) { return _ptype; };
virtual void Start(string ip, int port, int ttl = -1);
virtual void Stop(void);
protected:
void init_pthread(void *__this);
pthread_t pth;
int pth_id;
static void *pthread_f(void *_this);
void *thread(void);
pthread_mutex_t pthm_flow;
// virtual bool process(void) = 0;
virtual long process(void) = 0;
int _ptype;
bool _play;
/// semaphore to wait 'play' event
sem_t sem_play;
Socket *rtp_socket;
Socket *rtcp_socket;
unsigned short packet_num;
unsigned long SSRC;
struct timeval f_tv;
struct timeval rtcp_tv; // time of last SR
long rtcp_delay;
unsigned long timestamp;
unsigned long rtp_packets;
unsigned long rtp_octets;
void rtcp(void);
void rtcp_send_sr(void);
void rtcp_send_sdes(void);
string stream_name;
};
#endif //__H_RTP_STREAM__
This diff is collapsed.
#ifndef _RTSP_H_
#define _RTSP_H_
//#include "types.h"
#include "session.h"
#include "socket.h"
#include <map>
#include <list>
#include <string>
#include <iostream>
using namespace std;
class _Request {
public:
_Request(const string &req);
inline const string &get_method(void) {
return method;
}
inline const string &get_uri(void) {
return uri;
}
inline map<string, string> &get_fields(void) {
return fields;
}
protected:
string method;
string uri;
map<string, string> fields;
};
class _Responce {
public:
enum status {STATUS_EMPTY, STATUS_BUSY, STATUS_OK};
_Responce():_status(STATUS_EMPTY) {};
inline void set_status(status st) {
_status = st;
}
void add_field(const string &name, const string &value);
void add_include(const string &include);
string serialize(void);