diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 5d9d0a05..96fd7481 100755 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -42,6 +42,12 @@ #include "../webrtc/WebRtcPusher.h" #include "../webrtc/WebRtcEchoTest.h" #endif +#ifdef _WIN32 +#include +#include +#include +#endif // _WIN32 + using namespace toolkit; using namespace mediakit; @@ -508,6 +514,59 @@ void installWebApi() { }); val["msg"] = "服务器将在一秒后自动重启"; }); +#else + //增加Windows下的重启代码 + api_regist("/index/api/restartServer", [](API_ARGS_MAP) { + CHECK_SECRET(); + //创建重启批处理脚本文件 + FILE *pf; + errno_t err = ::_wfopen_s(&pf, L"RestartServer.cmd", L"w"); //“w”如果该文件存在,其内容将被覆盖 + if (err == 0) { + char szExeName[1024]; + char drive[_MAX_DRIVE] = { 0 }; + char dir[_MAX_DIR] = { 0 }; + char fname[_MAX_FNAME] = { 0 }; + char ext[_MAX_EXT] = { 0 }; + char exeName[_MAX_FNAME] = { 0 }; + GetModuleFileNameA(NULL, szExeName, 1024); //获取进程的全路径 + _splitpath(szExeName, drive, dir, fname, ext); + strcpy(exeName, fname); + strcat(exeName, ext); + fprintf(pf, "@echo off\ntaskkill /f /im %s\nstart \"\" \"%s\"\ndel %%0", exeName, szExeName); + fclose(pf); + // 1秒后执行创建的批处理脚本 + EventPollerPool::Instance().getPoller()->doDelayTask(1000, []() { + STARTUPINFO si; + PROCESS_INFORMATION pi; + ZeroMemory(&si, sizeof si); + ZeroMemory(&pi, sizeof pi); + si.cb = sizeof si; + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + TCHAR winSysDir[1024]; + ZeroMemory(winSysDir, sizeof winSysDir); + GetSystemDirectory(winSysDir, 1024); + TCHAR appName[1024]; + ZeroMemory(appName, sizeof appName); + + _stprintf(appName, "%s\\cmd.exe", winSysDir); + BOOL bRet = CreateProcess(appName, " /c RestartServer.cmd", NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + + if (bRet == FALSE) { + int err = GetLastError(); + cout << endl << "无法执行重启操作,错误代码:" << err << endl; + } + WaitForSingleObject(pi.hProcess, INFINITE); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return 0; + }); + val["msg"] = "服务器将在一秒后自动重启"; + } else { + val["msg"] = "创建重启脚本文件失败"; + val["code"] = API::OtherFailed; + } + }); #endif//#if !defined(_WIN32) //获取流列表,可选筛选参数 diff --git a/src/Common/MediaSink.h b/src/Common/MediaSink.h index 2714cafe..1d29b038 100644 --- a/src/Common/MediaSink.h +++ b/src/Common/MediaSink.h @@ -107,7 +107,14 @@ public: * @param trackReady 是否获取已经准备好的Track */ vector getTracks(bool trackReady = true) const override; - + + /** + * 返回是否所有track已经准备完成 + */ + bool isAllTrackReady() const { + return _all_track_ready; + } + /** * 添加aac静音轨道 */ diff --git a/src/Http/HlsPlayer.cpp b/src/Http/HlsPlayer.cpp index 29140bfe..57dfe8a5 100644 --- a/src/Http/HlsPlayer.cpp +++ b/src/Http/HlsPlayer.cpp @@ -324,6 +324,12 @@ void HlsDemuxer::start(const EventPoller::Ptr &poller, TrackListener *listener) } bool HlsDemuxer::inputFrame(const Frame::Ptr &frame) { + //为了避免track准备时间过长, 因此在没准备好之前, 直接消费掉所有的帧 + if (!_delegate.isAllTrackReady()) { + _delegate.inputFrame(frame); + return true; + } + //计算相对时间戳 int64_t dts, pts; _stamp[frame->getTrackType()].revise(frame->dts(), frame->pts(), dts, pts); diff --git a/src/Http/HttpClient.cpp b/src/Http/HttpClient.cpp index 69079a12..14aaba46 100644 --- a/src/Http/HttpClient.cpp +++ b/src/Http/HttpClient.cpp @@ -9,6 +9,7 @@ */ #include +#include "Util/base64.h" #include "HttpClient.h" #include "Common/config.h" @@ -39,6 +40,13 @@ void HttpClient::sendRequest(const string &strUrl, float fTimeOutSec) { if (_path.empty()) { _path = "/"; } + auto pos = host.find('@'); + if (pos != string::npos) { + //去除?后面的字符串 + auto authStr = host.substr(0, pos); + host = host.substr(pos + 1, host.size()); + _header.emplace("Authorization", "Basic " + encodeBase64(authStr)); + } auto host_header = host; uint16_t port = atoi(FindField(host.data(), ":", NULL).data()); if (port <= 0) { @@ -328,4 +336,4 @@ void HttpClient::checkCookie(HttpClient::HttpHeader &headers) { } } -} /* namespace mediakit */ \ No newline at end of file +} /* namespace mediakit */ diff --git a/src/Record/MP4Reader.cpp b/src/Record/MP4Reader.cpp index a69ab28f..38d33a43 100644 --- a/src/Record/MP4Reader.cpp +++ b/src/Record/MP4Reader.cpp @@ -9,14 +9,16 @@ */ #ifdef ENABLE_MP4 + #include "MP4Reader.h" #include "Common/config.h" #include "Thread/WorkThreadPool.h" + using namespace toolkit; + namespace mediakit { MP4Reader::MP4Reader(const string &vhost, const string &app, const string &stream_id, const string &file_path) { - _poller = WorkThreadPool::Instance().getPoller(); _file_path = file_path; if (_file_path.empty()) { GET_CONFIG(string, recordPath, Record::kFilePath); @@ -37,12 +39,12 @@ MP4Reader::MP4Reader(const string &vhost, const string &app, const string &strea } _muxer = std::make_shared(vhost, app, stream_id, _demuxer->getDurationMS() / 1000.0f, true, true, false, false); auto tracks = _demuxer->getTracks(false); - if(tracks.empty()){ + if (tracks.empty()) { throw std::runtime_error(StrPrinter << "该mp4文件没有有效的track:" << _file_path); } - for(auto &track : tracks){ + for (auto &track : tracks) { _muxer->addTrack(track); - if(track->getTrackType() == TrackVideo){ + if (track->getTrackType() == TrackVideo) { _have_video = true; } } @@ -70,8 +72,8 @@ bool MP4Reader::readSample() { } } - GET_CONFIG(bool, fileRepeat, Record::kFileRepeat); - if (eof && (fileRepeat || _file_repeat)) { + GET_CONFIG(bool, file_repeat, Record::kFileRepeat); + if (eof && (file_repeat || _file_repeat)) { //需要从头开始看 seekTo(0); return true; @@ -80,37 +82,53 @@ bool MP4Reader::readSample() { return !eof; } +bool MP4Reader::readNextSample() { + bool keyFrame = false; + bool eof = false; + auto frame = _demuxer->readFrame(keyFrame, eof); + if (!frame) { + return false; + } + if (_muxer) { + _muxer->inputFrame(frame); + } + setCurrentStamp(frame->dts()); + return true; +} + void MP4Reader::stopReadMP4() { _timer = nullptr; } -void MP4Reader::startReadMP4(const EventPoller::Ptr &poller, uint64_t sample_ms, bool ref_self, bool file_repeat) { +void MP4Reader::startReadMP4(const EventPoller::Ptr &poller_in, uint64_t sample_ms, bool ref_self, bool file_repeat) { GET_CONFIG(uint32_t, sampleMS, Record::kSampleMS); - auto strongSelf = shared_from_this(); - if (_muxer) { _muxer->setMediaListener(strongSelf); } + auto strong_self = shared_from_this(); + if (_muxer) { + _muxer->setMediaListener(strong_self); + //一直读到所有track就绪为止 + while (!_muxer->isAllTrackReady() && readNextSample()) {} + } - //先获取关键帧 - seekTo(0); - //读sampleMS毫秒的数据用于产生MediaSource - setCurrentStamp(getCurrentStamp() + sampleMS); - readSample(); + //未指定线程,那么使用后台线程(读写文件采用后台线程) + auto poller = poller_in ? poller_in : WorkThreadPool::Instance().getPoller(); + auto timer_sec = (sample_ms ? sample_ms : sampleMS) / 1000.0f; //启动定时器 if (ref_self) { - _timer = std::make_shared((sample_ms ? sample_ms : sampleMS) / 1000.0f, [strongSelf]() { - lock_guard lck(strongSelf->_mtx); - return strongSelf->readSample(); - }, poller ? poller : _poller); + _timer = std::make_shared(timer_sec, [strong_self]() { + lock_guard lck(strong_self->_mtx); + return strong_self->readSample(); + }, poller); } else { - weak_ptr weak_self = strongSelf; - _timer = std::make_shared((sample_ms ? sample_ms : sampleMS) / 1000.0f, [weak_self]() { - auto strongSelf = weak_self.lock(); - if (!strongSelf) { + weak_ptr weak_self = strong_self; + _timer = std::make_shared(timer_sec, [weak_self]() { + auto strong_self = weak_self.lock(); + if (!strong_self) { return false; } - lock_guard lck(strongSelf->_mtx); - return strongSelf->readSample(); - }, poller ? poller : _poller); + lock_guard lck(strong_self->_mtx); + return strong_self->readSample(); + }, poller); } _file_repeat = file_repeat; @@ -121,10 +139,10 @@ const MP4Demuxer::Ptr &MP4Reader::getDemuxer() const { } uint32_t MP4Reader::getCurrentStamp() { - return (uint32_t)(_seek_to + !_paused * _speed * _seek_ticker.elapsedTime()); + return (uint32_t) (_seek_to + !_paused * _speed * _seek_ticker.elapsedTime()); } -void MP4Reader::setCurrentStamp(uint32_t new_stamp){ +void MP4Reader::setCurrentStamp(uint32_t new_stamp) { auto old_stamp = getCurrentStamp(); _seek_to = new_stamp; _last_dts = new_stamp; @@ -168,22 +186,21 @@ bool MP4Reader::speed(MediaSource &sender, float speed) { return true; } -bool MP4Reader::seekTo(uint32_t ui32Stamp) { +bool MP4Reader::seekTo(uint32_t stamp_seek) { lock_guard lck(_mtx); - if (ui32Stamp > _demuxer->getDurationMS()) { + if (stamp_seek > _demuxer->getDurationMS()) { //超过文件长度 return false; } - auto stamp = _demuxer->seekTo(ui32Stamp); - if(stamp == -1){ + auto stamp = _demuxer->seekTo(stamp_seek); + if (stamp == -1) { //seek失败 return false; } - if(!_have_video){ - //没有视频,不需要搜索关键帧 - //设置当前时间戳 - setCurrentStamp((uint32_t)stamp); + if (!_have_video) { + //没有视频,不需要搜索关键帧;设置当前时间戳 + setCurrentStamp((uint32_t) stamp); return true; } //搜索到下一帧关键帧 @@ -191,11 +208,11 @@ bool MP4Reader::seekTo(uint32_t ui32Stamp) { bool eof = false; while (!eof) { auto frame = _demuxer->readFrame(keyFrame, eof); - if(!frame){ + if (!frame) { //文件读完了都未找到下一帧关键帧 continue; } - if(keyFrame || frame->keyFrame() || frame->configFrame()){ + if (keyFrame || frame->keyFrame() || frame->configFrame()) { //定位到key帧 if (_muxer) { _muxer->inputFrame(frame); @@ -208,8 +225,8 @@ bool MP4Reader::seekTo(uint32_t ui32Stamp) { return false; } -bool MP4Reader::close(MediaSource &sender,bool force){ - if(!_muxer || (!force && _muxer->totalReaderCount())){ +bool MP4Reader::close(MediaSource &sender, bool force) { + if (!_muxer || (!force && _muxer->totalReaderCount())) { return false; } _timer.reset(); diff --git a/src/Record/MP4Reader.h b/src/Record/MP4Reader.h index 29337235..fa055387 100644 --- a/src/Record/MP4Reader.h +++ b/src/Record/MP4Reader.h @@ -16,27 +16,37 @@ using namespace toolkit; namespace mediakit { -class MP4Reader : public std::enable_shared_from_this ,public MediaSourceEvent{ +class MP4Reader : public std::enable_shared_from_this, public MediaSourceEvent { public: - typedef std::shared_ptr Ptr; - virtual ~MP4Reader() = default; + using Ptr = std::shared_ptr; /** - * 流化一个mp4文件,使之转换成RtspMediaSource和RtmpMediaSource + * 点播一个mp4文件,使之转换成MediaSource流媒体 * @param vhost 虚拟主机 * @param app 应用名 - * @param stream_id 流id + * @param stream_id 流id,置空时,只解复用mp4,但是不生成MediaSource * @param file_path 文件路径,如果为空则根据配置文件和上面参数自动生成,否则使用指定的文件 */ MP4Reader(const string &vhost, const string &app, const string &stream_id, const string &file_path = ""); + ~MP4Reader() override = default; /** - * 开始流化MP4文件,需要指出的是,MP4Reader对象一经过调用startReadMP4方法,它的强引用会自持有, - * 意思是在文件流化结束之前或中断之前,MP4Reader对象是不会被销毁的(不管有没有被外部对象持有) + * 开始解复用MP4文件 + * @param poller 解复用mp4定时器所绑定线程,置空则随机采用一条后台线程 + * @param sample_ms 每次读取文件数据量,单位毫秒,置0时采用配置文件配置 + * @param ref_self 是否让定时器引用此对象本身,如果无其他对象引用本身,在不循环读文件时,读取文件结束后本对象将自动销毁 + * @param file_repeat 是否循环读取文件,如果配置文件设置为循环读文件,此参数无效 */ void startReadMP4(const EventPoller::Ptr &poller = nullptr, uint64_t sample_ms = 0, bool ref_self = true, bool file_repeat = false); + + /** + * 停止解复用MP4定时器 + */ void stopReadMP4(); + /** + * 获取mp4解复用器 + */ const MP4Demuxer::Ptr& getDemuxer() const; private: @@ -51,9 +61,10 @@ private: string getOriginUrl(MediaSource &sender) const override; bool readSample(); + bool readNextSample(); uint32_t getCurrentStamp(); - void setCurrentStamp(uint32_t ui32Stamp); - bool seekTo(uint32_t ui32Stamp); + void setCurrentStamp(uint32_t stamp); + bool seekTo(uint32_t stamp_seek); private: bool _file_repeat = false; @@ -61,12 +72,11 @@ private: bool _paused = false; float _speed = 1.0; uint32_t _last_dts = 0; - uint32_t _seek_to; + uint32_t _seek_to = 0; string _file_path; recursive_mutex _mtx; Ticker _seek_ticker; Timer::Ptr _timer; - EventPoller::Ptr _poller; MP4Demuxer::Ptr _demuxer; MultiMediaSourceMuxer::Ptr _muxer; };