From 9d9849175b11b3ba9918ad4f980aa4a1c7c2afb0 Mon Sep 17 00:00:00 2001
From: houxiao <houxiao@454eff88-639b-444f-9e54-f578c98de674>
Date: 星期四, 22 十二月 2016 14:39:50 +0800
Subject: [PATCH] add pipeline

---
 RtspFace/make.sh              |   11 
 RtspFace/PipeLine.h           |   81 +++
 RtspFace/PL_AVFrameYUV420.h   |   24 +
 RtspFace/PL_H264Decoder.cpp   |  255 ++++++++++++
 RtspFace/RTSPClient.hpp       |  271 ++----------
 RtspFace/main.cpp             |   40 +
 RtspFace/PL_H264Decoder.h     |   24 +
 RtspFace/PL_RTSPClient.cpp    |  242 +++++++++++
 RtspFace/PL_RTSPClient.h      |   36 +
 RtspFace/PL_AVFrameYUV420.cpp |  115 +++++
 RtspFace/PipeLine.cpp         |  131 ++++++
 11 files changed, 1,014 insertions(+), 216 deletions(-)

diff --git a/RtspFace/PL_AVFrameYUV420.cpp b/RtspFace/PL_AVFrameYUV420.cpp
new file mode 100644
index 0000000..5927ce5
--- /dev/null
+++ b/RtspFace/PL_AVFrameYUV420.cpp
@@ -0,0 +1,115 @@
+#include "PL_AVFrameYUV420.h"
+
+extern "C"
+{
+	#include <libavcodec/avcodec.h>
+	#include <libavutil/frame.h>
+	#include <libavformat/avformat.h>
+}
+
+struct AVFrameYUV420_Internal
+{
+	uint8_t buffer[1920*1080*3];
+	size_t buffSize;
+	size_t buffSizeMax;
+	bool payError;
+	
+	AVFrameYUV420_Internal() : 
+		buffSize(0), buffSizeMax(sizeof(buffer)), 
+		payError(false)
+	{
+	}
+	
+	~AVFrameYUV420_Internal()
+	{
+	}
+	
+	void reset()
+	{
+		buffSize = 0;
+		payError = false;
+	}
+};
+
+PipeLineElem* create_PL_AVFrameYUV420()
+{
+	return new PL_AVFrameYUV420;
+}
+
+PL_AVFrameYUV420::PL_AVFrameYUV420() : internal(new AVFrameYUV420_Internal)
+{
+}
+
+PL_AVFrameYUV420::~PL_AVFrameYUV420()
+{
+	delete (AVFrameYUV420_Internal*)internal;
+	internal= nullptr;
+}
+
+bool PL_AVFrameYUV420::init(void* args)
+{
+	AVFrameYUV420_Internal* in = (AVFrameYUV420_Internal*)internal;
+	in->reset();
+	
+	return true;
+}
+
+void PL_AVFrameYUV420::finit()
+{
+	AVFrameYUV420_Internal* in = (AVFrameYUV420_Internal*)internal;
+	
+}
+
+bool PL_AVFrameYUV420::pay(const PipeMaterial& pm)
+{
+	AVFrameYUV420_Internal* in = (AVFrameYUV420_Internal*)internal;
+	
+		int picSize = in->pAVCodecContext->height * in->pAVCodecContext->width;
+		in->buffSize = picSize * 1.5;
+
+		int height = in->pAVFrame->height;
+		int width = in->pAVFrame->width;
+
+		// write yuv420
+		int a=0;
+		for (int i = 0; i < height; i++)
+		{
+			memcpy(in->buffer + a, in->pAVFrame->data[0] + i * in->pAVFrame->linesize[0], width);
+			a += width;
+		}
+		for (int i=0; i<height/2; i++)
+		{
+			memcpy(in->buffer + a, in->pAVFrame->data[1] + i * in->pAVFrame->linesize[1], width / 2);
+			a += width / 2;
+		}
+		for (int i=0; i<height/2; i++)
+		{
+			memcpy(in->buffer + a, in->pAVFrame->data[2] + i * in->pAVFrame->linesize[2], width / 2);
+			a += width / 2;
+		}
+
+		//in->buffer readly
+
+		//static size_t f=0;
+		//char fname[50];
+		//sprintf(fname, "%u.yuv420", ++f);
+		//FILE * pFile = fopen (fname,"wb");
+		//fwrite (in->buffer , sizeof(char), in->buffSize, pFile);
+		//fclose(pFile);
+	
+	
+	return in->payError;
+}
+
+bool PL_AVFrameYUV420::gain(PipeMaterial& pm)
+{
+	AVFrameYUV420_Internal* in = (AVFrameYUV420_Internal*)internal;
+
+	if (!in->payError)
+	{
+		pm.buffer = in->buffer;
+		pm.buffSize = in->buffSize;
+	}
+	pm.former = this;
+	return in->payError;
+}
diff --git a/RtspFace/PL_AVFrameYUV420.h b/RtspFace/PL_AVFrameYUV420.h
new file mode 100644
index 0000000..ae6c677
--- /dev/null
+++ b/RtspFace/PL_AVFrameYUV420.h
@@ -0,0 +1,24 @@
+#ifndef _PL_AVFrameYUV420_H_
+#define _PL_AVFrameYUV420_H_
+
+#include "PipeLine.h"
+
+class PL_AVFrameYUV420 : public PipeLineElem
+{
+public:
+	PL_AVFrameYUV420();
+	virtual ~PL_AVFrameYUV420();
+
+	virtual bool init(void* args);
+	virtual void finit();
+
+	virtual bool pay(const PipeMaterial& pm);
+	virtual bool gain(PipeMaterial& pm);
+	
+private:
+	void* internal;
+};
+
+PipeLineElem* create_PL_AVFrameYUV420();
+
+#endif
diff --git a/RtspFace/PL_H264Decoder.cpp b/RtspFace/PL_H264Decoder.cpp
new file mode 100644
index 0000000..b27918c
--- /dev/null
+++ b/RtspFace/PL_H264Decoder.cpp
@@ -0,0 +1,255 @@
+#include "PL_H264Decoder.h"
+
+#include <H264VideoRTPSource.hh> // for SPropRecord
+#include <libbase64.h>
+
+extern "C"
+{
+	#include <libavcodec/avcodec.h>
+	#include <libavutil/frame.h>
+	#include <libavformat/avformat.h>
+}
+
+struct H264Decoder_Internal
+{
+	uint8_t buffer[1920*1080*3];
+	size_t buffSize;
+	size_t buffSizeMax;
+	bool fmtp_set_to_context;
+	bool payError;
+
+	AVCodecContext* pAVCodecContext;
+	AVFrame* pAVFrame;
+	
+	H264Decoder_Internal() : 
+		buffSize(0), buffSizeMax(sizeof(buffer)), fmtp_set_to_context(false), 
+		payError(false), 
+		pAVCodecContext(nullptr), pAVFrame(nullptr)
+	{
+	}
+	
+	~H264Decoder_Internal()
+	{
+	}
+	
+	void reset()
+	{
+		buffSize = 0;
+		fmtp_set_to_context = false;
+		payError = false;
+	}
+};
+
+PipeLineElem* create_PL_H264Decoder()
+{
+	return new PL_H264Decoder;
+}
+
+PL_H264Decoder::PL_H264Decoder() : internal(new H264Decoder_Internal)
+{
+}
+
+PL_H264Decoder::~PL_H264Decoder()
+{
+	delete (H264Decoder_Internal*)internal;
+	internal= nullptr;
+}
+
+bool PL_H264Decoder::init(void* args)
+{
+	H264Decoder_Internal* in = (H264Decoder_Internal*)internal;
+	in->reset();
+	
+	return true;
+}
+
+void PL_H264Decoder::finit()
+{
+	H264Decoder_Internal* in = (H264Decoder_Internal*)internal;
+	
+}
+
+SPropRecord* parseSPropParameterSets(char const* sPropParameterSetsStr, size_t& numSPropRecords) {  
+  // Make a copy of the input string, so we can replace the commas with '\0's:  
+  char* inStr = strDup(sPropParameterSetsStr);  
+  if (inStr == NULL) {  
+    numSPropRecords = 0;  
+    return NULL;  
+  }  
+  
+  // Count the number of commas (and thus the number of parameter sets):  
+  numSPropRecords = 1;  
+  char* s;  
+  for (s = inStr; *s != '\0'; ++s) {  
+    if (*s == ',') {  
+      ++numSPropRecords;  
+      *s = '\0';  
+    }  
+  }  
+  
+  // Allocate and fill in the result array:  
+  SPropRecord* resultArray = new SPropRecord[numSPropRecords];
+  s = inStr;  
+  for (unsigned i = 0; i < numSPropRecords; ++i) {  
+    resultArray[i].sPropBytes = new uint8_t[256];
+	
+	size_t sPropLength = 0;
+	base64_decode(s, strlen(s), (char*)resultArray[i].sPropBytes, &sPropLength, 0);
+	resultArray[i].sPropLength = sPropLength;
+	
+    s += strlen(s) + 1;  
+  }  
+  
+  delete[] inStr;  
+  return resultArray;  
+}
+
+bool initH264DecoderEnv(H264Decoder_Internal* in, 
+	uint8_t* sps, size_t spsSize, uint8_t* pps, size_t ppsSize)
+{
+	av_register_all();
+
+	// find the video encoder
+	AVCodec* avCodec = avcodec_find_decoder(AV_CODEC_ID_H264);
+
+	if (!avCodec)   
+	{  
+		printf("codec not found!\n");  
+		return false;  
+	}  
+
+	in->pAVCodecContext = avcodec_alloc_context3(avCodec);
+
+	in->pAVCodecContext->time_base.num = 1;
+	in->pAVCodecContext->frame_number = 1;
+	in->pAVCodecContext->codec_type = AVMEDIA_TYPE_VIDEO;
+	in->pAVCodecContext->bit_rate = 0;
+	in->pAVCodecContext->time_base.den = 25;
+	in->pAVCodecContext->width = 1920;
+	in->pAVCodecContext->height = 1080;
+
+	if (in->pAVCodecContext->extradata == NULL)  
+    {  
+        int totalsize = 0;  
+        unsigned char* tmp = NULL;  
+        unsigned char nalu_header[4] = { 0, 0, 0, 1 };  
+  
+        totalsize = 8 + spsSize + ppsSize;  
+  
+        tmp = new unsigned char[totalsize];  
+        memcpy(tmp, nalu_header, 4);  
+        memcpy(tmp + 4, sps, spsSize);  
+        memcpy(tmp + 4 + spsSize, nalu_header, 4);  
+        memcpy(tmp + 4 + spsSize + 4, pps, ppsSize);  
+  
+        in->pAVCodecContext->extradata_size = totalsize;
+  
+        in->pAVCodecContext->extradata = tmp;  
+    }
+	
+	if(avcodec_open2(in->pAVCodecContext, avCodec, NULL) >= 0)
+		in->pAVFrame = av_frame_alloc();// Allocate video frame
+	else
+		return false;
+	
+	return true;
+}
+
+bool decodeH264(H264Decoder_Internal* in, uint8_t* buffer, size_t buffSize)  
+{
+	AVPacket packet = {0};
+	int frameFinished = buffSize;
+
+	if (av_packet_from_data(&packet, buffer, buffSize) != 0)
+	{  
+		printf("av_packet_from_data error\n");
+		return false;
+	}  
+	
+	// decode
+	avcodec_decode_video2(in->pAVCodecContext, in->pAVFrame, &frameFinished, &packet);
+	if(frameFinished)
+	{
+		// decode ok
+
+		int picSize = in->pAVCodecContext->height * in->pAVCodecContext->width;
+		in->buffSize = picSize * 1.5;
+
+		int height = in->pAVFrame->height;
+		int width = in->pAVFrame->width;
+
+		// write yuv420
+		int a=0;
+		for (int i = 0; i < height; i++)
+		{
+			memcpy(in->buffer + a, in->pAVFrame->data[0] + i * in->pAVFrame->linesize[0], width);
+			a += width;
+		}
+		for (int i=0; i<height/2; i++)
+		{
+			memcpy(in->buffer + a, in->pAVFrame->data[1] + i * in->pAVFrame->linesize[1], width / 2);
+			a += width / 2;
+		}
+		for (int i=0; i<height/2; i++)
+		{
+			memcpy(in->buffer + a, in->pAVFrame->data[2] + i * in->pAVFrame->linesize[2], width / 2);
+			a += width / 2;
+		}
+
+		//in->buffer readly
+
+		//static size_t f=0;
+		//char fname[50];
+		//sprintf(fname, "%u.yuv420", ++f);
+		//FILE * pFile = fopen (fname,"wb");
+		//fwrite (in->buffer , sizeof(char), in->buffSize, pFile);
+		//fclose(pFile);
+	}
+	else
+		printf("incomplete frame\n");
+}
+
+bool PL_H264Decoder::pay(const PipeMaterial& pm)
+{
+	H264Decoder_Internal* in = (H264Decoder_Internal*)internal;
+	
+	if (!in->fmtp_set_to_context)
+	{
+		if (manager == NULL)
+			return false;
+		
+		std::string fmtp(manager->get_global_param(PLGP_RTSP_FMTP));
+		if (fmtp.empty())
+			return false;
+		
+		size_t numSPropRecords = 0;
+		SPropRecord *p_record = parseSPropParameterSets(fmtp.c_str(), numSPropRecords);
+		if (numSPropRecords < 2)
+			return false;//#todo log
+
+		SPropRecord &sps = p_record[0];  
+		SPropRecord &pps = p_record[1];
+
+		bool ret = initH264DecoderEnv(in, sps.sPropBytes, sps.sPropLength, pps.sPropBytes, pps.sPropLength);
+		if (!ret)
+			return false; // #todo log
+		else
+			in->fmtp_set_to_context = true;
+	}
+	
+	in->payError = decodeH264(in, pm.buffer, pm.buffSize);
+	return in->payError;
+}
+
+bool PL_H264Decoder::gain(PipeMaterial& pm)
+{
+	H264Decoder_Internal* in = (H264Decoder_Internal*)internal;
+
+	if (!in->payError)
+	{
+		pm.buffer = in->buffer;
+		pm.buffSize = in->buffSize;
+	}
+	pm.former = this;
+	return in->payError;
+}
diff --git a/RtspFace/PL_H264Decoder.h b/RtspFace/PL_H264Decoder.h
new file mode 100644
index 0000000..c4770a9
--- /dev/null
+++ b/RtspFace/PL_H264Decoder.h
@@ -0,0 +1,24 @@
+#ifndef _PL_H264DECODER_H_
+#define _PL_H264DECODER_H_
+
+#include "PipeLine.h"
+
+class PL_H264Decoder : public PipeLineElem
+{
+public:
+	PL_H264Decoder();
+	virtual ~PL_H264Decoder();
+
+	virtual bool init(void* args);
+	virtual void finit();
+
+	virtual bool pay(const PipeMaterial& pm);
+	virtual bool gain(PipeMaterial& pm);
+	
+private:
+	void* internal;
+};
+
+PipeLineElem* create_PL_H264Decoder();
+
+#endif
diff --git a/RtspFace/PL_RTSPClient.cpp b/RtspFace/PL_RTSPClient.cpp
new file mode 100644
index 0000000..30c18fd
--- /dev/null
+++ b/RtspFace/PL_RTSPClient.cpp
@@ -0,0 +1,242 @@
+#include "PL_RTSPClient.h"
+#include <pthread.h>
+
+void rtsp_client_sdp_callback(void* arg, const char* val);
+void rtsp_client_fmtp_callback(void* arg, const char* val);
+void rtsp_client_frame_callback(void* arg, uint8_t* buffer, size_t buffSize);
+void rtsp_client_continue_callback(void* arg);
+#include "RTSPClient.hpp"
+
+struct RTSPClient_Internal
+{
+	PL_RTSPClient* client;
+	RTSPConfig rtspConfig;
+	pthread_t live_daemon_thid;
+	char eventLoopWatchVariable;
+	bool live_daemon_running;
+	pthread_mutex_t* frame_mutex;
+	pthread_mutex_t* continue_mutex;
+	
+	uint8_t* lastBuffer;
+	size_t lastBuffSize;
+	
+	RTSPClient_Internal() : 
+		client(nullptr), rtspConfig(), live_daemon_thid(0), 
+		eventLoopWatchVariable(0), live_daemon_running(false), 
+		frame_mutex(new pthread_mutex_t), continue_mutex(new pthread_mutex_t), 
+		lastBuffer(nullptr), lastBuffSize(0)
+	{
+		pthread_mutex_init(frame_mutex, NULL);
+		pthread_mutex_init(continue_mutex, NULL);
+	}
+	
+	~RTSPClient_Internal()
+	{
+		if (frame_mutex != nullptr)
+		{
+			pthread_mutex_destroy(frame_mutex);
+			delete frame_mutex;
+		}
+		
+		if (continue_mutex != nullptr)
+		{
+			pthread_mutex_destroy(continue_mutex);
+			delete continue_mutex;
+		}
+	}
+	
+	void reset()
+	{
+		client = nullptr;
+		rtspConfig.progName = "";
+		rtspConfig.rtspURL = "";
+		live_daemon_thid = 0;
+		eventLoopWatchVariable = 0;
+		live_daemon_running = false;
+
+		if (frame_mutex != nullptr)
+		{
+			pthread_mutex_destroy(frame_mutex);
+			delete frame_mutex;
+		}
+		
+		frame_mutex = new pthread_mutex_t;
+		pthread_mutex_init(frame_mutex, NULL);
+		
+		if (continue_mutex != nullptr)
+		{
+			pthread_mutex_destroy(continue_mutex);
+			delete continue_mutex;
+		}
+		
+		continue_mutex = new pthread_mutex_t;
+		pthread_mutex_init(continue_mutex, NULL);
+		
+		lastBuffer = nullptr;
+		lastBuffSize = 0;
+	}
+};
+
+void* live_daemon_thd(void* arg)
+{
+	RTSPClient_Internal* in = (RTSPClient_Internal*)arg;
+	
+	TaskScheduler* scheduler = BasicTaskScheduler::createNew();
+	UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler);
+
+	usage(*env, in->rtspConfig.progName.c_str());
+	
+	openURL(*env, in->client, in->rtspConfig.progName.c_str(), in->rtspConfig.rtspURL.c_str());
+	
+	in->live_daemon_running = true;
+	env->taskScheduler().doEventLoop(&(in->eventLoopWatchVariable));
+	in->live_daemon_running = false;
+}
+
+PipeLineElem* create_PL_RTSPClient()
+{
+	return new PL_RTSPClient;
+}
+
+PL_RTSPClient::PL_RTSPClient() : internal(new RTSPClient_Internal)
+{
+}
+
+PL_RTSPClient::~PL_RTSPClient()
+{
+	delete (RTSPClient_Internal*)internal;
+	internal= nullptr;
+}
+
+bool PL_RTSPClient::init(void* args)
+{
+	if (args == nullptr)
+		return false;
+
+	const RTSPConfig* config = reinterpret_cast<const RTSPConfig*>(args);
+	RTSPClient_Internal* in = (RTSPClient_Internal*)internal;
+	in->reset();
+	in->client = this;
+	in->rtspConfig = *config;
+	
+	int ret = pthread_mutex_lock(in->frame_mutex);
+	if(ret != 0)
+	{
+		printf("pthread_mutex_lock frame_mutex: %s/n", strerror(ret));
+		return false;
+	}
+	
+	ret = pthread_mutex_lock(in->continue_mutex);
+	if(ret != 0)
+	{
+		printf("pthread_mutex_lock continue_mutex: %s/n", strerror(ret));
+		return false;
+	}
+	
+	ret = pthread_create(&(in->live_daemon_thid), NULL, live_daemon_thd, in);
+	if(ret != 0)
+	{
+		printf("pthread_create: %s/n", strerror(ret));
+		return false;
+	}
+	
+	return true;
+}
+
+void PL_RTSPClient::finit()
+{
+	RTSPClient_Internal* in = (RTSPClient_Internal*)internal;
+	
+	in->eventLoopWatchVariable = 1;
+	pthread_join(in->live_daemon_thid, NULL);
+}
+
+bool PL_RTSPClient::pay(const PipeMaterial& pm)
+{
+	RTSPClient_Internal* in = (RTSPClient_Internal*)internal;
+	return in->live_daemon_running;
+}
+
+bool PL_RTSPClient::gain(PipeMaterial& pm)
+{
+	RTSPClient_Internal* in = (RTSPClient_Internal*)internal;
+
+	int ret = pthread_mutex_unlock(in->continue_mutex);
+	if(ret != 0)
+	{
+		printf("pthread_mutex_unlock continue_mutex: %s/n", strerror(ret));
+		return false;
+	}
+
+	ret = pthread_mutex_lock(in->frame_mutex);
+	if(ret != 0)
+	{
+		printf("pthread_mutex_lock: %s/n", strerror(ret));
+		return false;
+	}
+	
+	pm.buffer = in->lastBuffer;
+	pm.buffSize = in->lastBuffSize;
+	pm.former = this;
+
+	return true;
+}
+
+void rtsp_client_sdp_callback(void* arg, const char* val)
+{
+	if (arg == nullptr || val == nullptr)
+		return;
+
+	PL_RTSPClient* client = (PL_RTSPClient*)arg;
+
+	if (client->manager == nullptr)
+		return;
+	
+	client->manager->set_global_param(PLGP_RTSP_SDP, val);
+}
+
+void rtsp_client_fmtp_callback(void* arg, const char* val)
+{
+	if (arg == nullptr || val == nullptr)
+		return;
+
+	PL_RTSPClient* client = (PL_RTSPClient*)arg;
+
+	if (client->manager == nullptr)
+		return;
+	
+	client->manager->set_global_param(PLGP_RTSP_FMTP, val);
+}
+
+void rtsp_client_frame_callback(void* arg, uint8_t* buffer, size_t buffSize)
+{
+	if (arg == nullptr || buffer == nullptr || buffSize == 0)
+		return;
+
+	PL_RTSPClient* client = (PL_RTSPClient*)arg;
+	RTSPClient_Internal* in = (RTSPClient_Internal*)(client->internal);
+	
+	in->lastBuffer = buffer;
+	in->lastBuffSize = buffSize;
+
+	int ret = pthread_mutex_unlock(in->frame_mutex);
+	if(ret != 0)
+	{
+		printf("pthread_mutex_unlock frame_mutex: %s/n", strerror(ret));
+	}
+}
+
+void rtsp_client_continue_callback(void* arg)
+{
+	if (arg == nullptr)
+		return;
+
+	PL_RTSPClient* client = (PL_RTSPClient*)arg;
+	RTSPClient_Internal* in = (RTSPClient_Internal*)(client->internal);
+
+	int ret = pthread_mutex_lock(in->continue_mutex);
+	if(ret != 0)
+	{
+		printf("pthread_mutex_unlock continue_mutex: %s/n", strerror(ret));
+	}
+}
diff --git a/RtspFace/PL_RTSPClient.h b/RtspFace/PL_RTSPClient.h
new file mode 100644
index 0000000..b5367fb
--- /dev/null
+++ b/RtspFace/PL_RTSPClient.h
@@ -0,0 +1,36 @@
+#ifndef _PL_RTSPCLIENT_H_
+#define _PL_RTSPCLIENT_H_
+
+#include "PipeLine.h"
+#include <string>
+
+struct RTSPConfig
+{
+	std::string progName;
+	std::string rtspURL;
+	
+	RTSPConfig() : progName(), rtspURL() { }
+};
+
+class PL_RTSPClient : public PipeLineElem
+{
+	friend void rtsp_client_frame_callback(void* arg, uint8_t* buffer, size_t buffSize);
+	friend void rtsp_client_continue_callback(void* arg);
+
+public:
+	PL_RTSPClient();
+	virtual ~PL_RTSPClient();
+
+	virtual bool init(void* args);
+	virtual void finit();
+
+	virtual bool pay(const PipeMaterial& pm);
+	virtual bool gain(PipeMaterial& pm);
+	
+private:
+	void* internal;
+};
+
+PipeLineElem* create_PL_RTSPClient();
+
+#endif
diff --git a/RtspFace/PipeLine.cpp b/RtspFace/PipeLine.cpp
new file mode 100644
index 0000000..6f53b19
--- /dev/null
+++ b/RtspFace/PipeLine.cpp
@@ -0,0 +1,131 @@
+#include "PipeLine.h"
+
+PipeMaterial::PipeMaterial() : buffer(nullptr), buffSize(0), former(nullptr)
+{
+}
+
+PipeLine::PipeLine() : global_params_map(), elem_create_func_map(), elems()
+{
+}
+
+PipeLine::~PipeLine()
+{
+	// pipe stop
+	
+	for(elem_vec_t::iterator iter = elems.begin(); iter != elems.end(); ++iter)
+	{
+		PipeLineElem* elem = *iter;
+		if (elem != nullptr)
+		{
+			elem->finit();
+			delete *iter;
+		}
+	}
+	
+	elems.clear();
+}
+
+bool PipeLine::register_elem_creator(const std::string& type, elem_create_func_t func)
+{
+	if (type.empty() || func == nullptr)
+		return false;
+
+	elem_create_func_map_t::iterator iter = elem_create_func_map.find(type);
+	if (iter != elem_create_func_map.end())
+		return false;
+	
+	elem_create_func_map.insert(std::make_pair(type, func));
+	return true;
+}
+
+void PipeLine::push_elem(PipeLineElem* elem)
+{
+	if(elem != nullptr)
+	{
+		elem->manager = this;
+		elems.push_back(elem);
+	}
+}
+
+PipeLineElem* PipeLine::push_elem(const std::string& type)
+{
+	elem_create_func_map_t::iterator iter = elem_create_func_map.find(type);
+	if (iter == elem_create_func_map.end())
+		return nullptr;
+	
+	elem_create_func_t func = iter->second;
+	if (func == nullptr)
+		return nullptr;
+	
+	PipeLineElem* elem = func();
+	if (elem == nullptr)
+		return nullptr;
+	
+	elem->manager = this;
+	push_elem(elem);
+	return elem;
+}
+
+PipeLineElem* PipeLine::pipe(PipeMaterial* pm /*= nullptr*/)
+{
+	PipeLineElem* elem_begin = *elems.begin();
+	PipeLineElem* elem_last = *elems.rbegin();
+
+	if (elems.empty() || elem_begin == nullptr || elem_last == nullptr)
+		return nullptr;
+
+	uint8_t pmPlacement[sizeof(PipeMaterial)];
+	if (pm == nullptr)
+		pm = new (pmPlacement) PipeMaterial;
+	
+	if (elems.size() == 1)
+	{
+		elem_begin->gain(*pm);
+		return elem_begin;
+	}
+	else if (elems.size() == 2)
+	{
+		elem_begin->gain(*pm);
+		elem_last->pay(*pm);
+		return elem_last;
+	}
+	else
+	{
+		elem_begin->gain(*pm);
+		
+		elem_vec_t::iterator iter = elems.begin();
+		while (elem_begin != elem_last)
+		{
+			++iter;
+			elem_begin = *iter;
+			elem_begin->pay(*pm);
+			elem_begin->gain(*pm);
+		}
+	
+		elem_last->pay(*pm);
+		return elem_last;
+	}
+	
+	return nullptr;
+}
+
+void PipeLine::set_global_param(const std::string& name, const std::string& value)
+{
+	if (name.empty())
+		return;
+
+	global_params_map_t::iterator iter = global_params_map.find(name);
+	if (iter == global_params_map.end())
+		global_params_map.insert(std::make_pair(name, value));
+	else
+		iter->second = value;
+}
+
+std::string PipeLine::get_global_param(const std::string& name) const
+{
+	global_params_map_t::const_iterator iter = global_params_map.find(name);
+	if (iter == global_params_map.end())
+		return "";
+	else
+		return iter->second;
+}
diff --git a/RtspFace/PipeLine.h b/RtspFace/PipeLine.h
new file mode 100644
index 0000000..898e1f6
--- /dev/null
+++ b/RtspFace/PipeLine.h
@@ -0,0 +1,81 @@
+#ifndef _PIPELINE_H_
+#define _PIPELINE_H_
+
+#include <string>
+#include <stdint.h>
+#include <map>
+#include <vector>
+
+#define PLGP_RTSP_SDP "RTSP_SDP"
+#define PLGP_RTSP_FMTP "RTSP_FMTP"
+
+class PipeLineElem;
+class PipeLine;
+
+struct PipeMaterial
+{
+	uint8_t* buffer;
+	size_t buffSize;
+	PipeLineElem* former;
+	
+	PipeMaterial();
+};
+
+class PipeLineElem
+{
+public:
+	PipeLineElem() : manager(nullptr) { }
+	virtual ~PipeLineElem() { }
+
+	virtual bool init(void* args) = 0;
+	virtual void finit() = 0;
+	
+	// buffer: delete it who create it
+	virtual bool pay(const PipeMaterial& pm) = 0;
+	virtual bool gain(PipeMaterial& pm) = 0;
+
+public:
+	PipeLine* manager;
+};
+
+typedef PipeLineElem* (*elem_create_func_t)();
+
+// 0 (there is no elem). do nothing
+// 1 (there is one elem). gain
+// 2 (there is two elems). gain --> pay
+// 3 (there is more than two elems). gain --> pay gain --> pay gain --> ... --> pay
+class PipeLine
+{
+public:
+	PipeLine();
+	
+	// stop and delete all managed elements
+	~PipeLine();
+	
+	bool register_elem_creator(const std::string& type, elem_create_func_t func);
+	void push_elem(PipeLineElem* elem);
+	PipeLineElem* push_elem(const std::string& type);
+	
+	// do pipe sync. returns the element who returns false, or the last one.
+	PipeLineElem* pipe(PipeMaterial* pm = nullptr);
+	
+	// do pipe async
+	void pipe_start();
+	void pipe_notify(PipeLineElem*);
+	void pipe_stop();
+	
+	void set_global_param(const std::string& name, const std::string& value);
+	std::string get_global_param(const std::string& name) const;
+	
+private:
+	typedef std::map<const std::string, elem_create_func_t> elem_create_func_map_t;
+	elem_create_func_map_t elem_create_func_map;
+	
+	typedef std::vector<PipeLineElem*> elem_vec_t;
+	elem_vec_t elems;
+
+	typedef std::map<const std::string, std::string> global_params_map_t;
+	global_params_map_t global_params_map;
+};
+
+#endif
diff --git a/RtspFace/RTSPClient.cpp b/RtspFace/RTSPClient.hpp
similarity index 74%
rename from RtspFace/RTSPClient.cpp
rename to RtspFace/RTSPClient.hpp
index a306d89..78088ef 100644
--- a/RtspFace/RTSPClient.cpp
+++ b/RtspFace/RTSPClient.hpp
@@ -24,10 +24,6 @@
 #include "BasicUsageEnvironment.hh"
 
 #include <iostream>
-#include <libbase64.h>
-
-bool initH264DecoderEnv(uint8_t* sps, size_t spsSize, uint8_t* pps, size_t ppsSize);
-int decodeH264(uint8_t* pBuffer, int dwBufsize, const char *outfile)  ;
 
 // Forward function definitions:
 
@@ -43,7 +39,7 @@
   // called at the end of a stream's expected duration (if the stream has not already signaled its end using a RTCP "BYE")
 
 // The main streaming routine (for each "rtsp://" URL):
-void openURL(UsageEnvironment& env, char const* progName, char const* rtspURL);
+void openURL(UsageEnvironment& env, void* args, char const* progName, char const* rtspURL);
 
 // Used to iterate through each stream's 'subsessions', setting up each one:
 void setupNextSubsession(RTSPClient* rtspClient);
@@ -68,7 +64,7 @@
 
 char eventLoopWatchVariable = 0;
 
-int main(int argc, char** argv) {
+int test_main(int argc, char** argv) {
   // Begin by setting up our usage environment:
   TaskScheduler* scheduler = BasicTaskScheduler::createNew();
   UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler);
@@ -81,7 +77,7 @@
 
   // There are argc-1 URLs: argv[1] through argv[argc-1].  Open and start streaming each one:
   for (int i = 1; i <= argc-1; ++i) {
-    openURL(*env, argv[0], argv[i]);
+    openURL(*env, NULL, argv[0], argv[i]);
   }
 
   // All subsequent activity takes place within the event loop:
@@ -134,6 +130,7 @@
 
 public:
   StreamClientState scs;
+  void* args;
 };
 
 // Define a data sink (a subclass of "MediaSink") to receive the data for each subsession (i.e., each audio or video 'substream').
@@ -141,39 +138,44 @@
 // Or it might be a "FileSink", for outputting the received data into a file (as is done by the "openRTSP" application).
 // In this example code, however, we define a simple 'dummy' sink that receives incoming data, but does nothing with it.
 
-class DummySink: public MediaSink {
+class DummySink: public MediaSink
+{
 public:
-  static DummySink* createNew(UsageEnvironment& env,
-			      MediaSubsession& subsession, // identifies the kind of data that's being received
-			      char const* streamId = NULL); // identifies the stream itself (optional)
+	static DummySink* createNew(UsageEnvironment& env, 
+					void* _args, 
+				  MediaSubsession& subsession, // identifies the kind of data that's being received
+				  char const* streamId = NULL); // identifies the stream itself (optional)
 
 private:
-  DummySink(UsageEnvironment& env, MediaSubsession& subsession, char const* streamId);
-    // called only by "createNew()"
-  virtual ~DummySink();
+	DummySink(UsageEnvironment& env, void* _args, MediaSubsession& subsession, char const* streamId);
+	// called only by "createNew()"
+	virtual ~DummySink();
 
-  static void afterGettingFrame(void* clientData, unsigned frameSize,
-                                unsigned numTruncatedBytes,
+	static void afterGettingFrame(void* clientData, unsigned frameSize,
+								unsigned numTruncatedBytes,
 				struct timeval presentationTime,
-                                unsigned durationInMicroseconds);
-  void afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes,
+								unsigned durationInMicroseconds);
+	void afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes,
 			 struct timeval presentationTime, unsigned durationInMicroseconds);
 
-private:
-  // redefined virtual functions:
-  virtual Boolean continuePlaying();
+public:
+	void* args;
 
 private:
-  u_int8_t* fReceiveBuffer;
-  MediaSubsession& fSubsession;
-  char* fStreamId;
+	// redefined virtual functions:
+	virtual Boolean continuePlaying();
+
+private:
+	u_int8_t* fReceiveBuffer;
+	MediaSubsession& fSubsession;
+	char* fStreamId;
 };
 
 #define RTSP_CLIENT_VERBOSITY_LEVEL 1 // by default, print verbose output from each "RTSPClient"
 
 static unsigned rtspClientCount = 0; // Counts how many streams (i.e., "RTSPClient"s) are currently in use.
 
-void openURL(UsageEnvironment& env, char const* progName, char const* rtspURL) {
+void openURL(UsageEnvironment& env, void* args, char const* progName, char const* rtspURL) {
   // Begin by creating a "RTSPClient" object.  Note that there is a separate "RTSPClient" object for each stream that we wish
   // to receive (even if more than stream uses the same "rtsp://" URL).
   RTSPClient* rtspClient = ourRTSPClient::createNew(env, rtspURL, RTSP_CLIENT_VERBOSITY_LEVEL, progName);
@@ -181,6 +183,8 @@
     env << "Failed to create a RTSP client for URL \"" << rtspURL << "\": " << env.getResultMsg() << "\n";
     return;
   }
+  
+  ((ourRTSPClient*)rtspClient)->args = args;
 
   ++rtspClientCount;
 
@@ -290,7 +294,9 @@
     // (This will prepare the data sink to receive data; the actual flow of data from the client won't start happening until later,
     // after we've sent a RTSP "PLAY" command.)
 
-    scs.subsession->sink = DummySink::createNew(env, *scs.subsession, rtspClient->url());
+	DummySink* mySink;
+    scs.subsession->sink = mySink = DummySink::createNew(env, ((ourRTSPClient*)rtspClient)->args, 
+			*scs.subsession, rtspClient->url());
       // perhaps use your own custom "MediaSink" subclass instead
     if (scs.subsession->sink == NULL) {
       env << *rtspClient << "Failed to create a data sink for the \"" << *scs.subsession
@@ -447,7 +453,9 @@
 
 ourRTSPClient::ourRTSPClient(UsageEnvironment& env, char const* rtspURL,
 			     int verbosityLevel, char const* applicationName, portNumBits tunnelOverHTTPPortNum)
-  : RTSPClient(env,rtspURL, verbosityLevel, applicationName, tunnelOverHTTPPortNum, -1) {
+  : RTSPClient(env,rtspURL, verbosityLevel, applicationName, tunnelOverHTTPPortNum, -1),
+	args(nullptr)
+{
 }
 
 ourRTSPClient::~ourRTSPClient() {
@@ -471,70 +479,33 @@
   }
 }
 
-SPropRecord* parseSPropParameterSets(char const* sPropParameterSetsStr,  
-                                     // result parameter:  
-                                     size_t& numSPropRecords) {  
-  // Make a copy of the input string, so we can replace the commas with '\0's:  
-  char* inStr = strDup(sPropParameterSetsStr);  
-  if (inStr == NULL) {  
-    numSPropRecords = 0;  
-    return NULL;  
-  }  
-  
-  // Count the number of commas (and thus the number of parameter sets):  
-  numSPropRecords = 1;  
-  char* s;  
-  for (s = inStr; *s != '\0'; ++s) {  
-    if (*s == ',') {  
-      ++numSPropRecords;  
-      *s = '\0';  
-    }  
-  }  
-  
-  // Allocate and fill in the result array:  
-  SPropRecord* resultArray = new SPropRecord[numSPropRecords]; //****** 鐪嬪埌 杩欓噷浜� 鎶� *******/  
-  s = inStr;  
-  for (unsigned i = 0; i < numSPropRecords; ++i) {  
-    resultArray[i].sPropBytes = new uint8_t[256];
-	
-	size_t sPropLength = 0;
-	base64_decode(s, strlen(s), (char*)resultArray[i].sPropBytes, &sPropLength, 0);
-	resultArray[i].sPropLength = sPropLength;
-	
-    s += strlen(s) + 1;  
-  }  
-  
-  delete[] inStr;  
-  return resultArray;  
-}
-
 // Implementation of "DummySink":
 
 // Even though we're not going to be doing anything with the incoming data, we still need to receive it.
 // Define the size of the buffer that we'll use:
-#define DUMMY_SINK_RECEIVE_BUFFER_SIZE 100000
+#define DUMMY_SINK_RECEIVE_BUFFER_SIZE 1920*1080*3
 
-DummySink* DummySink::createNew(UsageEnvironment& env, MediaSubsession& subsession, char const* streamId) {
-  return new DummySink(env, subsession, streamId);
+DummySink* DummySink::createNew(UsageEnvironment& env, void* _args, MediaSubsession& subsession, char const* streamId)
+{
+  return new DummySink(env, _args, subsession, streamId);
 }
 
-DummySink::DummySink(UsageEnvironment& env, MediaSubsession& subsession, char const* streamId)
-  : MediaSink(env),
-    fSubsession(subsession) {
-  fStreamId = strDup(streamId);
-  fReceiveBuffer = new u_int8_t[DUMMY_SINK_RECEIVE_BUFFER_SIZE];
-  
-  //parse sdp
-  //const char* strSDP = fSubsession.savedSDPLines();
-  const char* strFmtp = fSubsession.fmtp_spropparametersets();
-  //std::cout << strFmtp << std::endl;
-  
-  size_t numSPropRecords = 0;
-  SPropRecord *p_record = parseSPropParameterSets(fSubsession.fmtp_spropparametersets(), numSPropRecords);
-	SPropRecord &sps = p_record[0];  
-	SPropRecord &pps = p_record[1];
+DummySink::DummySink(UsageEnvironment& env, void* _args, MediaSubsession& subsession, char const* streamId)
+  : MediaSink(env), args(_args), fSubsession(subsession)
+{
+	fStreamId = strDup(streamId);
+	fReceiveBuffer = new u_int8_t[DUMMY_SINK_RECEIVE_BUFFER_SIZE];
+
+	// ffmpeg need AUX header
+	fReceiveBuffer[0]=0x00; fReceiveBuffer[1]=0x00; fReceiveBuffer[2]=0x00; fReceiveBuffer[3]=0x01;
+
+	//parse sdp
+	const char* strSDP = fSubsession.savedSDPLines();
+	rtsp_client_sdp_callback(args, strSDP);
 	
-	initH264DecoderEnv(sps.sPropBytes, sps.sPropLength, pps.sPropBytes, pps.sPropLength);
+	const char* strFmtp = fSubsession.fmtp_spropparametersets();
+	rtsp_client_fmtp_callback(args, strFmtp);
+	//std::cout << strFmtp << std::endl;
 }
 
 DummySink::~DummySink() {
@@ -547,7 +518,7 @@
   DummySink* sink = (DummySink*)clientData;
 
   if (frameSize > 0)
-	decodeH264(sink->fReceiveBuffer, frameSize, NULL);
+	rtsp_client_frame_callback(sink->args, sink->fReceiveBuffer, frameSize + 4);
   
   sink->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime, durationInMicroseconds);
 }
@@ -581,137 +552,11 @@
 Boolean DummySink::continuePlaying() {
   if (fSource == NULL) return False; // sanity check (should not happen)
 
+	  rtsp_client_continue_callback(args);
+	  
   // Request the next frame of data from our input source.  "afterGettingFrame()" will get called later, when it arrives:
-  fSource->getNextFrame(fReceiveBuffer, DUMMY_SINK_RECEIVE_BUFFER_SIZE,
+  fSource->getNextFrame(fReceiveBuffer + 4, DUMMY_SINK_RECEIVE_BUFFER_SIZE,
                         afterGettingFrame, this,
                         onSourceClosure, this);
   return True;
-}
-
-
-/*********
-
-*********/
-
-extern "C"
-{
-	#include <libavcodec/avcodec.h>
-	#include <libavutil/frame.h>
-	#include <libavformat/avformat.h>
-}
-
-AVCodecContext* g_pAVCodecContext  = NULL;
-AVFrame* g_pAVFrame = NULL;
-
-bool initH264DecoderEnv(uint8_t* sps, size_t spsSize, uint8_t* pps, size_t ppsSize)
-{
-	av_register_all();
-
-	// find the video encoder
-	AVCodec* avCodec = avcodec_find_decoder(AV_CODEC_ID_H264);
-
-	if (!avCodec)   
-	{  
-		printf("codec not found!\n");  
-		return -1;  
-	}  
-
-	g_pAVCodecContext = avcodec_alloc_context3(avCodec);
-
-	//鍒濆鍖栧弬鏁帮紝涓嬮潰鐨勫弬鏁板簲璇ョ敱鍏蜂綋鐨勪笟鍔″喅瀹�
-	g_pAVCodecContext->time_base.num = 1;
-	g_pAVCodecContext->frame_number = 1; //姣忓寘涓�涓棰戝抚
-	g_pAVCodecContext->codec_type = AVMEDIA_TYPE_VIDEO;
-	g_pAVCodecContext->bit_rate = 0;
-	g_pAVCodecContext->time_base.den = 25;
-	g_pAVCodecContext->width = 1920;
-	g_pAVCodecContext->height = 1080;
-
-	if (g_pAVCodecContext->extradata == NULL)  
-    {  
-        int totalsize = 0;  
-        unsigned char* tmp = NULL;  
-        unsigned char nalu_header[4] = { 0, 0, 0, 1 };  
-  
-        totalsize = 8 + spsSize + ppsSize;  
-  
-        tmp = new unsigned char[totalsize];  
-        memcpy(tmp, nalu_header, 4);  
-        memcpy(tmp + 4, sps, spsSize);  
-        memcpy(tmp + 4 + spsSize, nalu_header, 4);  
-        memcpy(tmp + 4 + spsSize + 4, pps, ppsSize);  
-  
-        g_pAVCodecContext->extradata_size = totalsize; // g_pAVCodecContext 涓烘垜瑙g爜鏃跺�欎娇鐢ㄧ殑涓婁笅鏂�  
-  
-        g_pAVCodecContext->extradata = tmp;  
-    }
-	
-	if(avcodec_open2(g_pAVCodecContext, avCodec, NULL) >= 0)
-		g_pAVFrame = av_frame_alloc();// Allocate video frame
-	else
-		return false;
-	
-	return true;
-}
-
-int decodeH264(uint8_t* pBuffer, int dwBufsize, const char *outfile)  
-{
-	AVPacket packet = {0};
-	int frameFinished = dwBufsize;//杩欎釜鏄殢渚垮~鍏ユ暟瀛楋紝娌′粈涔堜綔鐢�
-
-	uint8_t newBuff[dwBufsize+4];
-	newBuff[0]=0x00; newBuff[1]=0x00; newBuff[2]=0x00; newBuff[3]=0x01;
-	memcpy(newBuff + 4, pBuffer, dwBufsize);
-	
-	//packet.data = pBuffer;//杩欓噷濉叆涓�涓寚鍚戝畬鏁碒264鏁版嵁甯х殑鎸囬拡
-	//packet.size = dwBufsize;//杩欎釜濉叆H264鏁版嵁甯х殑澶у皬
-
-	if (av_packet_from_data(&packet, newBuff, dwBufsize + 4) != 0){  
-		printf("exchange data failed!\n");
-	}  
-	
-	//涓嬮潰寮�濮嬬湡姝g殑瑙g爜
-	avcodec_decode_video2(g_pAVCodecContext, g_pAVFrame, &frameFinished, &packet);
-	if(frameFinished)//鎴愬姛瑙g爜
-	{
-		int picSize = g_pAVCodecContext->height * g_pAVCodecContext->width;
-		int newSize = picSize * 1.5;
-
-		//鐢宠鍐呭瓨
-		uint8_t *buff = new uint8_t[newSize];
-
-		int height = g_pAVFrame->height;
-		int width = g_pAVFrame->width;
-
-		//鍐欏叆鏁版嵁
-		int a=0;
-		for (int i=0; i<height; i++)
-		{
-			memcpy(buff+a,g_pAVFrame->data[0] + i * g_pAVFrame->linesize[0], width);
-			a+=width;
-		}
-		for (int i=0; i<height/2; i++)
-		{
-			memcpy(buff+a,g_pAVFrame->data[1] + i * g_pAVFrame->linesize[1], width/2);
-			a+=width/2;
-		}
-		for (int i=0; i<height/2; i++)
-		{
-			memcpy(buff+a,g_pAVFrame->data[2] + i * g_pAVFrame->linesize[2], width/2);
-			a+=width/2;
-		}
-
-		//buff readly
-
-		//static size_t f=0;
-		//char fname[50];
-		//sprintf(fname, "%u.yuv420", ++f);
-		//FILE * pFile = fopen (fname,"wb");
-		//fwrite (buff , sizeof(char), newSize, pFile);
-		//fclose(pFile);
-		
-		delete[] buff;
-	}
-	else
-		printf("incomplete frame\n");
 }
diff --git a/RtspFace/main.cpp b/RtspFace/main.cpp
new file mode 100644
index 0000000..e9c6a48
--- /dev/null
+++ b/RtspFace/main.cpp
@@ -0,0 +1,40 @@
+#include "PipeLine.h"
+#include "PL_RTSPClient.h"
+#include "PL_H264Decoder.h"
+#include "PL_AVFrameYUV420.h"
+
+#include <iostream>
+using namespace std;
+
+int main(int argc, char** argv)
+{
+	PipeLine pipeLine;
+	
+	pipeLine.register_elem_creator("PL_RTSPClient", create_PL_RTSPClient);
+	pipeLine.register_elem_creator("PL_H264Decoder", create_PL_H264Decoder);
+	pipeLine.register_elem_creator("PL_AVFrameYUV420", create_PL_H264Decoder);
+	
+	PL_RTSPClient* rtspClient = (PL_RTSPClient*)pipeLine.push_elem("PL_RTSPClient");
+	RTSPConfig rtspConfig;
+	rtspConfig.progName = argv[0];
+	rtspConfig.rtspURL = argv[1];
+	bool ret = rtspClient->init(&rtspConfig);
+	if (!ret)
+	{
+		cout << "rtspClient.init error" << endl;
+		exit(EXIT_FAILURE);
+	}
+	
+	PL_H264Decoder* h264Decoder = (PL_H264Decoder*)pipeLine.push_elem("PL_H264Decoder");
+	h264Decoder->init(nullptr);
+	
+	PL_AVFrameYUV420* AVFrameYUV420 = (PL_AVFrameYUV420*)pipeLine.push_elem("PL_AVFrameYUV420");
+	AVFrameYUV420->init(nullptr);
+	
+	while(true)
+	{
+		//cout << "begin pipe" << endl;
+		pipeLine.pipe();
+		//cout << "end pipe" << endl;
+	}
+}
diff --git a/RtspFace/make.sh b/RtspFace/make.sh
index fbe9d62..3568267 100644
--- a/RtspFace/make.sh
+++ b/RtspFace/make.sh
@@ -10,14 +10,19 @@
 LIBBASE64_INC="-I$LIBBASE64_BASE/include"
 LIBBASE64_LIB="$LIBBASE64_BASE/lib/libbase64.o"
 
-CPPFLAGS+="$LIVEMEDIA_INC $FFMPEG_INC $LIBBASE64_INC"
-LDFLAGS+="$LIVEMEDIA_LIB $FFMPEG_LIB $LIBBASE64_LIB"
+CPPFLAGS+="-pthread $LIVEMEDIA_INC $FFMPEG_INC $LIBBASE64_INC"
+LDFLAGS+="-pthread $LIVEMEDIA_LIB $FFMPEG_LIB $LIBBASE64_LIB"
 
 CFLAGS+="-D__STDC_CONSTANT_MACROS"
 
 rm rtsp_face
+rm *.o
 
-g++ -g RTSPClient.cpp $CFLAGS $CPPFLAGS $LDFLAGS -o rtsp_face
+g++ -g -c -std=c++11 main.cpp $CFLAGS $CPPFLAGS
+g++ -g -c -std=c++11 PL_RTSPClient.cpp $CFLAGS $CPPFLAGS
+g++ -g -c -std=c++11 PL_H264Decoder.cpp $CFLAGS $CPPFLAGS
+g++ -g -c -std=c++11 PipeLine.cpp $CFLAGS $CPPFLAGS
+g++ -g -std=c++11 main.o PL_RTSPClient.o PL_H264Decoder.o PipeLine.o $LDFLAGS -o rtsp_face
 
 #export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$FFMPEG_BASE/lib
 #./rtsp_face rtsp://admin:admin12345@192.168.1.63:554/h264/ch1/main/av_stream

--
Gitblit v1.8.0