diff --git a/src/Player/Frame.h b/src/Player/Frame.h
index 53afbb2..ff48c67 100644
--- a/src/Player/Frame.h
+++ b/src/Player/Frame.h
@@ -44,6 +44,7 @@ typedef enum {
     TrackInvalid = -1,
     TrackVideo = 0,
     TrackAudio,
+    TrackTitle,
     TrackMax
 } TrackType;
 
diff --git a/src/RtmpMuxer/RtmpMetedata.h b/src/RtmpMuxer/RtmpMetedata.h
index ea0b811..fa193b8 100644
--- a/src/RtmpMuxer/RtmpMetedata.h
+++ b/src/RtmpMuxer/RtmpMetedata.h
@@ -75,7 +75,7 @@ public:
      * @return
      */
     TrackType getTrackType() const override {
-        return TrackInvalid;
+        return TrackTitle;
     }
 
     /**
diff --git a/src/Rtsp/Rtsp.cpp b/src/Rtsp/Rtsp.cpp
index 721dee4..b17b751 100644
--- a/src/Rtsp/Rtsp.cpp
+++ b/src/Rtsp/Rtsp.cpp
@@ -49,78 +49,124 @@ string FindField(const char* buf, const char* start, const char *end ,int bufSiz
 	}
 	return string(msg_start, msg_end);
 }
-int parserSDP(const string& sdp, RtspTrack Track[2]) {
-	map<string,map<char ,map<string ,string> > > sdpAttr;
-	string sdpTrack = "";
+
+
+void SdpAttr::load(const string &sdp) {
+	_track_map.clear();
+	string key;
+	SdpTrack::Ptr track = std::make_shared<SdpTrack>();
 
 	auto lines = split(sdp,"\n");
 	for (auto &line : lines){
 		trim(line);
-		if(line.size() < 2){
-			continue;
-		}
-		if(line[1] != '='){
+		if(line.size() < 2 || line[1] != '='){
 			continue;
 		}
 		char opt = line[0];
 		string opt_val = line.substr(2);
 		switch (opt){
 			case 'o':
+				track->_o = opt_val;
+				break;
 			case 's':
+				track->_s = opt_val;
+				break;
 			case 'i':
+				track->_i = opt_val;
+				break;
 			case 'c':
-			case 't':{
-				sdpAttr[sdpTrack][opt][""] = opt_val;
-			}
+				track->_c = opt_val;
+				break;
+			case 't':
+				track->_t = opt_val;
+				break;
+			case 'b':
+				track->_b = opt_val;
 				break;
 			case 'm':{
-				sdpTrack = FindField(opt_val.data(), nullptr," ");
-				sdpAttr[sdpTrack][opt][""] = opt_val;
+				_track_map[key] = track;
+				track = std::make_shared<SdpTrack>();
+				key = FindField(opt_val.data(), nullptr," ");;
+				track->_m = opt_val;
 			}
 				break;
 			case 'a':{
 				string attr = FindField(opt_val.data(), nullptr,":");
 				if(attr.empty()){
-					sdpAttr[sdpTrack][opt][opt_val] = opt_val;
+					track->_attr[opt_val] = "";
 				}else{
-					sdpAttr[sdpTrack][opt][attr] = FindField(opt_val.data(),":", nullptr);
+					track->_attr[attr] = FindField(opt_val.data(),":", nullptr);
 				}
 			}
 				break;
 			default:
+				track->_other[opt] = opt_val;
 				break;
 		}
 	}
+	_track_map[key] = track;
+
 
-	for (auto &pr : sdpAttr) {
-		TrackType trackType = TrackInvalid;
-		if (pr.first == "video") {
-			trackType = TrackVideo;
+	for (auto &pr : _track_map) {
+		auto &track = *pr.second;
+		if (pr.first == "") {
+			track._type = TrackTitle;
+		} else if (pr.first == "video") {
+			track._type = TrackVideo;
 		} else if (pr.first == "audio") {
-			trackType = TrackAudio;
-		} else if (pr.first == "") {
-			//title
-			auto range = pr.second['a']["range"];
-			char name[16] = {0},start[16] = {0},end[16] = {0};
-			if (2 == sscanf(range.data(), "%15[^=]=%15[^-]-%15s", name, start, end)) {
+			track._type = TrackAudio;
+		} else {
+			track._type = TrackInvalid;
+		}
 
+		auto it = track._attr.find("range");
+		if (it != track._attr.end()) {
+			char name[16] = {0}, start[16] = {0}, end[16] = {0};
+			int ret = sscanf(it->second.data(), "%15[^=]=%15[^-]-%15s", name, start, end);
+			if (3 == ret || 2 == ret) {
+				if (strcmp(start, "now") == 0) {
+					strcpy(start, "0");
+				}
+				track._start = atof(start);
+				track._end = atof(end);
+				track._duration = track._end - track._start;
 			}
-			DebugL << range;
-			continue;
-		} else {
-			continue;
 		}
 
-		auto rtpmap = pr.second['a']["rtpmap"];
-		int pt, samplerate;
-		char codec[16] = {0};
-		if (3 == sscanf(rtpmap.data(), "%d %15[^/]/%d", &pt, codec, &samplerate)) {
+		it = track._attr.find("rtpmap");
+		if(it != track._attr.end()){
+			auto rtpmap = it->second;
+			int pt, samplerate;
+			char codec[16] = {0};
+			if (3 == sscanf(rtpmap.data(), "%d %15[^/]/%d", &pt, codec, &samplerate)) {
+				track._pt = pt;
+				track._codec = codec;
+				track._samplerate = samplerate;
+			}
+		}
+
+		it = track._attr.find("fmtp");
+		if(it != track._attr.end()) {
+			track._fmtp = it->second;
+		}
 
+		it = track._attr.find("control");
+		if(it != track._attr.end()) {
+			track._control = it->second;
 		}
-		DebugL << codec;
 	}
+}
 
+SdpTrack::Ptr SdpAttr::getTrack(TrackType type) {
+	for (auto &pr : _track_map){
+		if(pr.second->_type == type){
+			return pr.second;
+		}
+	}
+	return nullptr;
+}
 
+int parserSDP(const string& sdp, RtspTrack Track[2]) {
 	int track_cnt = 0;
 	string::size_type pos_head = 0;
 	while ((pos_head = sdp.find("m=",pos_head)) != string::npos ) {
@@ -169,6 +215,7 @@ static  onceToken s_token([](){
            "a=recvonly";
     RtspTrack track[2];
     parserSDP(str,track);
+	SdpAttr attr(str);
     track[0].inited=true;
 });
 bool MakeNalu(uint8_t in, NALU &nal) {
@@ -190,3 +237,5 @@ bool MakeFU(uint8_t in, FU &fu) {
 	}
 	return true;
 }
+
+
diff --git a/src/Rtsp/Rtsp.h b/src/Rtsp/Rtsp.h
index a5d66a7..9120b99 100644
--- a/src/Rtsp/Rtsp.h
+++ b/src/Rtsp/Rtsp.h
@@ -38,6 +38,43 @@ using namespace std;
 using namespace toolkit;
 using namespace mediakit;
 
+class SdpTrack{
+public:
+	typedef std::shared_ptr<SdpTrack> Ptr;
+
+	string _m;
+	string _o;
+	string _s;
+	string _i;
+	string _c;
+	string _t;
+	string _b;
+
+	float _duration = 0;
+	float _start = 0;
+	float _end = 0;
+
+	map<char,string> _other;
+	map<string,string> _attr;
+public:
+	int _pt;
+	string _codec;
+	int _samplerate;
+	string _fmtp;
+	string _control;
+	TrackType _type;
+};
+class SdpAttr {
+public:
+	typedef std::shared_ptr<SdpAttr> Ptr;
+	SdpAttr(const string &sdp){load(sdp);};
+	~SdpAttr(){}
+
+	void load(const string &sdp);
+	SdpTrack::Ptr getTrack(TrackType type);
+private:
+	map<string,SdpTrack::Ptr> _track_map;
+};
 
 class RtspTrack{
 public:
@@ -63,7 +100,6 @@ public:
 string FindField(const char* buf, const char* start, const char *end,int bufSize = 0 );
 int parserSDP(const string& sdp, RtspTrack Track[2]);
 
-
 struct StrCaseCompare
 {
     bool operator()(const string& __x, const string& __y) const
diff --git a/src/RtspMuxer/RtspSdp.h b/src/RtspMuxer/RtspSdp.h
index ce76665..37ddd0e 100644
--- a/src/RtspMuxer/RtspSdp.h
+++ b/src/RtspMuxer/RtspSdp.h
@@ -115,7 +115,7 @@ public:
      * @return
      */
     TrackType getTrackType() const override {
-        return TrackInvalid;
+        return TrackTitle;
     }
 
     /**