SkeyeRMS录像服务器如何实现精确查找_ HLS+M3U8录像回放

2023-04-21 10:18:54 浏览数 (2)

SkeyeRMS作为面向云端的录播服务器,目前正处于开发阶段,以往的SkeyeRMS查找通常只能精确到M3U8列表,而不能实现精确到时间点的查询,为了能实现精确到点的查询,我将M3U8列表从新遍历一遍,重新生成一个精确到在关键位置开始和结束的切片文件(ts)的M3U8列表,然后返回,我们先不讨论这个方法效率如何,但是确实可以将精确度提高到切片文件单位级。

精确查找流程如下:

  1. 遍历本地(数据库)指定录像存储路径,找出在指定开始时间和结束时间范围内的M3U8列表,注意:这里找到的M3U8列表的区间是要小于开始时间,和大于结束时间的最接近值,以确保指定时间范围在查找到的M3U8列表队列中;
  2. 再在指定的M3U8列表里面查找在开始时间和结束时间内的TS,并重新生成TS列表,如果时间戳连续,甚至可以将TS文件连接成一个M3U8列表,从而播放器播放可以不用跨M3U8列表直接播放;

实现代码如下:(基于本地查找)

代码语言:txt复制
bool SkeyeRecordQuery::GetExactM3U8(const char *beginDif, const char *endDif, vector<string> *records)
{
	namespace fs = boost::filesystem;
	fs::path fullpath = fs::path(SkeyeRecordSession::sLocalRecordPath) / name_;

	if (!fs::exists(fullpath))
	{
		return false;
	}

	string begin = beginDif;
	string end = endDif;
	// 通过时间戳和开始结束时间差计算开始时间和结束时间 [12/30/2016 dingshuai]
	//时间戳字串转换成 
	time_t nBegin = StringToTime(begin);
	time_t nEnd	 = StringToTime(end);

	// 算法描述:获取包含begin-end录像开始的时间命名文件夹以及录像结束的时间命名文件夹 [12/30/2016 dingshuai]
	vector<string>* m3u8List = new vector<string>;

	string sStartURL;
	char split = '/';
#ifdef _WIN32
	split = '\';
#endif

	fs::recursive_directory_iterator end_iter;
	for (fs::recursive_directory_iterator iter(fullpath); iter != end_iter; iter  )
	{
		try
		{			//cout << iter->path().string() << endl;
			if (!fs::is_directory(*iter))
			{
				string file = iter->path().string();
				boost::string_ref m3u8_file(file);
				//cout << "string_ref: " << m3u8_file.data() << endl;

				//以流名称开始,.m3u8结束的文件我们视为正常录像的列表文件
				string sM3u8Name = name_   ".m3u8";

				if (m3u8_file.ends_with(sM3u8Name))
				{

					int pos = m3u8_file.find_last_of(split);
					boost::string_ref file_time = m3u8_file.substr(pos - 14, 14); // /20151123114500/*.m3u8
					string sFileTime = file_time.to_string();

					time_t tFileEndTime = StringToTime(sFileTime.c_str()) SkeyeRecordSession::sRecordDuration*60;//s
					string sFileEndTime = TimeToString(tFileEndTime);
					boost::string_ref sFileTimeRef(sFileEndTime); 

					//cout << "file_time: " << file_time << " condition[" << begin << " - " << end << "]" <<endl;
					if( (file_time <= begin.c_str() && sFileEndTime>begin.c_str()) || (file_time >begin.c_str()&&file_time<end.c_str()) )
					{
						// 						if (sStartURL<=file_time)
						// 						{
						// 							sStartURL = file_time.to_string();
						// 						}	
						// 						string url = string(SkeyeRecordSession::sHTTPRootDir)   name_   split   sStartURL;
						// 						url = boost::algorithm::replace_all_copy(url, "\", "/");
						m3u8List->push_back(file);                          
					}
					else //file_time > end
					{
						//这部分完全超出范围
					}					
				}
			}
		}
		catch (const std::exception & ex)
		{
			std::cerr << ex.what() << std::endl;
			continue;
		}
	}

	//解析m3u8List->生成请求的list
	for(vector<string>::iterator it = m3u8List->begin(); it != m3u8List->end(); it  )
	{
		std::string record = *it;

		//打开指定的M3U8列表文件处理
		int flags=0;
		int pos = record.find_last_of(split);
		string file_time = record.substr(pos - 14, 14); // /20151123114500/*.m3u8
		time_t tFile_time = StringToTime(file_time);
		string file_time_folder = record.substr(0, pos 1);

		flags=O_BINARY|O_RDONLY;
		int file=::open(record.c_str(),flags,0644);
		if(file!=-1)//打开M3U8列表成功
		{
			//cout<<"open"<<record.c_str()<<"		success!!!!!!!!!!!!"<<endl;
			string sFileList;
			char sListContent[MAX_FileRead_Size 1];
			//读取m3u8列表文件
			while(file)
			{
				// 注意:这里定义必须MAX_SIZE 1,否则子串将因为没有结束符而出现乱码 [1/15/2017 dingshuai]
				memset(sListContent, 0x00, MAX_FileRead_Size 1);
				int m=::read(file,sListContent,MAX_FileRead_Size);
				if(m==-1 || !m)
					break;
				sFileList =sListContent;
			}
			::close(file);

			//删除空格
			sFileList = boost::algorithm::replace_all_copy(sFileList, " ", "");

			//新建m3u8列表文件
			int newStructM3u8ListFile = -1;
			string sNewFilePath = file_time_folder   name_   "_"   begin   "_"   end   ".m3u8";
			//解析m3u8列表
			int nEXTINFStart = 0;
			bool bEndOfFile = false;
			// time_t to double,maybe lost data??? [1/3/2017 dingshuai]
			double dbTSTimestamp = (double)tFile_time;
			while(!bEndOfFile&&!sFileList.empty())
			{
				if(nEXTINFStart == 0)
				{
					int nStartPos = sFileList.find("#EXTINF", nEXTINFStart);
					if(nStartPos < 0)
					{
						if(newStructM3u8ListFile != -1)
						{
							string sTails = "#EXT-X-ENDLISTn";
							::write(newStructM3u8ListFile, sTails.c_str(), sTails.length());
							::close(newStructM3u8ListFile);

						}
						break;	
					}
					nEXTINFStart = nStartPos;
				}
				//find the next
				int nEXTINFNext = sFileList.find("#EXTINF", nEXTINFStart 1);
				if (nEXTINFNext < 0)//没有找到#EXTINF,则指向文件末尾
				{
					nEXTINFNext = sFileList.find("#EXT-X-ENDLIST", nEXTINFStart 1);
					bEndOfFile = true;
				}
				//get ts file Duration and Name, like this: "10.000,03D5CF4120304456A7885E9063181A5B_0_0.ts"
				string sTSFile = sFileList.substr(nEXTINFStart, nEXTINFNext-nEXTINFStart);
				//cout<<"sTSFile="<<sTSFile.c_str()<<endl;

				nEXTINFStart = nEXTINFNext;

				int nDivPos = sTSFile.find(",", 0);
				//TS duration = 10.000
				string sTSDuration = sTSFile.substr(8,nDivPos);
				//TS name = 03D5CF4120304456A7885E9063181A5B_0_0.ts
				string sTSFileName = sTSFile.substr(nDivPos 1, (sTSFile.length()-nDivPos));
				double fTSDuratin = atof(sTSDuration.c_str());

				//cout<<"-sTSFileName="<<sTSFileName.c_str()<<"-fTSDuratin="<<fTSDuratin<<endl;

				double dbBegin = nBegin;
				double dbEnd = nEnd;


				//获取在查询时间范围内的TS列表
				// 				time_t nBegin = tTimeStamp - atoi(beginDif);
				// 				time_t nEnd	 = tTimeStamp   atoi(endDif);	
				if( (dbTSTimestamp <= dbBegin && (dbTSTimestamp fTSDuratin)>dbBegin) || (dbTSTimestamp > dbBegin && dbTSTimestamp < dbEnd) )
				{
					if(newStructM3u8ListFile == -1)
					{
						//以读写方式创建新的m3u8文件
						flags = O_CREAT|O_TRUNC|O_BINARY|O_WRONLY;
						newStructM3u8ListFile=::open(sNewFilePath.c_str(),flags,0644);
						//写入m3u8头部文件
						int nEXTINFFirst = sFileList.find("#EXTINF", 0);

						string sHeader = sFileList.substr(0, nEXTINFFirst);
						::write(newStructM3u8ListFile, sHeader.c_str(), sHeader.length());
					}

					//写入TS-TIME NAME
					::write(newStructM3u8ListFile, sTSFile.c_str(), sTSFile.length());

					if(bEndOfFile)//写入m3u8列表结尾
					{
						string sTails = "#EXT-X-ENDLISTn";
						::write(newStructM3u8ListFile, sTails.c_str(), sTails.length());
						::close(newStructM3u8ListFile);

					}
				}
				else if(dbTSTimestamp > nEnd)
				{
					if(newStructM3u8ListFile != -1)
					{
						string sTails = "#EXT-X-ENDLISTn";
						::write(newStructM3u8ListFile, sTails.c_str(), sTails.length());
						::close(newStructM3u8ListFile);

					}
					break;	
				}
				dbTSTimestamp  = fTSDuratin;
			}
			if(newStructM3u8ListFile != -1)//M3U8列表文件已创建
			{
				string newM3u8Path = sNewFilePath.c_str() strlen(SkeyeRecordSession::sLocalRecordPath);
				string url = string(SkeyeRecordSession::sHTTPRootDir)   newM3u8Path;
				url = boost::algorithm::replace_all_copy(url, "\", "/");				
				records->push_back(url);  
			}
		}
	}


	delete m3u8List;
	return !records->empty();
}

0 人点赞