diff --git a/server/VideoStack.cpp b/server/VideoStack.cpp index 1b420480..8b5514f1 100644 --- a/server/VideoStack.cpp +++ b/server/VideoStack.cpp @@ -18,509 +18,506 @@ // 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); } Channel::Channel(const std::string& id, int width, int height, AVPixelFormat pixfmt) - : _id(id), _width(width), _height(height), _pixfmt(pixfmt) { - _tmp = std::make_shared(); + : _id(id), _width(width), _height(height), _pixfmt(pixfmt) { + _tmp = std::make_shared(); - _tmp->get()->width = _width; - _tmp->get()->height = _height; - _tmp->get()->format = _pixfmt; + _tmp->get()->width = _width; + _tmp->get()->height = _height; + _tmp->get()->format = _pixfmt; - av_frame_get_buffer(_tmp->get(), 32); + av_frame_get_buffer(_tmp->get(), 32); - memset(_tmp->get()->data[0], 0, _tmp->get()->linesize[0] * _height); - memset(_tmp->get()->data[1], 0, _tmp->get()->linesize[1] * _height / 2); - memset(_tmp->get()->data[2], 0, _tmp->get()->linesize[2] * _height / 2); + memset(_tmp->get()->data[0], 0, _tmp->get()->linesize[0] * _height); + memset(_tmp->get()->data[1], 0, _tmp->get()->linesize[1] * _height / 2); + memset(_tmp->get()->data[2], 0, _tmp->get()->linesize[2] * _height / 2); - auto frame = VideoStackManager::Instance().getBgImg(); - _sws = std::make_shared(_pixfmt, _width, _height); + auto frame = VideoStackManager::Instance().getBgImg(); + _sws = std::make_shared(_pixfmt, _width, _height); - _tmp = _sws->inputFrame(frame); + _tmp = _sws->inputFrame(frame); } void Channel::addParam(const std::weak_ptr& p) { - std::lock_guard lock(_mx); - _params.push_back(p); + std::lock_guard lock(_mx); + _params.push_back(p); } 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; } - self->_tmp = self->_sws->inputFrame(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; } + self->_tmp = self->_sws->inputFrame(frame); - self->forEachParam([self](const Param::Ptr& p) { self->fillBuffer(p); }); - }); + self->forEachParam([self](const Param::Ptr& p) { self->fillBuffer(p); }); + }); } void Channel::forEachParam(const std::function& func) { - for (auto& wp : _params) { - if (auto sp = wp.lock()) { func(sp); } - } + for (auto& wp : _params) { + if (auto sp = wp.lock()) { func(sp); } + } } void Channel::fillBuffer(const Param::Ptr& p) { - if (auto buf = p->weak_buf.lock()) { copyData(buf, p); } + if (auto buf = p->weak_buf.lock()) { copyData(buf, 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); + 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); - // 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; - } + // 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; + } - 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) { - std::lock_guard lock(_mx); - _channels.push_back(chn); + std::lock_guard lock(_mx); + _channels.push_back(chn); } void StackPlayer::play() { - auto url = _url; - //创建拉流 解码对象 - _player = std::make_shared(); - std::weak_ptr weakPlayer = _player; + auto url = _url; + // 创建拉流 解码对象 + _player = std::make_shared(); + std::weak_ptr weakPlayer = _player; - std::weak_ptr weakSelf = shared_from_this(); + std::weak_ptr weakSelf = shared_from_this(); - (*_player)[mediakit::Client::kWaitTrackReady] = false; - (*_player)[mediakit::Client::kRtpType] = mediakit::Rtsp::RTP_TCP; + (*_player)[mediakit::Client::kWaitTrackReady] = false; + (*_player)[mediakit::Client::kRtpType] = mediakit::Rtsp::RTP_TCP; - _player->setOnPlayResult([weakPlayer, weakSelf, url](const toolkit::SockException& ex) mutable { - TraceL << "StackPlayer: " << url << " OnPlayResult: " << ex.what(); - auto strongPlayer = weakPlayer.lock(); - if (!strongPlayer) { return; } - auto self = weakSelf.lock(); - if (!self) { return; } + _player->setOnPlayResult([weakPlayer, weakSelf, url](const toolkit::SockException& ex) mutable { + TraceL << "StackPlayer: " << url << " OnPlayResult: " << ex.what(); + auto strongPlayer = weakPlayer.lock(); + if (!strongPlayer) { return; } + auto self = weakSelf.lock(); + if (!self) { return; } - if (!ex) { - // 取消定时器 - self->_timer.reset(); - self->_failedCount = 0; + if (!ex) { + // 取消定时器 + self->_timer.reset(); + self->_failedCount = 0; - } else { - self->onDisconnect(); - self->rePlay(url); - } + } else { + self->onDisconnect(); + 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, 0, std::vector{"h264", "hevc"}); + if (videoTrack) { + // 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; } + decoder->setOnDecode([weakSelf](const mediakit::FFmpegFrame::Ptr& frame) mutable { + auto self = weakSelf.lock(); + if (!self) { return; } - self->onFrame(frame); - }); + self->onFrame(frame); + }); - videoTrack->addDelegate([decoder](const mediakit::Frame::Ptr& frame) { - return decoder->inputFrame(frame, false, true); - }); - } - }); + videoTrack->addDelegate([decoder](const mediakit::Frame::Ptr& frame) { + return decoder->inputFrame(frame, false, true); + }); + } + }); - _player->setOnShutdown([weakPlayer, url, weakSelf](const toolkit::SockException& ex) { - TraceL << "StackPlayer: " << url << " OnShutdown: " << ex.what(); - auto strongPlayer = weakPlayer.lock(); - if (!strongPlayer) { return; } + _player->setOnShutdown([weakPlayer, url, weakSelf](const toolkit::SockException& ex) { + TraceL << "StackPlayer: " << url << " OnShutdown: " << ex.what(); + auto strongPlayer = weakPlayer.lock(); + if (!strongPlayer) { return; } - auto self = weakSelf.lock(); - if (!self) { return; } + auto self = weakSelf.lock(); + if (!self) { return; } - self->onDisconnect(); + self->onDisconnect(); - self->rePlay(url); - }); + self->rePlay(url); + }); - _player->play(url); + _player->play(url); } 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); } - } + std::lock_guard lock(_mx); + for (auto& weak_chn : _channels) { + if (auto chn = weak_chn.lock()) { chn->onFrame(frame); } + } } void StackPlayer::onDisconnect() { - std::lock_guard lock(_mx); - for (auto& weak_chn : _channels) { - if (auto chn = weak_chn.lock()) { - auto frame = VideoStackManager::Instance().getBgImg(); - chn->onFrame(frame); - } - } + std::lock_guard lock(_mx); + for (auto& weak_chn : _channels) { + if (auto chn = weak_chn.lock()) { + auto frame = VideoStackManager::Instance().getBgImg(); + chn->onFrame(frame); + } + } } void StackPlayer::rePlay(const std::string& url) { - _failedCount++; - 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); + _failedCount++; + 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); } 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) { + float fps, int bitRate) + : _id(id), _width(width), _height(height), _pixfmt(pixfmt), _fps(fps), _bitRate(bitRate) { - _buffer = std::make_shared(); + _buffer = std::make_shared(); - _buffer->get()->width = _width; - _buffer->get()->height = _height; - _buffer->get()->format = _pixfmt; + _buffer->get()->width = _width; + _buffer->get()->height = _height; + _buffer->get()->format = _pixfmt; - av_frame_get_buffer(_buffer->get(), 32); + 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; - info.iWidth = _width; - info.iHeight = _height; - info.iFrameRate = _fps; - info.iBitRate = _bitRate; + mediakit::VideoInfo info; + info.codecId = mediakit::CodecH264; + info.iWidth = _width; + info.iHeight = _height; + info.iFrameRate = _fps; + info.iBitRate = _bitRate; - _dev->initVideo(info); - //dev->initAudio(); //TODO:音频 - _dev->addTrackCompleted(); + _dev->initVideo(info); + // dev->initAudio(); //TODO:音频 + _dev->addTrackCompleted(); - _isExit = false; + _isExit = false; } VideoStack::~VideoStack() { - _isExit = true; - if (_thread.joinable()) { _thread.join(); } + _isExit = true; + if (_thread.joinable()) { _thread.join(); } } void VideoStack::setParam(const Params& params) { - if (_params) { - for (auto& p : (*_params)) { - if (!p) continue; - p->weak_buf.reset(); - } - } + if (_params) { + for (auto& p : (*_params)) { + if (!p) continue; + p->weak_buf.reset(); + } + } - initBgColor(); - for (auto& p : (*params)) { - if (!p) continue; - p->weak_buf = _buffer; - if (auto chn = p->weak_chn.lock()) { - chn->addParam(p); - chn->fillBuffer(p); - } - } - _params = params; + initBgColor(); + for (auto& p : (*params)) { + if (!p) continue; + p->weak_buf = _buffer; + if (auto chn = p->weak_chn.lock()) { + chn->addParam(p); + chn->fillBuffer(p); + } + } + _params = params; } 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)) { - lastEncTP = std::chrono::steady_clock::now(); + _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)) { + lastEncTP = std::chrono::steady_clock::now(); - _dev->inputYUV((char**)_buffer->get()->data, _buffer->get()->linesize, pts); - pts += frameInterval; - } - } - }); + _dev->inputYUV((char**)_buffer->get()->data, _buffer->get()->linesize, pts); + pts += frameInterval; + } + } + }); } void VideoStack::initBgColor() { - //填充底色 - auto R = 20; - auto G = 20; - auto B = 20; + // 填充底色 + auto R = 20; + auto G = 20; + auto B = 20; - double Y = RGB_TO_Y(R, G, B); - double U = RGB_TO_U(R, G, B); - double V = RGB_TO_V(R, G, B); + double Y = RGB_TO_Y(R, G, B); + double U = RGB_TO_U(R, G, B); + double V = RGB_TO_V(R, G, B); - memset(_buffer->get()->data[0], Y, _buffer->get()->linesize[0] * _height); - memset(_buffer->get()->data[1], U, _buffer->get()->linesize[1] * _height / 2); - memset(_buffer->get()->data[2], V, _buffer->get()->linesize[2] * _height / 2); + memset(_buffer->get()->data[0], Y, _buffer->get()->linesize[0] * _height); + memset(_buffer->get()->data[1], U, _buffer->get()->linesize[1] * _height / 2); + 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) { + 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(); } + 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(); } - return createChannel(id, width, height, pixfmt); + return createChannel(id, width, height, pixfmt); } void VideoStackManager::unrefChannel(const std::string& id, int width, int height, - AVPixelFormat pixfmt) { + AVPixelFormat pixfmt) { - std::lock_guard lock(_mx); - auto key = id + std::to_string(width) + std::to_string(height) + std::to_string(pixfmt); - auto chn_it = _channelMap.find(key); - if (chn_it != _channelMap.end() && chn_it->second->dispose()) { - _channelMap.erase(chn_it); + std::lock_guard lock(_mx); + auto key = id + std::to_string(width) + std::to_string(height) + std::to_string(pixfmt); + auto chn_it = _channelMap.find(key); + if (chn_it != _channelMap.end() && chn_it->second->dispose()) { + _channelMap.erase(chn_it); - auto player_it = _playerMap.find(id); - if (player_it != _playerMap.end() && player_it->second->dispose()) { - _playerMap.erase(player_it); - } - } + auto player_it = _playerMap.find(id); + if (player_it != _playerMap.end() && player_it->second->dispose()) { + _playerMap.erase(player_it); + } + } } int VideoStackManager::startVideoStack(const Json::Value& json) { - std::string id; - int width, height; - auto params = parseParams(json, id, width, height); + std::string id; + int width, height; + auto params = parseParams(json, id, width, height); - if (!params) { - ErrorL << "Videostack parse params failed!"; - return -1; - } + if (!params) { + ErrorL << "Videostack parse params failed!"; + return -1; + } - auto stack = std::make_shared(id, width, height); + auto stack = std::make_shared(id, width, height); - for (auto& p : (*params)) { - if (!p) continue; - p->weak_chn = getChannel(p->id, p->width, p->height, p->pixfmt); - } + for (auto& p : (*params)) { + if (!p) continue; + p->weak_chn = getChannel(p->id, p->width, p->height, p->pixfmt); + } - stack->setParam(params); - stack->start(); + stack->setParam(params); + stack->start(); - std::lock_guard lock(_mx); - _stackMap[id] = stack; - return 0; + std::lock_guard lock(_mx); + _stackMap[id] = stack; + return 0; } int VideoStackManager::resetVideoStack(const Json::Value& json) { - std::string id; - int width, height; - auto params = parseParams(json, id, width, height); + std::string id; + int width, height; + auto params = parseParams(json, id, width, height); - if (!params) { - ErrorL << "Videostack parse params failed!"; - return -1; - } + if (!params) { + ErrorL << "Videostack parse params failed!"; + return -1; + } - VideoStack::Ptr stack; - { - std::lock_guard lock(_mx); - auto it = _stackMap.find(id); - if (it == _stackMap.end()) { return -2; } - stack = it->second; - } + VideoStack::Ptr stack; + { + std::lock_guard lock(_mx); + auto it = _stackMap.find(id); + if (it == _stackMap.end()) { return -2; } + stack = it->second; + } - for (auto& p : (*params)) { - if (!p) continue; - p->weak_chn = getChannel(p->id, p->width, p->height, p->pixfmt); - } + for (auto& p : (*params)) { + if (!p) continue; + p->weak_chn = getChannel(p->id, p->width, p->height, p->pixfmt); + } - stack->setParam(params); - return 0; + stack->setParam(params); + return 0; } int VideoStackManager::stopVideoStack(const std::string& id) { - std::lock_guard lock(_mx); - auto it = _stackMap.find(id); - if (it != _stackMap.end()) { - _stackMap.erase(it); - InfoL << "VideoStack stop: " << id; - return 0; - } - return -1; + std::lock_guard lock(_mx); + auto it = _stackMap.find(id); + if (it != _stackMap.end()) { + _stackMap.erase(it); + InfoL << "VideoStack stop: " << id; + return 0; + } + return -1; } mediakit::FFmpegFrame::Ptr VideoStackManager::getBgImg() { return _bgImg; } 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(); + if (!json.isMember(key)) { + throw Json::LogicError("VideoStack parseParams missing required field: " + key); + } + return json[key].as(); } Params VideoStackManager::parseParams(const Json::Value& json, std::string& id, int& width, - int& height) { + int& height) { - id = getJsonValue(json, "id"); - width = getJsonValue(json, "width"); - height = getJsonValue(json, "height"); - int rows = getJsonValue(json, "row");//行数 - int cols = getJsonValue(json, "col");//列数 + 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();//水平间距 + 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 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; + // 根据间距计算格子宽高 + 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); + 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(); + 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; + 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; - } - } + (*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; + // 判断是否需要合并格子 (焦点屏) + 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 (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; + 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 = std::make_shared(); - _bgImg->get()->width = 1280; - _bgImg->get()->height = 720; - _bgImg->get()->format = AV_PIX_FMT_YUV420P; + _bgImg->get()->width = 1280; + _bgImg->get()->height = 720; + _bgImg->get()->format = AV_PIX_FMT_YUV420P; - av_frame_get_buffer(_bgImg->get(), 32); + av_frame_get_buffer(_bgImg->get(), 32); - std::ifstream file(path, std::ios::binary); - if (!file.is_open()) { return false; } + std::ifstream file(path, std::ios::binary); + 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 - return true; + 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; } void VideoStackManager::clear() { _stackMap.clear(); } Channel::Ptr VideoStackManager::createChannel(const std::string& id, int width, int height, - AVPixelFormat pixfmt) { + AVPixelFormat pixfmt) { - std::lock_guard lock(_mx); - StackPlayer::Ptr player; - auto it = _playerMap.find(id); - if (it != _playerMap.end()) { - player = it->second->acquire(); - } else { - player = createPlayer(id); - } + std::lock_guard lock(_mx); + StackPlayer::Ptr player; + auto it = _playerMap.find(id); + if (it != _playerMap.end()) { + player = it->second->acquire(); + } else { + player = createPlayer(id); + } - auto refChn = std::make_shared>( - std::make_shared(id, width, height, pixfmt)); - auto chn = refChn->acquire(); - player->addChannel(chn); + 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; - return chn; + _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) { - std::lock_guard lock(_mx); - auto refPlayer = - std::make_shared>(std::make_shared(id)); - _playerMap[id] = refPlayer; + std::lock_guard lock(_mx); + auto refPlayer = + std::make_shared>(std::make_shared(id)); + _playerMap[id] = refPlayer; - auto player = refPlayer->acquire(); - if (!id.empty()) { player->play(); } + auto player = refPlayer->acquire(); + if (!id.empty()) { player->play(); } - return player; + return player; } #endif diff --git a/server/VideoStack.h b/server/VideoStack.h index 5121f0c2..609060a7 100644 --- a/server/VideoStack.h +++ b/server/VideoStack.h @@ -7,184 +7,184 @@ #include template class RefWrapper { public: - using Ptr = std::shared_ptr>; + 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() { - ++_rc; - return _entity; - } + T acquire() { + ++_rc; + return _entity; + } - bool dispose() { return --_rc <= 0; } + bool dispose() { return --_rc <= 0; } private: - std::atomic _rc; - T _entity; + std::atomic _rc; + T _entity; }; class Channel; struct Param { - using Ptr = std::shared_ptr; + using Ptr = std::shared_ptr; - int posX = 0; - int posY = 0; - int width = 0; - int height = 0; - AVPixelFormat pixfmt = AV_PIX_FMT_YUV420P; - std::string id{}; + int posX = 0; + int posY = 0; + int width = 0; + int height = 0; + AVPixelFormat pixfmt = AV_PIX_FMT_YUV420P; + std::string id{}; - // runtime - std::weak_ptr weak_chn; - std::weak_ptr weak_buf; + // runtime + std::weak_ptr weak_chn; + std::weak_ptr weak_buf; - ~Param(); + ~Param(); }; using Params = std::shared_ptr>; class Channel : public std::enable_shared_from_this { public: - using Ptr = std::shared_ptr; + using Ptr = std::shared_ptr; - Channel(const std::string& id, int width, int height, AVPixelFormat pixfmt); + Channel(const std::string& id, int width, int height, AVPixelFormat pixfmt); - void addParam(const std::weak_ptr& p); + void addParam(const std::weak_ptr& p); - void onFrame(const mediakit::FFmpegFrame::Ptr& frame); + void onFrame(const mediakit::FFmpegFrame::Ptr& frame); - void fillBuffer(const Param::Ptr& p); + void fillBuffer(const Param::Ptr& p); protected: - void forEachParam(const std::function& func); + void forEachParam(const std::function& func); - void copyData(const mediakit::FFmpegFrame::Ptr& buf, const Param::Ptr& p); + void copyData(const mediakit::FFmpegFrame::Ptr& buf, const Param::Ptr& p); private: - std::string _id; - int _width; - int _height; - AVPixelFormat _pixfmt; + std::string _id; + int _width; + int _height; + AVPixelFormat _pixfmt; - mediakit::FFmpegFrame::Ptr _tmp; + mediakit::FFmpegFrame::Ptr _tmp; - std::recursive_mutex _mx; - std::vector> _params; + std::recursive_mutex _mx; + std::vector> _params; - mediakit::FFmpegSws::Ptr _sws; - toolkit::EventPoller::Ptr _poller; + mediakit::FFmpegSws::Ptr _sws; + toolkit::EventPoller::Ptr _poller; }; class StackPlayer : public std::enable_shared_from_this { public: - using Ptr = std::shared_ptr; + 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); + void addChannel(const std::weak_ptr& chn); - void play(); + void play(); - void onFrame(const mediakit::FFmpegFrame::Ptr& frame); + void onFrame(const mediakit::FFmpegFrame::Ptr& frame); - void onDisconnect(); + void onDisconnect(); protected: - void rePlay(const std::string& url); + void rePlay(const std::string& url); private: - std::string _url; - mediakit::MediaPlayer::Ptr _player; + std::string _url; + mediakit::MediaPlayer::Ptr _player; - //用于断线重连 - toolkit::Timer::Ptr _timer; - int _failedCount = 0; + // 用于断线重连 + toolkit::Timer::Ptr _timer; + int _failedCount = 0; - std::recursive_mutex _mx; - std::vector> _channels; + std::recursive_mutex _mx; + std::vector> _channels; }; class VideoStack { public: - using Ptr = std::shared_ptr; + 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(); + ~VideoStack(); - void setParam(const Params& params); + void setParam(const Params& params); - void start(); + void start(); protected: - void initBgColor(); + void initBgColor(); public: - Params _params; + Params _params; - mediakit::FFmpegFrame::Ptr _buffer; + mediakit::FFmpegFrame::Ptr _buffer; private: - std::string _id; - int _width; - int _height; - AVPixelFormat _pixfmt; - float _fps; - int _bitRate; + std::string _id; + int _width; + int _height; + AVPixelFormat _pixfmt; + float _fps; + int _bitRate; - mediakit::DevChannel::Ptr _dev; + mediakit::DevChannel::Ptr _dev; - bool _isExit; + bool _isExit; - std::thread _thread; + std::thread _thread; }; class VideoStackManager { public: - //创建拼接流 - int startVideoStack(const Json::Value& json); + // 创建拼接流 + int startVideoStack(const Json::Value& json); - //停止拼接流 - int stopVideoStack(const std::string& id); + // 停止拼接流 + int stopVideoStack(const std::string& id); - //可以在不断流的情况下,修改拼接流的配置(实现切换拼接屏内容) - int resetVideoStack(const Json::Value& json); + // 可以在不断流的情况下,修改拼接流的配置(实现切换拼接屏内容) + int resetVideoStack(const Json::Value& json); public: - static VideoStackManager& Instance(); + static VideoStackManager& Instance(); - Channel::Ptr getChannel(const std::string& id, int width, int height, AVPixelFormat pixfmt); + 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); + void unrefChannel(const std::string& id, int width, int height, AVPixelFormat pixfmt); - bool loadBgImg(const std::string& path); + bool loadBgImg(const std::string& path); - void clear(); + void clear(); - mediakit::FFmpegFrame::Ptr getBgImg(); + mediakit::FFmpegFrame::Ptr getBgImg(); protected: - Params parseParams(const Json::Value& json, std::string& id, int& width, int& height); + 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); + Channel::Ptr createChannel(const std::string& id, int width, int height, AVPixelFormat pixfmt); - StackPlayer::Ptr createPlayer(const std::string& id); + StackPlayer::Ptr createPlayer(const std::string& id); private: - mediakit::FFmpegFrame::Ptr _bgImg; + mediakit::FFmpegFrame::Ptr _bgImg; private: - std::recursive_mutex _mx; + std::recursive_mutex _mx; - std::unordered_map _stackMap; + std::unordered_map _stackMap; - std::unordered_map::Ptr> _channelMap; - - std::unordered_map::Ptr> _playerMap; + 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 ebd00785..5fbe4e19 100755 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -1949,42 +1949,42 @@ void installWebApi() { VideoStackManager::Instance().stopVideoStack(id); }); - api_regist("/index/api/stack/start", [](API_ARGS_JSON_ASYNC) { - CHECK_SECRET(); - 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/start", [](API_ARGS_JSON_ASYNC) { + CHECK_SECRET(); + 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()); - }); + 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()); + }); - api_regist("/index/api/stack/stop", [](API_ARGS_MAP_ASYNC) { - CHECK_SECRET(); - CHECK_ARGS("id"); - auto ret = VideoStackManager::Instance().stopVideoStack(allArgs["id"]); - val["code"] = ret; + api_regist("/index/api/stack/stop", [](API_ARGS_MAP_ASYNC) { + CHECK_SECRET(); + CHECK_ARGS("id"); + auto ret = VideoStackManager::Instance().stopVideoStack(allArgs["id"]); + val["code"] = ret; val["msg"] = ret ? "failed" : "success"; invoker(200, headerOut, val.toStyledString()); - }); + }); #endif } @@ -1996,8 +1996,8 @@ void unInstallWebApi(){ s_rtp_server.clear(); #endif #if defined(ENABLE_VIDEOSTACK) && defined(ENABLE_FFMPEG) && defined(ENABLE_X264) - VideoStackManager::Instance().clear(); + VideoStackManager::Instance().clear(); #endif - NoticeCenter::Instance().delListener(&web_api_tag); + NoticeCenter::Instance().delListener(&web_api_tag); }