// // Copyright (c) 2013-2025 The SRS Authors // // SPDX-License-Identifier: MIT // #include #include #include #include #include #include // Global instance SrsStreamPublishTokenManager *_srs_stream_publish_tokens = NULL; ISrsStreamPublishToken::ISrsStreamPublishToken() { } ISrsStreamPublishToken::~ISrsStreamPublishToken() { } SrsStreamPublishToken::SrsStreamPublishToken(const std::string &stream_url, ISrsStreamPublishTokenManager *manager) { stream_url_ = stream_url; acquired_ = false; manager_ = manager; publisher_cid_ = SrsContextId(); } SrsStreamPublishToken::~SrsStreamPublishToken() { // Automatically release the token when destroyed if (acquired_ && manager_) { manager_->release_token(stream_url_); } } std::string SrsStreamPublishToken::stream_url() { return stream_url_; } bool SrsStreamPublishToken::is_acquired() { return acquired_; } void SrsStreamPublishToken::set_acquired(bool acquired) { acquired_ = acquired; } const SrsContextId &SrsStreamPublishToken::publisher_cid() { return publisher_cid_; } void SrsStreamPublishToken::set_publisher_cid(const SrsContextId &cid) { publisher_cid_ = cid; } ISrsStreamPublishTokenManager::ISrsStreamPublishTokenManager() { } ISrsStreamPublishTokenManager::~ISrsStreamPublishTokenManager() { } SrsStreamPublishTokenManager::SrsStreamPublishTokenManager() { mutex_ = srs_mutex_new(); } SrsStreamPublishTokenManager::~SrsStreamPublishTokenManager() { // Clean up all remaining tokens. Each token's destructor automatically calls // release_token() which removes it from tokens_ map, so we use while loop // to avoid iterator invalidation issues. while (!tokens_.empty()) { SrsStreamPublishToken *token = tokens_.begin()->second; // Token destructor will call release_token() and remove this entry from map srs_freep(token); } srs_mutex_destroy(mutex_); srs_trace("stream publish token manager destroyed"); } srs_error_t SrsStreamPublishTokenManager::acquire_token(ISrsRequest *req, SrsStreamPublishToken *&token) { srs_error_t err = srs_success; std::string stream_url = req->get_stream_url(); SrsContextId current_cid = _srs_context->get_id(); SrsLocker(&mutex_); // Get or create token for this stream SrsStreamPublishToken *stream_token = NULL; std::map::iterator it = tokens_.find(stream_url); if (it != tokens_.end()) { stream_token = it->second; } else { stream_token = new SrsStreamPublishToken(stream_url, this); tokens_[stream_url] = stream_token; } // Check if token is already acquired by another publisher if (stream_token->is_acquired()) { SrsContextId existing_cid = stream_token->publisher_cid(); return srs_error_new(ERROR_SYSTEM_STREAM_BUSY, "stream %s is busy, acquired by cid=%s, current cid=%s", stream_url.c_str(), existing_cid.c_str(), current_cid.c_str()); } else { stream_token->set_acquired(true); stream_token->set_publisher_cid(current_cid); } // Return the token from the map (caller will manage its lifetime) token = stream_token; return err; } void SrsStreamPublishTokenManager::release_token(const std::string &stream_url) { SrsLocker(&mutex_); // Find and erase the token from the map std::map::iterator it = tokens_.find(stream_url); srs_assert(it != tokens_.end()); SrsStreamPublishToken *token = it->second; token->set_acquired(false); srs_trace("stream publish token released and deleted, url=%s", stream_url.c_str()); // Erase from map first, then delete the token tokens_.erase(it); }