Merge branch '2.0release' into develop

pull/556/head
winlin 10 years ago
commit 0a4d78383c

4
trunk/configure vendored

@ -159,7 +159,7 @@ MODULE_DEPENDS=("CORE" "KERNEL")
ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSSLRoot})
MODULE_FILES=("srs_rtmp_amf0" "srs_rtmp_io" "srs_rtmp_stack"
"srs_rtmp_handshake" "srs_rtmp_utility" "srs_rtmp_msg_array" "srs_protocol_buffer"
"srs_raw_avc" "srs_rtsp_stack" "srs_http_stack" "srs_protocol_kbps")
"srs_raw_avc" "srs_rtsp_stack" "srs_http_stack" "srs_protocol_kbps" "srs_protocol_json")
PROTOCOL_INCS="src/protocol"; MODULE_DIR=${PROTOCOL_INCS} . auto/modules.sh
PROTOCOL_OBJS="${MODULE_OBJS[@]}"
#
@ -172,7 +172,7 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
"srs_app_refer" "srs_app_hls" "srs_app_forward" "srs_app_encoder" "srs_app_http_stream"
"srs_app_thread" "srs_app_bandwidth" "srs_app_st" "srs_app_log" "srs_app_config"
"srs_app_pithy_print" "srs_app_reload" "srs_app_http_api" "srs_app_http_conn" "srs_app_http_hooks"
"srs_app_json" "srs_app_ingest" "srs_app_ffmpeg" "srs_app_utility" "srs_app_dvr" "srs_app_edge"
"srs_app_ingest" "srs_app_ffmpeg" "srs_app_utility" "srs_app_dvr" "srs_app_edge"
"srs_app_heartbeat" "srs_app_empty" "srs_app_http_client" "srs_app_http_static"
"srs_app_recv_thread" "srs_app_security" "srs_app_statistic" "srs_app_hds"
"srs_app_mpegts_udp" "srs_app_rtsp" "srs_app_listener" "srs_app_async_call"

@ -58,6 +58,8 @@ file
../../src/protocol/srs_rtmp_amf0.cpp,
../../src/protocol/srs_protocol_buffer.hpp,
../../src/protocol/srs_protocol_buffer.cpp,
../../src/protocol/srs_protocol_json.hpp,
../../src/protocol/srs_protocol_json.cpp,
../../src/protocol/srs_rtmp_handshake.hpp,
../../src/protocol/srs_rtmp_handshake.cpp,
../../src/protocol/srs_rtmp_io.hpp,
@ -109,8 +111,6 @@ file
../../src/app/srs_app_http_static.cpp,
../../src/app/srs_app_ingest.hpp,
../../src/app/srs_app_ingest.cpp,
../../src/app/srs_app_json.hpp,
../../src/app/srs_app_json.cpp,
../../src/app/srs_app_listener.hpp,
../../src/app/srs_app_listener.cpp,
../../src/app/srs_app_log.hpp,

@ -11,6 +11,7 @@
3C036B561B2D0AC10078E2E0 /* srs_app_http_stream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C036B531B2D0AC10078E2E0 /* srs_app_http_stream.cpp */; };
3C068D6A1B10149F00AA722C /* srs_protocol_kbps.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C068D681B10149F00AA722C /* srs_protocol_kbps.cpp */; };
3C068D6D1B10175500AA722C /* srs_protocol_buffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C068D6B1B10175500AA722C /* srs_protocol_buffer.cpp */; };
3C0D422E1B87165900C2508B /* srs_protocol_json.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C0D422C1B87165900C2508B /* srs_protocol_json.cpp */; };
3C0E1B8D1B0F5ADF003ADEF7 /* srs_http_stack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C0E1B8B1B0F5ADF003ADEF7 /* srs_http_stack.cpp */; };
3C1231F61AAE652D00CE8F6C /* srs_core_autofree.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C1231F01AAE652C00CE8F6C /* srs_core_autofree.cpp */; };
3C1231F71AAE652D00CE8F6C /* srs_core_performance.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C1231F21AAE652C00CE8F6C /* srs_core_performance.cpp */; };
@ -52,7 +53,6 @@
3C1232A11AAE81D900CE8F6C /* srs_app_http_conn.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C1232661AAE81D900CE8F6C /* srs_app_http_conn.cpp */; };
3C1232A21AAE81D900CE8F6C /* srs_app_http_hooks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C1232681AAE81D900CE8F6C /* srs_app_http_hooks.cpp */; };
3C1232A41AAE81D900CE8F6C /* srs_app_ingest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C12326C1AAE81D900CE8F6C /* srs_app_ingest.cpp */; };
3C1232A51AAE81D900CE8F6C /* srs_app_json.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C12326E1AAE81D900CE8F6C /* srs_app_json.cpp */; };
3C1232A71AAE81D900CE8F6C /* srs_app_listener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C1232721AAE81D900CE8F6C /* srs_app_listener.cpp */; };
3C1232A81AAE81D900CE8F6C /* srs_app_log.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C1232741AAE81D900CE8F6C /* srs_app_log.cpp */; };
3C1232A91AAE81D900CE8F6C /* srs_app_mpegts_udp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C1232761AAE81D900CE8F6C /* srs_app_mpegts_udp.cpp */; };
@ -131,6 +131,8 @@
3C068D691B10149F00AA722C /* srs_protocol_kbps.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_protocol_kbps.hpp; path = ../../../src/protocol/srs_protocol_kbps.hpp; sourceTree = "<group>"; };
3C068D6B1B10175500AA722C /* srs_protocol_buffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_protocol_buffer.cpp; path = ../../../src/protocol/srs_protocol_buffer.cpp; sourceTree = "<group>"; };
3C068D6C1B10175500AA722C /* srs_protocol_buffer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_protocol_buffer.hpp; path = ../../../src/protocol/srs_protocol_buffer.hpp; sourceTree = "<group>"; };
3C0D422C1B87165900C2508B /* srs_protocol_json.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_protocol_json.cpp; path = ../../../src/protocol/srs_protocol_json.cpp; sourceTree = "<group>"; };
3C0D422D1B87165900C2508B /* srs_protocol_json.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_protocol_json.hpp; path = ../../../src/protocol/srs_protocol_json.hpp; sourceTree = "<group>"; };
3C0E1B8B1B0F5ADF003ADEF7 /* srs_http_stack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_http_stack.cpp; path = ../../../src/protocol/srs_http_stack.cpp; sourceTree = "<group>"; };
3C0E1B8C1B0F5ADF003ADEF7 /* srs_http_stack.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_http_stack.hpp; path = ../../../src/protocol/srs_http_stack.hpp; sourceTree = "<group>"; };
3C1231E51AAE64A400CE8F6C /* srs_xcode */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = srs_xcode; sourceTree = BUILT_PRODUCTS_DIR; };
@ -214,8 +216,6 @@
3C1232691AAE81D900CE8F6C /* srs_app_http_hooks.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_app_http_hooks.hpp; path = ../../../src/app/srs_app_http_hooks.hpp; sourceTree = "<group>"; };
3C12326C1AAE81D900CE8F6C /* srs_app_ingest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_app_ingest.cpp; path = ../../../src/app/srs_app_ingest.cpp; sourceTree = "<group>"; };
3C12326D1AAE81D900CE8F6C /* srs_app_ingest.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_app_ingest.hpp; path = ../../../src/app/srs_app_ingest.hpp; sourceTree = "<group>"; };
3C12326E1AAE81D900CE8F6C /* srs_app_json.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_app_json.cpp; path = ../../../src/app/srs_app_json.cpp; sourceTree = "<group>"; };
3C12326F1AAE81D900CE8F6C /* srs_app_json.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_app_json.hpp; path = ../../../src/app/srs_app_json.hpp; sourceTree = "<group>"; };
3C1232721AAE81D900CE8F6C /* srs_app_listener.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_app_listener.cpp; path = ../../../src/app/srs_app_listener.cpp; sourceTree = "<group>"; };
3C1232731AAE81D900CE8F6C /* srs_app_listener.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = srs_app_listener.hpp; path = ../../../src/app/srs_app_listener.hpp; sourceTree = "<group>"; };
3C1232741AAE81D900CE8F6C /* srs_app_log.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = srs_app_log.cpp; path = ../../../src/app/srs_app_log.cpp; sourceTree = "<group>"; };
@ -496,6 +496,8 @@
3C0E1B8C1B0F5ADF003ADEF7 /* srs_http_stack.hpp */,
3C068D6B1B10175500AA722C /* srs_protocol_buffer.cpp */,
3C068D6C1B10175500AA722C /* srs_protocol_buffer.hpp */,
3C0D422C1B87165900C2508B /* srs_protocol_json.cpp */,
3C0D422D1B87165900C2508B /* srs_protocol_json.hpp */,
3C068D681B10149F00AA722C /* srs_protocol_kbps.cpp */,
3C068D691B10149F00AA722C /* srs_protocol_kbps.hpp */,
3C12322D1AAE81A400CE8F6C /* srs_raw_avc.cpp */,
@ -563,8 +565,6 @@
3C036B541B2D0AC10078E2E0 /* srs_app_http_stream.hpp */,
3C12326C1AAE81D900CE8F6C /* srs_app_ingest.cpp */,
3C12326D1AAE81D900CE8F6C /* srs_app_ingest.hpp */,
3C12326E1AAE81D900CE8F6C /* srs_app_json.cpp */,
3C12326F1AAE81D900CE8F6C /* srs_app_json.hpp */,
3C1232721AAE81D900CE8F6C /* srs_app_listener.cpp */,
3C1232731AAE81D900CE8F6C /* srs_app_listener.hpp */,
3C1232741AAE81D900CE8F6C /* srs_app_log.cpp */,
@ -862,12 +862,12 @@
3C689F961AB6AAAC00C9CEEE /* event.c in Sources */,
3C1232A81AAE81D900CE8F6C /* srs_app_log.cpp in Sources */,
3C1232A41AAE81D900CE8F6C /* srs_app_ingest.cpp in Sources */,
3C0D422E1B87165900C2508B /* srs_protocol_json.cpp in Sources */,
3C1232B41AAE81D900CE8F6C /* srs_app_st.cpp in Sources */,
3C1232481AAE81A400CE8F6C /* srs_rtmp_stack.cpp in Sources */,
3C1232B01AAE81D900CE8F6C /* srs_app_security.cpp in Sources */,
3C12322B1AAE814D00CE8F6C /* srs_kernel_utility.cpp in Sources */,
3C12324A1AAE81A400CE8F6C /* srs_rtsp_stack.cpp in Sources */,
3C1232A51AAE81D900CE8F6C /* srs_app_json.cpp in Sources */,
3C36DB5D1ABD1CB90066CCAF /* srs_librtmp.cpp in Sources */,
3C12329F1AAE81D900CE8F6C /* srs_app_http_api.cpp in Sources */,
3C1EE6AE1AB1055800576EE9 /* srs_app_hds.cpp in Sources */,

@ -40,7 +40,7 @@ using namespace std;
#include <srs_kernel_file.hpp>
#include <srs_rtmp_amf0.hpp>
#include <srs_kernel_stream.hpp>
#include <srs_app_json.hpp>
#include <srs_protocol_json.hpp>
#include <srs_app_utility.hpp>
// update the flv duration and filesize every this interval in ms.

@ -32,7 +32,7 @@ using namespace std;
#include <srs_kernel_log.hpp>
#include <srs_app_config.hpp>
#include <srs_app_http_client.hpp>
#include <srs_app_json.hpp>
#include <srs_protocol_json.hpp>
#include <srs_app_utility.hpp>
#include <srs_core_autofree.hpp>
#include <srs_app_http_conn.hpp>

@ -33,7 +33,7 @@ using namespace std;
#include <srs_kernel_error.hpp>
#include <srs_app_st.hpp>
#include <srs_core_autofree.hpp>
#include <srs_app_json.hpp>
#include <srs_protocol_json.hpp>
#include <srs_kernel_utility.hpp>
#include <srs_app_utility.hpp>
#include <srs_app_statistic.hpp>
@ -112,6 +112,7 @@ int SrsGoApiV1::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
<< SRS_JFIELD_STR("requests", "the request itself, for http debug") << SRS_JFIELD_CONT
<< SRS_JFIELD_STR("vhosts", "dumps vhost to json") << SRS_JFIELD_CONT
<< SRS_JFIELD_STR("streams", "dumps streams to json") << SRS_JFIELD_CONT
<< SRS_JFIELD_STR("clients", "dumps clients to json") << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("test", SRS_JOBJECT_START)
<< SRS_JFIELD_STR("requests", "show the request info") << SRS_JFIELD_CONT
<< SRS_JFIELD_STR("errors", "always return an error 100") << SRS_JFIELD_CONT
@ -442,17 +443,45 @@ SrsGoApiVhosts::~SrsGoApiVhosts()
int SrsGoApiVhosts::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
{
std::stringstream data;
SrsStatistic* stat = SrsStatistic::instance();
int ret = stat->dumps_vhosts(data);
int ret = ERROR_SUCCESS;
SrsStatistic* stat = SrsStatistic::instance();
std::stringstream ss;
ss << SRS_JOBJECT_START
<< SRS_JFIELD_ERROR(ret) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("server", stat->server_id()) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("vhosts", data.str())
<< SRS_JOBJECT_END;
// path: {pattern}{vhost_id}
// e.g. /api/v1/vhosts/100 pattern= /api/v1/vhosts/, vhost_id=100
int vid = r->parse_rest_id(entry->pattern);
SrsStatisticVhost* vhost = NULL;
if (vid > 0 && (vhost = stat->find_vhost(vid)) == NULL) {
ret = ERROR_RTMP_STREAM_NOT_FOUND;
srs_error("vhost id=%d not found. ret=%d", vid, ret);
return srs_http_response_code(w, ret);
}
if (r->is_http_get()) {
std::stringstream data;
if (!vhost) {
ret = stat->dumps_vhosts(data);
ss << SRS_JOBJECT_START
<< SRS_JFIELD_ERROR(ret) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("server", stat->server_id()) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("vhosts", data.str())
<< SRS_JOBJECT_END;
} else {
ret = vhost->dumps(data);
ss << SRS_JOBJECT_START
<< SRS_JFIELD_ERROR(ret) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("server", stat->server_id()) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("vhost", data.str())
<< SRS_JOBJECT_END;
}
return srs_http_response_json(w, ss.str());
}
return srs_http_response_json(w, ss.str());
}
@ -468,63 +497,114 @@ SrsGoApiStreams::~SrsGoApiStreams()
int SrsGoApiStreams::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
{
int ret = ERROR_SUCCESS;
SrsStatistic* stat = SrsStatistic::instance();
std::stringstream ss;
// path: {pattern}{stream_id}
// e.g. /api/v1/streams/100 pattern= /api/v1/streams/, stream_id=100
int sid = r->parse_rest_id(entry->pattern);
SrsStatisticStream* stream = NULL;
if (sid >= 0 && (stream = stat->find_stream(sid)) == NULL) {
ret = ERROR_RTMP_STREAM_NOT_FOUND;
srs_error("stream stream_id=%d not found. ret=%d", sid, ret);
return srs_http_response_code(w, ret);
}
if (r->is_http_delete()) {
// path: {pattern}{stream_id}
// e.g. /api/v1/streams/100 pattern= /api/v1/streams/, stream_id=100
string sid = r->path().substr((int)entry->pattern.length());
if (sid.empty()) {
ret = ERROR_REQUEST_DATA;
srs_error("invalid param, stream_id=%s. ret=%d", sid.c_str(), ret);
ss << SRS_JOBJECT_START
<< SRS_JFIELD_ERROR(ret)
<< SRS_JOBJECT_END;
return srs_http_response_json(w, ss.str());
srs_assert(stream);
SrsSource* source = SrsSource::fetch(stream->vhost->vhost, stream->app, stream->stream);
if (!source) {
ret = ERROR_SOURCE_NOT_FOUND;
srs_warn("source not found for sid=%d", sid);
return srs_http_response_code(w, ret);
}
int stream_id = ::atoi(sid.c_str());
SrsStatisticStream* stream = stat->find_stream(stream_id);
if (stream == NULL) {
ret = ERROR_RTMP_STREAM_NOT_FOUND;
srs_error("stream stream_id=%s not found. ret=%d", sid.c_str(), ret);
source->set_expired();
srs_warn("disconnent stream=%d successfully. vhost=%s, app=%s, stream=%s.",
sid, stream->vhost->vhost.c_str(), stream->app.c_str(), stream->stream.c_str());
return srs_http_response_code(w, ret);
} else if (r->is_http_get()) {
std::stringstream data;
if (!stream) {
ret = stat->dumps_streams(data);
ss << SRS_JOBJECT_START
<< SRS_JFIELD_ERROR(ret)
<< SRS_JOBJECT_END;
return srs_http_response_json(w, ss.str());
}
SrsSource* source = SrsSource::fetch(stream->vhost->vhost, stream->app, stream->stream);
if (source) {
source->set_expired();
srs_warn("disconnent stream=%d successfully. vhost=%s, app=%s, stream=%s.",
stream_id, stream->vhost->vhost.c_str(), stream->app.c_str(), stream->stream.c_str());
<< SRS_JFIELD_ERROR(ret) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("server", stat->server_id()) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("streams", data.str())
<< SRS_JOBJECT_END;
} else {
ret = ERROR_SOURCE_NOT_FOUND;
ret = stream->dumps(data);
ss << SRS_JOBJECT_START
<< SRS_JFIELD_ERROR(ret) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("server", stat->server_id()) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("stream", data.str())
<< SRS_JOBJECT_END;
}
return srs_http_response_json(w, ss.str());
}
return ret;
}
SrsGoApiClients::SrsGoApiClients()
{
}
ss << SRS_JOBJECT_START
<< SRS_JFIELD_ERROR(ret)
<< SRS_JOBJECT_END;
SrsGoApiClients::~SrsGoApiClients()
{
}
return srs_http_response_json(w, ss.str());
} else {
int SrsGoApiClients::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
{
int ret = ERROR_SUCCESS;
SrsStatistic* stat = SrsStatistic::instance();
std::stringstream ss;
// path: {pattern}{client_id}
// e.g. /api/v1/clients/100 pattern= /api/v1/clients/, client_id=100
int cid = r->parse_rest_id(entry->pattern);
SrsStatisticClient* client = NULL;
if (cid >= 0 && (client = stat->find_client(cid)) == NULL) {
ret = ERROR_RTMP_STREAM_NOT_FOUND;
srs_error("stream client_id=%d not found. ret=%d", cid, ret);
return srs_http_response_code(w, ret);
}
if (r->is_http_get()) {
std::stringstream data;
int ret = stat->dumps_streams(data);
ss << SRS_JOBJECT_START
<< SRS_JFIELD_ERROR(ret) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("server", stat->server_id()) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("streams", data.str())
<< SRS_JOBJECT_END;
if (!client) {
ret = stat->dumps_clients(data, 0, 10);
ss << SRS_JOBJECT_START
<< SRS_JFIELD_ERROR(ret) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("server", stat->server_id()) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("clients", data.str())
<< SRS_JOBJECT_END;
} else {
ret = client->dumps(data);
ss << SRS_JOBJECT_START
<< SRS_JFIELD_ERROR(ret) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("server", stat->server_id()) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("client", data.str())
<< SRS_JOBJECT_END;
}
return srs_http_response_json(w, ss.str());
}
return ret;
}
SrsGoApiError::SrsGoApiError()
@ -537,15 +617,7 @@ SrsGoApiError::~SrsGoApiError()
int SrsGoApiError::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
{
std::stringstream ss;
ss << SRS_JOBJECT_START
<< SRS_JFIELD_ERROR(100) << SRS_JFIELD_CONT
<< SRS_JFIELD_STR("msg", "SRS demo error.") << SRS_JFIELD_CONT
<< SRS_JFIELD_STR("path", r->path())
<< SRS_JOBJECT_END;
return srs_http_response_json(w, ss.str());
return srs_http_response_code(w, 100);
}

@ -159,6 +159,15 @@ public:
virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
};
class SrsGoApiClients : public ISrsHttpHandler
{
public:
SrsGoApiClients();
virtual ~SrsGoApiClients();
public:
virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
};
class SrsGoApiError : public ISrsHttpHandler
{
public:

@ -654,6 +654,7 @@ string SrsHttpMessage::uri()
uri += host();
uri += path();
return uri;
}
@ -677,6 +678,21 @@ string SrsHttpMessage::ext()
return _ext;
}
int SrsHttpMessage::parse_rest_id(string pattern)
{
string p = _uri->get_path();
if (p.length() <= pattern.length()) {
return -1;
}
string id = p.substr((int)pattern.length());
if (!id.empty()) {
return ::atoi(id.c_str());
}
return -1;
}
int SrsHttpMessage::body_read_all(string& body)
{
int ret = ERROR_SUCCESS;

@ -246,6 +246,10 @@ public:
virtual std::string host();
virtual std::string path();
virtual std::string ext();
/**
* get the RESTful matched id.
*/
virtual int parse_rest_id(std::string pattern);
public:
/**
* read body to string.

@ -31,7 +31,7 @@ using namespace std;
#include <srs_kernel_error.hpp>
#include <srs_rtmp_stack.hpp>
#include <srs_app_st.hpp>
#include <srs_app_json.hpp>
#include <srs_protocol_json.hpp>
#include <srs_app_dvr.hpp>
#include <srs_app_http_client.hpp>
#include <srs_core_autofree.hpp>

@ -773,10 +773,10 @@ int SrsServer::http_handle()
if ((ret = http_api_mux->handle("/", new SrsHttpNotFoundHandler())) != ERROR_SUCCESS) {
return ret;
}
if ((ret = http_api_mux->handle("/api", new SrsGoApiApi())) != ERROR_SUCCESS) {
if ((ret = http_api_mux->handle("/api/", new SrsGoApiApi())) != ERROR_SUCCESS) {
return ret;
}
if ((ret = http_api_mux->handle("/api/v1", new SrsGoApiV1())) != ERROR_SUCCESS) {
if ((ret = http_api_mux->handle("/api/v1/", new SrsGoApiV1())) != ERROR_SUCCESS) {
return ret;
}
if ((ret = http_api_mux->handle("/api/v1/versions", new SrsGoApiVersion())) != ERROR_SUCCESS) {
@ -800,12 +800,15 @@ int SrsServer::http_handle()
if ((ret = http_api_mux->handle("/api/v1/authors", new SrsGoApiAuthors())) != ERROR_SUCCESS) {
return ret;
}
if ((ret = http_api_mux->handle("/api/v1/vhosts", new SrsGoApiVhosts())) != ERROR_SUCCESS) {
if ((ret = http_api_mux->handle("/api/v1/vhosts/", new SrsGoApiVhosts())) != ERROR_SUCCESS) {
return ret;
}
if ((ret = http_api_mux->handle("/api/v1/streams/", new SrsGoApiStreams())) != ERROR_SUCCESS) {
return ret;
}
if ((ret = http_api_mux->handle("/api/v1/clients/", new SrsGoApiClients())) != ERROR_SUCCESS) {
return ret;
}
// test the request info.
if ((ret = http_api_mux->handle("/api/v1/test/requests", new SrsGoApiRequests())) != ERROR_SUCCESS) {

@ -28,7 +28,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
using namespace std;
#include <srs_rtmp_stack.hpp>
#include <srs_app_json.hpp>
#include <srs_protocol_json.hpp>
#include <srs_protocol_kbps.hpp>
#include <srs_app_conn.hpp>
#include <srs_app_config.hpp>
@ -47,6 +47,8 @@ SrsStatisticVhost::SrsStatisticVhost()
kbps = new SrsKbps();
kbps->set_io(NULL, NULL);
nb_clients = 0;
}
SrsStatisticVhost::~SrsStatisticVhost()
@ -54,6 +56,33 @@ SrsStatisticVhost::~SrsStatisticVhost()
srs_freep(kbps);
}
int SrsStatisticVhost::dumps(stringstream& ss)
{
int ret = ERROR_SUCCESS;
// dumps the config of vhost.
bool hls_enabled = _srs_config->get_hls_enabled(vhost);
bool enabled = _srs_config->get_vhost_enabled(vhost);
ss << SRS_JOBJECT_START
<< SRS_JFIELD_ORG("id", id) << SRS_JFIELD_CONT
<< SRS_JFIELD_STR("name", vhost) << SRS_JFIELD_CONT
<< SRS_JFIELD_BOOL("enabled", enabled) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("clients", nb_clients) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("send_bytes", kbps->get_send_bytes()) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("recv_bytes", kbps->get_recv_bytes()) << SRS_JFIELD_CONT
<< SRS_JFIELD_NAME("hls") << SRS_JOBJECT_START
<< SRS_JFIELD_BOOL("enabled", hls_enabled);
if (hls_enabled) {
ss << SRS_JFIELD_CONT;
ss << SRS_JFIELD_ORG("fragment", _srs_config->get_hls_fragment(vhost));
}
ss << SRS_JOBJECT_END
<< SRS_JOBJECT_END;
return ret;
}
SrsStatisticStream::SrsStatisticStream()
{
id = srs_generate_id();
@ -73,6 +102,8 @@ SrsStatisticStream::SrsStatisticStream()
kbps = new SrsKbps();
kbps->set_io(NULL, NULL);
nb_clients = 0;
}
SrsStatisticStream::~SrsStatisticStream()
@ -80,6 +111,48 @@ SrsStatisticStream::~SrsStatisticStream()
srs_freep(kbps);
}
int SrsStatisticStream::dumps(stringstream& ss)
{
int ret = ERROR_SUCCESS;
ss << SRS_JOBJECT_START
<< SRS_JFIELD_ORG("id", id) << SRS_JFIELD_CONT
<< SRS_JFIELD_STR("name", stream) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("vhost", vhost->id) << SRS_JFIELD_CONT
<< SRS_JFIELD_STR("app", app) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("clients", nb_clients) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("send_bytes", kbps->get_send_bytes()) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("recv_bytes", kbps->get_recv_bytes()) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("live_ms", srs_get_system_time_ms()) << SRS_JFIELD_CONT
<< SRS_JFIELD_STR("status", status) << SRS_JFIELD_CONT;
if (!has_video) {
ss << SRS_JFIELD_NULL("video") << SRS_JFIELD_CONT;
} else {
ss << SRS_JFIELD_NAME("video") << SRS_JOBJECT_START
<< SRS_JFIELD_STR("codec", srs_codec_video2str(vcodec)) << SRS_JFIELD_CONT
<< SRS_JFIELD_STR("profile", srs_codec_avc_profile2str(avc_profile)) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("level", srs_codec_avc_level2str(avc_level))
<< SRS_JOBJECT_END
<< SRS_JFIELD_CONT;
}
if (!has_audio) {
ss << SRS_JFIELD_NULL("audio");
} else {
ss << SRS_JFIELD_NAME("audio") << SRS_JOBJECT_START
<< SRS_JFIELD_STR("codec", srs_codec_audio2str(acodec)) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("sample_rate", (int)flv_sample_rates[asample_rate]) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("channel", (int)asound_type + 1) << SRS_JFIELD_CONT
<< SRS_JFIELD_STR("profile", srs_codec_aac_object2str(aac_object))
<< SRS_JOBJECT_END;
}
ss << SRS_JOBJECT_END;
return ret;
}
void SrsStatisticStream::publish()
{
status = STATISTIC_STREAM_STATUS_PUBLISHING;
@ -92,6 +165,26 @@ void SrsStatisticStream::close()
status = STATISTIC_STREAM_STATUS_IDLING;
}
SrsStatisticClient::SrsStatisticClient()
{
id = 0;
}
SrsStatisticClient::~SrsStatisticClient()
{
}
int SrsStatisticClient::dumps(stringstream& ss)
{
int ret = ERROR_SUCCESS;
ss << SRS_JOBJECT_START
<< SRS_JFIELD_ORG("id", id)
<< SRS_JOBJECT_END;
return ret;
}
SrsStatistic* SrsStatistic::_instance = new SrsStatistic();
SrsStatistic::SrsStatistic()
@ -107,14 +200,14 @@ SrsStatistic::~SrsStatistic()
srs_freep(kbps);
if (true) {
std::map<std::string, SrsStatisticVhost*>::iterator it;
std::map<int64_t, SrsStatisticVhost*>::iterator it;
for (it = vhosts.begin(); it != vhosts.end(); it++) {
SrsStatisticVhost* vhost = it->second;
srs_freep(vhost);
}
}
if (true) {
std::map<std::string, SrsStatisticStream*>::iterator it;
std::map<int64_t, SrsStatisticStream*>::iterator it;
for (it = streams.begin(); it != streams.end(); it++) {
SrsStatisticStream* stream = it->second;
srs_freep(stream);
@ -127,6 +220,11 @@ SrsStatistic::~SrsStatistic()
srs_freep(client);
}
}
vhosts.clear();
rvhosts.clear();
streams.clear();
rstreams.clear();
}
SrsStatistic* SrsStatistic::instance()
@ -134,16 +232,29 @@ SrsStatistic* SrsStatistic::instance()
return _instance;
}
SrsStatisticStream* SrsStatistic::find_stream(int stream_id)
SrsStatisticVhost* SrsStatistic::find_vhost(int vid)
{
std::map<int64_t, SrsStatisticVhost*>::iterator it;
if ((it = vhosts.find(vid)) != vhosts.end()) {
return it->second;
}
return NULL;
}
SrsStatisticStream* SrsStatistic::find_stream(int sid)
{
std::map<int64_t, SrsStatisticStream*>::iterator it;
if ((it = streams.find(sid)) != streams.end()) {
return it->second;
}
return NULL;
}
SrsStatisticClient* SrsStatistic::find_client(int cid)
{
std::map<int, SrsStatisticClient*>::iterator it;
for (it = clients.begin(); it != clients.end(); it++) {
SrsStatisticClient* client = it->second;
SrsStatisticStream* stream = client->stream;
if (stream_id == stream->id) {
return stream;
}
if ((it = clients.find(cid)) != clients.end()) {
return it->second;
}
return NULL;
}
@ -215,6 +326,10 @@ int SrsStatistic::on_client(int id, SrsRequest* req)
} else {
client = clients[id];
}
// got client.
stream->nb_clients++;
vhost->nb_clients++;
return ret;
}
@ -222,12 +337,19 @@ int SrsStatistic::on_client(int id, SrsRequest* req)
void SrsStatistic::on_disconnect(int id)
{
std::map<int, SrsStatisticClient*>::iterator it;
it = clients.find(id);
if (it != clients.end()) {
SrsStatisticClient* client = it->second;
srs_freep(client);
clients.erase(it);
if ((it = clients.find(id)) == clients.end()) {
return;
}
SrsStatisticClient* client = it->second;
SrsStatisticStream* stream = client->stream;
SrsStatisticVhost* vhost = stream->vhost;
srs_freep(client);
clients.erase(it);
stream->nb_clients--;
vhost->nb_clients--;
}
void SrsStatistic::kbps_add_delta(SrsConnection* conn)
@ -256,14 +378,14 @@ SrsKbps* SrsStatistic::kbps_sample()
{
kbps->sample();
if (true) {
std::map<std::string, SrsStatisticVhost*>::iterator it;
std::map<int64_t, SrsStatisticVhost*>::iterator it;
for (it = vhosts.begin(); it != vhosts.end(); it++) {
SrsStatisticVhost* vhost = it->second;
vhost->kbps->sample();
}
}
if (true) {
std::map<std::string, SrsStatisticStream*>::iterator it;
std::map<int64_t, SrsStatisticStream*>::iterator it;
for (it = streams.begin(); it != streams.end(); it++) {
SrsStatisticStream* stream = it->second;
stream->kbps->sample();
@ -283,29 +405,17 @@ int SrsStatistic::dumps_vhosts(stringstream& ss)
int ret = ERROR_SUCCESS;
ss << SRS_JARRAY_START;
std::map<std::string, SrsStatisticVhost*>::iterator it;
std::map<int64_t, SrsStatisticVhost*>::iterator it;
for (it = vhosts.begin(); it != vhosts.end(); it++) {
SrsStatisticVhost* vhost = it->second;
if (it != vhosts.begin()) {
ss << SRS_JFIELD_CONT;
}
// dumps the config of vhost.
bool hls_enabled = _srs_config->get_hls_enabled(vhost->vhost);
ss << SRS_JOBJECT_START
<< SRS_JFIELD_ORG("id", vhost->id) << SRS_JFIELD_CONT
<< SRS_JFIELD_STR("name", vhost->vhost) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("send_bytes", vhost->kbps->get_send_bytes()) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("recv_bytes", vhost->kbps->get_recv_bytes()) << SRS_JFIELD_CONT
<< SRS_JFIELD_NAME("hls") << SRS_JOBJECT_START
<< SRS_JFIELD_BOOL("enabled", hls_enabled);
if (hls_enabled) {
ss << SRS_JFIELD_CONT;
ss << SRS_JFIELD_ORG("fragment", _srs_config->get_hls_fragment(vhost->vhost));
if ((ret = vhost->dumps(ss)) != ERROR_SUCCESS) {
return ret;
}
ss << SRS_JOBJECT_END
<< SRS_JOBJECT_END;
}
ss << SRS_JARRAY_END;
@ -317,61 +427,47 @@ int SrsStatistic::dumps_streams(stringstream& ss)
int ret = ERROR_SUCCESS;
ss << SRS_JARRAY_START;
std::map<std::string, SrsStatisticStream*>::iterator it;
std::map<int64_t, SrsStatisticStream*>::iterator it;
for (it = streams.begin(); it != streams.end(); it++) {
SrsStatisticStream* stream = it->second;
if (it != streams.begin()) {
ss << SRS_JFIELD_CONT;
}
int client_num = 0;
std::map<int, SrsStatisticClient*>::iterator it_client;
for (it_client = clients.begin(); it_client != clients.end(); it_client++) {
SrsStatisticClient* client = it_client->second;
if (client->stream == stream) {
client_num++;
}
if ((ret = stream->dumps(ss)) != ERROR_SUCCESS) {
return ret;
}
}
ss << SRS_JARRAY_END;
return ret;
}
ss << SRS_JOBJECT_START
<< SRS_JFIELD_ORG("id", stream->id) << SRS_JFIELD_CONT
<< SRS_JFIELD_STR("name", stream->stream) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("vhost", stream->vhost->id) << SRS_JFIELD_CONT
<< SRS_JFIELD_STR("app", stream->app) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("clients", client_num) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("send_bytes", stream->kbps->get_send_bytes()) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("recv_bytes", stream->kbps->get_recv_bytes()) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("live_ms", srs_get_system_time_ms()) << SRS_JFIELD_CONT
<< SRS_JFIELD_STR("status", stream->status) << SRS_JFIELD_CONT;
if (!stream->has_video) {
ss << SRS_JFIELD_NULL("video") << SRS_JFIELD_CONT;
} else {
ss << SRS_JFIELD_NAME("video")
<< SRS_JOBJECT_START
<< SRS_JFIELD_STR("codec", srs_codec_video2str(stream->vcodec)) << SRS_JFIELD_CONT
<< SRS_JFIELD_STR("profile", srs_codec_avc_profile2str(stream->avc_profile)) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("level", srs_codec_avc_level2str(stream->avc_level))
<< SRS_JOBJECT_END
<< SRS_JFIELD_CONT;
int SrsStatistic::dumps_clients(stringstream& ss, int start, int count)
{
int ret = ERROR_SUCCESS;
ss << SRS_JARRAY_START;
std::map<int, SrsStatisticClient*>::iterator it = clients.begin();
for (int i = 0; i < count && it != clients.end(); it++) {
if (i < start) {
continue;
}
if (!stream->has_audio) {
ss << SRS_JFIELD_NULL("audio");
} else {
ss << SRS_JFIELD_NAME("audio")
<< SRS_JOBJECT_START
<< SRS_JFIELD_STR("codec", srs_codec_audio2str(stream->acodec)) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("sample_rate", (int)flv_sample_rates[stream->asample_rate]) << SRS_JFIELD_CONT
<< SRS_JFIELD_ORG("channel", (int)stream->asound_type + 1) << SRS_JFIELD_CONT
<< SRS_JFIELD_STR("profile", srs_codec_aac_object2str(stream->aac_object))
<< SRS_JOBJECT_END;
SrsStatisticClient* client = it->second;
if (i != start) {
ss << SRS_JFIELD_CONT;
}
ss << SRS_JOBJECT_END;
if ((ret = client->dumps(ss)) != ERROR_SUCCESS) {
return ret;
}
}
ss << SRS_JARRAY_END;
return ret;
}
@ -380,14 +476,15 @@ SrsStatisticVhost* SrsStatistic::create_vhost(SrsRequest* req)
SrsStatisticVhost* vhost = NULL;
// create vhost if not exists.
if (vhosts.find(req->vhost) == vhosts.end()) {
if (rvhosts.find(req->vhost) == rvhosts.end()) {
vhost = new SrsStatisticVhost();
vhost->vhost = req->vhost;
vhosts[req->vhost] = vhost;
rvhosts[req->vhost] = vhost;
vhosts[vhost->id] = vhost;
return vhost;
}
vhost = vhosts[req->vhost];
vhost = rvhosts[req->vhost];
return vhost;
}
@ -399,17 +496,18 @@ SrsStatisticStream* SrsStatistic::create_stream(SrsStatisticVhost* vhost, SrsReq
SrsStatisticStream* stream = NULL;
// create stream if not exists.
if (streams.find(url) == streams.end()) {
if (rstreams.find(url) == rstreams.end()) {
stream = new SrsStatisticStream();
stream->vhost = vhost;
stream->stream = req->stream;
stream->app = req->app;
stream->url = url;
streams[url] = stream;
rstreams[url] = stream;
streams[stream->id] = stream;
return stream;
}
stream = streams[url];
stream = rstreams[url];
return stream;
}

@ -47,6 +47,7 @@ struct SrsStatisticVhost
public:
int64_t id;
std::string vhost;
int nb_clients;
public:
/**
* vhost total kbps.
@ -55,6 +56,8 @@ public:
public:
SrsStatisticVhost();
virtual ~SrsStatisticVhost();
public:
virtual int dumps(std::stringstream& ss);
};
struct SrsStatisticStream
@ -66,6 +69,7 @@ public:
std::string stream;
std::string url;
std::string status;
int nb_clients;
public:
/**
* stream total kbps.
@ -93,6 +97,8 @@ public:
public:
SrsStatisticStream();
virtual ~SrsStatisticStream();
public:
virtual int dumps(std::stringstream& ss);
public:
/**
* publish the stream.
@ -109,6 +115,11 @@ struct SrsStatisticClient
public:
SrsStatisticStream* stream;
int id;
public:
SrsStatisticClient();
virtual ~SrsStatisticClient();
public:
virtual int dumps(std::stringstream& ss);
};
class SrsStatistic
@ -117,10 +128,19 @@ private:
static SrsStatistic *_instance;
// the id to identify the sever.
int64_t _server_id;
// key: vhost name, value: vhost object.
std::map<std::string, SrsStatisticVhost*> vhosts;
// key: stream url, value: stream object.
std::map<std::string, SrsStatisticStream*> streams;
private:
// key: vhost id, value: vhost object.
std::map<int64_t, SrsStatisticVhost*> vhosts;
// key: vhost url, value: vhost Object.
// @remark a fast index for vhosts.
std::map<std::string, SrsStatisticVhost*> rvhosts;
private:
// key: stream id, value: stream Object.
std::map<int64_t, SrsStatisticStream*> streams;
// key: stream url, value: stream Object.
// @remark a fast index for streams.
std::map<std::string, SrsStatisticStream*> rstreams;
private:
// key: client id, value: stream object.
std::map<int, SrsStatisticClient*> clients;
// server total kbps.
@ -131,7 +151,10 @@ private:
public:
static SrsStatistic* instance();
public:
virtual SrsStatisticStream* find_stream(int stream_id);
virtual SrsStatisticVhost* find_vhost(int vid);
virtual SrsStatisticStream* find_stream(int sid);
virtual SrsStatisticClient* find_client(int cid);
public:
/**
* when got video info for stream.
*/
@ -192,6 +215,12 @@ public:
* dumps the streams to sstream in json.
*/
virtual int dumps_streams(std::stringstream& ss);
/**
* dumps the clients to sstream in json.
* @param start the start index, from 0.
* @param count the max count of clients to dump.
*/
virtual int dumps_clients(std::stringstream& ss, int start, int count);
private:
virtual SrsStatisticVhost* create_vhost(SrsRequest* req);
virtual SrsStatisticStream* create_stream(SrsStatisticVhost* vhost, SrsRequest* req);

@ -43,7 +43,7 @@ using namespace std;
#include <srs_kernel_utility.hpp>
#include <srs_kernel_error.hpp>
#include <srs_protocol_kbps.hpp>
#include <srs_app_json.hpp>
#include <srs_protocol_json.hpp>
#include <srs_kernel_stream.hpp>
// the longest time to wait for a process to quit.

@ -32,6 +32,7 @@ using namespace std;
#include <srs_kernel_log.hpp>
#include <srs_kernel_utility.hpp>
#include <srs_kernel_file.hpp>
#include <srs_protocol_json.hpp>
#define SRS_HTTP_DEFAULT_PAGE "index.html"
@ -143,6 +144,17 @@ int srs_http_response_json(ISrsHttpResponseWriter* w, string data)
return w->write((char*)data.data(), (int)data.length());
}
int srs_http_response_code(ISrsHttpResponseWriter* w, int code)
{
std::stringstream ss;
ss << SRS_JOBJECT_START
<< SRS_JFIELD_ERROR(code)
<< SRS_JOBJECT_END;
return srs_http_response_json(w, ss.str());
}
SrsHttpHeader::SrsHttpHeader()
{
}

@ -78,6 +78,11 @@ class ISrsHttpResponseWriter;
// helper function: response in json format.
extern int srs_http_response_json(ISrsHttpResponseWriter* w, std::string data);
/**
* response a typical code object, for example:
* {code : 100}
*/
extern int srs_http_response_code(ISrsHttpResponseWriter* w, int code);
// get the status text of code.
extern std::string srs_generate_http_status_text(int status);
@ -488,6 +493,14 @@ public:
virtual std::string host() = 0;
virtual std::string path() = 0;
virtual std::string ext() = 0;
/**
* get the RESTful id,
* for example, pattern is /api/v1/streams, path is /api/v1/streams/100,
* then the rest id is 100.
* @param pattern the handler pattern which will serve the request.
* @return the REST id; -1 if not matched.
*/
virtual int parse_rest_id(std::string pattern) = 0;
public:
/**
* read body to string.

@ -21,7 +21,7 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <srs_app_json.hpp>
#include <srs_protocol_json.hpp>
using namespace std;

@ -21,11 +21,11 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef SRS_APP_JSON_HPP
#define SRS_APP_JSON_HPP
#ifndef SRS_PROTOCOL_JSON_HPP
#define SRS_PROTOCOL_JSON_HPP
/*
#include <srs_app_json.hpp>
#include <srs_protocol_json.hpp>
*/
#include <srs_core.hpp>
Loading…
Cancel
Save