Restructuring so our libraries can go into a separate libmist project.
This commit is contained in:
parent
87f4c4723c
commit
af12c6a94e
50 changed files with 541 additions and 6816 deletions
|
@ -1,12 +1,9 @@
|
|||
AM_CPPFLAGS = $(MIST_CFLAGS)
|
||||
LDADD = $(MIST_LIBS)
|
||||
SUBDIRS=converters analysers
|
||||
bin_PROGRAMS=MistBuffer MistController MistConnRAW MistConnRTMP MistConnHTTP
|
||||
MistBuffer_SOURCES=buffer.cpp buffer_user.h buffer_user.cpp buffer_stream.h buffer_stream.cpp ../VERSION
|
||||
MistBuffer_LDADD=../lib/libjson.la ../lib/libsocket.la ../lib/libdtsc.la ../lib/libtinythread.la
|
||||
MistBuffer_SOURCES=buffer.cpp buffer_user.h buffer_user.cpp buffer_stream.h buffer_stream.cpp tinythread.cpp tinythread.h ../VERSION
|
||||
MistController_SOURCES=controller.cpp ../VERSION
|
||||
MistController_LDADD=../lib/libjson.la ../lib/libsocket.la ../lib/libprocs.la ../lib/libconfig.la ../lib/libhttp_parser.la ../lib/libauth.la ../lib/libbase64.la
|
||||
MistConnRAW_SOURCES=conn_raw.cpp ../VERSION
|
||||
MistConnRAW_LDADD=../lib/libsocket.la
|
||||
MistConnRTMP_SOURCES=conn_rtmp.cpp server_setup.h ../VERSION
|
||||
MistConnRTMP_LDADD=../lib/libdtsc.la ../lib/librtmpchunks.la ../lib/libconfig.la
|
||||
MistConnHTTP_SOURCES=conn_http.cpp server_setup.h ../VERSION
|
||||
MistConnHTTP_LDADD=../lib/libdtsc.la ../lib/libflv_tag.la ../lib/libjson.la ../lib/libhttp_parser.la ../lib/libmp4.la ../lib/libprocs.la ../lib/libbase64.la ../lib/libconfig.la
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
AM_CPPFLAGS = $(MIST_CFLAGS)
|
||||
LDADD = $(MIST_LIBS)
|
||||
bin_PROGRAMS=MistAnalyserRTMP MistAnalyserFLV MistAnalyserDTSC MistAnalyserAMF
|
||||
MistAnalyserRTMP_SOURCES=rtmp_analyser.cpp
|
||||
MistAnalyserRTMP_LDADD=../../lib/librtmpchunks.la ../../lib/libdtsc.la ../../lib/libkeycrypto.la
|
||||
MistAnalyserFLV_SOURCES=flv_analyser.cpp
|
||||
MistAnalyserFLV_LDADD=../../lib/libflv_tag.la ../../lib/libdtsc.la
|
||||
MistAnalyserDTSC_SOURCES=dtsc_analyser.cpp
|
||||
MistAnalyserDTSC_LDADD=../../lib/libdtsc.la
|
||||
MistAnalyserAMF_SOURCES=amf_analyser.cpp
|
||||
MistAnalyserAMF_LDADD=../../lib/libamf.la
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include "../../lib/amf.h"
|
||||
#include <mist/amf.h>
|
||||
|
||||
/// Debugging tool for AMF data.
|
||||
/// Expects AMF data through stdin, outputs human-readable information to stderr.
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include "../../lib/dtsc.h" //DTSC support
|
||||
#include <mist/dtsc.h> //DTSC support
|
||||
|
||||
/// Reads DTSC from stdin and outputs human-readable information to stderr.
|
||||
int main() {
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include "../../lib/flv_tag.h" //FLV support
|
||||
#include <mist/flv_tag.h> //FLV support
|
||||
|
||||
/// Reads FLV from stdin and outputs human-readable information to stderr.
|
||||
int main() {
|
||||
|
|
|
@ -14,9 +14,9 @@
|
|||
#include <fstream>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include "../../lib/flv_tag.h"
|
||||
#include "../../lib/amf.h"
|
||||
#include "../../lib/rtmpchunks.h"
|
||||
#include <mist/flv_tag.h>
|
||||
#include <mist/amf.h>
|
||||
#include <mist/rtmpchunks.h>
|
||||
|
||||
int Detail = 0;
|
||||
#define DETAIL_RECONSTRUCT 1
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "../lib/tinythread.h"
|
||||
#include "../lib/json.h"
|
||||
#include <mist/json.h>
|
||||
#include "tinythread.h"
|
||||
#include "buffer_user.h"
|
||||
|
||||
namespace Buffer{
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "../lib/dtsc.h"
|
||||
#include "../lib/socket.h"
|
||||
#include "../lib/tinythread.h"
|
||||
#include <mist/dtsc.h>
|
||||
#include <mist/socket.h>
|
||||
#include "tinythread.h"
|
||||
|
||||
namespace Buffer{
|
||||
/// Converts a stats line to up, down, host, connector and conntime values.
|
||||
|
|
|
@ -11,14 +11,14 @@
|
|||
#include <sys/wait.h>
|
||||
#include <getopt.h>
|
||||
#include <ctime>
|
||||
#include "../lib/socket.h"
|
||||
#include "../lib/http_parser.h"
|
||||
#include "../lib/json.h"
|
||||
#include "../lib/dtsc.h"
|
||||
#include "../lib/flv_tag.h"
|
||||
#include "../lib/base64.h"
|
||||
#include "../lib/amf.h"
|
||||
#include "../lib/mp4.h"
|
||||
#include <mist/socket.h>
|
||||
#include <mist/http_parser.h>
|
||||
#include <mist/json.h>
|
||||
#include <mist/dtsc.h>
|
||||
#include <mist/flv_tag.h>
|
||||
#include <mist/base64.h>
|
||||
#include <mist/amf.h>
|
||||
#include <mist/mp4.h>
|
||||
|
||||
/// Holds everything unique to HTTP Connector.
|
||||
namespace Connector_HTTP{
|
||||
|
|
235
src/conn_http_dynamic.cpp
Normal file
235
src/conn_http_dynamic.cpp
Normal file
|
@ -0,0 +1,235 @@
|
|||
/// \file conn_http_dynamic.cpp
|
||||
/// Contains the main code for the HTTP Dynamic Connector
|
||||
|
||||
#include <iostream>
|
||||
#include <queue>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <cmath>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <getopt.h>
|
||||
#include <ctime>
|
||||
#include <mist/socket.h>
|
||||
#include <mist/http_parser.h>
|
||||
#include <mist/json.h>
|
||||
#include <mist/dtsc.h>
|
||||
#include <mist/flv_tag.h>
|
||||
#include <mist/base64.h>
|
||||
#include <mist/amf.h>
|
||||
#include <mist/mp4.h>
|
||||
|
||||
/// Holds everything unique to HTTP Dynamic Connector.
|
||||
namespace Connector_HTTP_Dynamic{
|
||||
|
||||
/// Returns AMF-format metadata
|
||||
std::string GetMetaData( ) {
|
||||
/// \todo Make this actually do what it should - even though it seems to be ignored completely by all media players.
|
||||
AMF::Object amfreply("container", AMF::AMF0_DDV_CONTAINER);
|
||||
amfreply.addContent(AMF::Object("onMetaData",AMF::AMF0_STRING));
|
||||
amfreply.addContent(AMF::Object("",AMF::AMF0_ECMA_ARRAY));
|
||||
amfreply.getContentP(1)->addContent(AMF::Object("trackinfo", AMF::AMF0_STRICT_ARRAY));
|
||||
amfreply.getContentP(1)->getContentP(0)->addContent(AMF::Object("arrVal"));
|
||||
//amfreply.getContentP(1)->getContentP(0)->getContentP(0)->addContent(AMF::Object("timescale",(double)1000));
|
||||
//amfreply.getContentP(1)->getContentP(0)->getContentP(0)->addContent(AMF::Object("length",(double)59641700));
|
||||
amfreply.getContentP(1)->getContentP(0)->getContentP(0)->addContent(AMF::Object("language","eng"));
|
||||
amfreply.getContentP(1)->getContentP(0)->getContentP(0)->addContent(AMF::Object("sampledescription", AMF::AMF0_STRICT_ARRAY));
|
||||
amfreply.getContentP(1)->getContentP(0)->getContentP(0)->getContentP(1)->addContent(AMF::Object("arrVal"));
|
||||
amfreply.getContentP(1)->getContentP(0)->getContentP(0)->getContentP(1)->getContentP(0)->addContent(AMF::Object("sampletype","avc1"));
|
||||
amfreply.getContentP(1)->getContentP(0)->addContent(AMF::Object("arrVal"));
|
||||
//amfreply.getContentP(1)->getContentP(0)->getContentP(1)->addContent(AMF::Object("timescale",(double)44100));
|
||||
//amfreply.getContentP(1)->getContentP(0)->getContentP(1)->addContent(AMF::Object("length",(double)28630000));
|
||||
amfreply.getContentP(1)->getContentP(0)->getContentP(1)->addContent(AMF::Object("language","eng"));
|
||||
amfreply.getContentP(1)->getContentP(0)->getContentP(1)->addContent(AMF::Object("sampledescription", AMF::AMF0_STRICT_ARRAY));
|
||||
amfreply.getContentP(1)->getContentP(0)->getContentP(1)->getContentP(1)->addContent(AMF::Object("arrVal"));
|
||||
amfreply.getContentP(1)->getContentP(0)->getContentP(1)->getContentP(1)->getContentP(0)->addContent(AMF::Object("sampletype","mp4a"));
|
||||
amfreply.getContentP(1)->addContent(AMF::Object("audiochannels",(double)2));
|
||||
amfreply.getContentP(1)->addContent(AMF::Object("audiosamplerate",(double)44100));
|
||||
amfreply.getContentP(1)->addContent(AMF::Object("videoframerate",(double)25));
|
||||
amfreply.getContentP(1)->addContent(AMF::Object("aacaot",(double)2));
|
||||
amfreply.getContentP(1)->addContent(AMF::Object("avclevel",(double)12));
|
||||
amfreply.getContentP(1)->addContent(AMF::Object("avcprofile",(double)77));
|
||||
amfreply.getContentP(1)->addContent(AMF::Object("audiocodecid","mp4a"));
|
||||
amfreply.getContentP(1)->addContent(AMF::Object("videocodecid","avc1"));
|
||||
amfreply.getContentP(1)->addContent(AMF::Object("width",(double)1280));
|
||||
amfreply.getContentP(1)->addContent(AMF::Object("height",(double)720));
|
||||
amfreply.getContentP(1)->addContent(AMF::Object("frameWidth",(double)1280));
|
||||
amfreply.getContentP(1)->addContent(AMF::Object("frameHeight",(double)720));
|
||||
amfreply.getContentP(1)->addContent(AMF::Object("displayWidth",(double)1280));
|
||||
amfreply.getContentP(1)->addContent(AMF::Object("displayHeight",(double)720));
|
||||
//amfreply.getContentP(1)->addContent(AMF::Object("moovposition",(double)35506700));
|
||||
//amfreply.getContentP(1)->addContent(AMF::Object("duration",(double)596.458));
|
||||
return amfreply.Pack( );
|
||||
}//getMetaData
|
||||
|
||||
/// Returns a F4M-format manifest file
|
||||
std::string BuildManifest(std::string MovieId) {
|
||||
std::string Result="<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
|
||||
"<manifest xmlns=\"http://ns.adobe.com/f4m/1.0\">\n" +
|
||||
"<id>" + MovieId + "</id>\n" +
|
||||
"<mimeType>video/mp4</mimeType>\n" +
|
||||
"<streamType>live</streamType>\n" +
|
||||
"<deliveryType>streaming</deliveryType>\n" +
|
||||
"<bootstrapInfo profile=\"named\" id=\"bootstrap1\">" + Base64::encode(MP4::GenerateLiveBootstrap(1)) + "</bootstrapInfo>\n" +
|
||||
"<media streamId=\"1\" bootstrapInfoId=\"bootstrap1\" url=\"" + MovieId + "/\">\n" +
|
||||
"<metadata>" + Base64::encode(GetMetaData()) + "</metadata>\n" +
|
||||
"</media>\n" +
|
||||
"</manifest>\n";
|
||||
return Result;
|
||||
}//BuildManifest
|
||||
|
||||
/// Main function for Connector_HTTP_Dynamic
|
||||
int Connector_HTTP_Dynamic(Socket::Connection conn){
|
||||
std::string FlashBuf;
|
||||
FLV::Tag tmp;//temporary tag, for init data
|
||||
|
||||
std::queue<std::string> Flash_FragBuffer;//Fragment buffer
|
||||
DTSC::Stream Strm;//Incoming stream buffer.
|
||||
HTTP::Parser HTTP_R, HTTP_S;//HTTP Receiver en HTTP Sender.
|
||||
|
||||
bool ready4data = false;//Set to true when streaming is to begin.
|
||||
bool inited = false;
|
||||
Socket::Connection ss(-1);
|
||||
std::string streamname;
|
||||
FLV::Tag tag;///< Temporary tag buffer.
|
||||
std::string recBuffer = "";
|
||||
|
||||
std::string Movie;
|
||||
std::string Quality;
|
||||
int Segment = -1;
|
||||
int ReqFragment = -1;
|
||||
int temp;
|
||||
int Flash_RequestPending = 0;
|
||||
unsigned int lastStats = 0;
|
||||
conn.setBlocking(false);//do not block on conn.spool() when no data is available
|
||||
|
||||
while (conn.connected()){
|
||||
if (conn.spool()){
|
||||
if (HTTP_R.Read(conn.Received())){
|
||||
#if DEBUG >= 4
|
||||
std::cout << "Received request: " << HTTP_R.url << std::endl;
|
||||
#endif
|
||||
if (HTTP_R.url.find("f4m") == std::string::npos){
|
||||
Movie = HTTP_R.url.substr(1);
|
||||
Movie = Movie.substr(0,Movie.find("/"));
|
||||
Quality = HTTP_R.url.substr( HTTP_R.url.find("/",1)+1 );
|
||||
Quality = Quality.substr(0, Quality.find("Seg"));
|
||||
temp = HTTP_R.url.find("Seg") + 3;
|
||||
Segment = atoi( HTTP_R.url.substr(temp,HTTP_R.url.find("-",temp)-temp).c_str());
|
||||
temp = HTTP_R.url.find("Frag") + 4;
|
||||
ReqFragment = atoi( HTTP_R.url.substr(temp).c_str() );
|
||||
#if DEBUG >= 4
|
||||
printf( "Quality: %s, Seg %d Frag %d\n", Quality.c_str(), Segment, ReqFragment);
|
||||
#endif
|
||||
Flash_RequestPending++;
|
||||
}else{
|
||||
Movie = HTTP_R.url.substr(1);
|
||||
Movie = Movie.substr(0,Movie.find("/"));
|
||||
HTTP_S.Clean();
|
||||
HTTP_S.SetHeader("Content-Type","text/xml");
|
||||
HTTP_S.SetHeader("Cache-Control","no-cache");
|
||||
std::string manifest = BuildManifest(Movie);
|
||||
HTTP_S.SetBody(manifest);
|
||||
conn.Send(HTTP_S.BuildResponse("200", "OK"));
|
||||
#if DEBUG >= 3
|
||||
printf("Sent manifest\n");
|
||||
#endif
|
||||
}
|
||||
ready4data = true;
|
||||
HTTP_R.Clean(); //clean for any possible next requests
|
||||
}else{
|
||||
#if DEBUG >= 3
|
||||
fprintf(stderr, "Could not parse the following:\n%s\n", conn.Received().c_str());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (ready4data){
|
||||
if (!inited){
|
||||
//we are ready, connect the socket!
|
||||
ss = Socket::getStream(streamname);
|
||||
if (!ss.connected()){
|
||||
#if DEBUG >= 1
|
||||
fprintf(stderr, "Could not connect to server!\n");
|
||||
#endif
|
||||
ss.close();
|
||||
HTTP_S.Clean();
|
||||
HTTP_S.SetBody("No such stream is available on the system. Please try again.\n");
|
||||
conn.Send(HTTP_S.BuildResponse("404", "Not found"));
|
||||
ready4data = false;
|
||||
continue;
|
||||
}
|
||||
#if DEBUG >= 3
|
||||
fprintf(stderr, "Everything connected, starting to send video data...\n");
|
||||
#endif
|
||||
inited = true;
|
||||
}
|
||||
if ((Flash_RequestPending > 0) && !Flash_FragBuffer.empty()){
|
||||
HTTP_S.Clean();
|
||||
HTTP_S.SetHeader("Content-Type","video/mp4");
|
||||
HTTP_S.SetBody(MP4::mdatFold(Flash_FragBuffer.front()));
|
||||
Flash_FragBuffer.pop();
|
||||
conn.Send(HTTP_S.BuildResponse("200", "OK"));
|
||||
Flash_RequestPending--;
|
||||
#if DEBUG >= 3
|
||||
fprintf(stderr, "Sending a video fragment. %i left in buffer, %i requested\n", (int)Flash_FragBuffer.size(), Flash_RequestPending);
|
||||
#endif
|
||||
}
|
||||
unsigned int now = time(0);
|
||||
if (now != lastStats){
|
||||
lastStats = now;
|
||||
ss.Send("S "+conn.getStats("HTTP_Dynamic"));
|
||||
}
|
||||
if (ss.spool() || ss.Received() != ""){
|
||||
if (Strm.parsePacket(ss.Received())){
|
||||
tag.DTSCLoader(Strm);
|
||||
if (Strm.getPacket(0).getContentP("keyframe")){
|
||||
if (FlashBuf != ""){
|
||||
Flash_FragBuffer.push(FlashBuf);
|
||||
while (Flash_FragBuffer.size() > 2){
|
||||
Flash_FragBuffer.pop();
|
||||
}
|
||||
#if DEBUG >= 4
|
||||
fprintf(stderr, "Received a fragment. Now %i in buffer.\n", (int)Flash_FragBuffer.size());
|
||||
#endif
|
||||
}
|
||||
FlashBuf.clear();
|
||||
//fill buffer with init data, if needed.
|
||||
if (Strm.metadata.getContentP("audio") && Strm.metadata.getContentP("audio")->getContentP("init")){
|
||||
tmp.DTSCAudioInit(Strm);
|
||||
FlashBuf.append(tmp.data, tmp.len);
|
||||
}
|
||||
if (Strm.metadata.getContentP("video") && Strm.metadata.getContentP("video")->getContentP("init")){
|
||||
tmp.DTSCVideoInit(Strm);
|
||||
FlashBuf.append(tmp.data, tmp.len);
|
||||
}
|
||||
}
|
||||
FlashBuf.append(tag.data, tag.len);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
conn.close();
|
||||
ss.close();
|
||||
#if DEBUG >= 1
|
||||
if (FLV::Parse_Error){fprintf(stderr, "FLV Parser Error: %s\n", FLV::Error_Str.c_str());}
|
||||
fprintf(stderr, "User %i disconnected.\n", conn.getSocket());
|
||||
if (inited){
|
||||
fprintf(stderr, "Status was: inited\n");
|
||||
}else{
|
||||
if (ready4data){
|
||||
fprintf(stderr, "Status was: ready4data\n");
|
||||
}else{
|
||||
fprintf(stderr, "Status was: connected\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}//Connector_HTTP_Dynamic main function
|
||||
|
||||
};//Connector_HTTP_Dynamic namespace
|
||||
|
||||
// Load http setup file with the correct settings for this HTTP connector
|
||||
#define MAINHANDLER Connector_HTTP_Dynamic::Connector_HTTP_Dynamic
|
||||
#define CONNECTOR dynamic
|
||||
#include "server_setup_http.h"
|
145
src/conn_http_progressive.cpp
Normal file
145
src/conn_http_progressive.cpp
Normal file
|
@ -0,0 +1,145 @@
|
|||
/// \file conn_http_progressive.cpp
|
||||
/// Contains the main code for the HTTP Progressive Connector
|
||||
|
||||
#include <iostream>
|
||||
#include <queue>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <cmath>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <getopt.h>
|
||||
#include <ctime>
|
||||
#include <mist/socket.h>
|
||||
#include <mist/http_parser.h>
|
||||
#include <mist/dtsc.h>
|
||||
#include <mist/flv_tag.h>
|
||||
#include <mist/amf.h>
|
||||
|
||||
/// Holds everything unique to HTTP Progressive Connector.
|
||||
namespace Connector_HTTP_Progressive{
|
||||
|
||||
/// Main function for Connector_HTTP_Progressive
|
||||
int Connector_HTTP_Progressive(Socket::Connection conn){
|
||||
bool progressive_has_sent_header = false;
|
||||
bool ready4data = false;///< Set to true when streaming is to begin.
|
||||
DTSC::Stream Strm;///< Incoming stream buffer.
|
||||
HTTP::Parser HTTP_R, HTTP_S;///<HTTP Receiver en HTTP Sender.
|
||||
bool inited = false;
|
||||
Socket::Connection ss(-1);
|
||||
std::string streamname;
|
||||
FLV::Tag tag;///< Temporary tag buffer.
|
||||
std::string recBuffer = "";
|
||||
|
||||
std::string Movie;
|
||||
std::string Quality;
|
||||
int Segment = -1;
|
||||
int ReqFragment = -1;
|
||||
int temp;
|
||||
int Flash_RequestPending = 0;
|
||||
unsigned int lastStats = 0;
|
||||
conn.setBlocking(false);//do not block on conn.spool() when no data is available
|
||||
|
||||
while (conn.connected()){
|
||||
//only parse input if available or not yet init'ed
|
||||
if (conn.spool()){
|
||||
if (HTTP_R.Read(conn.Received())){
|
||||
#if DEBUG >= 4
|
||||
std::cout << "Received request: " << HTTP_R.url << std::endl;
|
||||
#endif
|
||||
//we assume the URL is the stream name with a 3 letter extension
|
||||
std::string extension = HTTP_R.url.substr(HTTP_R.url.size()-4);
|
||||
Movie = HTTP_R.url.substr(0, HTTP_R.url.size()-4);//strip the extension
|
||||
/// \todo VoD streams will need support for position reading from the URL parameters
|
||||
ready4data = true;
|
||||
HTTP_R.Clean(); //clean for any possible next requests
|
||||
}else{
|
||||
#if DEBUG >= 3
|
||||
fprintf(stderr, "Could not parse the following:\n%s\n", conn.Received().c_str());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (ready4data){
|
||||
if (!inited){
|
||||
//we are ready, connect the socket!
|
||||
ss = Socket::getStream(streamname);
|
||||
if (!ss.connected()){
|
||||
#if DEBUG >= 1
|
||||
fprintf(stderr, "Could not connect to server!\n");
|
||||
#endif
|
||||
ss.close();
|
||||
HTTP_S.Clean();
|
||||
HTTP_S.SetBody("No such stream is available on the system. Please try again.\n");
|
||||
conn.Send(HTTP_S.BuildResponse("404", "Not found"));
|
||||
ready4data = false;
|
||||
continue;
|
||||
}
|
||||
#if DEBUG >= 3
|
||||
fprintf(stderr, "Everything connected, starting to send video data...\n");
|
||||
#endif
|
||||
inited = true;
|
||||
}
|
||||
unsigned int now = time(0);
|
||||
if (now != lastStats){
|
||||
lastStats = now;
|
||||
ss.Send("S "+conn.getStats("HTTP_Progressive"));
|
||||
}
|
||||
if (ss.spool() || ss.Received() != ""){
|
||||
if (Strm.parsePacket(ss.Received())){
|
||||
tag.DTSCLoader(Strm);
|
||||
if (!progressive_has_sent_header){
|
||||
HTTP_S.Clean();//make sure no parts of old requests are left in any buffers
|
||||
HTTP_S.SetHeader("Content-Type", "video/x-flv");//Send the correct content-type for FLV files
|
||||
//HTTP_S.SetHeader("Transfer-Encoding", "chunked");
|
||||
HTTP_S.protocol = "HTTP/1.0";
|
||||
conn.Send(HTTP_S.BuildResponse("200", "OK"));//no SetBody = unknown length - this is intentional, we will stream the entire file
|
||||
conn.Send(std::string(FLV::Header, 13));//write FLV header
|
||||
static FLV::Tag tmp;
|
||||
//write metadata
|
||||
tmp.DTSCMetaInit(Strm);
|
||||
conn.Send(std::string(tmp.data, tmp.len));
|
||||
//write video init data, if needed
|
||||
if (Strm.metadata.getContentP("video") && Strm.metadata.getContentP("video")->getContentP("init")){
|
||||
tmp.DTSCVideoInit(Strm);
|
||||
conn.Send(std::string(tmp.data, tmp.len));
|
||||
}
|
||||
//write audio init data, if needed
|
||||
if (Strm.metadata.getContentP("audio") && Strm.metadata.getContentP("audio")->getContentP("init")){
|
||||
tmp.DTSCAudioInit(Strm);
|
||||
conn.Send(std::string(tmp.data, tmp.len));
|
||||
}
|
||||
progressive_has_sent_header = true;
|
||||
#if DEBUG >= 1
|
||||
fprintf(stderr, "Sent progressive FLV header\n");
|
||||
#endif
|
||||
}
|
||||
conn.Send(std::string(tag.data, tag.len));//write the tag contents
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
conn.close();
|
||||
ss.close();
|
||||
#if DEBUG >= 1
|
||||
if (FLV::Parse_Error){fprintf(stderr, "FLV Parser Error: %s\n", FLV::Error_Str.c_str());}
|
||||
fprintf(stderr, "User %i disconnected.\n", conn.getSocket());
|
||||
if (inited){
|
||||
fprintf(stderr, "Status was: inited\n");
|
||||
}else{
|
||||
if (ready4data){
|
||||
fprintf(stderr, "Status was: ready4data\n");
|
||||
}else{
|
||||
fprintf(stderr, "Status was: connected\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}//Connector_HTTP main function
|
||||
|
||||
};//Connector_HTTP namespace
|
||||
|
||||
// Load http setup file with the correct settings for this HTTP connector
|
||||
#define MAINHANDLER Connector_HTTP_Progressive::Connector_HTTP_Progressive
|
||||
#define CONNECTOR progressive
|
||||
#include "server_setup_http.h"
|
|
@ -2,7 +2,7 @@
|
|||
/// Contains the main code for the RAW connector.
|
||||
|
||||
#include <iostream>
|
||||
#include "../lib/socket.h"
|
||||
#include <mist/socket.h>
|
||||
|
||||
/// Contains the main code for the RAW connector.
|
||||
/// Expects a single commandline argument telling it which stream to connect to,
|
||||
|
|
|
@ -11,10 +11,10 @@
|
|||
#include <sys/wait.h>
|
||||
#include <getopt.h>
|
||||
#include <sstream>
|
||||
#include "../lib/socket.h"
|
||||
#include "../lib/flv_tag.h"
|
||||
#include "../lib/amf.h"
|
||||
#include "../lib/rtmpchunks.h"
|
||||
#include <mist/socket.h>
|
||||
#include <mist/flv_tag.h>
|
||||
#include <mist/amf.h>
|
||||
#include <mist/rtmpchunks.h>
|
||||
|
||||
/// Holds all functions and data unique to the RTMP Connector
|
||||
namespace Connector_RTMP{
|
||||
|
|
|
@ -25,12 +25,12 @@
|
|||
#include <signal.h>
|
||||
#include <sstream>
|
||||
#include <openssl/md5.h>
|
||||
#include "../lib/socket.h"
|
||||
#include "../lib/http_parser.h"
|
||||
#include "../lib/json.h"
|
||||
#include "../lib/procs.h"
|
||||
#include "../lib/config.h"
|
||||
#include "../lib/auth.h"
|
||||
#include <mist/socket.h>
|
||||
#include <mist/http_parser.h>
|
||||
#include <mist/json.h>
|
||||
#include <mist/procs.h>
|
||||
#include <mist/config.h>
|
||||
#include <mist/auth.h>
|
||||
|
||||
#define UPLINK_INTERVAL 30
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
AM_CPPFLAGS = $(MIST_CFLAGS)
|
||||
LDADD = $(MIST_LIBS)
|
||||
bin_PROGRAMS=MistDTSC2FLV MistFLV2DTSC
|
||||
MistDTSC2FLV_SOURCES=dtsc2flv.cpp
|
||||
MistDTSC2FLV_LDADD=../../lib/libdtsc.la ../../lib/libflv_tag.la
|
||||
MistFLV2DTSC_SOURCES=flv2dtsc.cpp
|
||||
MistFLV2DTSC_LDADD=../../lib/libdtsc.la ../../lib/libflv_tag.la
|
||||
|
|
|
@ -10,9 +10,9 @@
|
|||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include "../../lib/flv_tag.h" //FLV support
|
||||
#include "../../lib/dtsc.h" //DTSC support
|
||||
#include "../../lib/amf.h" //AMF support
|
||||
#include <mist/flv_tag.h> //FLV support
|
||||
#include <mist/dtsc.h> //DTSC support
|
||||
#include <mist/amf.h> //AMF support
|
||||
|
||||
/// Holds all code that converts filetypes to/from DTSC.
|
||||
namespace Converters{
|
||||
|
|
|
@ -10,9 +10,9 @@
|
|||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include "../../lib/flv_tag.h" //FLV support
|
||||
#include "../../lib/dtsc.h" //DTSC support
|
||||
#include "../../lib/amf.h" //AMF support
|
||||
#include <mist/flv_tag.h> //FLV support
|
||||
#include <mist/dtsc.h> //DTSC support
|
||||
#include <mist/amf.h> //AMF support
|
||||
|
||||
/// Holds all code that converts filetypes to/from to DTSC.
|
||||
namespace Converters{
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/// \file server_setup.h
|
||||
/// Contains generic functions for setting up a DDVTECH Connector.
|
||||
/// Contains generic functions for setting up a Connector.
|
||||
|
||||
#ifndef MAINHANDLER
|
||||
/// Handler that is called for accepted incoming connections.
|
||||
|
@ -15,8 +15,8 @@
|
|||
#endif
|
||||
|
||||
|
||||
#include "../lib/socket.h" //Socket library
|
||||
#include "../lib/config.h" //utilities for config management
|
||||
#include <mist/socket.h> //Socket library
|
||||
#include <mist/config.h> //utilities for config management
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <pwd.h>
|
||||
|
|
112
src/server_setup_http.h
Normal file
112
src/server_setup_http.h
Normal file
|
@ -0,0 +1,112 @@
|
|||
/// \file server_setup_http.h
|
||||
/// Contains generic functions for setting up a HTTP Connector.
|
||||
|
||||
#ifndef MAINHANDLER
|
||||
/// Handler that is called for accepted incoming connections.
|
||||
#define MAINHANDLER NoHandler
|
||||
#error "No handler was set!"
|
||||
#endif
|
||||
|
||||
#ifndef CONNECTOR
|
||||
/// Connector name for the socket.
|
||||
#define CONNECTOR NoConnector
|
||||
#error "No connector was set!"
|
||||
#endif
|
||||
|
||||
#include <mist/socket.h> //Socket library
|
||||
#include <mist/config.h> //utilities for config management
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <pwd.h>
|
||||
#include <fstream>
|
||||
Socket::Server server_socket; ///< Placeholder for the server socket
|
||||
|
||||
/// Basic signal handler. Disconnects the server_socket if it receives
|
||||
/// a SIGINT, SIGHUP or SIGTERM signal, but does nothing for SIGPIPE.
|
||||
/// Disconnecting the server_socket will terminate the main listening loop
|
||||
/// and cleanly shut down the process.
|
||||
void signal_handler (int signum){
|
||||
switch (signum){
|
||||
case SIGINT:
|
||||
#if DEBUG >= 1
|
||||
fprintf(stderr, "Received SIGINT - closing server socket.\n");
|
||||
#endif
|
||||
break;
|
||||
case SIGHUP:
|
||||
#if DEBUG >= 1
|
||||
fprintf(stderr, "Received SIGHUP - closing server socket.\n");
|
||||
#endif
|
||||
break;
|
||||
case SIGTERM:
|
||||
#if DEBUG >= 1
|
||||
fprintf(stderr, "Received SIGTERM - closing server socket.\n");
|
||||
#endif
|
||||
break;
|
||||
case SIGCHLD:
|
||||
wait(0);
|
||||
return;
|
||||
break;
|
||||
default: return; break;
|
||||
}
|
||||
if (!server_socket.connected()) return;
|
||||
server_socket.close();
|
||||
}//signal_handler
|
||||
|
||||
/// Generic main entry point and loop for DDV HTTP-based Connectors.
|
||||
/// This sets up the proper termination handler, checks commandline options,
|
||||
/// parses config files and opens a listening socket.
|
||||
/// Any incoming connections will be accepted and start up the function #MAINHANDLER,
|
||||
/// which should be defined before including server_setup_http.cpp.
|
||||
/// The connector name is set by define #CONNECTOR.
|
||||
int main(int argc, char ** argv){
|
||||
Socket::Connection S;//placeholder for incoming connections
|
||||
|
||||
//setup signal handler
|
||||
struct sigaction new_action;
|
||||
new_action.sa_handler = signal_handler;
|
||||
sigemptyset (&new_action.sa_mask);
|
||||
new_action.sa_flags = 0;
|
||||
sigaction(SIGINT, &new_action, NULL);
|
||||
sigaction(SIGHUP, &new_action, NULL);
|
||||
sigaction(SIGTERM, &new_action, NULL);
|
||||
sigaction(SIGPIPE, &new_action, NULL);
|
||||
sigaction(SIGCHLD, &new_action, NULL);
|
||||
|
||||
//set and parse configuration
|
||||
Util::Config C;
|
||||
C.parseArgs(argc, argv);
|
||||
|
||||
//setup a new server socket, for the correct interface and port
|
||||
server_socket = Socket::Server("/tmp/mist/http_" CONNECTOR);
|
||||
if (!server_socket.connected()){
|
||||
#if DEBUG >= 1
|
||||
fprintf(stderr, "Error: could not make listening socket\n");
|
||||
#endif
|
||||
return 1;
|
||||
}else{
|
||||
#if DEBUG >= 3
|
||||
fprintf(stderr, "Made a listening socket on %s:%i...\n", C.interface.c_str(), C.listen_port);
|
||||
#endif
|
||||
}
|
||||
|
||||
Util::setUser(C.username);
|
||||
if (C.daemon_mode){Util::Daemonize();}
|
||||
|
||||
while (server_socket.connected()){
|
||||
S = server_socket.accept();
|
||||
if (S.connected()){//check if the new connection is valid
|
||||
pid_t myid = fork();
|
||||
if (myid == 0){//if new child, start MAINHANDLER
|
||||
return MAINHANDLER(S);
|
||||
}else{//otherwise, do nothing or output debugging text
|
||||
#if DEBUG >= 3
|
||||
fprintf(stderr, "Spawned new process %i for socket %i\n", (int)myid, S.getSocket());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}//while connected
|
||||
#if DEBUG >= 1
|
||||
fprintf(stderr, "Server socket closed, exiting.\n");
|
||||
#endif
|
||||
return 0;
|
||||
}//main
|
287
src/tinythread.cpp
Normal file
287
src/tinythread.cpp
Normal file
|
@ -0,0 +1,287 @@
|
|||
/*
|
||||
Copyright (c) 2010 Marcus Geelnard
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
*/
|
||||
|
||||
#include <exception>
|
||||
#include "tinythread.h"
|
||||
|
||||
#if defined(_TTHREAD_POSIX_)
|
||||
#include <unistd.h>
|
||||
#include <map>
|
||||
#elif defined(_TTHREAD_WIN32_)
|
||||
#include <process.h>
|
||||
#endif
|
||||
|
||||
|
||||
namespace tthread {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// condition_variable
|
||||
//------------------------------------------------------------------------------
|
||||
// NOTE 1: The Win32 implementation of the condition_variable class is based on
|
||||
// the corresponding implementation in GLFW, which in turn is based on a
|
||||
// description by Douglas C. Schmidt and Irfan Pyarali:
|
||||
// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
|
||||
//
|
||||
// NOTE 2: Windows Vista actually has native support for condition variables
|
||||
// (InitializeConditionVariable, WakeConditionVariable, etc), but we want to
|
||||
// be portable with pre-Vista Windows versions, so TinyThread++ does not use
|
||||
// Vista condition variables.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
#define _CONDITION_EVENT_ONE 0
|
||||
#define _CONDITION_EVENT_ALL 1
|
||||
#endif
|
||||
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
condition_variable::condition_variable() : mWaitersCount(0)
|
||||
{
|
||||
mEvents[_CONDITION_EVENT_ONE] = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
mEvents[_CONDITION_EVENT_ALL] = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
InitializeCriticalSection(&mWaitersCountLock);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
condition_variable::~condition_variable()
|
||||
{
|
||||
CloseHandle(mEvents[_CONDITION_EVENT_ONE]);
|
||||
CloseHandle(mEvents[_CONDITION_EVENT_ALL]);
|
||||
DeleteCriticalSection(&mWaitersCountLock);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
void condition_variable::_wait()
|
||||
{
|
||||
// Wait for either event to become signaled due to notify_one() or
|
||||
// notify_all() being called
|
||||
int result = WaitForMultipleObjects(2, mEvents, FALSE, INFINITE);
|
||||
|
||||
// Check if we are the last waiter
|
||||
EnterCriticalSection(&mWaitersCountLock);
|
||||
-- mWaitersCount;
|
||||
bool lastWaiter = (result == (WAIT_OBJECT_0 + _CONDITION_EVENT_ALL)) &&
|
||||
(mWaitersCount == 0);
|
||||
LeaveCriticalSection(&mWaitersCountLock);
|
||||
|
||||
// If we are the last waiter to be notified to stop waiting, reset the event
|
||||
if(lastWaiter)
|
||||
ResetEvent(mEvents[_CONDITION_EVENT_ALL]);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
void condition_variable::notify_one()
|
||||
{
|
||||
// Are there any waiters?
|
||||
EnterCriticalSection(&mWaitersCountLock);
|
||||
bool haveWaiters = (mWaitersCount > 0);
|
||||
LeaveCriticalSection(&mWaitersCountLock);
|
||||
|
||||
// If we have any waiting threads, send them a signal
|
||||
if(haveWaiters)
|
||||
SetEvent(mEvents[_CONDITION_EVENT_ONE]);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
void condition_variable::notify_all()
|
||||
{
|
||||
// Are there any waiters?
|
||||
EnterCriticalSection(&mWaitersCountLock);
|
||||
bool haveWaiters = (mWaitersCount > 0);
|
||||
LeaveCriticalSection(&mWaitersCountLock);
|
||||
|
||||
// If we have any waiting threads, send them a signal
|
||||
if(haveWaiters)
|
||||
SetEvent(mEvents[_CONDITION_EVENT_ALL]);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// POSIX pthread_t to unique thread::id mapping logic.
|
||||
// Note: Here we use a global thread safe std::map to convert instances of
|
||||
// pthread_t to small thread identifier numbers (unique within one process).
|
||||
// This method should be portable across different POSIX implementations.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(_TTHREAD_POSIX_)
|
||||
static thread::id _pthread_t_to_ID(const pthread_t &aHandle)
|
||||
{
|
||||
static mutex idMapLock;
|
||||
static std::map<pthread_t, unsigned long int> idMap;
|
||||
static unsigned long int idCount(1);
|
||||
|
||||
lock_guard<mutex> guard(idMapLock);
|
||||
if(idMap.find(aHandle) == idMap.end())
|
||||
idMap[aHandle] = idCount ++;
|
||||
return thread::id(idMap[aHandle]);
|
||||
}
|
||||
#endif // _TTHREAD_POSIX_
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// thread
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/// Information to pass to the new thread (what to run).
|
||||
struct _thread_start_info {
|
||||
void (*mFunction)(void *); ///< Pointer to the function to be executed.
|
||||
void * mArg; ///< Function argument for the thread function.
|
||||
thread * mThread; ///< Pointer to the thread object.
|
||||
};
|
||||
|
||||
// Thread wrapper function.
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
unsigned WINAPI thread::wrapper_function(void * aArg)
|
||||
#elif defined(_TTHREAD_POSIX_)
|
||||
void * thread::wrapper_function(void * aArg)
|
||||
#endif
|
||||
{
|
||||
// Get thread startup information
|
||||
_thread_start_info * ti = (_thread_start_info *) aArg;
|
||||
|
||||
try
|
||||
{
|
||||
// Call the actual client thread function
|
||||
ti->mFunction(ti->mArg);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
// Uncaught exceptions will terminate the application (default behavior
|
||||
// according to the C++0x draft)
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
// The thread is no longer executing
|
||||
lock_guard<mutex> guard(ti->mThread->mDataMutex);
|
||||
ti->mThread->mNotAThread = true;
|
||||
|
||||
// The thread is responsible for freeing the startup information
|
||||
delete ti;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
thread::thread(void (*aFunction)(void *), void * aArg)
|
||||
{
|
||||
// Serialize access to this thread structure
|
||||
lock_guard<mutex> guard(mDataMutex);
|
||||
|
||||
// Fill out the thread startup information (passed to the thread wrapper,
|
||||
// which will eventually free it)
|
||||
_thread_start_info * ti = new _thread_start_info;
|
||||
ti->mFunction = aFunction;
|
||||
ti->mArg = aArg;
|
||||
ti->mThread = this;
|
||||
|
||||
// The thread is now alive
|
||||
mNotAThread = false;
|
||||
|
||||
// Create the thread
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
mHandle = (HANDLE) _beginthreadex(0, 0, wrapper_function, (void *) ti, 0, &mWin32ThreadID);
|
||||
#elif defined(_TTHREAD_POSIX_)
|
||||
if(pthread_create(&mHandle, NULL, wrapper_function, (void *) ti) != 0)
|
||||
mHandle = 0;
|
||||
#endif
|
||||
|
||||
// Did we fail to create the thread?
|
||||
if(!mHandle)
|
||||
{
|
||||
mNotAThread = true;
|
||||
delete ti;
|
||||
}
|
||||
}
|
||||
|
||||
thread::~thread()
|
||||
{
|
||||
if(joinable())
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
void thread::join()
|
||||
{
|
||||
if(joinable())
|
||||
{
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
WaitForSingleObject(mHandle, INFINITE);
|
||||
#elif defined(_TTHREAD_POSIX_)
|
||||
pthread_join(mHandle, NULL);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool thread::joinable() const
|
||||
{
|
||||
mDataMutex.lock();
|
||||
bool result = !mNotAThread;
|
||||
mDataMutex.unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
thread::id thread::get_id() const
|
||||
{
|
||||
if(!joinable())
|
||||
return id();
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
return id((unsigned long int) mWin32ThreadID);
|
||||
#elif defined(_TTHREAD_POSIX_)
|
||||
return _pthread_t_to_ID(mHandle);
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned thread::hardware_concurrency()
|
||||
{
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
SYSTEM_INFO si;
|
||||
GetSystemInfo(&si);
|
||||
return (int) si.dwNumberOfProcessors;
|
||||
#elif defined(_SC_NPROCESSORS_ONLN)
|
||||
return (int) sysconf(_SC_NPROCESSORS_ONLN);
|
||||
#elif defined(_SC_NPROC_ONLN)
|
||||
return (int) sysconf(_SC_NPROC_ONLN);
|
||||
#else
|
||||
// The standard requires this function to return zero if the number of
|
||||
// hardware cores could not be determined.
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// this_thread
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
thread::id this_thread::get_id()
|
||||
{
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
return thread::id((unsigned long int) GetCurrentThreadId());
|
||||
#elif defined(_TTHREAD_POSIX_)
|
||||
return _pthread_t_to_ID(pthread_self());
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
644
src/tinythread.h
Normal file
644
src/tinythread.h
Normal file
|
@ -0,0 +1,644 @@
|
|||
/*
|
||||
Copyright (c) 2010 Marcus Geelnard
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
*/
|
||||
|
||||
#ifndef _TINYTHREAD_H_
|
||||
#define _TINYTHREAD_H_
|
||||
|
||||
// Which platform are we on?
|
||||
#if !defined(_TTHREAD_PLATFORM_DEFINED_)
|
||||
#if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__)
|
||||
#define _TTHREAD_WIN32_
|
||||
#else
|
||||
#define _TTHREAD_POSIX_
|
||||
#endif
|
||||
#define _TTHREAD_PLATFORM_DEFINED_
|
||||
#endif
|
||||
|
||||
// Platform specific includes
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <sched.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
// Generic includes
|
||||
#include <ostream>
|
||||
|
||||
/// TinyThread++ version (major number).
|
||||
#define TINYTHREAD_VERSION_MAJOR 1
|
||||
/// TinyThread++ version (minor number).
|
||||
#define TINYTHREAD_VERSION_MINOR 0
|
||||
/// TinyThread++ version (full version).
|
||||
#define TINYTHREAD_VERSION (TINYTHREAD_VERSION_MAJOR * 100 + TINYTHREAD_VERSION_MINOR)
|
||||
|
||||
// Do we have a fully featured C++0x compiler?
|
||||
#if (__cplusplus > 199711L) || (defined(__STDCXX_VERSION__) && (__STDCXX_VERSION__ >= 201001L))
|
||||
#define _TTHREAD_CPP0X_
|
||||
#endif
|
||||
|
||||
// ...at least partial C++0x?
|
||||
#if defined(_TTHREAD_CPP0X_) || defined(__GXX_EXPERIMENTAL_CXX0X__) || defined(__GXX_EXPERIMENTAL_CPP0X__)
|
||||
#define _TTHREAD_CPP0X_PARTIAL_
|
||||
#endif
|
||||
|
||||
// Macro for disabling assignments of objects.
|
||||
#ifdef _TTHREAD_CPP0X_PARTIAL_
|
||||
#define _TTHREAD_DISABLE_ASSIGNMENT(name) \
|
||||
name(const name&) = delete; \
|
||||
name& operator=(const name&) = delete;
|
||||
#else
|
||||
#define _TTHREAD_DISABLE_ASSIGNMENT(name) \
|
||||
name(const name&); \
|
||||
name& operator=(const name&);
|
||||
#endif
|
||||
|
||||
#if !defined(_TTHREAD_CPP0X_) && !defined(thread_local)
|
||||
#if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || defined(__IBMCPP__)
|
||||
#define thread_local __thread
|
||||
#else
|
||||
#define thread_local __declspec(thread)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/// Main name space for TinyThread++.
|
||||
/// This namespace is more or less equivalent to the \c std namespace for the
|
||||
/// C++0x thread classes. For instance, the tthread::mutex class corresponds to
|
||||
/// the std::mutex class.
|
||||
namespace tthread {
|
||||
|
||||
/// Mutex class.
|
||||
/// This is a mutual exclusion object for synchronizing access to shared
|
||||
/// memory areas for several threads. The mutex is non-recursive (i.e. a
|
||||
/// program may deadlock if the thread that owns a mutex object calls lock()
|
||||
/// on that object).
|
||||
/// @see recursive_mutex
|
||||
class mutex {
|
||||
public:
|
||||
/// Constructor.
|
||||
mutex()
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
: mAlreadyLocked(false)
|
||||
#endif
|
||||
{
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
InitializeCriticalSection(&mHandle);
|
||||
#else
|
||||
pthread_mutex_init(&mHandle, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Destructor.
|
||||
~mutex()
|
||||
{
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
DeleteCriticalSection(&mHandle);
|
||||
#else
|
||||
pthread_mutex_destroy(&mHandle);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Lock the mutex.
|
||||
/// The method will block the calling thread until a lock on the mutex can
|
||||
/// be obtained. The mutex remains locked until \c unlock() is called.
|
||||
/// @see lock_guard
|
||||
inline void lock()
|
||||
{
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
EnterCriticalSection(&mHandle);
|
||||
while(mAlreadyLocked) Sleep(1000); // Simulate deadlock...
|
||||
mAlreadyLocked = true;
|
||||
#else
|
||||
pthread_mutex_lock(&mHandle);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Try to lock the mutex.
|
||||
/// The method will try to lock the mutex. If it fails, the function will
|
||||
/// return immediately (non-blocking).
|
||||
/// @return \c true if the lock was acquired, or \c false if the lock could
|
||||
/// not be acquired.
|
||||
inline bool try_lock()
|
||||
{
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
bool ret = (TryEnterCriticalSection(&mHandle) ? true : false);
|
||||
if(ret && mAlreadyLocked)
|
||||
{
|
||||
LeaveCriticalSection(&mHandle);
|
||||
ret = false;
|
||||
}
|
||||
return ret;
|
||||
#else
|
||||
return (pthread_mutex_trylock(&mHandle) == 0) ? true : false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Unlock the mutex.
|
||||
/// If any threads are waiting for the lock on this mutex, one of them will
|
||||
/// be unblocked.
|
||||
inline void unlock()
|
||||
{
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
mAlreadyLocked = false;
|
||||
LeaveCriticalSection(&mHandle);
|
||||
#else
|
||||
pthread_mutex_unlock(&mHandle);
|
||||
#endif
|
||||
}
|
||||
|
||||
_TTHREAD_DISABLE_ASSIGNMENT(mutex)
|
||||
|
||||
private:
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
CRITICAL_SECTION mHandle;
|
||||
bool mAlreadyLocked;
|
||||
#else
|
||||
pthread_mutex_t mHandle;
|
||||
#endif
|
||||
|
||||
friend class condition_variable;
|
||||
};
|
||||
|
||||
/// Recursive mutex class.
|
||||
/// This is a mutual exclusion object for synchronizing access to shared
|
||||
/// memory areas for several threads. The mutex is recursive (i.e. a thread
|
||||
/// may lock the mutex several times, as long as it unlocks the mutex the same
|
||||
/// number of times).
|
||||
/// @see mutex
|
||||
class recursive_mutex {
|
||||
public:
|
||||
/// Constructor.
|
||||
recursive_mutex()
|
||||
{
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
InitializeCriticalSection(&mHandle);
|
||||
#else
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||
pthread_mutex_init(&mHandle, &attr);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Destructor.
|
||||
~recursive_mutex()
|
||||
{
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
DeleteCriticalSection(&mHandle);
|
||||
#else
|
||||
pthread_mutex_destroy(&mHandle);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Lock the mutex.
|
||||
/// The method will block the calling thread until a lock on the mutex can
|
||||
/// be obtained. The mutex remains locked until \c unlock() is called.
|
||||
/// @see lock_guard
|
||||
inline void lock()
|
||||
{
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
EnterCriticalSection(&mHandle);
|
||||
#else
|
||||
pthread_mutex_lock(&mHandle);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Try to lock the mutex.
|
||||
/// The method will try to lock the mutex. If it fails, the function will
|
||||
/// return immediately (non-blocking).
|
||||
/// @return \c true if the lock was acquired, or \c false if the lock could
|
||||
/// not be acquired.
|
||||
inline bool try_lock()
|
||||
{
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
return TryEnterCriticalSection(&mHandle) ? true : false;
|
||||
#else
|
||||
return (pthread_mutex_trylock(&mHandle) == 0) ? true : false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Unlock the mutex.
|
||||
/// If any threads are waiting for the lock on this mutex, one of them will
|
||||
/// be unblocked.
|
||||
inline void unlock()
|
||||
{
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
LeaveCriticalSection(&mHandle);
|
||||
#else
|
||||
pthread_mutex_unlock(&mHandle);
|
||||
#endif
|
||||
}
|
||||
|
||||
_TTHREAD_DISABLE_ASSIGNMENT(recursive_mutex)
|
||||
|
||||
private:
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
CRITICAL_SECTION mHandle;
|
||||
#else
|
||||
pthread_mutex_t mHandle;
|
||||
#endif
|
||||
|
||||
friend class condition_variable;
|
||||
};
|
||||
|
||||
/// Lock guard class.
|
||||
/// The constructor locks the mutex, and the destructor unlocks the mutex, so
|
||||
/// the mutex will automatically be unlocked when the lock guard goes out of
|
||||
/// scope. Example usage:
|
||||
/// @code
|
||||
/// mutex m;
|
||||
/// int counter;
|
||||
///
|
||||
/// void increment()
|
||||
/// {
|
||||
/// lock_guard<mutex> guard(m);
|
||||
/// ++ counter;
|
||||
/// }
|
||||
/// @endcode
|
||||
|
||||
template <class T>
|
||||
class lock_guard {
|
||||
public:
|
||||
typedef T mutex_type;
|
||||
|
||||
lock_guard() : mMutex(0) {}
|
||||
|
||||
/// The constructor locks the mutex.
|
||||
explicit lock_guard(mutex_type &aMutex)
|
||||
{
|
||||
mMutex = &aMutex;
|
||||
mMutex->lock();
|
||||
}
|
||||
|
||||
/// The destructor unlocks the mutex.
|
||||
~lock_guard()
|
||||
{
|
||||
if(mMutex)
|
||||
mMutex->unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
mutex_type * mMutex;
|
||||
};
|
||||
|
||||
/// Condition variable class.
|
||||
/// This is a signalling object for synchronizing the execution flow for
|
||||
/// several threads. Example usage:
|
||||
/// @code
|
||||
/// // Shared data and associated mutex and condition variable objects
|
||||
/// int count;
|
||||
/// mutex m;
|
||||
/// condition_variable cond;
|
||||
///
|
||||
/// // Wait for the counter to reach a certain number
|
||||
/// void wait_counter(int targetCount)
|
||||
/// {
|
||||
/// lock_guard<mutex> guard(m);
|
||||
/// while(count < targetCount)
|
||||
/// cond.wait(m);
|
||||
/// }
|
||||
///
|
||||
/// // Increment the counter, and notify waiting threads
|
||||
/// void increment()
|
||||
/// {
|
||||
/// lock_guard<mutex> guard(m);
|
||||
/// ++ count;
|
||||
/// cond.notify_all();
|
||||
/// }
|
||||
/// @endcode
|
||||
class condition_variable {
|
||||
public:
|
||||
/// Constructor.
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
condition_variable();
|
||||
#else
|
||||
condition_variable()
|
||||
{
|
||||
pthread_cond_init(&mHandle, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Destructor.
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
~condition_variable();
|
||||
#else
|
||||
~condition_variable()
|
||||
{
|
||||
pthread_cond_destroy(&mHandle);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Wait for the condition.
|
||||
/// The function will block the calling thread until the condition variable
|
||||
/// is woken by \c notify_one(), \c notify_all() or a spurious wake up.
|
||||
/// @param[in] aMutex A mutex that will be unlocked when the wait operation
|
||||
/// starts, an locked again as soon as the wait operation is finished.
|
||||
template <class _mutexT>
|
||||
inline void wait(_mutexT &aMutex)
|
||||
{
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
// Increment number of waiters
|
||||
EnterCriticalSection(&mWaitersCountLock);
|
||||
++ mWaitersCount;
|
||||
LeaveCriticalSection(&mWaitersCountLock);
|
||||
|
||||
// Release the mutex while waiting for the condition (will decrease
|
||||
// the number of waiters when done)...
|
||||
aMutex.unlock();
|
||||
_wait();
|
||||
aMutex.lock();
|
||||
#else
|
||||
pthread_cond_wait(&mHandle, &aMutex.mHandle);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Notify one thread that is waiting for the condition.
|
||||
/// If at least one thread is blocked waiting for this condition variable,
|
||||
/// one will be woken up.
|
||||
/// @note Only threads that started waiting prior to this call will be
|
||||
/// woken up.
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
void notify_one();
|
||||
#else
|
||||
inline void notify_one()
|
||||
{
|
||||
pthread_cond_signal(&mHandle);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Notify all threads that are waiting for the condition.
|
||||
/// All threads that are blocked waiting for this condition variable will
|
||||
/// be woken up.
|
||||
/// @note Only threads that started waiting prior to this call will be
|
||||
/// woken up.
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
void notify_all();
|
||||
#else
|
||||
inline void notify_all()
|
||||
{
|
||||
pthread_cond_broadcast(&mHandle);
|
||||
}
|
||||
#endif
|
||||
|
||||
_TTHREAD_DISABLE_ASSIGNMENT(condition_variable)
|
||||
|
||||
private:
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
void _wait();
|
||||
HANDLE mEvents[2]; ///< Signal and broadcast event HANDLEs.
|
||||
unsigned int mWaitersCount; ///< Count of the number of waiters.
|
||||
CRITICAL_SECTION mWaitersCountLock; ///< Serialize access to mWaitersCount.
|
||||
#else
|
||||
pthread_cond_t mHandle;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/// Thread class.
|
||||
class thread {
|
||||
public:
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
typedef HANDLE native_handle_type;
|
||||
#else
|
||||
typedef pthread_t native_handle_type;
|
||||
#endif
|
||||
|
||||
class id;
|
||||
|
||||
/// Default constructor.
|
||||
/// Construct a \c thread object without an associated thread of execution
|
||||
/// (i.e. non-joinable).
|
||||
thread() : mHandle(0), mNotAThread(true)
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
, mWin32ThreadID(0)
|
||||
#endif
|
||||
{}
|
||||
|
||||
/// Thread starting constructor.
|
||||
/// Construct a \c thread object with a new thread of execution.
|
||||
/// @param[in] aFunction A function pointer to a function of type:
|
||||
/// <tt>void fun(void * arg)</tt>
|
||||
/// @param[in] aArg Argument to the thread function.
|
||||
/// @note This constructor is not fully compatible with the standard C++
|
||||
/// thread class. It is more similar to the pthread_create() (POSIX) and
|
||||
/// CreateThread() (Windows) functions.
|
||||
thread(void (*aFunction)(void *), void * aArg);
|
||||
|
||||
/// Destructor.
|
||||
/// @note If the thread is joinable upon destruction, \c std::terminate()
|
||||
/// will be called, which terminates the process. It is always wise to do
|
||||
/// \c join() before deleting a thread object.
|
||||
~thread();
|
||||
|
||||
/// Wait for the thread to finish (join execution flows).
|
||||
void join();
|
||||
|
||||
/// Check if the thread is joinable.
|
||||
/// A thread object is joinable if it has an associated thread of execution.
|
||||
bool joinable() const;
|
||||
|
||||
/// Return the thread ID of a thread object.
|
||||
id get_id() const;
|
||||
|
||||
/// Get the native handle for this thread.
|
||||
/// @note Under Windows, this is a \c HANDLE, and under POSIX systems, this
|
||||
/// is a \c pthread_t.
|
||||
inline native_handle_type native_handle()
|
||||
{
|
||||
return mHandle;
|
||||
}
|
||||
|
||||
/// Determine the number of threads which can possibly execute concurrently.
|
||||
/// This function is useful for determining the optimal number of threads to
|
||||
/// use for a task.
|
||||
/// @return The number of hardware thread contexts in the system.
|
||||
/// @note If this value is not defined, the function returns zero (0).
|
||||
static unsigned hardware_concurrency();
|
||||
|
||||
_TTHREAD_DISABLE_ASSIGNMENT(thread)
|
||||
|
||||
private:
|
||||
native_handle_type mHandle; ///< Thread handle.
|
||||
mutable mutex mDataMutex; ///< Serializer for access to the thread private data.
|
||||
bool mNotAThread; ///< True if this object is not a thread of execution.
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
unsigned int mWin32ThreadID; ///< Unique thread ID (filled out by _beginthreadex).
|
||||
#endif
|
||||
|
||||
// This is the internal thread wrapper function.
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
static unsigned WINAPI wrapper_function(void * aArg);
|
||||
#else
|
||||
static void * wrapper_function(void * aArg);
|
||||
#endif
|
||||
};
|
||||
|
||||
/// Thread ID.
|
||||
/// The thread ID is a unique identifier for each thread.
|
||||
/// @see thread::get_id()
|
||||
class thread::id {
|
||||
public:
|
||||
/// Default constructor.
|
||||
/// The default constructed ID is that of thread without a thread of
|
||||
/// execution.
|
||||
id() : mId(0) {};
|
||||
|
||||
id(unsigned long int aId) : mId(aId) {};
|
||||
|
||||
id(const id& aId) : mId(aId.mId) {};
|
||||
|
||||
inline id & operator=(const id &aId)
|
||||
{
|
||||
mId = aId.mId;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline friend bool operator==(const id &aId1, const id &aId2)
|
||||
{
|
||||
return (aId1.mId == aId2.mId);
|
||||
}
|
||||
|
||||
inline friend bool operator!=(const id &aId1, const id &aId2)
|
||||
{
|
||||
return (aId1.mId != aId2.mId);
|
||||
}
|
||||
|
||||
inline friend bool operator<=(const id &aId1, const id &aId2)
|
||||
{
|
||||
return (aId1.mId <= aId2.mId);
|
||||
}
|
||||
|
||||
inline friend bool operator<(const id &aId1, const id &aId2)
|
||||
{
|
||||
return (aId1.mId < aId2.mId);
|
||||
}
|
||||
|
||||
inline friend bool operator>=(const id &aId1, const id &aId2)
|
||||
{
|
||||
return (aId1.mId >= aId2.mId);
|
||||
}
|
||||
|
||||
inline friend bool operator>(const id &aId1, const id &aId2)
|
||||
{
|
||||
return (aId1.mId > aId2.mId);
|
||||
}
|
||||
|
||||
inline friend std::ostream& operator <<(std::ostream &os, const id &obj)
|
||||
{
|
||||
os << obj.mId;
|
||||
return os;
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned long int mId;
|
||||
};
|
||||
|
||||
|
||||
// Related to <ratio> - minimal to be able to support chrono.
|
||||
typedef long long __intmax_t;
|
||||
|
||||
/// Minimal implementation of the \c ratio class. This class provides enough
|
||||
/// functionality to implement some basic \c chrono classes.
|
||||
template <__intmax_t N, __intmax_t D = 1> class ratio {
|
||||
public:
|
||||
static double _as_double() { return double(N) / double(D); }
|
||||
};
|
||||
|
||||
/// Minimal implementation of the \c chrono namespace.
|
||||
/// The \c chrono namespace provides types for specifying time intervals.
|
||||
namespace chrono {
|
||||
/// Duration template class. This class provides enough functionality to
|
||||
/// implement \c this_thread::sleep_for().
|
||||
template <class _Rep, class _Period = ratio<1> > class duration {
|
||||
private:
|
||||
_Rep rep_;
|
||||
public:
|
||||
typedef _Rep rep;
|
||||
typedef _Period period;
|
||||
|
||||
/// Construct a duration object with the given duration.
|
||||
template <class _Rep2>
|
||||
explicit duration(const _Rep2& r) : rep_(r) {};
|
||||
|
||||
/// Return the value of the duration object.
|
||||
rep count() const
|
||||
{
|
||||
return rep_;
|
||||
}
|
||||
};
|
||||
|
||||
// Standard duration types.
|
||||
typedef duration<__intmax_t, ratio<1, 1000000000> > nanoseconds; ///< Duration with the unit nanoseconds.
|
||||
typedef duration<__intmax_t, ratio<1, 1000000> > microseconds; ///< Duration with the unit microseconds.
|
||||
typedef duration<__intmax_t, ratio<1, 1000> > milliseconds; ///< Duration with the unit milliseconds.
|
||||
typedef duration<__intmax_t> seconds; ///< Duration with the unit seconds.
|
||||
typedef duration<__intmax_t, ratio<60> > minutes; ///< Duration with the unit minutes.
|
||||
typedef duration<__intmax_t, ratio<3600> > hours; ///< Duration with the unit hours.
|
||||
}
|
||||
|
||||
/// The namespace \c this_thread provides methods for dealing with the
|
||||
/// calling thread.
|
||||
namespace this_thread {
|
||||
/// Return the thread ID of the calling thread.
|
||||
thread::id get_id();
|
||||
|
||||
/// Yield execution to another thread.
|
||||
/// Offers the operating system the opportunity to schedule another thread
|
||||
/// that is ready to run on the current processor.
|
||||
inline void yield()
|
||||
{
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
Sleep(0);
|
||||
#else
|
||||
sched_yield();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Blocks the calling thread for a period of time.
|
||||
/// @param[in] aTime Minimum time to put the thread to sleep.
|
||||
/// Example usage:
|
||||
/// @code
|
||||
/// // Sleep for 100 milliseconds
|
||||
/// this_thread::sleep_for(chrono::milliseconds(100));
|
||||
/// @endcode
|
||||
/// @note Supported duration types are: nanoseconds, microseconds,
|
||||
/// milliseconds, seconds, minutes and hours.
|
||||
template <class _Rep, class _Period> void sleep_for(const chrono::duration<_Rep, _Period>& aTime)
|
||||
{
|
||||
#if defined(_TTHREAD_WIN32_)
|
||||
Sleep(int(double(aTime.count()) * (1000.0 * _Period::_as_double()) + 0.5));
|
||||
#else
|
||||
usleep(int(double(aTime.count()) * (1000000.0 * _Period::_as_double()) + 0.5));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Define/macro cleanup
|
||||
#undef _TTHREAD_DISABLE_ASSIGNMENT
|
||||
|
||||
#endif // _TINYTHREAD_H_
|
Loading…
Add table
Add a link
Reference in a new issue