diff --git a/CMakeLists.txt b/CMakeLists.txt index 44e03587..8816d398 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -452,6 +452,15 @@ elseif(NOT ANDROID OR IOS) update_cached_list(MK_LINK_LIBRARIES pthread) endif() +if(ENABLE_VIDEOSTACK) + if(ENABLE_FFMPEG AND ENABLE_X264) + message(STATUS "ENABLE_VIDEOSTACK defined") + update_cached_list(MK_COMPILE_DEFINITIONS ENABLE_VIDEOSTACK) + else() + message(WARNING "ENABLE_VIDEOSTACK requires ENABLE_FFMPEG and ENABLE_X264") + endif () +endif () + # ---------------------------------------------------------------------------- # Solution folders: # ---------------------------------------------------------------------------- diff --git a/server/VideoStack.cpp b/server/VideoStack.cpp index 21cb7ff6..8b5514f1 100644 --- a/server/VideoStack.cpp +++ b/server/VideoStack.cpp @@ -18,23 +18,15 @@ // ITU-R BT.709 #define RGB_TO_Y(R, G, B) (((47 * (R) + 157 * (G) + 16 * (B) + 128) >> 8) + 16) -#define RGB_TO_U(R, G, B) (((-26 * (R)-87 * (G) + 112 * (B) + 128) >> 8) + 128) -#define RGB_TO_V(R, G, B) (((112 * (R)-102 * (G)-10 * (B) + 128) >> 8) + 128) +#define RGB_TO_U(R, G, B) (((-26 * (R) - 87 * (G) + 112 * (B) + 128) >> 8) + 128) +#define RGB_TO_V(R, G, B) (((112 * (R) - 102 * (G) - 10 * (B) + 128) >> 8) + 128) INSTANCE_IMP(VideoStackManager) -Param::~Param() -{ - VideoStackManager::Instance().unrefChannel( - id, width, height, pixfmt); -} +Param::~Param() { VideoStackManager::Instance().unrefChannel(id, width, height, pixfmt); } Channel::Channel(const std::string& id, int width, int height, AVPixelFormat pixfmt) - : _id(id) - , _width(width) - , _height(height) - , _pixfmt(pixfmt) -{ + : _id(id), _width(width), _height(height), _pixfmt(pixfmt) { _tmp = std::make_shared(); _tmp->get()->width = _width; @@ -53,88 +45,72 @@ Channel::Channel(const std::string& id, int width, int height, AVPixelFormat pix _tmp = _sws->inputFrame(frame); } -void Channel::addParam(const std::weak_ptr& p) -{ +void Channel::addParam(const std::weak_ptr& p) { std::lock_guard lock(_mx); _params.push_back(p); } -void Channel::onFrame(const mediakit::FFmpegFrame::Ptr& frame) -{ +void Channel::onFrame(const mediakit::FFmpegFrame::Ptr& frame) { std::weak_ptr weakSelf = shared_from_this(); _poller = _poller ? _poller : toolkit::WorkThreadPool::Instance().getPoller(); _poller->async([weakSelf, frame]() { auto self = weakSelf.lock(); - if (!self) { - return; - } + if (!self) { return; } self->_tmp = self->_sws->inputFrame(frame); self->forEachParam([self](const Param::Ptr& p) { self->fillBuffer(p); }); }); } -void Channel::forEachParam(const std::function& func) -{ +void Channel::forEachParam(const std::function& func) { for (auto& wp : _params) { - if (auto sp = wp.lock()) { - func(sp); - } + if (auto sp = wp.lock()) { func(sp); } } } -void Channel::fillBuffer(const Param::Ptr& p) -{ - if (auto buf = p->weak_buf.lock()) { - copyData(buf, p); - } +void Channel::fillBuffer(const Param::Ptr& p) { + if (auto buf = p->weak_buf.lock()) { copyData(buf, p); } } -void Channel::copyData(const mediakit::FFmpegFrame::Ptr& buf, const Param::Ptr& p) -{ +void Channel::copyData(const mediakit::FFmpegFrame::Ptr& buf, const Param::Ptr& p) { switch (p->pixfmt) { - case AV_PIX_FMT_YUV420P: { - for (int i = 0; i < p->height; i++) { - memcpy(buf->get()->data[0] + buf->get()->linesize[0] * (i + p->posY) + p->posX, - _tmp->get()->data[0] + _tmp->get()->linesize[0] * i, - _tmp->get()->width); - } - //确保height为奇数时,也能正确的复制到最后一行uv数据 - for (int i = 0; i < (p->height + 1) / 2; i++) { - // U平面 - memcpy(buf->get()->data[1] + buf->get()->linesize[1] * (i + p->posY / 2) + p->posX / 2, - _tmp->get()->data[1] + _tmp->get()->linesize[1] * i, - _tmp->get()->width / 2); + case AV_PIX_FMT_YUV420P: { + for (int i = 0; i < p->height; i++) { + memcpy(buf->get()->data[0] + buf->get()->linesize[0] * (i + p->posY) + p->posX, + _tmp->get()->data[0] + _tmp->get()->linesize[0] * i, _tmp->get()->width); + } + // 确保height为奇数时,也能正确的复制到最后一行uv数据 + for (int i = 0; i < (p->height + 1) / 2; i++) { + // U平面 + memcpy(buf->get()->data[1] + buf->get()->linesize[1] * (i + p->posY / 2) + + p->posX / 2, + _tmp->get()->data[1] + _tmp->get()->linesize[1] * i, _tmp->get()->width / 2); - // V平面 - memcpy(buf->get()->data[2] + buf->get()->linesize[2] * (i + p->posY / 2) + p->posX / 2, - _tmp->get()->data[2] + _tmp->get()->linesize[2] * i, - _tmp->get()->width / 2); + // V平面 + memcpy(buf->get()->data[2] + buf->get()->linesize[2] * (i + p->posY / 2) + + p->posX / 2, + _tmp->get()->data[2] + _tmp->get()->linesize[2] * i, _tmp->get()->width / 2); + } + break; + } + case AV_PIX_FMT_NV12: { + // TODO: 待实现 + break; } - break; - } - case AV_PIX_FMT_NV12: { - //TODO: 待实现 - break; - } - default: - WarnL << "No support pixformat: " << av_get_pix_fmt_name(p->pixfmt); - break; + default: WarnL << "No support pixformat: " << av_get_pix_fmt_name(p->pixfmt); break; } } -void StackPlayer::addChannel(const std::weak_ptr& chn) -{ +void StackPlayer::addChannel(const std::weak_ptr& chn) { std::lock_guard lock(_mx); _channels.push_back(chn); } -void StackPlayer::play() -{ +void StackPlayer::play() { auto url = _url; - //创建拉流 解码对象 + // 创建拉流 解码对象 _player = std::make_shared(); std::weak_ptr weakPlayer = _player; @@ -146,13 +122,9 @@ void StackPlayer::play() _player->setOnPlayResult([weakPlayer, weakSelf, url](const toolkit::SockException& ex) mutable { TraceL << "StackPlayer: " << url << " OnPlayResult: " << ex.what(); auto strongPlayer = weakPlayer.lock(); - if (!strongPlayer) { - return; - } + if (!strongPlayer) { return; } auto self = weakSelf.lock(); - if (!self) { - return; - } + if (!self) { return; } if (!ex) { // 取消定时器 @@ -164,19 +136,18 @@ void StackPlayer::play() self->rePlay(url); } - auto videoTrack = std::dynamic_pointer_cast(strongPlayer->getTrack(mediakit::TrackVideo, false)); - //auto audioTrack = std::dynamic_pointer_cast(strongPlayer->getTrack(mediakit::TrackAudio, false)); + auto videoTrack = std::dynamic_pointer_cast( + strongPlayer->getTrack(mediakit::TrackVideo, false)); + // auto audioTrack = std::dynamic_pointer_cast(strongPlayer->getTrack(mediakit::TrackAudio, false)); if (videoTrack) { - //TODO:添加使用显卡还是cpu解码的判断逻辑 - //auto decoder = std::make_shared(videoTrack, 1, std::vector{ "hevc_cuvid", "h264_cuvid"}); - auto decoder = std::make_shared(videoTrack, 0, std::vector { "h264", "hevc" }); + // TODO:添加使用显卡还是cpu解码的判断逻辑 + auto decoder = std::make_shared( + videoTrack, 0, std::vector{"h264", "hevc"}); decoder->setOnDecode([weakSelf](const mediakit::FFmpegFrame::Ptr& frame) mutable { auto self = weakSelf.lock(); - if (!self) { - return; - } + if (!self) { return; } self->onFrame(frame); }); @@ -190,14 +161,10 @@ void StackPlayer::play() _player->setOnShutdown([weakPlayer, url, weakSelf](const toolkit::SockException& ex) { TraceL << "StackPlayer: " << url << " OnShutdown: " << ex.what(); auto strongPlayer = weakPlayer.lock(); - if (!strongPlayer) { - return; - } + if (!strongPlayer) { return; } auto self = weakSelf.lock(); - if (!self) { - return; - } + if (!self) { return; } self->onDisconnect(); @@ -207,18 +174,14 @@ void StackPlayer::play() _player->play(url); } -void StackPlayer::onFrame(const mediakit::FFmpegFrame::Ptr& frame) -{ +void StackPlayer::onFrame(const mediakit::FFmpegFrame::Ptr& frame) { std::lock_guard lock(_mx); for (auto& weak_chn : _channels) { - if (auto chn = weak_chn.lock()) { - chn->onFrame(frame); - } + if (auto chn = weak_chn.lock()) { chn->onFrame(frame); } } } -void StackPlayer::onDisconnect() -{ +void StackPlayer::onDisconnect() { std::lock_guard lock(_mx); for (auto& weak_chn : _channels) { if (auto chn = weak_chn.lock()) { @@ -228,31 +191,22 @@ void StackPlayer::onDisconnect() } } -void StackPlayer::rePlay(const std::string& url) -{ +void StackPlayer::rePlay(const std::string& url) { _failedCount++; - auto delay = MAX(2 * 1000, MIN(_failedCount * 3 * 1000, 60 * 1000)); //步进延迟 重试间隔 + auto delay = MAX(2 * 1000, MIN(_failedCount * 3 * 1000, 60 * 1000));// 步进延迟 重试间隔 std::weak_ptr weakSelf = shared_from_this(); - _timer = std::make_shared( - delay / 1000.0f, [weakSelf, url]() { - auto self = weakSelf.lock(); - if (!self) { - } - WarnL << "replay [" << self->_failedCount << "]:" << url; - self->_player->play(url); - return false; - }, - nullptr); + _timer = std::make_shared(delay / 1000.0f, [weakSelf, url]() { + auto self = weakSelf.lock(); + if (!self) {} + WarnL << "replay [" << self->_failedCount << "]:" << url; + self->_player->play(url); + return false; + }, nullptr); } -VideoStack::VideoStack(const std::string& id, int width, int height, AVPixelFormat pixfmt, float fps, int bitRate) - : _id(id) - , _width(width) - , _height(height) - , _pixfmt(pixfmt) - , _fps(fps) - , _bitRate(bitRate) -{ +VideoStack::VideoStack(const std::string& id, int width, int height, AVPixelFormat pixfmt, + float fps, int bitRate) + : _id(id), _width(width), _height(height), _pixfmt(pixfmt), _fps(fps), _bitRate(bitRate) { _buffer = std::make_shared(); @@ -262,7 +216,8 @@ VideoStack::VideoStack(const std::string& id, int width, int height, AVPixelForm av_frame_get_buffer(_buffer->get(), 32); - _dev = std::make_shared(mediakit::MediaTuple { DEFAULT_VHOST, "live", _id }); + _dev = std::make_shared( + mediakit::MediaTuple{DEFAULT_VHOST, "live", _id, ""}); mediakit::VideoInfo info; info.codecId = mediakit::CodecH264; @@ -272,34 +227,28 @@ VideoStack::VideoStack(const std::string& id, int width, int height, AVPixelForm info.iBitRate = _bitRate; _dev->initVideo(info); - //dev->initAudio(); //TODO:音频 + // dev->initAudio(); //TODO:音频 _dev->addTrackCompleted(); _isExit = false; } -VideoStack::~VideoStack() -{ +VideoStack::~VideoStack() { _isExit = true; - if (_thread.joinable()) { - _thread.join(); - } + if (_thread.joinable()) { _thread.join(); } } -void VideoStack::setParam(const Params& params) -{ +void VideoStack::setParam(const Params& params) { if (_params) { for (auto& p : (*_params)) { - if (!p) - continue; + if (!p) continue; p->weak_buf.reset(); } } initBgColor(); for (auto& p : (*params)) { - if (!p) - continue; + if (!p) continue; p->weak_buf = _buffer; if (auto chn = p->weak_chn.lock()) { chn->addParam(p); @@ -309,14 +258,14 @@ void VideoStack::setParam(const Params& params) _params = params; } -void VideoStack::start() -{ +void VideoStack::start() { _thread = std::thread([&]() { uint64_t pts = 0; int frameInterval = 1000 / _fps; auto lastEncTP = std::chrono::steady_clock::now(); while (!_isExit) { - if (std::chrono::steady_clock::now() - lastEncTP > std::chrono::milliseconds(frameInterval)) { + if (std::chrono::steady_clock::now() - lastEncTP > + std::chrono::milliseconds(frameInterval)) { lastEncTP = std::chrono::steady_clock::now(); _dev->inputYUV((char**)_buffer->get()->data, _buffer->get()->linesize, pts); @@ -326,9 +275,8 @@ void VideoStack::start() }); } -void VideoStack::initBgColor() -{ - //填充底色 +void VideoStack::initBgColor() { + // 填充底色 auto R = 20; auto G = 20; auto B = 20; @@ -342,27 +290,19 @@ void VideoStack::initBgColor() memset(_buffer->get()->data[2], V, _buffer->get()->linesize[2] * _height / 2); } -Channel::Ptr VideoStackManager::getChannel(const std::string& id, - int width, - int height, - AVPixelFormat pixfmt) -{ +Channel::Ptr VideoStackManager::getChannel(const std::string& id, int width, int height, + AVPixelFormat pixfmt) { std::lock_guard lock(_mx); auto key = id + std::to_string(width) + std::to_string(height) + std::to_string(pixfmt); auto it = _channelMap.find(key); - if (it != _channelMap.end()) { - return it->second->acquire(); - } + if (it != _channelMap.end()) { return it->second->acquire(); } return createChannel(id, width, height, pixfmt); } -void VideoStackManager::unrefChannel(const std::string& id, - int width, - int height, - AVPixelFormat pixfmt) -{ +void VideoStackManager::unrefChannel(const std::string& id, int width, int height, + AVPixelFormat pixfmt) { std::lock_guard lock(_mx); auto key = id + std::to_string(width) + std::to_string(height) + std::to_string(pixfmt); @@ -377,8 +317,7 @@ void VideoStackManager::unrefChannel(const std::string& id, } } -int VideoStackManager::startVideoStack(const Json::Value& json) -{ +int VideoStackManager::startVideoStack(const Json::Value& json) { std::string id; int width, height; @@ -392,8 +331,7 @@ int VideoStackManager::startVideoStack(const Json::Value& json) auto stack = std::make_shared(id, width, height); for (auto& p : (*params)) { - if (!p) - continue; + if (!p) continue; p->weak_chn = getChannel(p->id, p->width, p->height, p->pixfmt); } @@ -405,13 +343,13 @@ int VideoStackManager::startVideoStack(const Json::Value& json) return 0; } -int VideoStackManager::resetVideoStack(const Json::Value& json) -{ +int VideoStackManager::resetVideoStack(const Json::Value& json) { std::string id; int width, height; auto params = parseParams(json, id, width, height); if (!params) { + ErrorL << "Videostack parse params failed!"; return -1; } @@ -419,15 +357,12 @@ int VideoStackManager::resetVideoStack(const Json::Value& json) { std::lock_guard lock(_mx); auto it = _stackMap.find(id); - if (it == _stackMap.end()) { - return -2; - } + if (it == _stackMap.end()) { return -2; } stack = it->second; } for (auto& p : (*params)) { - if (!p) - continue; + if (!p) continue; p->weak_chn = getChannel(p->id, p->width, p->height, p->pixfmt); } @@ -435,8 +370,7 @@ int VideoStackManager::resetVideoStack(const Json::Value& json) return 0; } -int VideoStackManager::stopVideoStack(const std::string& id) -{ +int VideoStackManager::stopVideoStack(const std::string& id) { std::lock_guard lock(_mx); auto it = _stackMap.find(id); if (it != _stackMap.end()) { @@ -447,93 +381,90 @@ int VideoStackManager::stopVideoStack(const std::string& id) return -1; } -mediakit::FFmpegFrame::Ptr VideoStackManager::getBgImg() -{ - return _bgImg; -} +mediakit::FFmpegFrame::Ptr VideoStackManager::getBgImg() { return _bgImg; } -Params VideoStackManager::parseParams(const Json::Value& json, - std::string& id, - int& width, - int& height) -{ - try { - id = json["id"].asString(); - - width = json["width"].asInt(); - height = json["height"].asInt(); - - int rows = json["row"].asInt(); //堆叠行数 - int cols = json["col"].asInt(); //堆叠列数 - float gapv = json["gapv"].asFloat(); //垂直间距 - float gaph = json["gaph"].asFloat(); //水平间距 - - //单个间距 - int gaphPix = static_cast(round(width * gaph)); - int gapvPix = static_cast(round(height * gapv)); - - // 根据间距计算格子宽高 - int gridWidth = cols > 1 ? (width - gaphPix * (cols - 1)) / cols : width; - int gridHeight = rows > 1 ? (height - gapvPix * (rows - 1)) / rows : height; - - auto params = std::make_shared>(rows * cols); - - for (int row = 0; row < rows; row++) { - for (int col = 0; col < cols; col++) { - std::string url = json["url"][row][col].asString(); - - auto param = std::make_shared(); - param->posX = gridWidth * col + col * gaphPix; - param->posY = gridHeight * row + row * gapvPix; - param->width = gridWidth; - param->height = gridHeight; - param->id = url; - - (*params)[row * cols + col] = param; - } - } - - //判断是否需要合并格子 (焦点屏) - if (!json["span"].empty() && json.isMember("span")) { - for (const auto& subArray : json["span"]) { - if (!subArray.isArray() || subArray.size() != 2) { - throw Json::LogicError("Incorrect 'span' sub-array format in JSON"); - } - std::array mergePos; - int index = 0; - - for (const auto& innerArray : subArray) { - if (!innerArray.isArray() || innerArray.size() != 2) { - throw Json::LogicError("Incorrect 'span' inner-array format in JSON"); - } - for (const auto& number : innerArray) { - if (index < mergePos.size()) { - mergePos[index++] = number.asInt(); - } - } - } - - for (int i = mergePos[0]; i <= mergePos[2]; i++) { - for (int j = mergePos[1]; j <= mergePos[3]; j++) { - if (i == mergePos[0] && j == mergePos[1]) { - (*params)[i * cols + j]->width = (mergePos[3] - mergePos[1] + 1) * gridWidth + (mergePos[3] - mergePos[1]) * gapvPix; - (*params)[i * cols + j]->height = (mergePos[2] - mergePos[0] + 1) * gridHeight + (mergePos[2] - mergePos[0]) * gaphPix; - } else { - (*params)[i * cols + j] = nullptr; - } - } - } - } - } - return params; - } catch (const std::exception& e) { - ErrorL << "Videostack parse params failed! " << e.what(); - return nullptr; +template T getJsonValue(const Json::Value& json, const std::string& key) { + if (!json.isMember(key)) { + throw Json::LogicError("VideoStack parseParams missing required field: " + key); } + return json[key].as(); } -bool VideoStackManager::loadBgImg(const std::string& path) -{ +Params VideoStackManager::parseParams(const Json::Value& json, std::string& id, int& width, + int& height) { + + id = getJsonValue(json, "id"); + width = getJsonValue(json, "width"); + height = getJsonValue(json, "height"); + int rows = getJsonValue(json, "row");// 行数 + int cols = getJsonValue(json, "col");// 列数 + + float gapv = json["gapv"].asFloat();// 垂直间距 + float gaph = json["gaph"].asFloat();// 水平间距 + + // 单个间距 + int gaphPix = static_cast(round(width * gaph)); + int gapvPix = static_cast(round(height * gapv)); + + // 根据间距计算格子宽高 + int gridWidth = cols > 1 ? (width - gaphPix * (cols - 1)) / cols : width; + int gridHeight = rows > 1 ? (height - gapvPix * (rows - 1)) / rows : height; + + auto params = std::make_shared>(rows * cols); + + for (int row = 0; row < rows; row++) { + for (int col = 0; col < cols; col++) { + std::string url = json["url"][row][col].asString(); + + auto param = std::make_shared(); + param->posX = gridWidth * col + col * gaphPix; + param->posY = gridHeight * row + row * gapvPix; + param->width = gridWidth; + param->height = gridHeight; + param->id = url; + + (*params)[row * cols + col] = param; + } + } + + // 判断是否需要合并格子 (焦点屏) + if (json.isMember("span") && json["span"].isArray() && json["span"].size() > 0) { + for (const auto& subArray : json["span"]) { + if (!subArray.isArray() || subArray.size() != 2) { + throw Json::LogicError("Incorrect 'span' sub-array format in JSON"); + } + std::array mergePos; + unsigned int index = 0; + + for (const auto& innerArray : subArray) { + if (!innerArray.isArray() || innerArray.size() != 2) { + throw Json::LogicError("Incorrect 'span' inner-array format in JSON"); + } + for (const auto& number : innerArray) { + if (index < mergePos.size()) { mergePos[index++] = number.asInt(); } + } + } + + for (int i = mergePos[0]; i <= mergePos[2]; i++) { + for (int j = mergePos[1]; j <= mergePos[3]; j++) { + if (i == mergePos[0] && j == mergePos[1]) { + (*params)[i * cols + j]->width = + (mergePos[3] - mergePos[1] + 1) * gridWidth + + (mergePos[3] - mergePos[1]) * gapvPix; + (*params)[i * cols + j]->height = + (mergePos[2] - mergePos[0] + 1) * gridHeight + + (mergePos[2] - mergePos[0]) * gaphPix; + } else { + (*params)[i * cols + j] = nullptr; + } + } + } + } + } + return params; +} + +bool VideoStackManager::loadBgImg(const std::string& path) { _bgImg = std::make_shared(); _bgImg->get()->width = 1280; @@ -543,21 +474,21 @@ bool VideoStackManager::loadBgImg(const std::string& path) av_frame_get_buffer(_bgImg->get(), 32); std::ifstream file(path, std::ios::binary); - if (!file.is_open()) { - return false; - } + if (!file.is_open()) { return false; } - file.read((char*)_bgImg->get()->data[0], _bgImg->get()->linesize[0] * _bgImg->get()->height); // Y - file.read((char*)_bgImg->get()->data[1], _bgImg->get()->linesize[1] * _bgImg->get()->height / 2); // U - file.read((char*)_bgImg->get()->data[2], _bgImg->get()->linesize[2] * _bgImg->get()->height / 2); // V + file.read((char*)_bgImg->get()->data[0], + _bgImg->get()->linesize[0] * _bgImg->get()->height);// Y + file.read((char*)_bgImg->get()->data[1], + _bgImg->get()->linesize[1] * _bgImg->get()->height / 2);// U + file.read((char*)_bgImg->get()->data[2], + _bgImg->get()->linesize[2] * _bgImg->get()->height / 2);// V return true; } -Channel::Ptr VideoStackManager::createChannel(const std::string& id, - int width, - int height, - AVPixelFormat pixfmt) -{ +void VideoStackManager::clear() { _stackMap.clear(); } + +Channel::Ptr VideoStackManager::createChannel(const std::string& id, int width, int height, + AVPixelFormat pixfmt) { std::lock_guard lock(_mx); StackPlayer::Ptr player; @@ -568,24 +499,24 @@ Channel::Ptr VideoStackManager::createChannel(const std::string& id, player = createPlayer(id); } - auto refChn = std::make_shared>(std::make_shared(id, width, height, pixfmt)); + auto refChn = std::make_shared>( + std::make_shared(id, width, height, pixfmt)); auto chn = refChn->acquire(); player->addChannel(chn); - _channelMap[id + std::to_string(width) + std::to_string(height) + std::to_string(pixfmt)] = refChn; + _channelMap[id + std::to_string(width) + std::to_string(height) + std::to_string(pixfmt)] = + refChn; return chn; } -StackPlayer::Ptr VideoStackManager::createPlayer(const std::string& id) -{ +StackPlayer::Ptr VideoStackManager::createPlayer(const std::string& id) { std::lock_guard lock(_mx); - auto refPlayer = std::make_shared>(std::make_shared(id)); + auto refPlayer = + std::make_shared>(std::make_shared(id)); _playerMap[id] = refPlayer; auto player = refPlayer->acquire(); - if (!id.empty()) { - player->play(); - } + if (!id.empty()) { player->play(); } return player; } diff --git a/server/VideoStack.h b/server/VideoStack.h index cc0bfaa8..609060a7 100644 --- a/server/VideoStack.h +++ b/server/VideoStack.h @@ -5,29 +5,23 @@ #include "Player/MediaPlayer.h" #include "json/json.h" #include -template -class RefWrapper { - public: +template class RefWrapper { +public: using Ptr = std::shared_ptr>; - template - explicit RefWrapper(Args&&... args) - : _rc(0) - , _entity(std::forward(args)...) - { - } + template + explicit RefWrapper(Args&&... args) : _rc(0), _entity(std::forward(args)...) {} - T acquire() - { + T acquire() { ++_rc; return _entity; } bool dispose() { return --_rc <= 0; } - private: - T _entity; +private: std::atomic _rc; + T _entity; }; class Channel; @@ -40,7 +34,7 @@ struct Param { int width = 0; int height = 0; AVPixelFormat pixfmt = AV_PIX_FMT_YUV420P; - std::string id {}; + std::string id{}; // runtime std::weak_ptr weak_chn; @@ -52,7 +46,7 @@ struct Param { using Params = std::shared_ptr>; class Channel : public std::enable_shared_from_this { - public: +public: using Ptr = std::shared_ptr; Channel(const std::string& id, int width, int height, AVPixelFormat pixfmt); @@ -63,12 +57,12 @@ class Channel : public std::enable_shared_from_this { void fillBuffer(const Param::Ptr& p); - protected: +protected: void forEachParam(const std::function& func); void copyData(const mediakit::FFmpegFrame::Ptr& buf, const Param::Ptr& p); - private: +private: std::string _id; int _width; int _height; @@ -84,13 +78,10 @@ class Channel : public std::enable_shared_from_this { }; class StackPlayer : public std::enable_shared_from_this { - public: +public: using Ptr = std::shared_ptr; - StackPlayer(const std::string& url) - : _url(url) - { - } + StackPlayer(const std::string& url) : _url(url) {} void addChannel(const std::weak_ptr& chn); @@ -100,14 +91,14 @@ class StackPlayer : public std::enable_shared_from_this { void onDisconnect(); - protected: +protected: void rePlay(const std::string& url); - private: +private: std::string _url; mediakit::MediaPlayer::Ptr _player; - //用于断线重连 + // 用于断线重连 toolkit::Timer::Ptr _timer; int _failedCount = 0; @@ -116,15 +107,12 @@ class StackPlayer : public std::enable_shared_from_this { }; class VideoStack { - public: +public: using Ptr = std::shared_ptr; - VideoStack(const std::string& url, - int width = 1920, - int height = 1080, - AVPixelFormat pixfmt = AV_PIX_FMT_YUV420P, - float fps = 25.0, - int bitRate = 2 * 1024 * 1024); + VideoStack(const std::string& url, int width = 1920, int height = 1080, + AVPixelFormat pixfmt = AV_PIX_FMT_YUV420P, float fps = 25.0, + int bitRate = 2 * 1024 * 1024); ~VideoStack(); @@ -132,15 +120,15 @@ class VideoStack { void start(); - protected: +protected: void initBgColor(); - public: +public: Params _params; mediakit::FFmpegFrame::Ptr _buffer; - private: +private: std::string _id; int _width; int _height; @@ -156,53 +144,47 @@ class VideoStack { }; class VideoStackManager { - public: - static VideoStackManager& Instance(); - - Channel::Ptr getChannel(const std::string& id, - int width, - int height, - AVPixelFormat pixfmt); - - void unrefChannel(const std::string& id, - int width, - int height, - AVPixelFormat pixfmt); - +public: + // 创建拼接流 int startVideoStack(const Json::Value& json); + // 停止拼接流 + int stopVideoStack(const std::string& id); + + // 可以在不断流的情况下,修改拼接流的配置(实现切换拼接屏内容) int resetVideoStack(const Json::Value& json); - int stopVideoStack(const std::string& id); +public: + static VideoStackManager& Instance(); + + Channel::Ptr getChannel(const std::string& id, int width, int height, AVPixelFormat pixfmt); + + void unrefChannel(const std::string& id, int width, int height, AVPixelFormat pixfmt); bool loadBgImg(const std::string& path); + void clear(); + mediakit::FFmpegFrame::Ptr getBgImg(); - protected: - Params parseParams(const Json::Value& json, - std::string& id, - int& width, - int& height); +protected: + Params parseParams(const Json::Value& json, std::string& id, int& width, int& height); - protected: - Channel::Ptr createChannel(const std::string& id, - int width, - int height, - AVPixelFormat pixfmt); +protected: + Channel::Ptr createChannel(const std::string& id, int width, int height, AVPixelFormat pixfmt); StackPlayer::Ptr createPlayer(const std::string& id); - private: +private: mediakit::FFmpegFrame::Ptr _bgImg; - private: +private: std::recursive_mutex _mx; std::unordered_map _stackMap; std::unordered_map::Ptr> _channelMap; - + std::unordered_map::Ptr> _playerMap; }; #endif \ No newline at end of file diff --git a/server/WebApi.cpp b/server/WebApi.cpp index ba027ac4..5fbe4e19 100755 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -8,6 +8,7 @@ * may be found in the AUTHORS file in the root of the source tree. */ +#include #include #include #include @@ -1950,9 +1951,29 @@ void installWebApi() { api_regist("/index/api/stack/start", [](API_ARGS_JSON_ASYNC) { CHECK_SECRET(); - auto ret = VideoStackManager::Instance().startVideoStack(allArgs.args); - val["code"] = ret; - val["msg"] = ret ? "failed" : "success"; + int ret = 0; + try { + ret = VideoStackManager::Instance().startVideoStack(allArgs.args); + val["code"] = ret; + val["msg"] = ret ? "failed" : "success"; + } catch (const std::exception &e) { + val["code"] = -1; + val["msg"] = e.what(); + } + invoker(200, headerOut, val.toStyledString()); + }); + + api_regist("/index/api/stack/reset", [](API_ARGS_JSON_ASYNC) { + CHECK_SECRET(); + int ret = 0; + try { + auto ret = VideoStackManager::Instance().resetVideoStack(allArgs.args); + val["code"] = ret; + val["msg"] = ret ? "failed" : "success"; + } catch (const std::exception &e) { + val["code"] = -1; + val["msg"] = e.what(); + } invoker(200, headerOut, val.toStyledString()); }); @@ -1974,6 +1995,9 @@ void unInstallWebApi(){ #if defined(ENABLE_RTPPROXY) s_rtp_server.clear(); #endif +#if defined(ENABLE_VIDEOSTACK) && defined(ENABLE_FFMPEG) && defined(ENABLE_X264) + VideoStackManager::Instance().clear(); +#endif NoticeCenter::Instance().delListener(&web_api_tag); }