SkeyeRMS作为面向云端的录播服务器,目前正处于开发阶段,以往的SkeyeRMS查找通常只能精确到M3U8列表,而不能实现精确到时间点的查询,为了能实现精确到点的查询,我将M3U8列表从新遍历一遍,重新生成一个精确到在关键位置开始和结束的切片文件(ts)的M3U8列表,然后返回,我们先不讨论这个方法效率如何,但是确实可以将精确度提高到切片文件单位级。
精确查找流程如下:
- 遍历本地(数据库)指定录像存储路径,找出在指定开始时间和结束时间范围内的M3U8列表,注意:这里找到的M3U8列表的区间是要小于开始时间,和大于结束时间的最接近值,以确保指定时间范围在查找到的M3U8列表队列中;
- 再在指定的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();
}