前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SkeyeRMS录像服务器如何实现精确查找_ HLS+M3U8录像回放

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

原创
作者头像
Openskeye
发布2023-04-21 10:18:54
5370
发布2023-04-21 10:18:54
举报

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-ENDLIST\n";
							::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-ENDLIST\n";
						::write(newStructM3u8ListFile, sTails.c_str(), sTails.length());
						::close(newStructM3u8ListFile);

					}
				}
				else if(dbTSTimestamp > nEnd)
				{
					if(newStructM3u8ListFile != -1)
					{
						string sTails = "#EXT-X-ENDLIST\n";
						::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();
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档