!12 合并作者最新提交

Merge pull request !12 from yinxiang/master
This commit is contained in:
yinxiang 2022-11-22 03:18:30 +00:00 committed by Gitee
commit 68358155c9
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
233 changed files with 4267 additions and 8739 deletions

57
.github/ISSUE_TEMPLATE/compile.md vendored Normal file
View File

@ -0,0 +1,57 @@
---
name: 编译问题反馈
about: 反馈 ZLMediaKit 编译相关的问题
title: "[编译问题]: "
labels: 编译问题
assignees: ''
---
<!--
请仔细阅读相关注释提示, 请务必根据提示填写相关信息.
1. 信息不完整会影响问题的解决速度.
1. 乱七八糟的渲染格式也会影响开发者心情, 同样会影响问题的解决. 提交前请务必点击 Preview/预览下反馈的显示效果.
1. 不要删除模版内容, 模版的注释部分的内容不会显示,不需要删除,直接在各部分注释外面补充相关信息即可.
-->
<!--
markdown 语法参考:
* https://docs.github.com/cn/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax
* https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax
-->
### 相关日志及环境信息
<!--
由于编译日志通长较长, 建议将日志信息填写到下面 `````` block 内,或者上传日志文件
-->
**清除编译缓存后,完整执行 cmake && make 命令的输出**
<details>
<summary>展开查看详细编译日志</summary>
<pre>
```
详细日志粘在这里!
```
</pre>
</details>
编译目录下的 `CMakeCache.txt` 文件内容,请直接上传为附件。
### 各种环境信息
<!--
请填写相关环境信息, 详细的环境信息有助于快速复现定位问题.
* 代码提交记录, 可使用命令 `git rev-parse HEAD` 进行查看.
* 操作系统及版本, 如: Windows 10, CentOS 7, ...
* 硬件信息, 如: Intel, AMD, ARM, 飞腾, 龙芯, ...
-->
* **代码提交记录/git commit hash**:
* **操作系统及版本**:
* **硬件信息**:
* **其他需要补充的信息**:

View File

@ -33,6 +33,9 @@ jobs:
with:
cosign-release: 'v1.7.1'
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
# Workaround: https://github.com/docker/build-push-action/issues/461
- name: Setup Docker buildx
@ -67,6 +70,7 @@ jobs:
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: MODEL=Release
platforms: linux/amd64,linux/arm64
# Sign the resulting Docker image digest except on PRs.
# This will only write to the public Rekor transparency log when the Docker

3
.gitmodules vendored
View File

@ -4,3 +4,6 @@
[submodule "3rdpart/media-server"]
path = 3rdpart/media-server
url = https://gitee.com/ireader/media-server
[submodule "3rdpart/jsoncpp"]
path = 3rdpart/jsoncpp
url = https://gitee.com/mirrors/jsoncpp.git

View File

@ -4,3 +4,6 @@
[submodule "3rdpart/media-server"]
path = 3rdpart/media-server
url = https://github.com/ireader/media-server
[submodule "3rdpart/jsoncpp"]
path = 3rdpart/jsoncpp
url = https://github.com/open-source-parsers/jsoncpp.git

View File

@ -1,12 +0,0 @@
language: cpp
sudo: required
dist: trusty
compiler:
- clang
os:
- linux
script:
- ./build_for_linux.sh

View File

@ -24,15 +24,15 @@
##############################################################################
# jsoncpp
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/jsoncpp JSONCPP_SRC_LIST)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/jsoncpp/src/lib_json JSONCPP_SRC_LIST)
add_library(jsoncpp STATIC ${JSONCPP_SRC_LIST})
target_compile_options(jsoncpp
PRIVATE ${COMPILE_OPTIONS_DEFAULT})
target_include_directories(jsoncpp
PRIVATE
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>/jsoncpp/include"
PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>")
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>/jsoncpp/include")
update_cached_list(MK_LINK_LIBRARIES jsoncpp)
@ -90,8 +90,12 @@ if(ENABLE_RTPPROXY OR ENABLE_HLS)
aux_source_directory(${MediaServer_MPEG_ROOT}/source MPEG_SRC_LIST)
add_library(mpeg STATIC ${MPEG_SRC_LIST})
add_library(MediaServer::mpeg ALIAS mpeg)
# media-server
# MPEG_H26X_VERIFY -
# MPEG_ZERO_PAYLOAD_LENGTH - hik
# MPEG_DAHUA_AAC_FROM_G711 - dahua
target_compile_options(mpeg
PRIVATE ${COMPILE_OPTIONS_DEFAULT})
PRIVATE ${COMPILE_OPTIONS_DEFAULT} -DMPEG_H26X_VERIFY -DMPEG_ZERO_PAYLOAD_LENGTH -DMPEG_DAHUA_AAC_FROM_G711)
target_include_directories(mpeg
PRIVATE
"$<BUILD_INTERFACE:${MediaServer_MPEG_ROOT}/include>"

@ -1 +1 @@
Subproject commit 82f82e723eda127777aaf91ad0e4615e8d56e8d5
Subproject commit 617b6b1db23f13e2592b29204b84b1b9dbbf81c0

1
3rdpart/jsoncpp Submodule

@ -0,0 +1 @@
Subproject commit 8190e061bc2d95da37479a638aa2c9e483e58ec6

View File

@ -1,53 +0,0 @@
// Copyright 2007-2010 Baptiste Lepilleur
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef CPPTL_JSON_ASSERTIONS_H_INCLUDED
#define CPPTL_JSON_ASSERTIONS_H_INCLUDED
#include <stdlib.h>
#include <sstream>
#if !defined(JSON_IS_AMALGAMATION)
#include "../jsoncpp/config.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
/** It should not be possible for a maliciously designed file to
* cause an abort() or seg-fault, so these macros are used only
* for pre-condition violations and internal logic errors.
*/
#if JSON_USE_EXCEPTION
// @todo <= add detail about condition in exception
# define JSON_ASSERT(condition) \
{if (!(condition)) {Json::throwLogicError( "assert json failed" );}}
# define JSON_FAIL_MESSAGE(message) \
{ \
std::ostringstream oss; oss << message; \
Json::throwLogicError(oss.str()); \
}
#else // JSON_USE_EXCEPTION
# define JSON_ASSERT(condition) assert(condition)
// The call to assert() will show the failure message in debug builds. In
// release builds we abort, for a core-dump or debugger.
# define JSON_FAIL_MESSAGE(message) \
{ \
std::ostringstream oss; oss << message; \
assert(false && oss.str().c_str()); \
abort(); \
}
#endif
#define JSON_ASSERT_MESSAGE(condition, message) \
if (!(condition)) { \
JSON_FAIL_MESSAGE(message); \
}
#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED

View File

@ -1,25 +0,0 @@
// Copyright 2007-2010 Baptiste Lepilleur
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_AUTOLINK_H_INCLUDED
#define JSON_AUTOLINK_H_INCLUDED
#include "../jsoncpp/config.h"
#ifdef JSON_IN_CPPTL
#include <cpptl/cpptl_autolink.h>
#endif
#if !defined(JSON_NO_AUTOLINK) && !defined(JSON_DLL_BUILD) && \
!defined(JSON_IN_CPPTL)
#define CPPTL_AUTOLINK_NAME "json"
#undef CPPTL_AUTOLINK_DLL
#ifdef JSON_DLL
#define CPPTL_AUTOLINK_DLL
#endif
#include "autolink.h"
#endif
#endif // JSON_AUTOLINK_H_INCLUDED

View File

@ -1,109 +0,0 @@
// Copyright 2007-2010 Baptiste Lepilleur
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_CONFIG_H_INCLUDED
#define JSON_CONFIG_H_INCLUDED
/// If defined, indicates that json library is embedded in CppTL library.
//# define JSON_IN_CPPTL 1
/// If defined, indicates that json may leverage CppTL library
//# define JSON_USE_CPPTL 1
/// If defined, indicates that cpptl vector based map should be used instead of
/// std::map
/// as Value container.
//# define JSON_USE_CPPTL_SMALLMAP 1
// If non-zero, the library uses exceptions to report bad input instead of C
// assertion macros. The default is to use exceptions.
#ifndef JSON_USE_EXCEPTION
#define JSON_USE_EXCEPTION 1
#endif
/// If defined, indicates that the source file is amalgated
/// to prevent private header inclusion.
/// Remarks: it is automatically defined in the generated amalgated header.
// #define JSON_IS_AMALGAMATION
#ifdef JSON_IN_CPPTL
#include <cpptl/config.h>
#ifndef JSON_USE_CPPTL
#define JSON_USE_CPPTL 1
#endif
#endif
#ifdef JSON_IN_CPPTL
#define JSON_API CPPTL_API
#elif defined(JSON_DLL_BUILD)
#if defined(_MSC_VER)
#define JSON_API __declspec(dllexport)
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
#endif // if defined(_MSC_VER)
#elif defined(JSON_DLL)
#if defined(_MSC_VER)
#define JSON_API __declspec(dllimport)
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
#endif // if defined(_MSC_VER)
#endif // ifdef JSON_IN_CPPTL
#if !defined(JSON_API)
#define JSON_API
#endif
// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for
// integer
// Storages, and 64 bits integer support is disabled.
// #define JSON_NO_INT64 1
#if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC 6
// Microsoft Visual Studio 6 only support conversion from __int64 to double
// (no conversion from unsigned __int64).
#define JSON_USE_INT64_DOUBLE_CONVERSION 1
// Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255'
// characters in the debug information)
// All projects I've ever seen with VS6 were using this globally (not bothering
// with pragma push/pop).
#pragma warning(disable : 4786)
#endif // if defined(_MSC_VER) && _MSC_VER < 1200 // MSVC 6
#if defined(_MSC_VER) && _MSC_VER >= 1500 // MSVC 2008
/// Indicates that the following function is deprecated.
#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
#elif defined(__clang__) && defined(__has_feature)
#if __has_feature(attribute_deprecated_with_message)
#define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message)))
#endif
#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
#define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message)))
#elif defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__))
#endif
#if !defined(JSONCPP_DEPRECATED)
#define JSONCPP_DEPRECATED(message)
#endif // if !defined(JSONCPP_DEPRECATED)
namespace Json {
typedef int Int;
typedef unsigned int UInt;
#if defined(JSON_NO_INT64)
typedef int LargestInt;
typedef unsigned int LargestUInt;
#undef JSON_HAS_INT64
#else // if defined(JSON_NO_INT64)
// For Microsoft Visual use specific types as long long is not supported
#if defined(_MSC_VER) // Microsoft Visual Studio
typedef __int64 Int64;
typedef unsigned __int64 UInt64;
#else // if defined(_MSC_VER) // Other platforms, use long long
typedef long long int Int64;
typedef unsigned long long int UInt64;
#endif // if defined(_MSC_VER)
typedef Int64 LargestInt;
typedef UInt64 LargestUInt;
#define JSON_HAS_INT64
#endif // if defined(JSON_NO_INT64)
} // end namespace Json
#endif // JSON_CONFIG_H_INCLUDED

View File

@ -1,57 +0,0 @@
// Copyright 2007-2010 Baptiste Lepilleur
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef CPPTL_JSON_FEATURES_H_INCLUDED
#define CPPTL_JSON_FEATURES_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include "../jsoncpp/forwards.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
namespace Json {
/** \brief Configuration passed to reader and writer.
* This configuration object can be used to force the Reader or Writer
* to behave in a standard conforming way.
*/
class JSON_API Features {
public:
/** \brief A configuration that allows all features and assumes all strings
* are UTF-8.
* - C & C++ comments are allowed
* - Root object can be any JSON value
* - Assumes Value strings are encoded in UTF-8
*/
static Features all();
/** \brief A configuration that is strictly compatible with the JSON
* specification.
* - Comments are forbidden.
* - Root object must be either an array or an object value.
* - Assumes Value strings are encoded in UTF-8
*/
static Features strictMode();
/** \brief Initialize the configuration like JsonConfig::allFeatures;
*/
Features();
/// \c true if comments are allowed. Default: \c true.
bool allowComments_;
/// \c true if root must be either an array or an object value. Default: \c
/// false.
bool strictRoot_;
/// \c true if dropped null placeholders are allowed. Default: \c false.
bool allowDroppedNullPlaceholders_;
/// \c true if numeric object key are allowed. Default: \c false.
bool allowNumericKeys_;
};
} // namespace Json
#endif // CPPTL_JSON_FEATURES_H_INCLUDED

View File

@ -1,37 +0,0 @@
// Copyright 2007-2010 Baptiste Lepilleur
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_FORWARDS_H_INCLUDED
#define JSON_FORWARDS_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include "config.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
namespace Json {
// writer.h
class FastWriter;
class StyledWriter;
// reader.h
class Reader;
// features.h
class Features;
// value.h
typedef unsigned int ArrayIndex;
class StaticString;
class Path;
class PathArgument;
class Value;
class ValueIteratorBase;
class ValueIterator;
class ValueConstIterator;
} // namespace Json
#endif // JSON_FORWARDS_H_INCLUDED

View File

@ -1,15 +0,0 @@
// Copyright 2007-2010 Baptiste Lepilleur
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_JSON_H_INCLUDED
#define JSON_JSON_H_INCLUDED
#include "autolink.h"
#include "features.h"
#include "reader.h"
#include "writer.h"
#include "value.h"
#endif // JSON_JSON_H_INCLUDED

File diff suppressed because it is too large Load Diff

View File

@ -1,87 +0,0 @@
// Copyright 2007-2010 Baptiste Lepilleur
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED
#define LIB_JSONCPP_JSON_TOOL_H_INCLUDED
/* This header provides common string manipulation support, such as UTF-8,
* portable conversion from/to string...
*
* It is an internal header that must not be exposed.
*/
namespace Json {
/// Converts a unicode code-point to UTF-8.
static inline std::string codePointToUTF8(unsigned int cp) {
std::string result;
// based on description from http://en.wikipedia.org/wiki/UTF-8
if (cp <= 0x7f) {
result.resize(1);
result[0] = static_cast<char>(cp);
} else if (cp <= 0x7FF) {
result.resize(2);
result[1] = static_cast<char>(0x80 | (0x3f & cp));
result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6)));
} else if (cp <= 0xFFFF) {
result.resize(3);
result[2] = static_cast<char>(0x80 | (0x3f & cp));
result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
result[0] = static_cast<char>(0xE0 | (0xf & (cp >> 12)));
} else if (cp <= 0x10FFFF) {
result.resize(4);
result[3] = static_cast<char>(0x80 | (0x3f & cp));
result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12)));
result[0] = static_cast<char>(0xF0 | (0x7 & (cp >> 18)));
}
return result;
}
/// Returns true if ch is a control character (in range [1,31]).
static inline bool isControlCharacter(char ch) { return ch > 0 && ch <= 0x1F; }
enum {
/// Constant that specify the size of the buffer that must be passed to
/// uintToString.
uintToStringBufferSize = 3 * sizeof(LargestUInt) + 1
};
// Defines a char buffer for use with uintToString().
typedef char UIntToStringBuffer[uintToStringBufferSize];
/** Converts an unsigned integer to string.
* @param value Unsigned interger to convert to string
* @param current Input/Output string buffer.
* Must have at least uintToStringBufferSize chars free.
*/
static inline void uintToString(LargestUInt value, char*& current) {
*--current = 0;
do {
*--current = static_cast<signed char>(value % 10U + static_cast<unsigned>('0'));
value /= 10;
} while (value != 0);
}
/** Change ',' to '.' everywhere in buffer.
*
* We had a sophisticated way, but it did not work in WinCE.
* @see https://github.com/open-source-parsers/jsoncpp/pull/9
*/
static inline void fixNumericLocale(char* begin, char* end) {
while (begin < end) {
if (*begin == ',') {
*begin = '.';
}
++begin;
}
}
} // namespace Json {
#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED

File diff suppressed because it is too large Load Diff

View File

@ -1,162 +0,0 @@
// Copyright 2007-2010 Baptiste Lepilleur
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
// included by json_value.cpp
namespace Json {
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class ValueIteratorBase
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
ValueIteratorBase::ValueIteratorBase()
: current_(), isNull_(true) {
}
ValueIteratorBase::ValueIteratorBase(
const Value::ObjectValues::iterator& current)
: current_(current), isNull_(false) {}
Value& ValueIteratorBase::deref() const {
return current_->second;
}
void ValueIteratorBase::increment() {
++current_;
}
void ValueIteratorBase::decrement() {
--current_;
}
ValueIteratorBase::difference_type
ValueIteratorBase::computeDistance(const SelfType& other) const {
#ifdef JSON_USE_CPPTL_SMALLMAP
return other.current_ - current_;
#else
// Iterator for null value are initialized using the default
// constructor, which initialize current_ to the default
// std::map::iterator. As begin() and end() are two instance
// of the default std::map::iterator, they can not be compared.
// To allow this, we handle this comparison specifically.
if (isNull_ && other.isNull_) {
return 0;
}
// Usage of std::distance is not portable (does not compile with Sun Studio 12
// RogueWave STL,
// which is the one used by default).
// Using a portable hand-made version for non random iterator instead:
// return difference_type( std::distance( current_, other.current_ ) );
difference_type myDistance = 0;
for (Value::ObjectValues::iterator it = current_; it != other.current_;
++it) {
++myDistance;
}
return myDistance;
#endif
}
bool ValueIteratorBase::isEqual(const SelfType& other) const {
if (isNull_) {
return other.isNull_;
}
return current_ == other.current_;
}
void ValueIteratorBase::copy(const SelfType& other) {
current_ = other.current_;
isNull_ = other.isNull_;
}
Value ValueIteratorBase::key() const {
const Value::CZString czstring = (*current_).first;
if (czstring.data()) {
if (czstring.isStaticString())
return Value(StaticString(czstring.data()));
return Value(czstring.data(), czstring.data() + czstring.length());
}
return Value(czstring.index());
}
UInt ValueIteratorBase::index() const {
const Value::CZString czstring = (*current_).first;
if (!czstring.data())
return czstring.index();
return Value::UInt(-1);
}
std::string ValueIteratorBase::name() const {
char const* keey;
char const* end;
keey = memberName(&end);
if (!keey) return std::string();
return std::string(keey, end);
}
char const* ValueIteratorBase::memberName() const {
const char* cname = (*current_).first.data();
return cname ? cname : "";
}
char const* ValueIteratorBase::memberName(char const** end) const {
const char* cname = (*current_).first.data();
if (!cname) {
*end = NULL;
return NULL;
}
*end = cname + (*current_).first.length();
return cname;
}
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class ValueConstIterator
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
ValueConstIterator::ValueConstIterator() {}
ValueConstIterator::ValueConstIterator(
const Value::ObjectValues::iterator& current)
: ValueIteratorBase(current) {}
ValueConstIterator& ValueConstIterator::
operator=(const ValueIteratorBase& other) {
copy(other);
return *this;
}
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class ValueIterator
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
ValueIterator::ValueIterator() {}
ValueIterator::ValueIterator(const Value::ObjectValues::iterator& current)
: ValueIteratorBase(current) {}
ValueIterator::ValueIterator(const ValueConstIterator& other)
: ValueIteratorBase(other) {}
ValueIterator::ValueIterator(const ValueIterator& other)
: ValueIteratorBase(other) {}
ValueIterator& ValueIterator::operator=(const SelfType& other) {
copy(other);
return *this;
}
} // namespace Json

File diff suppressed because it is too large Load Diff

View File

@ -1,401 +0,0 @@
// Copyright 2007-2010 Baptiste Lepilleur
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef CPPTL_JSON_READER_H_INCLUDED
#define CPPTL_JSON_READER_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include "../jsoncpp/features.h"
#include "../jsoncpp/value.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
#include <deque>
#include <iosfwd>
#include <stack>
#include <string>
#include <istream>
// Disable warning C4251: <data member>: <type> needs to have dll-interface to
// be used by...
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma warning(push)
#pragma warning(disable : 4251)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
namespace Json {
/** \brief Unserialize a <a HREF="http://www.json.org">JSON</a> document into a
*Value.
*
* \deprecated Use CharReader and CharReaderBuilder.
*/
class JSON_API Reader {
public:
typedef char Char;
typedef const Char* Location;
/** \brief An error tagged with where in the JSON text it was encountered.
*
* The offsets give the [start, limit) range of bytes within the text. Note
* that this is bytes, not codepoints.
*
*/
struct StructuredError {
size_t offset_start;
size_t offset_limit;
std::string message;
};
/** \brief Constructs a Reader allowing all features
* for parsing.
*/
Reader();
/** \brief Constructs a Reader allowing the specified feature set
* for parsing.
*/
Reader(const Features& features);
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
* document.
* \param document UTF-8 encoded string containing the document to read.
* \param root [out] Contains the root value of the document if it was
* successfully parsed.
* \param collectComments \c true to collect comment and allow writing them
* back during
* serialization, \c false to discard comments.
* This parameter is ignored if
* Features::allowComments_
* is \c false.
* \return \c true if the document was successfully parsed, \c false if an
* error occurred.
*/
bool
parse(const std::string& document, Value& root, bool collectComments = true);
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
document.
* \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the
document to read.
* \param endDoc Pointer on the end of the UTF-8 encoded string of the
document to read.
* Must be >= beginDoc.
* \param root [out] Contains the root value of the document if it was
* successfully parsed.
* \param collectComments \c true to collect comment and allow writing them
back during
* serialization, \c false to discard comments.
* This parameter is ignored if
Features::allowComments_
* is \c false.
* \return \c true if the document was successfully parsed, \c false if an
error occurred.
*/
bool parse(const char* beginDoc,
const char* endDoc,
Value& root,
bool collectComments = true);
/// \brief Parse from input stream.
/// \see Json::operator>>(std::istream&, Json::Value&).
bool parse(std::istream& is, Value& root, bool collectComments = true);
/** \brief Returns a user friendly string that list errors in the parsed
* document.
* \return Formatted error message with the list of errors with their location
* in
* the parsed document. An empty string is returned if no error
* occurred
* during parsing.
* \deprecated Use getFormattedErrorMessages() instead (typo fix).
*/
JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.")
std::string getFormatedErrorMessages() const;
/** \brief Returns a user friendly string that list errors in the parsed
* document.
* \return Formatted error message with the list of errors with their location
* in
* the parsed document. An empty string is returned if no error
* occurred
* during parsing.
*/
std::string getFormattedErrorMessages() const;
/** \brief Returns a vector of structured erros encounted while parsing.
* \return A (possibly empty) vector of StructuredError objects. Currently
* only one error can be returned, but the caller should tolerate
* multiple
* errors. This can occur if the parser recovers from a non-fatal
* parse error and then encounters additional errors.
*/
std::vector<StructuredError> getStructuredErrors() const;
/** \brief Add a semantic error message.
* \param value JSON Value location associated with the error
* \param message The error message.
* \return \c true if the error was successfully added, \c false if the
* Value offset exceeds the document size.
*/
bool pushError(const Value& value, const std::string& message);
/** \brief Add a semantic error message with extra context.
* \param value JSON Value location associated with the error
* \param message The error message.
* \param extra Additional JSON Value location to contextualize the error
* \return \c true if the error was successfully added, \c false if either
* Value offset exceeds the document size.
*/
bool pushError(const Value& value, const std::string& message, const Value& extra);
/** \brief Return whether there are any errors.
* \return \c true if there are no errors to report \c false if
* errors have occurred.
*/
bool good() const;
private:
enum TokenType {
tokenEndOfStream = 0,
tokenObjectBegin,
tokenObjectEnd,
tokenArrayBegin,
tokenArrayEnd,
tokenString,
tokenNumber,
tokenTrue,
tokenFalse,
tokenNull,
tokenArraySeparator,
tokenMemberSeparator,
tokenComment,
tokenError
};
class Token {
public:
TokenType type_;
Location start_;
Location end_;
};
class ErrorInfo {
public:
Token token_;
std::string message_;
Location extra_;
};
typedef std::deque<ErrorInfo> Errors;
bool readToken(Token& token);
void skipSpaces();
bool match(Location pattern, int patternLength);
bool readComment();
bool readCStyleComment();
bool readCppStyleComment();
bool readString();
void readNumber();
bool readValue();
bool readObject(Token& token);
bool readArray(Token& token);
bool decodeNumber(Token& token);
bool decodeNumber(Token& token, Value& decoded);
bool decodeString(Token& token);
bool decodeString(Token& token, std::string& decoded);
bool decodeDouble(Token& token);
bool decodeDouble(Token& token, Value& decoded);
bool decodeUnicodeCodePoint(Token& token,
Location& current,
Location end,
unsigned int& unicode);
bool decodeUnicodeEscapeSequence(Token& token,
Location& current,
Location end,
unsigned int& unicode);
bool addError(const std::string& message, Token& token, Location extra = 0);
bool recoverFromError(TokenType skipUntilToken);
bool addErrorAndRecover(const std::string& message,
Token& token,
TokenType skipUntilToken);
void skipUntilSpace();
Value& currentValue();
Char getNextChar();
void
getLocationLineAndColumn(Location location, int& line, int& column) const;
std::string getLocationLineAndColumn(Location location) const;
void addComment(Location begin, Location end, CommentPlacement placement);
void skipCommentTokens(Token& token);
typedef std::stack<Value*> Nodes;
Nodes nodes_;
Errors errors_;
std::string document_;
Location begin_;
Location end_;
Location current_;
Location lastValueEnd_;
Value* lastValue_;
std::string commentsBefore_;
Features features_;
bool collectComments_;
}; // Reader
/** Interface for reading JSON from a char array.
*/
class JSON_API CharReader {
public:
virtual ~CharReader() {}
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
document.
* The document must be a UTF-8 encoded string containing the document to read.
*
* \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the
document to read.
* \param endDoc Pointer on the end of the UTF-8 encoded string of the
document to read.
* Must be >= beginDoc.
* \param root [out] Contains the root value of the document if it was
* successfully parsed.
* \param errs [out] Formatted error messages (if not NULL)
* a user friendly string that lists errors in the parsed
* document.
* \return \c true if the document was successfully parsed, \c false if an
error occurred.
*/
virtual bool parse(
char const* beginDoc, char const* endDoc,
Value* root, std::string* errs) = 0;
class Factory {
public:
virtual ~Factory() {}
/** \brief Allocate a CharReader via operator new().
* \throw std::exception if something goes wrong (e.g. invalid settings)
*/
virtual CharReader* newCharReader() const = 0;
}; // Factory
}; // CharReader
/** \brief Build a CharReader implementation.
Usage:
\code
using namespace Json;
CharReaderBuilder builder;
builder["collectComments"] = false;
Value value;
std::string errs;
bool ok = parseFromStream(builder, std::cin, &value, &errs);
\endcode
*/
class JSON_API CharReaderBuilder : public CharReader::Factory {
public:
// Note: We use a Json::Value so that we can add data-members to this class
// without a major version bump.
/** Configuration of this builder.
These are case-sensitive.
Available settings (case-sensitive):
- `"collectComments": false or true`
- true to collect comment and allow writing them
back during serialization, false to discard comments.
This parameter is ignored if allowComments is false.
- `"allowComments": false or true`
- true if comments are allowed.
- `"strictRoot": false or true`
- true if root must be either an array or an object value
- `"allowDroppedNullPlaceholders": false or true`
- true if dropped null placeholders are allowed. (See StreamWriterBuilder.)
- `"allowNumericKeys": false or true`
- true if numeric object keys are allowed.
- `"allowSingleQuotes": false or true`
- true if '' are allowed for strings (both keys and values)
- `"stackLimit": integer`
- Exceeding stackLimit (recursive depth of `readValue()`) will
cause an exception.
- This is a security issue (seg-faults caused by deeply nested JSON),
so the default is low.
- `"failIfExtra": false or true`
- If true, `parse()` returns false when extra non-whitespace trails
the JSON value in the input string.
- `"rejectDupKeys": false or true`
- If true, `parse()` returns false when a key is duplicated within an object.
You can examine 'settings_` yourself
to see the defaults. You can also write and read them just like any
JSON Value.
\sa setDefaults()
*/
Json::Value settings_;
CharReaderBuilder();
virtual ~CharReaderBuilder();
virtual CharReader* newCharReader() const;
/** \return true if 'settings' are legal and consistent;
* otherwise, indicate bad settings via 'invalid'.
*/
bool validate(Json::Value* invalid) const;
/** A simple way to update a specific setting.
*/
Value& operator[](std::string key);
/** Called by ctor, but you can use this to reset settings_.
* \pre 'settings' != NULL (but Json::null is fine)
* \remark Defaults:
* \snippet src/lib_json/json_reader.cpp CharReaderBuilderDefaults
*/
static void setDefaults(Json::Value* settings);
/** Same as old Features::strictMode().
* \pre 'settings' != NULL (but Json::null is fine)
* \remark Defaults:
* \snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode
*/
static void strictMode(Json::Value* settings);
};
/** Consume entire stream and use its begin/end.
* Someday we might have a real StreamReader, but for now this
* is convenient.
*/
bool JSON_API parseFromStream(
CharReader::Factory const&,
std::istream&,
Value* root, std::string* errs);
/** \brief Read from 'sin' into 'root'.
Always keep comments from the input JSON.
This can be used to read a file into a particular sub-object.
For example:
\code
Json::Value root;
cin >> root["dir"]["file"];
cout << root;
\endcode
Result:
\verbatim
{
"dir": {
"file": {
// The input stream JSON would be nested here.
}
}
}
\endverbatim
\throw std::exception on parse error.
\see Json::operator<<()
*/
JSON_API std::istream& operator>>(std::istream&, Value&);
} // namespace Json
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma warning(pop)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#endif // CPPTL_JSON_READER_H_INCLUDED

View File

@ -1,841 +0,0 @@
// Copyright 2007-2010 Baptiste Lepilleur
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef CPPTL_JSON_H_INCLUDED
#define CPPTL_JSON_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include "forwards.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
#include <string>
#include <vector>
#include <exception>
#ifndef JSON_USE_CPPTL_SMALLMAP
#include <map>
#else
#include <cpptl/smallmap.h>
#endif
#ifdef JSON_USE_CPPTL
#include <cpptl/forwards.h>
#endif
// Disable warning C4251: <data member>: <type> needs to have dll-interface to
// be used by...
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma warning(push)
#pragma warning(disable : 4251)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
/** \brief JSON (JavaScript Object Notation).
*/
namespace Json {
/** Base class for all exceptions we throw.
*
* We use nothing but these internally. Of course, STL can throw others.
*/
class JSON_API Exception : public std::exception {
public:
Exception(std::string const& msg);
virtual ~Exception() throw();
virtual char const* what() const throw();
protected:
std::string const msg_;
};
/** Exceptions which the user cannot easily avoid.
*
* E.g. out-of-memory (when we use malloc), stack-overflow, malicious input
*
* \remark derived from Json::Exception
*/
class JSON_API RuntimeError : public Exception {
public:
RuntimeError(std::string const& msg);
};
/** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros.
*
* These are precondition-violations (user bugs) and internal errors (our bugs).
*
* \remark derived from Json::Exception
*/
class JSON_API LogicError : public Exception {
public:
LogicError(std::string const& msg);
};
/// used internally
void throwRuntimeError(std::string const& msg);
/// used internally
void throwLogicError(std::string const& msg);
/** \brief Type of the value held by a Value object.
*/
enum ValueType {
nullValue = 0, ///< 'null' value
intValue, ///< signed integer value
uintValue, ///< unsigned integer value
realValue, ///< double value
stringValue, ///< UTF-8 string value
booleanValue, ///< bool value
arrayValue, ///< array value (ordered list)
objectValue ///< object value (collection of name/value pairs).
};
enum CommentPlacement {
commentBefore = 0, ///< a comment placed on the line before a value
commentAfterOnSameLine, ///< a comment just after a value on the same line
commentAfter, ///< a comment on the line after a value (only make sense for
/// root value)
numberOfCommentPlacement
};
//# ifdef JSON_USE_CPPTL
// typedef CppTL::AnyEnumerator<const char *> EnumMemberNames;
// typedef CppTL::AnyEnumerator<const Value &> EnumValues;
//# endif
/** \brief Lightweight wrapper to tag static string.
*
* Value constructor and objectValue member assignement takes advantage of the
* StaticString and avoid the cost of string duplication when storing the
* string or the member name.
*
* Example of usage:
* \code
* Json::Value aValue( StaticString("some text") );
* Json::Value object;
* static const StaticString code("code");
* object[code] = 1234;
* \endcode
*/
class JSON_API StaticString {
public:
explicit StaticString(const char* czstring) : c_str_(czstring) {}
operator const char*() const { return c_str_; }
const char* c_str() const { return c_str_; }
private:
const char* c_str_;
};
/** \brief Represents a <a HREF="http://www.json.org">JSON</a> value.
*
* This class is a discriminated union wrapper that can represents a:
* - signed integer [range: Value::minInt - Value::maxInt]
* - unsigned integer (range: 0 - Value::maxUInt)
* - double
* - UTF-8 string
* - boolean
* - 'null'
* - an ordered list of Value
* - collection of name/value pairs (javascript object)
*
* The type of the held value is represented by a #ValueType and
* can be obtained using type().
*
* Values of an #objectValue or #arrayValue can be accessed using operator[]()
* methods.
* Non-const methods will automatically create the a #nullValue element
* if it does not exist.
* The sequence of an #arrayValue will be automatically resized and initialized
* with #nullValue. resize() can be used to enlarge or truncate an #arrayValue.
*
* The get() methods can be used to obtain default value in the case the
* required element does not exist.
*
* It is possible to iterate over the list of a #objectValue values using
* the getMemberNames() method.
*
* \note #Value string-length fit in size_t, but keys must be < 2^30.
* (The reason is an implementation detail.) A #CharReader will raise an
* exception if a bound is exceeded to avoid security holes in your app,
* but the Value API does *not* check bounds. That is the responsibility
* of the caller.
*/
class JSON_API Value {
friend class ValueIteratorBase;
public:
typedef std::vector<std::string> Members;
typedef ValueIterator iterator;
typedef ValueConstIterator const_iterator;
typedef Json::UInt UInt;
typedef Json::Int Int;
#if defined(JSON_HAS_INT64)
typedef Json::UInt64 UInt64;
typedef Json::Int64 Int64;
#endif // defined(JSON_HAS_INT64)
typedef Json::LargestInt LargestInt;
typedef Json::LargestUInt LargestUInt;
typedef Json::ArrayIndex ArrayIndex;
static const Value& null; ///< We regret this reference to a global instance; prefer the simpler Value().
static const Value& nullRef; ///< just a kludge for binary-compatibility; same as null
/// Minimum signed integer value that can be stored in a Json::Value.
static const LargestInt minLargestInt;
/// Maximum signed integer value that can be stored in a Json::Value.
static const LargestInt maxLargestInt;
/// Maximum unsigned integer value that can be stored in a Json::Value.
static const LargestUInt maxLargestUInt;
/// Minimum signed int value that can be stored in a Json::Value.
static const Int minInt;
/// Maximum signed int value that can be stored in a Json::Value.
static const Int maxInt;
/// Maximum unsigned int value that can be stored in a Json::Value.
static const UInt maxUInt;
#if defined(JSON_HAS_INT64)
/// Minimum signed 64 bits int value that can be stored in a Json::Value.
static const Int64 minInt64;
/// Maximum signed 64 bits int value that can be stored in a Json::Value.
static const Int64 maxInt64;
/// Maximum unsigned 64 bits int value that can be stored in a Json::Value.
static const UInt64 maxUInt64;
#endif // defined(JSON_HAS_INT64)
private:
#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
class CZString {
public:
enum DuplicationPolicy {
noDuplication = 0,
duplicate,
duplicateOnCopy
};
CZString(ArrayIndex index);
CZString(char const* str, unsigned length, DuplicationPolicy allocate);
CZString(CZString const& other);
~CZString();
CZString& operator=(CZString other);
bool operator<(CZString const& other) const;
bool operator==(CZString const& other) const;
ArrayIndex index() const;
//const char* c_str() const; ///< \deprecated
char const* data() const;
unsigned length() const;
bool isStaticString() const;
private:
void swap(CZString& other);
struct StringStorage {
unsigned policy_: 2;
unsigned length_: 30; // 1GB max
};
char const* cstr_; // actually, a prefixed string, unless policy is noDup
union {
ArrayIndex index_;
StringStorage storage_;
};
};
public:
#ifndef JSON_USE_CPPTL_SMALLMAP
typedef std::map<CZString, Value> ObjectValues;
#else
typedef CppTL::SmallMap<CZString, Value> ObjectValues;
#endif // ifndef JSON_USE_CPPTL_SMALLMAP
#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
public:
/** \brief Create a default Value of the given type.
This is a very useful constructor.
To create an empty array, pass arrayValue.
To create an empty object, pass objectValue.
Another Value can then be set to this one by assignment.
This is useful since clear() and resize() will not alter types.
Examples:
\code
Json::Value null_value; // null
Json::Value arr_value(Json::arrayValue); // []
Json::Value obj_value(Json::objectValue); // {}
\endcode
*/
Value(ValueType type = nullValue);
Value(Int value);
Value(UInt value);
#if defined(JSON_HAS_INT64)
Value(Int64 value);
Value(UInt64 value);
#endif // if defined(JSON_HAS_INT64)
Value(double value);
Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.)
Value(const char* begin, const char* end); ///< Copy all, incl zeroes.
/** \brief Constructs a value from a static string.
* Like other value string constructor but do not duplicate the string for
* internal storage. The given string must remain alive after the call to this
* constructor.
* \note This works only for null-terminated strings. (We cannot change the
* size of this class, so we have nowhere to store the length,
* which might be computed later for various operations.)
*
* Example of usage:
* \code
* static StaticString foo("some text");
* Json::Value aValue(foo);
* \endcode
*/
Value(const StaticString& value);
Value(const std::string& value); ///< Copy data() til size(). Embedded zeroes too.
#ifdef JSON_USE_CPPTL
Value(const CppTL::ConstString& value);
#endif
Value(bool value);
/// Deep copy.
Value(const Value& other);
~Value();
/// Deep copy, then swap(other).
/// \note Over-write existing comments. To preserve comments, use #swapPayload().
Value& operator=(Value other);
/// Swap everything.
void swap(Value& other);
/// Swap values but leave comments and source offsets in place.
void swapPayload(Value& other);
ValueType type() const;
/// Compare payload only, not comments etc.
bool operator<(const Value& other) const;
bool operator<=(const Value& other) const;
bool operator>=(const Value& other) const;
bool operator>(const Value& other) const;
bool operator==(const Value& other) const;
bool operator!=(const Value& other) const;
int compare(const Value& other) const;
const char* asCString() const; ///< Embedded zeroes could cause you trouble!
std::string asString() const; ///< Embedded zeroes are possible.
/** Get raw char* of string-value.
* \return false if !string. (Seg-fault if str or end are NULL.)
*/
bool getString(
char const** begin, char const** end) const;
#ifdef JSON_USE_CPPTL
CppTL::ConstString asConstString() const;
#endif
Int asInt() const;
UInt asUInt() const;
#if defined(JSON_HAS_INT64)
Int64 asInt64() const;
UInt64 asUInt64() const;
#endif // if defined(JSON_HAS_INT64)
LargestInt asLargestInt() const;
LargestUInt asLargestUInt() const;
float asFloat() const;
double asDouble() const;
bool asBool() const;
bool isNull() const;
bool isBool() const;
bool isInt() const;
bool isInt64() const;
bool isUInt() const;
bool isUInt64() const;
bool isIntegral() const;
bool isDouble() const;
bool isNumeric() const;
bool isString() const;
bool isArray() const;
bool isObject() const;
bool isConvertibleTo(ValueType other) const;
/// Number of values in array or object
ArrayIndex size() const;
/// \brief Return true if empty array, empty object, or null;
/// otherwise, false.
bool empty() const;
/// Return isNull()
bool operator!() const;
/// Remove all object members and array elements.
/// \pre type() is arrayValue, objectValue, or nullValue
/// \post type() is unchanged
void clear();
/// Resize the array to size elements.
/// New elements are initialized to null.
/// May only be called on nullValue or arrayValue.
/// \pre type() is arrayValue or nullValue
/// \post type() is arrayValue
void resize(ArrayIndex size);
/// Access an array element (zero based index ).
/// If the array contains less than index element, then null value are
/// inserted
/// in the array so that its size is index+1.
/// (You may need to say 'value[0u]' to get your compiler to distinguish
/// this from the operator[] which takes a string.)
Value& operator[](ArrayIndex index);
/// Access an array element (zero based index ).
/// If the array contains less than index element, then null value are
/// inserted
/// in the array so that its size is index+1.
/// (You may need to say 'value[0u]' to get your compiler to distinguish
/// this from the operator[] which takes a string.)
Value& operator[](int index);
/// Access an array element (zero based index )
/// (You may need to say 'value[0u]' to get your compiler to distinguish
/// this from the operator[] which takes a string.)
const Value& operator[](ArrayIndex index) const;
/// Access an array element (zero based index )
/// (You may need to say 'value[0u]' to get your compiler to distinguish
/// this from the operator[] which takes a string.)
const Value& operator[](int index) const;
/// If the array contains at least index+1 elements, returns the element
/// value,
/// otherwise returns defaultValue.
Value get(ArrayIndex index, const Value& defaultValue) const;
/// Return true if index < size().
bool isValidIndex(ArrayIndex index) const;
/// \brief Append value to array at the end.
///
/// Equivalent to jsonvalue[jsonvalue.size()] = value;
Value& append(const Value& value);
/// Access an object value by name, create a null member if it does not exist.
/// \note Because of our implementation, keys are limited to 2^30 -1 chars.
/// Exceeding that will cause an exception.
Value& operator[](const char* key);
/// Access an object value by name, returns null if there is no member with
/// that name.
const Value& operator[](const char* key) const;
/// Access an object value by name, create a null member if it does not exist.
/// \param key may contain embedded nulls.
Value& operator[](const std::string& key);
/// Access an object value by name, returns null if there is no member with
/// that name.
/// \param key may contain embedded nulls.
const Value& operator[](const std::string& key) const;
/** \brief Access an object value by name, create a null member if it does not
exist.
* If the object has no entry for that name, then the member name used to store
* the new entry is not duplicated.
* Example of use:
* \code
* Json::Value object;
* static const StaticString code("code");
* object[code] = 1234;
* \endcode
*/
Value& operator[](const StaticString& key);
#ifdef JSON_USE_CPPTL
/// Access an object value by name, create a null member if it does not exist.
Value& operator[](const CppTL::ConstString& key);
/// Access an object value by name, returns null if there is no member with
/// that name.
const Value& operator[](const CppTL::ConstString& key) const;
#endif
/// Return the member named key if it exist, defaultValue otherwise.
/// \note deep copy
Value get(const char* key, const Value& defaultValue) const;
/// Return the member named key if it exist, defaultValue otherwise.
/// \note deep copy
/// \note key may contain embedded nulls.
Value get(const char* begin, const char* end, const Value& defaultValue) const;
/// Return the member named key if it exist, defaultValue otherwise.
/// \note deep copy
/// \param key may contain embedded nulls.
Value get(const std::string& key, const Value& defaultValue) const;
#ifdef JSON_USE_CPPTL
/// Return the member named key if it exist, defaultValue otherwise.
/// \note deep copy
Value get(const CppTL::ConstString& key, const Value& defaultValue) const;
#endif
/// Most general and efficient version of isMember()const, get()const,
/// and operator[]const
/// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30
Value const* find(char const* begin, char const* end) const;
/// Most general and efficient version of object-mutators.
/// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30
/// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue.
Value const* demand(char const* begin, char const* end);
/// \brief Remove and return the named member.
///
/// Do nothing if it did not exist.
/// \return the removed Value, or null.
/// \pre type() is objectValue or nullValue
/// \post type() is unchanged
/// \deprecated
Value removeMember(const char* key);
/// Same as removeMember(const char*)
/// \param key may contain embedded nulls.
/// \deprecated
Value removeMember(const std::string& key);
/// Same as removeMember(const char* begin, const char* end, Value* removed),
/// but 'key' is null-terminated.
bool removeMember(const char* key, Value* removed);
/** \brief Remove the named map member.
Update 'removed' iff removed.
\param key may contain embedded nulls.
\return true iff removed (no exceptions)
*/
bool removeMember(std::string const& key, Value* removed);
/// Same as removeMember(std::string const& key, Value* removed)
bool removeMember(const char* begin, const char* end, Value* removed);
/** \brief Remove the indexed array element.
O(n) expensive operations.
Update 'removed' iff removed.
\return true iff removed (no exceptions)
*/
bool removeIndex(ArrayIndex i, Value* removed);
/// Return true if the object has a member named key.
/// \note 'key' must be null-terminated.
bool isMember(const char* key) const;
/// Return true if the object has a member named key.
/// \param key may contain embedded nulls.
bool isMember(const std::string& key) const;
/// Same as isMember(std::string const& key)const
bool isMember(const char* begin, const char* end) const;
#ifdef JSON_USE_CPPTL
/// Return true if the object has a member named key.
bool isMember(const CppTL::ConstString& key) const;
#endif
/// \brief Return a list of the member names.
///
/// If null, return an empty list.
/// \pre type() is objectValue or nullValue
/// \post if type() was nullValue, it remains nullValue
Members getMemberNames() const;
//# ifdef JSON_USE_CPPTL
// EnumMemberNames enumMemberNames() const;
// EnumValues enumValues() const;
//# endif
/// \deprecated Always pass len.
JSONCPP_DEPRECATED("Use setComment(std::string const&) instead.")
void setComment(const char* comment, CommentPlacement placement);
/// Comments must be //... or /* ... */
void setComment(const char* comment, size_t len, CommentPlacement placement);
/// Comments must be //... or /* ... */
void setComment(const std::string& comment, CommentPlacement placement);
bool hasComment(CommentPlacement placement) const;
/// Include delimiters and embedded newlines.
std::string getComment(CommentPlacement placement) const;
std::string toStyledString() const;
const_iterator begin() const;
const_iterator end() const;
iterator begin();
iterator end();
// Accessors for the [start, limit) range of bytes within the JSON text from
// which this value was parsed, if any.
void setOffsetStart(size_t start);
void setOffsetLimit(size_t limit);
size_t getOffsetStart() const;
size_t getOffsetLimit() const;
private:
void initBasic(ValueType type, bool allocated = false);
Value& resolveReference(const char* key);
Value& resolveReference(const char* key, const char* end);
struct CommentInfo {
CommentInfo();
~CommentInfo();
void setComment(const char* text, size_t len);
char* comment_;
};
// struct MemberNamesTransform
//{
// typedef const char *result_type;
// const char *operator()( const CZString &name ) const
// {
// return name.c_str();
// }
//};
union ValueHolder {
LargestInt int_;
LargestUInt uint_;
double real_;
bool bool_;
char* string_; // actually ptr to unsigned, followed by str, unless !allocated_
ObjectValues* map_;
} value_;
ValueType type_ : 8;
unsigned int allocated_ : 1; // Notes: if declared as bool, bitfield is useless.
// If not allocated_, string_ must be null-terminated.
CommentInfo* comments_;
// [start, limit) byte offsets in the source JSON text from which this Value
// was extracted.
size_t start_;
size_t limit_;
};
/** \brief Experimental and untested: represents an element of the "path" to
* access a node.
*/
class JSON_API PathArgument {
public:
friend class Path;
PathArgument();
PathArgument(ArrayIndex index);
PathArgument(const char* key);
PathArgument(const std::string& key);
private:
enum Kind {
kindNone = 0,
kindIndex,
kindKey
};
std::string key_;
ArrayIndex index_;
Kind kind_;
};
/** \brief Experimental and untested: represents a "path" to access a node.
*
* Syntax:
* - "." => root node
* - ".[n]" => elements at index 'n' of root node (an array value)
* - ".name" => member named 'name' of root node (an object value)
* - ".name1.name2.name3"
* - ".[0][1][2].name1[3]"
* - ".%" => member name is provided as parameter
* - ".[%]" => index is provied as parameter
*/
class JSON_API Path {
public:
Path(const std::string& path,
const PathArgument& a1 = PathArgument(),
const PathArgument& a2 = PathArgument(),
const PathArgument& a3 = PathArgument(),
const PathArgument& a4 = PathArgument(),
const PathArgument& a5 = PathArgument());
const Value& resolve(const Value& root) const;
Value resolve(const Value& root, const Value& defaultValue) const;
/// Creates the "path" to access the specified node and returns a reference on
/// the node.
Value& make(Value& root) const;
private:
typedef std::vector<const PathArgument*> InArgs;
typedef std::vector<PathArgument> Args;
void makePath(const std::string& path, const InArgs& in);
void addPathInArg(const std::string& path,
const InArgs& in,
InArgs::const_iterator& itInArg,
PathArgument::Kind kind);
void invalidPath(const std::string& path, int location);
Args args_;
};
/** \brief base class for Value iterators.
*
*/
class JSON_API ValueIteratorBase {
public:
typedef std::bidirectional_iterator_tag iterator_category;
typedef unsigned int size_t;
typedef int difference_type;
typedef ValueIteratorBase SelfType;
bool operator==(const SelfType& other) const { return isEqual(other); }
bool operator!=(const SelfType& other) const { return !isEqual(other); }
difference_type operator-(const SelfType& other) const {
return other.computeDistance(*this);
}
/// Return either the index or the member name of the referenced value as a
/// Value.
Value key() const;
/// Return the index of the referenced Value, or -1 if it is not an arrayValue.
UInt index() const;
/// Return the member name of the referenced Value, or "" if it is not an
/// objectValue.
/// \note Avoid `c_str()` on result, as embedded zeroes are possible.
std::string name() const;
/// Return the member name of the referenced Value. "" if it is not an
/// objectValue.
/// \deprecated This cannot be used for UTF-8 strings, since there can be embedded nulls.
JSONCPP_DEPRECATED("Use `key = name();` instead.")
char const* memberName() const;
/// Return the member name of the referenced Value, or NULL if it is not an
/// objectValue.
/// \note Better version than memberName(). Allows embedded nulls.
char const* memberName(char const** end) const;
protected:
Value& deref() const;
void increment();
void decrement();
difference_type computeDistance(const SelfType& other) const;
bool isEqual(const SelfType& other) const;
void copy(const SelfType& other);
private:
Value::ObjectValues::iterator current_;
// Indicates that iterator is for a null value.
bool isNull_;
public:
// For some reason, BORLAND needs these at the end, rather
// than earlier. No idea why.
ValueIteratorBase();
explicit ValueIteratorBase(const Value::ObjectValues::iterator& current);
};
/** \brief const iterator for object and array value.
*
*/
class JSON_API ValueConstIterator : public ValueIteratorBase {
friend class Value;
public:
typedef const Value value_type;
//typedef unsigned int size_t;
//typedef int difference_type;
typedef const Value& reference;
typedef const Value* pointer;
typedef ValueConstIterator SelfType;
ValueConstIterator();
private:
/*! \internal Use by Value to create an iterator.
*/
explicit ValueConstIterator(const Value::ObjectValues::iterator& current);
public:
SelfType& operator=(const ValueIteratorBase& other);
SelfType operator++(int) {
SelfType temp(*this);
++*this;
return temp;
}
SelfType operator--(int) {
SelfType temp(*this);
--*this;
return temp;
}
SelfType& operator--() {
decrement();
return *this;
}
SelfType& operator++() {
increment();
return *this;
}
reference operator*() const { return deref(); }
pointer operator->() const { return &deref(); }
};
/** \brief Iterator for object and array value.
*/
class JSON_API ValueIterator : public ValueIteratorBase {
friend class Value;
public:
typedef Value value_type;
typedef unsigned int size_t;
typedef int difference_type;
typedef Value& reference;
typedef Value* pointer;
typedef ValueIterator SelfType;
ValueIterator();
ValueIterator(const ValueConstIterator& other);
ValueIterator(const ValueIterator& other);
private:
/*! \internal Use by Value to create an iterator.
*/
explicit ValueIterator(const Value::ObjectValues::iterator& current);
public:
SelfType& operator=(const SelfType& other);
SelfType operator++(int) {
SelfType temp(*this);
++*this;
return temp;
}
SelfType operator--(int) {
SelfType temp(*this);
--*this;
return temp;
}
SelfType& operator--() {
decrement();
return *this;
}
SelfType& operator++() {
increment();
return *this;
}
reference operator*() const { return deref(); }
pointer operator->() const { return &deref(); }
};
} // namespace Json
namespace std {
/// Specialize std::swap() for Json::Value.
template<>
inline void swap(Json::Value& a, Json::Value& b) { a.swap(b); }
}
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma warning(pop)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#endif // CPPTL_JSON_H_INCLUDED

View File

@ -1,13 +0,0 @@
// DO NOT EDIT. This file (and "version") is generated by CMake.
// Run CMake configure step to update it.
#ifndef JSON_VERSION_H_INCLUDED
# define JSON_VERSION_H_INCLUDED
# define JSONCPP_VERSION_STRING "1.6.5"
# define JSONCPP_VERSION_MAJOR 1
# define JSONCPP_VERSION_MINOR 6
# define JSONCPP_VERSION_PATCH 5
# define JSONCPP_VERSION_QUALIFIER
# define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8))
#endif // JSON_VERSION_H_INCLUDED

View File

@ -1,13 +0,0 @@
// DO NOT EDIT. This file (and "version") is generated by CMake.
// Run CMake configure step to update it.
#ifndef JSON_VERSION_H_INCLUDED
# define JSON_VERSION_H_INCLUDED
# define JSONCPP_VERSION_STRING "@JSONCPP_VERSION@"
# define JSONCPP_VERSION_MAJOR @JSONCPP_VERSION_MAJOR@
# define JSONCPP_VERSION_MINOR @JSONCPP_VERSION_MINOR@
# define JSONCPP_VERSION_PATCH @JSONCPP_VERSION_PATCH@
# define JSONCPP_VERSION_QUALIFIER
# define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8))
#endif // JSON_VERSION_H_INCLUDED

View File

@ -1,327 +0,0 @@
// Copyright 2007-2010 Baptiste Lepilleur
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_WRITER_H_INCLUDED
#define JSON_WRITER_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include "value.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
#include <vector>
#include <string>
#include <ostream>
// Disable warning C4251: <data member>: <type> needs to have dll-interface to
// be used by...
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma warning(push)
#pragma warning(disable : 4251)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
namespace Json {
class Value;
/**
Usage:
\code
using namespace Json;
void writeToStdout(StreamWriter::Factory const& factory, Value const& value) {
std::unique_ptr<StreamWriter> const writer(
factory.newStreamWriter());
writer->write(value, &std::cout);
std::cout << std::endl; // add lf and flush
}
\endcode
*/
class JSON_API StreamWriter {
protected:
std::ostream* sout_; // not owned; will not delete
public:
StreamWriter();
virtual ~StreamWriter();
/** Write Value into document as configured in sub-class.
Do not take ownership of sout, but maintain a reference during function.
\pre sout != NULL
\return zero on success (For now, we always return zero, so check the stream instead.)
\throw std::exception possibly, depending on configuration
*/
virtual int write(Value const& root, std::ostream* sout) = 0;
/** \brief A simple abstract factory.
*/
class JSON_API Factory {
public:
virtual ~Factory();
/** \brief Allocate a CharReader via operator new().
* \throw std::exception if something goes wrong (e.g. invalid settings)
*/
virtual StreamWriter* newStreamWriter() const = 0;
}; // Factory
}; // StreamWriter
/** \brief Write into stringstream, then return string, for convenience.
* A StreamWriter will be created from the factory, used, and then deleted.
*/
std::string JSON_API writeString(StreamWriter::Factory const& factory, Value const& root);
/** \brief Build a StreamWriter implementation.
Usage:
\code
using namespace Json;
Value value = ...;
StreamWriterBuilder builder;
builder["commentStyle"] = "None";
builder["indentation"] = " "; // or whatever you like
std::unique_ptr<Json::StreamWriter> writer(
builder.newStreamWriter());
writer->write(value, &std::cout);
std::cout << std::endl; // add lf and flush
\endcode
*/
class JSON_API StreamWriterBuilder : public StreamWriter::Factory {
public:
// Note: We use a Json::Value so that we can add data-members to this class
// without a major version bump.
/** Configuration of this builder.
Available settings (case-sensitive):
- "commentStyle": "None" or "All"
- "indentation": "<anything>"
- "enableYAMLCompatibility": false or true
- slightly change the whitespace around colons
- "dropNullPlaceholders": false or true
- Drop the "null" string from the writer's output for nullValues.
Strictly speaking, this is not valid JSON. But when the output is being
fed to a browser's Javascript, it makes for smaller output and the
browser can handle the output just fine.
You can examine 'settings_` yourself
to see the defaults. You can also write and read them just like any
JSON Value.
\sa setDefaults()
*/
Json::Value settings_;
StreamWriterBuilder();
virtual ~StreamWriterBuilder();
/**
* \throw std::exception if something goes wrong (e.g. invalid settings)
*/
virtual StreamWriter* newStreamWriter() const;
/** \return true if 'settings' are legal and consistent;
* otherwise, indicate bad settings via 'invalid'.
*/
bool validate(Json::Value* invalid) const;
/** A simple way to update a specific setting.
*/
Value& operator[](std::string key);
/** Called by ctor, but you can use this to reset settings_.
* \pre 'settings' != NULL (but Json::null is fine)
* \remark Defaults:
* \snippet src/lib_json/json_writer.cpp StreamWriterBuilderDefaults
*/
static void setDefaults(Json::Value* settings);
};
/** \brief Abstract class for writers.
* \deprecated Use StreamWriter. (And really, this is an implementation detail.)
*/
class JSON_API Writer {
public:
virtual ~Writer();
virtual std::string write(const Value& root) = 0;
};
/** \brief Outputs a Value in <a HREF="http://www.json.org">JSON</a> format
*without formatting (not human friendly).
*
* The JSON document is written in a single line. It is not intended for 'human'
*consumption,
* but may be usefull to support feature such as RPC where bandwith is limited.
* \sa Reader, Value
* \deprecated Use StreamWriterBuilder.
*/
class JSON_API FastWriter : public Writer {
public:
FastWriter();
virtual ~FastWriter() {}
void enableYAMLCompatibility();
/** \brief Drop the "null" string from the writer's output for nullValues.
* Strictly speaking, this is not valid JSON. But when the output is being
* fed to a browser's Javascript, it makes for smaller output and the
* browser can handle the output just fine.
*/
void dropNullPlaceholders();
void omitEndingLineFeed();
public: // overridden from Writer
virtual std::string write(const Value& root);
private:
void writeValue(const Value& value);
std::string document_;
bool yamlCompatiblityEnabled_;
bool dropNullPlaceholders_;
bool omitEndingLineFeed_;
};
/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a
*human friendly way.
*
* The rules for line break and indent are as follow:
* - Object value:
* - if empty then print {} without indent and line break
* - if not empty the print '{', line break & indent, print one value per
*line
* and then unindent and line break and print '}'.
* - Array value:
* - if empty then print [] without indent and line break
* - if the array contains no object value, empty array or some other value
*types,
* and all the values fit on one lines, then print the array on a single
*line.
* - otherwise, it the values do not fit on one line, or the array contains
* object or non empty array, then print one value per line.
*
* If the Value have comments then they are outputed according to their
*#CommentPlacement.
*
* \sa Reader, Value, Value::setComment()
* \deprecated Use StreamWriterBuilder.
*/
class JSON_API StyledWriter : public Writer {
public:
StyledWriter();
virtual ~StyledWriter() {}
public: // overridden from Writer
/** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
* \param root Value to serialize.
* \return String containing the JSON document that represents the root value.
*/
virtual std::string write(const Value& root);
private:
void writeValue(const Value& value);
void writeArrayValue(const Value& value);
bool isMultineArray(const Value& value);
void pushValue(const std::string& value);
void writeIndent();
void writeWithIndent(const std::string& value);
void indent();
void unindent();
void writeCommentBeforeValue(const Value& root);
void writeCommentAfterValueOnSameLine(const Value& root);
bool hasCommentForValue(const Value& value);
static std::string normalizeEOL(const std::string& text);
typedef std::vector<std::string> ChildValues;
ChildValues childValues_;
std::string document_;
std::string indentString_;
int rightMargin_;
int indentSize_;
bool addChildValues_;
};
/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a
human friendly way,
to a stream rather than to a string.
*
* The rules for line break and indent are as follow:
* - Object value:
* - if empty then print {} without indent and line break
* - if not empty the print '{', line break & indent, print one value per
line
* and then unindent and line break and print '}'.
* - Array value:
* - if empty then print [] without indent and line break
* - if the array contains no object value, empty array or some other value
types,
* and all the values fit on one lines, then print the array on a single
line.
* - otherwise, it the values do not fit on one line, or the array contains
* object or non empty array, then print one value per line.
*
* If the Value have comments then they are outputed according to their
#CommentPlacement.
*
* \param indentation Each level will be indented by this amount extra.
* \sa Reader, Value, Value::setComment()
* \deprecated Use StreamWriterBuilder.
*/
class JSON_API StyledStreamWriter {
public:
StyledStreamWriter(std::string indentation = "\t");
~StyledStreamWriter() {}
public:
/** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
* \param out Stream to write to. (Can be ostringstream, e.g.)
* \param root Value to serialize.
* \note There is no point in deriving from Writer, since write() should not
* return a value.
*/
void write(std::ostream& out, const Value& root);
private:
void writeValue(const Value& value);
void writeArrayValue(const Value& value);
bool isMultineArray(const Value& value);
void pushValue(const std::string& value);
void writeIndent();
void writeWithIndent(const std::string& value);
void indent();
void unindent();
void writeCommentBeforeValue(const Value& root);
void writeCommentAfterValueOnSameLine(const Value& root);
bool hasCommentForValue(const Value& value);
static std::string normalizeEOL(const std::string& text);
typedef std::vector<std::string> ChildValues;
ChildValues childValues_;
std::ostream* document_;
std::string indentString_;
int rightMargin_;
std::string indentation_;
bool addChildValues_ : 1;
bool indented_ : 1;
};
#if defined(JSON_HAS_INT64)
std::string JSON_API valueToString(Int value);
std::string JSON_API valueToString(UInt value);
#endif // if defined(JSON_HAS_INT64)
std::string JSON_API valueToString(LargestInt value);
std::string JSON_API valueToString(LargestUInt value);
std::string JSON_API valueToString(double value);
std::string JSON_API valueToString(bool value);
std::string JSON_API valueToQuotedString(const char* value);
/// \brief Output using the StyledStreamWriter.
/// \see Json::operator>>()
JSON_API std::ostream& operator<<(std::ostream&, const Value& root);
} // namespace Json
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma warning(pop)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#endif // JSON_WRITER_H_INCLUDED

@ -1 +1 @@
Subproject commit 8dc2fbf275628ab306a0976ac54533dd0e181f65
Subproject commit 357ef885afa5112c8cb873eab21d94d26744c0ab

View File

@ -2,7 +2,7 @@ apply plugin: 'com.android.application'
android {
compileSdkVersion 28
ndkVersion "25.0.8775105"
//ndkVersion "25.1.xxx"
defaultConfig {
applicationId "com.zlmediakit.demo"
minSdkVersion 15

View File

@ -177,11 +177,11 @@ JNI_API(jboolean, startDemo, jstring ini_dir){
s_tread_id = pthread_self();
try {
//http根目录修改默认路径
mINI::Instance()[Http::kRootPath] = mINI::Instance()[Hls::kFilePath] = sd_path + "/httpRoot";
mINI::Instance()[Http::kRootPath] = sd_path + "/httpRoot";
//mp4录制点播根目录修改默认路径
mINI::Instance()[Record::kFilePath] = mINI::Instance()[Hls::kFilePath] = sd_path + "/httpRoot";
mINI::Instance()[Protocol::kMP4SavePath] = sd_path + "/httpRoot";
//hls根目录修改默认路径
mINI::Instance()[Hls::kFilePath] = mINI::Instance()[Hls::kFilePath] = sd_path + "/httpRoot";
mINI::Instance()[Protocol::kHlsSavePath] = sd_path + "/httpRoot";
//替换默认端口号(在配置文件未生成时有效)
mINI::Instance()["http.port"] = 8080;
mINI::Instance()["http.sslport"] = 8443;
@ -232,18 +232,18 @@ JNI_API(jlong, createMediaPlayer, jstring url, jobject callback){
auto viedoTrack = strongPlayer->getTrack(TrackVideo);
if (viedoTrack) {
viedoTrack->addDelegate(std::make_shared<FrameWriterInterfaceHelper>([globalWeakRef](const Frame::Ptr &frame) {
viedoTrack->addDelegate([globalWeakRef](const Frame::Ptr &frame) {
emitEvent((jobject)globalWeakRef,"onData","(L" MediaFrameSign ";)V",makeJavaFrame(env,frame));
return true;
}));
});
}
auto audioTrack = strongPlayer->getTrack(TrackAudio);
if (audioTrack) {
audioTrack->addDelegate(std::make_shared<FrameWriterInterfaceHelper>([globalWeakRef](const Frame::Ptr &frame) {
audioTrack->addDelegate([globalWeakRef](const Frame::Ptr &frame) {
emitEvent((jobject)globalWeakRef,"onData","(L" MediaFrameSign ";)V",makeJavaFrame(env,frame));
return true;
}));
});
}
});

View File

@ -52,7 +52,7 @@ option(ENABLE_SRT "Enable SRT" ON)
option(ENABLE_TESTS "Enable Tests" ON)
option(ENABLE_WEBRTC "Enable WebRTC" ON)
option(ENABLE_X264 "Enable x264" OFF)
option(ENABLE_WEPOLL "Enable wepoll" OFF)
option(ENABLE_WEPOLL "Enable wepoll" ON)
option(USE_SOLUTION_FOLDERS "Enable solution dir supported" ON)
@ -97,11 +97,12 @@ set(LIBRARY_OUTPUT_PATH ${OUTPUT_DIR})
set(EXECUTABLE_OUTPUT_PATH ${OUTPUT_DIR})
# git
set(COMMIT_HASH "Git_NotFound_Unkown_commit")
set(BRANCH_NAME "Git_NotFound_Unkown_branch")
set(COMMIT_HASH "Git_Unkown_commit")
set(COMMIT_TIME "Git_Unkown_time")
set(BRANCH_NAME "Git_Unkown_branch")
set(BUILD_TIME "")
string(TIMESTAMP BUILD_TIME "%Y-%m-%d %H:%M:%S")
string(TIMESTAMP BUILD_TIME "%Y-%m-%dT%H:%M:%S")
find_package(Git QUIET)
if(GIT_FOUND)
@ -117,6 +118,13 @@ if(GIT_FOUND)
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
execute_process(
COMMAND ${GIT_EXECUTABLE} log --format=format:%aI -1
OUTPUT_VARIABLE COMMIT_TIME
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
endif()
configure_file(
@ -124,7 +132,7 @@ configure_file(
${CMAKE_CURRENT_BINARY_DIR}/version.h
@ONLY)
message(STATUS "Git version is ${BRANCH_NAME}:${COMMIT_HASH}:${BUILD_TIME}")
message(STATUS "Git version is ${BRANCH_NAME} ${COMMIT_HASH}/${COMMIT_TIME} ${BUILD_TIME}")
##############################################################################
@ -172,12 +180,12 @@ if(UNIX)
"-Wno-unused-function;-Wno-unused-parameter;-Wno-unused-variable"
"-Wno-error=extra;-Wno-error=missing-field-initializers;-Wno-error=type-limits")
elseif(WIN32)
set(COMPILE_OPTIONS_DEFAULT
# TODO: /wd4819
"/wd4566;/wd4819"
# warning C4530: C++ exception handler used, but unwind semantics are not enabled.
"/EHsc")
if (MSVC)
set(COMPILE_OPTIONS_DEFAULT
# TODO: /wd4819
"/wd4566;/wd4819"
# warning C4530: C++ exception handler used, but unwind semantics are not enabled.
"/EHsc")
# disable Windows logo
list(APPEND COMPILE_OPTIONS_DEFAULT "/nologo")
list(APPEND CMAKE_STATIC_LINKER_FLAGS "/nologo")
@ -299,7 +307,7 @@ if(ENABLE_FFMPEG)
endif()
if(ENABLE_MEM_DEBUG)
list(APPEND COMPILE_OPTIONS_DEFAULT
update_cached_list(MK_LINK_LIBRARIES
"-Wl,-wrap,free;-Wl,-wrap,malloc;-Wl,-wrap,realloc;-Wl,-wrap,calloc")
update_cached_list(MK_COMPILE_DEFINITIONS ENABLE_MEM_DEBUG)
message(STATUS "已启用内存调试功能")
@ -308,6 +316,10 @@ endif()
if(ENABLE_ASAN)
list(APPEND COMPILE_OPTIONS_DEFAULT
"-fsanitize=address;-fno-omit-frame-pointer")
# https://github.com/google/sanitizers/wiki/AddressSanitizer#using-addresssanitizer
# > In order to use AddressSanitizer you will need to
# > compile and link your program using clang with the -fsanitize=address switch.
update_cached_list(MK_LINK_LIBRARIES "-fsanitize=address")
message(STATUS "已启用 Address Sanitize")
endif()
@ -413,6 +425,9 @@ endif()
# for version.h
include_directories(${CMAKE_CURRENT_BINARY_DIR})
# for assert.h
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/3rdpart)
add_subdirectory(3rdpart)
add_subdirectory(src)

View File

@ -43,7 +43,7 @@
## 功能清单
### 功能一览
<img width="800" alt="功能一览" src="https://user-images.githubusercontent.com/11495632/114176523-d50fce80-996d-11eb-81f8-0a2e2715ba7b.png">
<img width="800" alt="功能一览" src="https://user-images.githubusercontent.com/11495632/190864440-91c45f8f-480f-43db-8110-5bb44e6300ff.png">
- RTSP[S]
- RTSP[S] 服务器支持RTMP/MP4/HLS转RTSP[S],支持亚马逊echo show这样的设备
@ -92,10 +92,11 @@
- 支持http文件访问鉴权
- GB28181与RTP推流
- 支持UDP/TCP国标RTP(PS或TS)推流服务器可以转换成RTSP/RTMP/HLS等协议
- 支持RTSP/RTMP/HLS转国标推流客户端支持TCP/UDP模式提供相应restful api
- 支持UDP/TCP RTP(PS/TS/ES)推流服务器可以转换成RTSP/RTMP/HLS等协议
- 支持RTSP/RTMP/HLS等协议转rtp推流客户端支持TCP/UDP模式提供相应restful api,支持主动被动方式。
- 支持H264/H265/AAC/G711/OPUS编码
- 支持海康ehome推流
- 支持GB28181主动拉流模式
- MP4点播与录制
- 支持录制为FLV/HLS/MP4
@ -114,6 +115,7 @@
- 支持rtp扩展解析
- 支持GOP缓冲webrtc播放秒开
- 支持datachannel
- 支持webrtc over tcp模式
- [SRT支持](./srt/srt.md)
- 其他
- 支持丰富的restful api以及web hook事件

View File

@ -213,9 +213,9 @@ git submodule update --init
WarnL << "none video Track!";
return;
}
viedoTrack->addDelegate(std::make_shared<FrameWriterInterfaceHelper>([](const Frame::Ptr &frame) {
viedoTrack->addDelegate([](const Frame::Ptr &frame) {
//please decode video here
}));
});
});
player->setOnShutdown([](const SockException &ex) {

View File

@ -1 +0,0 @@
theme: jekyll-theme-cayman

View File

@ -28,7 +28,7 @@ file(GLOB API_SRC_LIST
source/*.cpp
source/*.h)
set(LINK_LIBRARIES ${MK_LINK_LIBRARIES} jsoncpp)
set(LINK_LIBRARIES ${MK_LINK_LIBRARIES})
if(IOS)
add_library(mk_api STATIC ${API_SRC_LIST})
@ -38,7 +38,10 @@ if(IOS)
endif ()
set(COMPILE_DEFINITIONS ${MK_COMPILE_DEFINITIONS})
list(APPEND COMPILE_DEFINITIONS GENERATE_EXPORT)
if (MSVC)
list(APPEND COMPILE_DEFINITIONS GENERATE_EXPORT)
endif ()
if(ENABLE_API_STATIC_LIB)
add_library(mk_api STATIC ${API_SRC_LIST})
@ -62,7 +65,7 @@ if(CMAKE_SYSTEM_NAME MATCHES "Linux")
elseif(CMAKE_SYSTEM_NAME MATCHES "Android")
target_link_libraries(mk_api log -Wl,--start-group ${LINK_LIBRARIES} -Wl,--end-group)
else()
target_link_libraries(mk_api jsoncpp ${LINK_LIBRARIES})
target_link_libraries(mk_api ${LINK_LIBRARIES})
endif()
generate_export_header(mk_api

View File

@ -162,11 +162,32 @@ API_EXPORT uint16_t API_CALL mk_rtp_server_start(uint16_t port);
/**
* rtc服务器
* @param port rtp监听端口
* @param port rtc监听端口
* @return 0:,0:
*/
API_EXPORT uint16_t API_CALL mk_rtc_server_start(uint16_t port);
//获取webrtc answer sdp回调函数
typedef void(API_CALL *on_mk_webrtc_get_answer_sdp)(void *user_data, const char *answer, const char *err);
/**
* webrtc交换sdpoffer sdp生成answer sdp
* @param user_data
* @param cb
* @param type webrtc插件类型echo,play,push
* @param offer webrtc offer sdp
* @param url rtc url, rtc://__defaultVhost/app/stream?key1=val1&key2=val2
*/
API_EXPORT void API_CALL mk_webrtc_get_answer_sdp(void *user_data, on_mk_webrtc_get_answer_sdp cb, const char *type,
const char *offer, const char *url);
/**
* srt服务器
* @param port srt监听端口
* @return 0:,0:
*/
API_EXPORT uint16_t API_CALL mk_srt_server_start(uint16_t port);
/**
* shell服务器

View File

@ -53,8 +53,10 @@ typedef struct {
* 广
* @param url_info url相关信息
* @param sender
* @return 1
* 0
*/
void (API_CALL *on_mk_media_not_found)(const mk_media_info url_info,
int (API_CALL *on_mk_media_not_found)(const mk_media_info url_info,
const mk_sock_info sender);
/**
@ -152,7 +154,7 @@ typedef struct {
size_t total_seconds,
int is_player,
const mk_sock_info sender);
/**
* 广
@ -178,4 +180,3 @@ API_EXPORT void API_CALL mk_events_listen(const mk_events *events);
}
#endif
#endif //MK_EVENTS_H

View File

@ -177,14 +177,6 @@ API_EXPORT void API_CALL mk_http_response_invoker_do(const mk_http_response_invo
int response_code,
const char **response_header,
const mk_http_body response_body);
/**
* HttpSession::HttpResponseInvoker(const string &codeOut, const StrCaseMap &headerOut, const HttpBody::Ptr &body);
Parser();
SockInfo();
*/
API_EXPORT void API_CALL mk_webrtc_http_response_invoker_do( const mk_http_response_invoker invoker,
const mk_parser parser,
const mk_sock_info sender);
/**
* HttpSession::HttpResponseInvoker(const string &codeOut, const StrCaseMap &headerOut, const string &body);

View File

@ -19,11 +19,27 @@ typedef void* mk_rtp_server;
/**
* GB28181 RTP
* @param port 0
* @param enable_tcp udp端口时是否同时监听tcp端口
* @param tcp_mode tcp模式(0: 1: 2: )
* @param stream_id id
* @return
*/
API_EXPORT mk_rtp_server API_CALL mk_rtp_server_create(uint16_t port, int enable_tcp, const char *stream_id);
API_EXPORT mk_rtp_server API_CALL mk_rtp_server_create(uint16_t port, int tcp_mode, const char *stream_id);
/**
* TCP
*/
typedef void(API_CALL *on_mk_rtp_server_connected)(void *user_data, int err, const char *what, int sys_err);
/**
* TCP
* @param @param ctx
* @param dst_url
* @param dst_port
* @param cb
* @param user_data
* @return
*/
API_EXPORT void API_CALL mk_rtp_server_connect(mk_rtp_server ctx, const char *dst_url, uint16_t dst_port, on_mk_rtp_server_connected cb, void *user_data);
/**
* GB28181 RTP

View File

@ -37,7 +37,13 @@ static std::shared_ptr<RtpServer> rtpServer;
#ifdef ENABLE_WEBRTC
#include "../webrtc/WebRtcSession.h"
static std::shared_ptr<UdpServer> rtcServer;
static std::shared_ptr<UdpServer> rtcServer_udp;
static std::shared_ptr<TcpServer> rtcServer_tcp;
#endif
#if defined(ENABLE_SRT)
#include "../srt/SrtSession.hpp"
static std::shared_ptr<UdpServer> srtServer;
#endif
//////////////////////////environment init///////////////////////////
@ -62,8 +68,16 @@ API_EXPORT void API_CALL mk_stop_all_server(){
CLEAR_ARR(rtsp_server);
CLEAR_ARR(rtmp_server);
CLEAR_ARR(http_server);
shell_server = nullptr;
#ifdef ENABLE_RTPPROXY
rtpServer = nullptr;
#endif
#ifdef ENABLE_WEBRTC
rtcServer_udp = nullptr;
rtcServer_tcp = nullptr;
#endif
#ifdef ENABLE_SRT
srtServer = nullptr;
#endif
stopAllTcpServer();
}
@ -160,13 +174,13 @@ API_EXPORT uint16_t API_CALL mk_http_server_start(uint16_t port, int ssl) {
try {
http_server[ssl] = std::make_shared<TcpServer>();
if(ssl){
http_server[ssl]->start<TcpSessionWithSSL<HttpSession> >(port);
http_server[ssl]->start<SessionWithSSL<HttpSession> >(port);
} else{
http_server[ssl]->start<HttpSession>(port);
}
return http_server[ssl]->getPort();
} catch (std::exception &ex) {
http_server[ssl].reset();
http_server[ssl] = nullptr;;
WarnL << ex.what();
return 0;
}
@ -177,13 +191,13 @@ API_EXPORT uint16_t API_CALL mk_rtsp_server_start(uint16_t port, int ssl) {
try {
rtsp_server[ssl] = std::make_shared<TcpServer>();
if(ssl){
rtsp_server[ssl]->start<TcpSessionWithSSL<RtspSession> >(port);
rtsp_server[ssl]->start<SessionWithSSL<RtspSession> >(port);
}else{
rtsp_server[ssl]->start<RtspSession>(port);
}
return rtsp_server[ssl]->getPort();
} catch (std::exception &ex) {
rtsp_server[ssl].reset();
rtsp_server[ssl] = nullptr;;
WarnL << ex.what();
return 0;
}
@ -194,13 +208,13 @@ API_EXPORT uint16_t API_CALL mk_rtmp_server_start(uint16_t port, int ssl) {
try {
rtmp_server[ssl] = std::make_shared<TcpServer>();
if(ssl){
rtmp_server[ssl]->start<TcpSessionWithSSL<RtmpSession> >(port);
rtmp_server[ssl]->start<SessionWithSSL<RtmpSession> >(port);
}else{
rtmp_server[ssl]->start<RtmpSession>(port);
}
return rtmp_server[ssl]->getPort();
} catch (std::exception &ex) {
rtmp_server[ssl].reset();
rtmp_server[ssl] = nullptr;;
WarnL << ex.what();
return 0;
}
@ -214,7 +228,7 @@ API_EXPORT uint16_t API_CALL mk_rtp_server_start(uint16_t port){
rtpServer->start(port);
return rtpServer->getPort();
} catch (std::exception &ex) {
rtpServer.reset();
rtpServer = nullptr;;
WarnL << ex.what();
return 0;
}
@ -227,9 +241,9 @@ API_EXPORT uint16_t API_CALL mk_rtp_server_start(uint16_t port){
API_EXPORT uint16_t API_CALL mk_rtc_server_start(uint16_t port) {
#ifdef ENABLE_WEBRTC
try {
//创建rtc服务器
rtcServer = std::make_shared<UdpServer>();
rtcServer->setOnCreateSocket([](const EventPoller::Ptr &poller, const Buffer::Ptr &buf, struct sockaddr *, int) {
//创建rtc udp服务器
rtcServer_udp = std::make_shared<UdpServer>();
rtcServer_udp->setOnCreateSocket([](const EventPoller::Ptr &poller, const Buffer::Ptr &buf, struct sockaddr *, int) {
if (!buf) {
return Socket::createSocket(poller, false);
}
@ -240,11 +254,82 @@ API_EXPORT uint16_t API_CALL mk_rtc_server_start(uint16_t port) {
}
return Socket::createSocket(new_poller, false);
});
rtcServer->start<WebRtcSession>(port);
return rtcServer->getPort();
rtcServer_udp->start<WebRtcSession>(port);
//创建rtc tcp服务器
rtcServer_tcp = std::make_shared<TcpServer>();
rtcServer_tcp->start<WebRtcSession>(rtcServer_udp->getPort());
return rtcServer_udp->getPort();
} catch (std::exception &ex) {
rtcServer.reset();
rtcServer_udp = nullptr;
rtcServer_tcp = nullptr;
WarnL << ex.what();
return 0;
}
#else
WarnL << "未启用webrtc功能, 编译时请开启ENABLE_WEBRTC";
return 0;
#endif
}
#ifdef ENABLE_WEBRTC
class WebRtcArgsUrl : public mediakit::WebRtcArgs {
public:
WebRtcArgsUrl(std::string url) { _url = std::move(url); }
~WebRtcArgsUrl() = default;
toolkit::variant operator[](const std::string &key) const override {
if (key == "url") {
return _url;
}
return "";
}
private:
std::string _url;
};
#endif
API_EXPORT void API_CALL mk_webrtc_get_answer_sdp(void *user_data, on_mk_webrtc_get_answer_sdp cb, const char *type,
const char *offer, const char *url) {
#ifdef ENABLE_WEBRTC
assert(type && offer && url && cb);
auto session = std::make_shared<HttpSession>(Socket::createSocket());
std::string offer_str = offer;
WebRtcPluginManager::Instance().getAnswerSdp(*session, type, WebRtcArgsUrl(url),
[offer_str, session, user_data, cb](const WebRtcInterface &exchanger) mutable {
try {
auto sdp_answer = const_cast<WebRtcInterface &>(exchanger).getAnswerSdp(offer_str);
cb(user_data, sdp_answer.data(), nullptr);
} catch (std::exception &ex) {
cb(user_data, nullptr, ex.what());
}
});
#else
WarnL << "未启用webrtc功能, 编译时请开启ENABLE_WEBRTC";
#endif
}
API_EXPORT uint16_t API_CALL mk_srt_server_start(uint16_t port) {
#ifdef ENABLE_SRT
try {
srtServer = std::make_shared<UdpServer>();
srtServer->setOnCreateSocket([](const EventPoller::Ptr &poller, const Buffer::Ptr &buf, struct sockaddr *, int) {
if (!buf) {
return Socket::createSocket(poller, false);
}
auto new_poller = SRT::SrtSession::queryPoller(buf);
if (!new_poller) {
//握手第一阶段
return Socket::createSocket(poller, false);
}
return Socket::createSocket(new_poller, false);
});
srtServer->start<SRT::SrtSession>(port);
return srtServer->getPort();
} catch (std::exception &ex) {
srtServer = nullptr;;
WarnL << ex.what();
return 0;
}
@ -260,7 +345,7 @@ API_EXPORT uint16_t API_CALL mk_shell_server_start(uint16_t port){
shell_server->start<ShellSession>(port);
return shell_server->getPort();
} catch (std::exception &ex) {
shell_server.reset();
shell_server = nullptr;;
WarnL << ex.what();
return 0;
}

View File

@ -141,8 +141,10 @@ API_EXPORT void API_CALL mk_events_listen(const mk_events *events){
NoticeCenter::Instance().addListener(&s_tag,Broadcast::kBroadcastNotFoundStream,[](BroadcastNotFoundStreamArgs){
if (s_events.on_mk_media_not_found) {
s_events.on_mk_media_not_found((mk_media_info) &args,
(mk_sock_info) &sender);
if (s_events.on_mk_media_not_found((mk_media_info) &args,
(mk_sock_info) &sender)) {
closePlayer();
}
}
});

View File

@ -18,11 +18,6 @@
#include "Http/HttpClient.h"
#include "Rtsp/RtspSession.h"
#ifdef ENABLE_WEBRTC
#include "jsoncpp/json.h"
#include "mk_webrtc_private.h"
#endif
using namespace toolkit;
using namespace mediakit;
@ -330,53 +325,6 @@ API_EXPORT void API_CALL mk_http_response_invoker_do(const mk_http_response_invo
(*invoker)(response_code,header,*body);
}
API_EXPORT void API_CALL mk_webrtc_http_response_invoker_do(const mk_http_response_invoker ctx_invoker,
const mk_parser ctx_parser,
const mk_sock_info ctx_sock ) {
assert(ctx_parser && ctx_invoker && ctx_sock);
#ifdef ENABLE_WEBRTC
static auto webrtc_cb = [](API_ARGS_STRING_ASYNC){
CHECK_ARGS("type");
auto type = allArgs["type"];
auto offer = allArgs.getArgs();
CHECK(!offer.empty(), "http body(webrtc offer sdp) is empty");
WebRtcPluginManager::Instance().getAnswerSdp(
*(static_cast<Session *>(&sender)), type, offer, WebRtcArgsImp(allArgs, sender.getIdentifier()),
[invoker, val, offer, headerOut](const WebRtcInterface &exchanger) mutable {
//设置返回类型
headerOut["Content-Type"] = HttpFileManager::getContentType(".json");
//设置跨域
headerOut["Access-Control-Allow-Origin"] = "*";
try {
val["sdp"] = const_cast<WebRtcInterface &>(exchanger).getAnswerSdp(offer);
val["id"] = exchanger.getIdentifier();
val["type"] = "answer";
invoker(200, headerOut, val.toStyledString());
} catch (std::exception &ex) {
val["code"] = API::Exception;
val["msg"] = ex.what();
invoker(200, headerOut, val.toStyledString());
}
});
};
Parser *parser = (Parser *)ctx_parser;
HttpSession::HttpResponseInvoker *invoker = (HttpSession::HttpResponseInvoker *)ctx_invoker;
SockInfo* sender = (SockInfo*)ctx_sock;
GET_CONFIG(std::string, charSet, Http::kCharSet);
HttpSession::KeyValue headerOut;
headerOut["Content-Type"] = std::string("application/json; charset=") + charSet;
Json::Value val;
val["code"] = API::Success;
webrtc_cb(*sender, headerOut, HttpAllArgs<std::string>(*parser, (std::string &)parser->Content()), val, *invoker);
#endif
};
API_EXPORT mk_http_response_invoker API_CALL mk_http_response_invoker_clone(const mk_http_response_invoker ctx){
assert(ctx);
HttpSession::HttpResponseInvoker *invoker = (HttpSession::HttpResponseInvoker *)ctx;

View File

@ -161,16 +161,16 @@ API_EXPORT uint32_t API_CALL mk_frame_get_flags(mk_frame frame) {
auto &ref = *((Frame::Ptr *) frame);
uint32_t ret = 0;
if (ref->keyFrame()) {
ret &= MK_FRAME_FLAG_IS_KEY;
ret |= MK_FRAME_FLAG_IS_KEY;
}
if (ref->configFrame()) {
ret &= MK_FRAME_FLAG_IS_CONFIG;
ret |= MK_FRAME_FLAG_IS_CONFIG;
}
if (ref->dropAble()) {
ret &= MK_FRAME_FLAG_DROP_ABLE;
ret |= MK_FRAME_FLAG_DROP_ABLE;
}
if (!ref->decodeAble()) {
ret &= MK_FRAME_FLAG_NOT_DECODE_ABLE;
ret |= MK_FRAME_FLAG_NOT_DECODE_ABLE;
}
return ret;
}
}

View File

@ -60,19 +60,15 @@ public:
protected:
// 通知其停止推流
bool close(MediaSource &sender,bool force) override{
if(!force && _channel->totalReaderCount()){
//非强制关闭且正有人在观看该视频
return false;
}
if(!_on_close){
bool close(MediaSource &sender) override {
if (!_on_close) {
//未设置回调,没法关闭
WarnL << "请使用mk_media_set_on_close函数设置回调函数!";
return false;
}
//请在回调中调用mk_media_release函数释放资源,否则MediaSource::close()操作不会生效
_on_close(_on_close_data);
WarnL << "close media:" << sender.getSchema() << "/" << sender.getVhost() << "/" << sender.getApp() << "/" << sender.getId() << " " << force;
WarnL << "close media: " << sender.getUrl();
return true;
}
@ -262,7 +258,7 @@ API_EXPORT void API_CALL mk_media_start_send_rtp(mk_media ctx, const char *dst_u
// sender参数无用
auto ref = *obj;
(*obj)->getOwnerPoller(MediaSource::NullMediaSource())->async([args, ref, cb, user_data]() {
(*obj)->getChannel()->getOwnerPoller(MediaSource::NullMediaSource())->async([args, ref, cb, user_data]() {
ref->getChannel()->startSendRtp(MediaSource::NullMediaSource(), args, [cb, user_data](uint16_t local_port, const SockException &ex) {
if (cb) {
cb(user_data, local_port, ex.getErrCode(), ex.what());
@ -277,12 +273,12 @@ API_EXPORT void API_CALL mk_media_stop_send_rtp(mk_media ctx, const char *ssrc){
// sender参数无用
auto ref = *obj;
string ssrc_str = ssrc ? ssrc : "";
(*obj)->getOwnerPoller(MediaSource::NullMediaSource())->async([ref, ssrc_str]() {
(*obj)->getChannel()->getOwnerPoller(MediaSource::NullMediaSource())->async([ref, ssrc_str]() {
ref->getChannel()->stopSendRtp(MediaSource::NullMediaSource(), ssrc_str);
});
}
API_EXPORT mk_thread API_CALL mk_media_get_owner_thread(mk_media ctx) {
MediaHelper::Ptr *obj = (MediaHelper::Ptr *)ctx;
return (*obj)->getOwnerPoller(MediaSource::NullMediaSource()).get();
return (*obj)->getChannel()->getOwnerPoller(MediaSource::NullMediaSource()).get();
}

View File

@ -16,23 +16,34 @@ using namespace toolkit;
#include "Rtp/RtpServer.h"
using namespace mediakit;
API_EXPORT mk_rtp_server API_CALL mk_rtp_server_create(uint16_t port, int enable_tcp, const char *stream_id){
API_EXPORT mk_rtp_server API_CALL mk_rtp_server_create(uint16_t port, int tcp_mode, const char *stream_id) {
RtpServer::Ptr *server = new RtpServer::Ptr(new RtpServer);
(*server)->start(port, stream_id, enable_tcp);
(*server)->start(port, stream_id, (RtpServer::TcpMode)tcp_mode);
return server;
}
API_EXPORT void API_CALL mk_rtp_server_release(mk_rtp_server ctx){
API_EXPORT void API_CALL mk_rtp_server_connect(mk_rtp_server ctx, const char *dst_url, uint16_t dst_port, on_mk_rtp_server_connected cb, void *user_data) {
RtpServer::Ptr *server = (RtpServer::Ptr *)ctx;
if (server) {
(*server)->connectToServer(dst_url, dst_port, [cb, user_data](const SockException &ex) {
if (cb) {
cb(user_data, ex.getErrCode(), ex.what(), ex.getCustomCode());
}
});
}
}
API_EXPORT void API_CALL mk_rtp_server_release(mk_rtp_server ctx) {
RtpServer::Ptr *server = (RtpServer::Ptr *)ctx;
delete server;
}
API_EXPORT uint16_t API_CALL mk_rtp_server_port(mk_rtp_server ctx){
API_EXPORT uint16_t API_CALL mk_rtp_server_port(mk_rtp_server ctx) {
RtpServer::Ptr *server = (RtpServer::Ptr *)ctx;
return (*server)->getPort();
}
API_EXPORT void API_CALL mk_rtp_server_set_on_detach(mk_rtp_server ctx, on_mk_rtp_server_detach cb, void *user_data){
API_EXPORT void API_CALL mk_rtp_server_set_on_detach(mk_rtp_server ctx, on_mk_rtp_server_detach cb, void *user_data) {
RtpServer::Ptr *server = (RtpServer::Ptr *) ctx;
if (cb) {
(*server)->setOnDetach([cb, user_data]() {
@ -45,21 +56,21 @@ API_EXPORT void API_CALL mk_rtp_server_set_on_detach(mk_rtp_server ctx, on_mk_rt
#else
API_EXPORT mk_rtp_server API_CALL mk_rtp_server_create(uint16_t port, int enable_tcp, const char *stream_id){
API_EXPORT mk_rtp_server API_CALL mk_rtp_server_create(uint16_t port, int enable_tcp, const char *stream_id) {
WarnL << "请打开ENABLE_RTPPROXY后再编译";
return nullptr;
}
API_EXPORT void API_CALL mk_rtp_server_release(mk_rtp_server ctx){
API_EXPORT void API_CALL mk_rtp_server_release(mk_rtp_server ctx) {
WarnL << "请打开ENABLE_RTPPROXY后再编译";
}
API_EXPORT uint16_t API_CALL mk_rtp_server_port(mk_rtp_server ctx){
API_EXPORT uint16_t API_CALL mk_rtp_server_port(mk_rtp_server ctx) {
WarnL << "请打开ENABLE_RTPPROXY后再编译";
return 0;
}
API_EXPORT void API_CALL mk_rtp_server_set_on_detach(mk_rtp_server ctx, on_mk_rtp_server_detach cb, void *user_data){
API_EXPORT void API_CALL mk_rtp_server_set_on_detach(mk_rtp_server ctx, on_mk_rtp_server_detach cb, void *user_data) {
WarnL << "请打开ENABLE_RTPPROXY后再编译";
}

View File

@ -109,19 +109,19 @@ API_EXPORT uint16_t API_CALL mk_sock_info_local_port(const mk_sock_info ctx){
////////////////////////////////////////////////////////////////////////////////////////
API_EXPORT mk_sock_info API_CALL mk_tcp_session_get_sock_info(const mk_tcp_session ctx){
assert(ctx);
TcpSessionForC *session = (TcpSessionForC *)ctx;
SessionForC *session = (SessionForC *)ctx;
return (SockInfo *)session;
}
API_EXPORT void API_CALL mk_tcp_session_shutdown(const mk_tcp_session ctx,int err,const char *err_msg){
assert(ctx);
TcpSessionForC *session = (TcpSessionForC *)ctx;
SessionForC *session = (SessionForC *)ctx;
session->safeShutdown(SockException((ErrCode)err,err_msg));
}
API_EXPORT void API_CALL mk_tcp_session_send_buffer(const mk_tcp_session ctx, mk_buffer buffer) {
assert(ctx && buffer);
TcpSessionForC *session = (TcpSessionForC *) ctx;
SessionForC *session = (SessionForC *) ctx;
session->send(*((Buffer::Ptr *) buffer));
}
@ -134,9 +134,9 @@ API_EXPORT void API_CALL mk_tcp_session_send(const mk_tcp_session ctx, const cha
API_EXPORT void API_CALL mk_tcp_session_send_buffer_safe(const mk_tcp_session ctx, mk_buffer buffer) {
assert(ctx && buffer);
try {
std::weak_ptr<TcpSession> weak_session = ((TcpSessionForC *) ctx)->shared_from_this();
std::weak_ptr<Session> weak_session = ((SessionForC *) ctx)->shared_from_this();
auto ref = mk_buffer_ref(buffer);
((TcpSessionForC *) ctx)->async([weak_session, ref]() {
((SessionForC *) ctx)->async([weak_session, ref]() {
auto session_session = weak_session.lock();
if (session_session) {
session_session->send(*((Buffer::Ptr *) ref));
@ -149,16 +149,16 @@ API_EXPORT void API_CALL mk_tcp_session_send_buffer_safe(const mk_tcp_session ct
}
API_EXPORT mk_tcp_session_ref API_CALL mk_tcp_session_ref_from(const mk_tcp_session ctx) {
auto ref = ((TcpSessionForC *) ctx)->shared_from_this();
return new std::shared_ptr<TcpSessionForC>(std::dynamic_pointer_cast<TcpSessionForC>(ref));
auto ref = ((SessionForC *) ctx)->shared_from_this();
return new std::shared_ptr<SessionForC>(std::dynamic_pointer_cast<SessionForC>(ref));
}
API_EXPORT void mk_tcp_session_ref_release(const mk_tcp_session_ref ref) {
delete (std::shared_ptr<TcpSessionForC> *) ref;
delete (std::shared_ptr<SessionForC> *) ref;
}
API_EXPORT mk_tcp_session mk_tcp_session_from_ref(const mk_tcp_session_ref ref) {
return ((std::shared_ptr<TcpSessionForC> *) ref)->get();
return ((std::shared_ptr<SessionForC> *) ref)->get();
}
API_EXPORT void API_CALL mk_tcp_session_send_safe(const mk_tcp_session ctx, const char *data, size_t len) {
@ -167,30 +167,30 @@ API_EXPORT void API_CALL mk_tcp_session_send_safe(const mk_tcp_session ctx, cons
mk_buffer_unref(buffer);
}
////////////////////////////////////////TcpSessionForC////////////////////////////////////////////////
////////////////////////////////////////SessionForC////////////////////////////////////////////////
static TcpServer::Ptr s_tcp_server[4];
static mk_tcp_session_events s_events_server = {0};
TcpSessionForC::TcpSessionForC(const Socket::Ptr &pSock) : TcpSession(pSock) {
SessionForC::SessionForC(const Socket::Ptr &pSock) : Session(pSock) {
_local_port = get_local_port();
if (s_events_server.on_mk_tcp_session_create) {
s_events_server.on_mk_tcp_session_create(_local_port,this);
}
}
void TcpSessionForC::onRecv(const Buffer::Ptr &buffer) {
void SessionForC::onRecv(const Buffer::Ptr &buffer) {
if (s_events_server.on_mk_tcp_session_data) {
s_events_server.on_mk_tcp_session_data(_local_port, this, (mk_buffer)&buffer);
}
}
void TcpSessionForC::onError(const SockException &err) {
void SessionForC::onError(const SockException &err) {
if (s_events_server.on_mk_tcp_session_disconnect) {
s_events_server.on_mk_tcp_session_disconnect(_local_port,this, err.getErrCode(), err.what());
}
}
void TcpSessionForC::onManager() {
void SessionForC::onManager() {
if (s_events_server.on_mk_tcp_session_manager) {
s_events_server.on_mk_tcp_session_manager(_local_port,this);
}
@ -202,13 +202,13 @@ void stopAllTcpServer(){
API_EXPORT void API_CALL mk_tcp_session_set_user_data(mk_tcp_session session,void *user_data){
assert(session);
TcpSessionForC *obj = (TcpSessionForC *)session;
SessionForC *obj = (SessionForC *)session;
obj->_user_data = user_data;
}
API_EXPORT void* API_CALL mk_tcp_session_get_user_data(mk_tcp_session session){
assert(session);
TcpSessionForC *obj = (TcpSessionForC *)session;
SessionForC *obj = (SessionForC *)session;
return obj->_user_data;
}
@ -226,18 +226,18 @@ API_EXPORT uint16_t API_CALL mk_tcp_server_start(uint16_t port, mk_tcp_type type
s_tcp_server[type] = std::make_shared<TcpServer>();
switch (type) {
case mk_type_tcp:
s_tcp_server[type]->start<TcpSessionForC>(port);
s_tcp_server[type]->start<SessionForC>(port);
break;
case mk_type_ssl:
s_tcp_server[type]->start<TcpSessionWithSSL<TcpSessionForC> >(port);
s_tcp_server[type]->start<SessionWithSSL<SessionForC> >(port);
break;
case mk_type_ws:
//此处你也可以修改WebSocketHeader::BINARY
s_tcp_server[type]->start<WebSocketSession<TcpSessionForC, HttpSession, WebSocketHeader::TEXT> >(port);
s_tcp_server[type]->start<WebSocketSession<SessionForC, HttpSession, WebSocketHeader::TEXT> >(port);
break;
case mk_type_wss:
//此处你也可以修改WebSocketHeader::BINARY
s_tcp_server[type]->start<WebSocketSession<TcpSessionForC, HttpsSession, WebSocketHeader::TEXT> >(port);
s_tcp_server[type]->start<WebSocketSession<SessionForC, HttpsSession, WebSocketHeader::TEXT> >(port);
break;
default:
return 0;
@ -295,7 +295,7 @@ TcpClientForC::Ptr *mk_tcp_client_create_l(mk_tcp_client_events *events, mk_tcp_
case mk_type_tcp:
return new TcpClientForC::Ptr(new TcpClientForC(events));
case mk_type_ssl:
return (TcpClientForC::Ptr *)new std::shared_ptr<TcpSessionWithSSL<TcpClientForC> >(new TcpSessionWithSSL<TcpClientForC>(events));
return (TcpClientForC::Ptr *)new std::shared_ptr<SessionWithSSL<TcpClientForC> >(new SessionWithSSL<TcpClientForC>(events));
case mk_type_ws:
//此处你也可以修改WebSocketHeader::BINARY
return (TcpClientForC::Ptr *)new std::shared_ptr<WebSocketClient<TcpClientForC, WebSocketHeader::TEXT, false> >(new WebSocketClient<TcpClientForC, WebSocketHeader::TEXT, false>(events));

View File

@ -13,7 +13,7 @@
#include "mk_tcp.h"
#include "Network/TcpClient.h"
#include "Network/TcpSession.h"
#include "Network/Session.h"
class TcpClientForC : public toolkit::TcpClient {
public:
@ -31,10 +31,10 @@ private:
mk_tcp_client _client;
};
class TcpSessionForC : public toolkit::TcpSession {
class SessionForC : public toolkit::Session {
public:
TcpSessionForC(const toolkit::Socket::Ptr &pSock) ;
~TcpSessionForC() override = default;
SessionForC(const toolkit::Socket::Ptr &pSock) ;
~SessionForC() override = default;
void onRecv(const toolkit::Buffer::Ptr &buffer) override ;
void onError(const toolkit::SockException &err) override;
void onManager() override;

View File

@ -18,7 +18,7 @@ using namespace toolkit;
API_EXPORT mk_thread API_CALL mk_thread_from_tcp_session(mk_tcp_session ctx){
assert(ctx);
TcpSessionForC *obj = (TcpSessionForC *)ctx;
SessionForC *obj = (SessionForC *)ctx;
return obj->getPoller().get();
}

View File

@ -115,12 +115,10 @@ API_EXPORT int API_CALL mk_track_bit_rate(mk_track track) {
API_EXPORT void *API_CALL mk_track_add_delegate(mk_track track, on_mk_frame_out cb, void *user_data) {
assert(track && cb);
auto delegate = std::make_shared<FrameWriterInterfaceHelper>([cb, user_data](const Frame::Ptr &frame) {
return (*((Track::Ptr *) track))->addDelegate([cb, user_data](const Frame::Ptr &frame) {
cb(user_data, (mk_frame) &frame);
return true;
});
(*((Track::Ptr *) track))->addDelegate(delegate);
return delegate.get();
}
API_EXPORT void API_CALL mk_track_del_delegate(mk_track track, void *tag) {

View File

@ -1,226 +0,0 @@
/*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
*
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifdef ENABLE_WEBRTC
#ifndef MK_WEBRTC_API_H
#define MK_WEBRTC_API_H
#include "Common/Parser.h"
#include "Http/HttpSession.h"
#include "Network/Socket.h"
#include "jsoncpp/json.h"
#include <functional>
#include <string>
#include "../webrtc/WebRtcEchoTest.h"
#include "../webrtc/WebRtcPlayer.h"
#include "../webrtc/WebRtcPusher.h"
#include "../webrtc/WebRtcTransport.h"
namespace API {
typedef enum {
NotFound = -500, //未找到
Exception = -400, //代码抛异常
InvalidArgs = -300, //参数不合法
SqlFailed = -200, // sql执行失败
AuthFailed = -100, //鉴权失败
OtherFailed = -1, //业务代码执行失败,
Success = 0 //执行成功
} ApiErr;
} // namespace API
class ApiRetException : public std::runtime_error {
public:
ApiRetException(const char *str = "success", int code = API::Success)
: runtime_error(str) {
_code = code;
}
~ApiRetException() = default;
int code() { return _code; }
private:
int _code;
};
class AuthException : public ApiRetException {
public:
AuthException(const char *str)
: ApiRetException(str, API::AuthFailed) {}
~AuthException() = default;
};
class InvalidArgsException : public ApiRetException {
public:
InvalidArgsException(const char *str)
: ApiRetException(str, API::InvalidArgs) {}
~InvalidArgsException() = default;
};
class SuccessException : public ApiRetException {
public:
SuccessException()
: ApiRetException("success", API::Success) {}
~SuccessException() = default;
};
using ApiArgsType = std::map<std::string, std::string, mediakit::StrCaseCompare>;
template <typename Args, typename First>
std::string getValue(Args &args, const First &first) {
return args[first];
}
template <typename First>
std::string getValue(Json::Value &args, const First &first) {
return args[first].asString();
}
template <typename First>
std::string getValue(std::string &args, const First &first) {
return "";
}
template <typename First>
std::string getValue(const mediakit::Parser &parser, const First &first) {
auto ret = parser.getUrlArgs()[first];
if (!ret.empty()) {
return ret;
}
return parser.getHeader()[first];
}
template <typename First>
std::string getValue(mediakit::Parser &parser, const First &first) {
return getValue((const mediakit::Parser &)parser, first);
}
template <typename Args, typename First>
std::string getValue(const mediakit::Parser &parser, Args &args, const First &first) {
auto ret = getValue(args, first);
if (!ret.empty()) {
return ret;
}
return getValue(parser, first);
}
template <typename Args>
class HttpAllArgs {
public:
HttpAllArgs(const mediakit::Parser &parser, Args &args) {
_get_args = [&args]() { return (void *)&args; };
_get_parser = [&parser]() -> const mediakit::Parser & { return parser; };
_get_value
= [](HttpAllArgs &that, const std::string &key) { return getValue(that.getParser(), that.getArgs(), key); };
_clone = [&](HttpAllArgs &that) {
that._get_args = [args]() { return (void *)&args; };
that._get_parser = [parser]() -> const mediakit::Parser & { return parser; };
that._get_value = [](HttpAllArgs &that, const std::string &key) {
return getValue(that.getParser(), that.getArgs(), key);
};
that._cache_able = true;
};
}
HttpAllArgs(const HttpAllArgs &that) {
if (that._cache_able) {
_get_args = that._get_args;
_get_parser = that._get_parser;
_get_value = that._get_value;
_cache_able = true;
} else {
that._clone(*this);
}
}
~HttpAllArgs() = default;
template <typename Key>
toolkit::variant operator[](const Key &key) const {
return (toolkit::variant)_get_value(*(HttpAllArgs *)this, key);
}
const mediakit::Parser &getParser() const { return _get_parser(); }
Args &getArgs() { return *((Args *)_get_args()); }
const Args &getArgs() const { return *((Args *)_get_args()); }
private:
bool _cache_able = false;
std::function<void *()> _get_args;
std::function<const mediakit::Parser &()> _get_parser;
std::function<std::string(HttpAllArgs &that, const std::string &key)> _get_value;
std::function<void(HttpAllArgs &that)> _clone;
};
#define API_ARGS_MAP \
toolkit::SockInfo &sender, mediakit::HttpSession::KeyValue &headerOut, const HttpAllArgs<ApiArgsType> &allArgs, \
Json::Value &val
#define API_ARGS_MAP_ASYNC API_ARGS_MAP, const mediakit::HttpSession::HttpResponseInvoker &invoker
#define API_ARGS_JSON \
toolkit::SockInfo &sender, mediakit::HttpSession::KeyValue &headerOut, const HttpAllArgs<Json::Value> &allArgs, \
Json::Value &val
#define API_ARGS_JSON_ASYNC API_ARGS_JSON, const mediakit::HttpSession::HttpResponseInvoker &invoker
#define API_ARGS_STRING \
toolkit::SockInfo &sender, mediakit::HttpSession::KeyValue &headerOut, const HttpAllArgs<std::string> &allArgs, \
Json::Value &val
#define API_ARGS_STRING_ASYNC API_ARGS_STRING, const mediakit::HttpSession::HttpResponseInvoker &invoker
#define API_ARGS_VALUE sender, headerOut, allArgs, val
//注册http请求参数是http原始请求信息的异步回复的http api
void api_regist(const std::string &api_path, const std::function<void(API_ARGS_STRING_ASYNC)> &func);
template <typename Args, typename First>
bool checkArgs(Args &args, const First &first) {
return !args[first].empty();
}
template <typename Args, typename First, typename... KeyTypes>
bool checkArgs(Args &args, const First &first, const KeyTypes &...keys) {
return checkArgs(args, first) && checkArgs(args, keys...);
}
//检查http url中或body中或http header参数是否为空的宏
#define CHECK_ARGS(...) \
if (!checkArgs(allArgs, ##__VA_ARGS__)) { \
throw InvalidArgsException("缺少必要参数:" #__VA_ARGS__); \
}
#ifdef ENABLE_WEBRTC
class WebRtcArgsImp : public WebRtcArgs {
public:
WebRtcArgsImp(const HttpAllArgs<std::string> &args, std::string session_id)
: _args(args)
, _session_id(std::move(session_id)) {}
~WebRtcArgsImp() override = default;
variant operator[](const std::string &key) const override {
if (key == "url") {
return getUrl();
}
return _args[key];
}
private:
std::string getUrl() const {
auto &allArgs = _args;
CHECK_ARGS("app", "stream");
return StrPrinter << RTC_SCHEMA << "://" << _args["Host"] << "/" << _args["app"] << "/" << _args["stream"]
<< "?" << _args.getParser().Params() + "&session=" + _session_id;
}
private:
HttpAllArgs<std::string> _args;
std::string _session_id;
};
#endif
#endif // MK_WEBRTC_API_H
#endif // ENABLE_WEBRTC

View File

@ -88,8 +88,10 @@ void API_CALL on_mk_media_play(const mk_media_info url_info,
* 广
* @param url_info url相关信息
* @param sender
* @return 1
* 0
*/
void API_CALL on_mk_media_not_found(const mk_media_info url_info,
int API_CALL on_mk_media_not_found(const mk_media_info url_info,
const mk_sock_info sender) {
char ip[64];
log_printf(LOG_LEV,
@ -104,6 +106,7 @@ void API_CALL on_mk_media_not_found(const mk_media_info url_info,
mk_media_info_get_app(url_info),
mk_media_info_get_stream(url_info),
mk_media_info_get_params(url_info));
return 0;
}
/**
@ -119,6 +122,72 @@ void API_CALL on_mk_media_no_reader(const mk_media_source sender) {
mk_media_source_get_stream(sender));
}
//按照json转义规则转义webrtc answer sdp
static char *escape_string(const char *ptr){
char *escaped = malloc(2 * strlen(ptr));
char *ptr_escaped = escaped;
while (1) {
switch (*ptr) {
case '\r': {
*(ptr_escaped++) = '\\';
*(ptr_escaped++) = 'r';
break;
}
case '\n': {
*(ptr_escaped++) = '\\';
*(ptr_escaped++) = 'n';
break;
}
case '\t': {
*(ptr_escaped++) = '\\';
*(ptr_escaped++) = 't';
break;
}
default: {
*(ptr_escaped++) = *ptr;
if (!*ptr) {
return escaped;
}
break;
}
}
++ptr;
}
}
static void on_mk_webrtc_get_answer_sdp_func(void *user_data, const char *answer, const char *err) {
const char *response_header[] = { "Content-Type", "application/json", "Access-Control-Allow-Origin", "*" , NULL};
if (answer) {
answer = escape_string(answer);
}
size_t len = answer ? 2 * strlen(answer) : 1024;
char *response_content = (char *)malloc(len);
if (answer) {
snprintf(response_content, len,
"{"
"\"sdp\":\"%s\","
"\"type\":\"answer\","
"\"code\":0"
"}",
answer);
} else {
snprintf(response_content, len,
"{"
"\"msg\":\"%s\","
"\"code\":-1"
"}",
err);
}
mk_http_response_invoker_do_string(user_data, 200, response_header, response_content);
mk_http_response_invoker_clone_release(user_data);
free(response_content);
if (answer) {
free((void *)answer);
}
}
/**
* http api请求广播(GET/POST)
* @param parser http请求内容对象
@ -153,7 +222,7 @@ void API_CALL on_mk_http_request(const mk_parser parser,
*consumed = 1;
//拦截api: /api/test
if(strcmp(url,"/api/test") == 0) {
if (strcmp(url, "/api/test") == 0) {
const char *response_header[] = { "Content-Type", "text/html", NULL };
const char *content = "<html>"
"<head>"
@ -168,12 +237,16 @@ void API_CALL on_mk_http_request(const mk_parser parser,
mk_http_body body = mk_http_body_from_string(content, 0);
mk_http_response_invoker_do(invoker, 200, response_header, body);
mk_http_body_release(body);
}
//拦截api: /index/api/webrtc
else if(strcmp(url,"/index/api/webrtc") == 0){
mk_webrtc_http_response_invoker_do(invoker,parser,sender);
}
else{
} else if (strcmp(url, "/index/api/webrtc") == 0) {
//拦截api: /index/api/webrtc
char rtc_url[1024];
snprintf(rtc_url, sizeof(rtc_url), "rtc://%s/%s/%s?%s", mk_parser_get_header(parser, "Host"),
mk_parser_get_url_param(parser, "app"), mk_parser_get_url_param(parser, "stream"),
mk_parser_get_url_params(parser));
mk_webrtc_get_answer_sdp(mk_http_response_invoker_clone(invoker), on_mk_webrtc_get_answer_sdp_func,
mk_parser_get_url_param(parser, "type"), mk_parser_get_content(parser, NULL), rtc_url);
} else {
*consumed = 0;
return;
}
@ -413,6 +486,7 @@ int main(int argc, char *argv[]) {
mk_shell_server_start(9000);
mk_rtp_server_start(10000);
mk_rtc_server_start(8000);
mk_srt_server_start(9000);
mk_events events = {
.on_mk_media_changed = on_mk_media_changed,

View File

@ -1,31 +1,36 @@
#!/bin/bash
set -e
while getopts c:t:m:v: opt
while getopts c:t:p:m:v: opt
do
case $opt in
t)
type=$OPTARG
;;
v)
case $opt in
t)
type=$OPTARG
;;
v)
version=$OPTARG
;;
p)
platform=$OPTARG
;;
m)
model=$OPTARG
;;
?)
echo "unkonwn"
exit
;;
?)
echo "unkonwn"
exit
;;
esac
done
help_string=".sh [-t build|push] [-p (amd64|arm64|...,default is `arch`) ] [-m Debug|Release] [-v [version]]"
if [[ ! -n $type ]];then
echo ".sh [-t build|push] [-m Debug|Release] [-v [version]]"
echo $help_string
exit
fi
if [[ ! -n $model ]];then
echo ".sh [-t build|push] [-m Debug|Release] [-v [version]]"
echo $help_string
exit
fi
@ -34,16 +39,36 @@ if [[ ! -n $version ]];then
version="latest"
fi
if [[ ! -n $platform ]];then
platform=`arch`
echo "auto select arch:${platform}"
fi
case $platform in
"arm64")
#eg:osx
platform="linux/arm64"
;;
"x86_64"|"amd64")
platform="linux/amd64"
;;
*)
echo "unknown cpu-arch ${platform}"
echo "Use 'docker buildx ls' to get supported ARCH"
exit
;;
esac
case $model in
'Debug')
;;
'Release')
;;
*)
echo "unkonwn model"
echo ".sh [-t build|push] [-m Debug|Release] [-v [version]]"
exit
;;
echo "unkonwn model"
echo $help_string
exit
;;
esac
namespace="zlmediakit"
@ -53,7 +78,8 @@ case $type in
'build')
rm -rf ./build/CMakeCache.txt
# 以腾讯云账号为例
docker build --network=host --build-arg MODEL=$model -t $namespace/$packagename:$model.$version .
docker buildx build --platform=$platform --network=host --build-arg MODEL=$model -t $namespace/$packagename:$model.$version .
#docker build --network=host --build-arg MODEL=$model -t $namespace/$packagename:$model.$version .
;;
'push')
echo "push to dst registry"
@ -62,8 +88,8 @@ case $type in
docker push $namespace/$packagename:$model.$version
;;
*)
echo "unkonwn type"
echo ".sh [-t build|push] [-m Debug|Release] [-v [version]]"
exit
;;
echo "unkonwn type"
echo $help_string
exit
;;
esac

View File

@ -29,6 +29,56 @@ log=./ffmpeg/ffmpeg.log
# 自动重启的时间(秒), 默认为0, 也就是不自动重启. 主要是为了避免长时间ffmpeg拉流导致的不同步现象
restart_sec=0
#转协议相关开关如果addStreamProxy api和on_publish hook回复未指定转协议参数则采用这些配置项
[protocol]
#转协议时,是否开启帧级时间戳覆盖
modify_stamp=0
#转协议是否开启音频
enable_audio=1
#添加acc静音音频在关闭音频时此开关无效
add_mute_audio=1
#推流断开后可以在超时时间内重新连接上继续推流,这样播放器会接着播放。
#置0关闭此特性(推流断开会导致立即断开播放器)
#此参数不应大于播放器超时时间;单位毫秒
continue_push_ms=15000
#是否开启转换为hls
enable_hls=1
#是否开启MP4录制
enable_mp4=0
#是否开启转换为rtsp/webrtc
enable_rtsp=1
#是否开启转换为rtmp/flv
enable_rtmp=1
#是否开启转换为http-ts/ws-ts
enable_ts=1
#是否开启转换为http-fmp4/ws-fmp4
enable_fmp4=1
#是否将mp4录制当做观看者
mp4_as_player=0
#mp4切片大小单位秒
mp4_max_second=3600
#mp4录制保存路径
mp4_save_path=./www
#hls录制保存路径
hls_save_path=./www
###### 以下是按需转协议的开关在测试ZLMediaKit的接收推流性能时请把下面开关置1
###### 如果某种协议你用不到你可以把以下开关置1以便节省资源(但是还是可以播放,只是第一个播放者体验稍微差点)
###### 如果某种协议你想获取最好的用户体验请置0(第一个播放者可以秒开,且不花屏)
#hls协议是否按需生成如果hls.segNum配置为0(意味着hls录制)那么hls将一直生成(不管此开关)
hls_demand=0
#rtsp[s]协议是否按需生成
rtsp_demand=0
#rtmp[s]、http[s]-flv、ws[s]-flv协议是否按需生成
rtmp_demand=0
#http[s]-ts协议是否按需生成
ts_demand=0
#http[s]-fmp4、ws[s]-fmp4协议是否按需生成
fmp4_demand=0
[general]
#是否启用虚拟主机
enableVhost=0
@ -44,43 +94,14 @@ maxStreamWaitMS=15000
#某个流无人观看时触发hook.on_stream_none_reader事件的最大等待时间单位毫秒
#在配合hook.on_stream_none_reader事件时可以做到无人观看自动停止拉流或停止接收推流
streamNoneReaderDelayMS=20000
#是否全局添加静音aac音频转协议时有效
#有些播放器在打开单视频流时不能秒开,添加静音音频可以加快秒开速度
addMuteAudio=1
#拉流代理时如果断流再重连成功是否删除前一次的媒体流数据,如果删除将重新开始,
#如果不删除将会接着上一次的数据继续写(录制hls/mp4时会继续在前一个文件后面写)
resetWhenRePlay=1
#是否默认推流时转换成hlshook接口(on_publish)中可以覆盖该设置
publishToHls=1
#是否默认推流时mp4录像hook接口(on_publish)中可以覆盖该设置
publishToMP4=0
#合并写缓存大小(单位毫秒)合并写指服务器缓存一定的数据后才会一次性写入socket这样能提高性能但是会提高延时
#开启后会同时关闭TCP_NODELAY并开启MSG_MORE
mergeWriteMS=0
#全局的时间戳覆盖开关在转协议时对frame进行时间戳覆盖
#该开关对rtsp/rtmp/rtp推流、rtsp/rtmp/hls拉流代理转协议时生效
#会直接影响rtsp/rtmp/hls/mp4/flv等协议的时间戳
#同协议情况下不影响(例如rtsp/rtmp推流那么播放rtsp/rtmp时不会影响时间戳)
modifyStamp=0
#服务器唯一id用于触发hook时区别是哪台服务器
mediaServerId=your_server_id
#转协议是否全局开启或关闭音频
enable_audio=1
###### 以下是按需转协议的开关在测试ZLMediaKit的接收推流性能时请把下面开关置1
###### 如果某种协议你用不到你可以把以下开关置1以便节省资源(但是还是可以播放,只是第一个播放者体验稍微差点)
###### 如果某种协议你想获取最好的用户体验请置0(第一个播放者可以秒开,且不花屏)
#hls协议是否按需生成如果hls.segNum配置为0(意味着hls录制)那么hls将一直生成(不管此开关)
hls_demand=0
#rtsp[s]协议是否按需生成
rtsp_demand=0
#rtmp[s]、http[s]-flv、ws[s]-flv协议是否按需生成
rtmp_demand=0
#http[s]-ts协议是否按需生成
ts_demand=0
#http[s]-fmp4、ws[s]-fmp4协议是否按需生成
fmp4_demand=0
#最多等待未初始化的Track时间单位毫秒超时之后会忽略未初始化的Track
wait_track_ready_ms=10000
@ -89,17 +110,10 @@ wait_track_ready_ms=10000
wait_add_track_ms=3000
#如果track未就绪我们先缓存帧数据但是有最大个数限制防止内存溢出
unready_frame_cache=100
#推流断开后可以在超时时间内重新连接上继续推流,这样播放器会接着播放。
#置0关闭此特性(推流断开会导致立即断开播放器)
#此参数不应大于播放器超时时间
continue_push_ms=15000
[hls]
#hls写文件的buf大小调整参数可以提高文件io性能
fileBufSize=65536
#hls保存文件路径
#可以为相对(相对于本可执行程序目录)或绝对路径
filePath=./www
#hls最大切片时间
segDur=2
#m3u8索引中,hls保留切片个数(实际保留切片个数大2~3个)
@ -110,7 +124,7 @@ segRetain=5
#是否广播 ts 切片完成通知
broadcastRecordTs=0
#直播hls文件删除延时单位秒issue: #913
deleteDelaySec=0
deleteDelaySec=10
#是否保留hls文件此功能部分等效于segNum=0的情况
#不同的是这个保留不会在m3u8文件中体现
#0为不保留不起作用
@ -155,6 +169,9 @@ on_server_started=https://127.0.0.1/index/hook/on_server_started
on_server_keepalive=https://127.0.0.1/index/hook/on_server_keepalive
#发送rtp(startSendRtp)被动关闭时回调
on_send_rtp_stopped=https://127.0.0.1/index/hook/on_send_rtp_stopped
#rtp server 超时未收到数据
on_rtp_server_timeout=https://127.0.0.1/index/hook/on_rtp_server_timeout
#hook api最大等待回复时间单位秒
timeoutSec=10
#keepalive hook触发间隔,单位秒float类型
@ -228,11 +245,6 @@ udpTTL=64
appName=record
#mp4录制写文件缓存单位BYTE,调整参数可以提高文件io性能
fileBufSize=65536
#mp4录制保存、mp4点播根路径
#可以为相对(相对于本可执行程序目录)或绝对路径
filePath=./www
#mp4录制切片时间单位秒
fileSecond=3600
#mp4点播每次流化数据量单位毫秒
#减少该值可以让点播数据发送量更平滑增大该值则更节省cpu资源
sampleMS=500
@ -240,8 +252,6 @@ sampleMS=500
fastStart=0
#MP4点播(rtsp/rtmp/http-flv/ws-flv)是否循环播放文件
fileRepeat=0
#MP4录制是否当做播放器参与播放人数统计
mp4_as_player=0
[rtmp]
#rtmp必须在此时间内完成握手否则服务器会断开链接单位秒
@ -264,6 +274,8 @@ audioMtuSize=600
videoMtuSize=1400
#rtp包最大长度限制单位KB,主要用于识别TCP上下文破坏时获取到错误的rtp
rtpMaxSize=10
# rtp 打包时低延迟开关默认关闭为0h264存在一帧多个sliceNAL的情况在这种情况下如果开启可能会导致画面花屏
lowLatency=0
[rtp_proxy]
#导出调试数据(包括rtp/ps/h264)至该目录,置空则关闭数据导出
@ -275,25 +287,18 @@ timeoutSec=15
#随机端口范围最少确保36个端口
#该范围同时限制rtsp服务器udp端口范围
port_range=30000-35000
#rtp h264 负载的pt
h264_pt=98
#rtp h265 负载的pt
h265_pt=99
#rtp ps 负载的pt
ps_pt=96
#rtp ts 负载的pt
ts_pt=33
#rtp opus 负载的pt
opus_pt=100
#rtp g711u 负载的pt
g711u_pt=0
#rtp g711a 负载的pt
g711a_pt=8
@ -308,6 +313,10 @@ externIP=
#该端口是多线程的,同时支持客户端网络切换导致的连接迁移
#需要注意的是如果服务器在nat内需要做端口映射时必须确保外网映射端口跟该端口一致
port=8000
#rtc tcp服务器监听端口号在udp 不通的情况下会使用tcp传输数据
#该端口是多线程的,同时支持客户端网络切换导致的连接迁移
#需要注意的是如果服务器在nat内需要做端口映射时必须确保外网映射端口跟该端口一致
tcpPort = 8000
#设置remb比特率非0时关闭twcc并开启remb。该设置在rtc推流时有效可以控制推流画质
#目前已经实现twcc自动调整码率关闭remb根据真实网络状况调整码率
rembBitRate=0
@ -316,7 +325,7 @@ rembBitRate=0
preferredCodecA=PCMU,PCMA,opus,mpeg4-generic
#rtc支持的视频codec类型,在前面的优先级更高
#以下范例为所有支持的视频codec
preferredCodecV=H264,H265,AV1X,VP9,VP8
preferredCodecV=H264,H265,AV1,VP9,VP8
[srt]
#srt播放推流、播放超时时间,单位秒
@ -324,10 +333,8 @@ timeoutSec=5
#srt udp服务器监听端口号所有srt客户端将通过该端口传输srt数据
#该端口是多线程的,同时支持客户端网络切换导致的连接迁移
port=9000
#srt 协议中延迟缓存的估算参数在握手阶段估算rtt ,然后latencyMul*rtt 为最大缓存时长,此参数越大,表示等待重传的时长就越大
latencyMul=4
#包缓存的大小
pktBufSize=8192
@ -352,7 +359,8 @@ keepAliveSecond=15
port=554
#rtsps服务器监听地址
sslport=0
#rtsp 转发是否使用低延迟模式当开启时不会缓存rtp包来提高并发可以降低一帧的延迟
lowLatency=0
[shell]
#调试telnet服务器接受最大bufffer大小
maxReqSize=1024

View File

@ -86,4 +86,4 @@ M0r5LUvStcr82QDWYNPaUy4taCQmyaJ+VB+6wxHstSigOlSNF2a6vg4rgexixeiV
4YSB03Yqp2t3TeZHM9ESfkus74nQyW7pRGezj+TC44xCagCQQOzzNmzEAP2SnCrJ
sNE2DpRVMnL8J6xBRdjmOsC3N6cQuKuRXbzByVBjCqAA8t1L0I+9wXJerLPyErjy
rMKWaBFLmfK/AHNF4ZihwPGOc7w6UHczBZXH5RFzJNnww+WnKuTPI0HfnVH8lg==
-----END CERTIFICATE-----
-----END CERTIFICATE-----

View File

@ -53,7 +53,7 @@ int main(int argc, char *argv[]) {
Logger::Instance().add(std::make_shared<ConsoleChannel>());
Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());
if (argc != 3) {
if (argc < 3) {
ErrorL << "\r\n测试方法:./test_player rtxp_url rtp_type\r\n"
<< "例如:./test_player rtsp://admin:123456@127.0.0.1/live/0 0\r\n"
<< endl;
@ -83,10 +83,9 @@ int main(int argc, char *argv[]) {
return true;
});
});
auto delegate = std::make_shared<FrameWriterInterfaceHelper>([decoder](const Frame::Ptr &frame) {
videoTrack->addDelegate([decoder](const Frame::Ptr &frame) {
return decoder->inputFrame(frame, false, true);
});
videoTrack->addDelegate(delegate);
}
if (audioTrack) {
@ -105,10 +104,9 @@ int main(int argc, char *argv[]) {
auto len = pcm->get()->nb_samples * pcm->get()->channels * av_get_bytes_per_sample((enum AVSampleFormat)pcm->get()->format);
audio_player->playPCM((const char *) (pcm->get()->data[0]), MIN(len, frame->get()->linesize[0]));
});
auto audio_delegate = std::make_shared<FrameWriterInterfaceHelper>( [decoder](const Frame::Ptr &frame) {
audioTrack->addDelegate([decoder](const Frame::Ptr &frame) {
return decoder->inputFrame(frame, false, true);
});
audioTrack->addDelegate(audio_delegate);
}
});
@ -119,6 +117,9 @@ int main(int argc, char *argv[]) {
(*player)[Client::kRtpType] = atoi(argv[2]);
//不等待track ready再回调播放成功事件这样可以加快秒开速度
(*player)[Client::kWaitTrackReady] = false;
if (argc > 3) {
(*player)[Client::kPlayTrack] = atoi(argv[3]);
}
player->play(argv[1]);
SDLDisplayerHelper::Instance().runLoop();
return 0;

View File

@ -348,7 +348,7 @@
"response": []
},
{
"name": "获取TcpSession列表(getAllSession)",
"name": "获取Session列表(getAllSession)",
"request": {
"method": "GET",
"header": [],
@ -1384,9 +1384,9 @@
"description": "绑定的端口0时为随机端口"
},
{
"key": "enable_tcp",
"key": "tcp_mode",
"value": "1",
"description": "创建 udp端口时是否同时监听tcp端口"
"description": "tcp模式0时为不启用tcp监听1时为启用tcp监听2时为tcp主动连接模式"
},
{
"key": "stream_id",
@ -1410,6 +1410,47 @@
},
"response": []
},
{
"name": "连接RTP服务器(connectRtpServer)",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{ZLMediaKit_URL}}/index/api/connectRtpServer?secret={{ZLMediaKit_secret}}&dst_url=127.0.0.1&dst_port=10000&stream_id=test",
"host": [
"{{ZLMediaKit_URL}}"
],
"path": [
"index",
"api",
"connectRtpServer"
],
"query": [
{
"key": "secret",
"value": "{{ZLMediaKit_secret}}",
"description": "api操作密钥(配置文件配置)如果操作ip是127.0.0.1,则不需要此参数"
},
{
"key": "dst_url",
"value": "0",
"description": "tcp主动模式时服务端地址"
},
{
"key": "dst_port",
"value": "1",
"description": "tcp主动模式时服务端端口"
},
{
"key": "stream_id",
"value": "test",
"description": "OpenRtpServer时绑定的流id\n"
}
]
}
},
"response": []
},
{
"name": "关闭RTP服务器(closeRtpServer)",
"request": {

View File

@ -273,14 +273,14 @@ void FFmpegSource::setOnClose(const function<void()> &cb){
_onClose = cb;
}
bool FFmpegSource::close(MediaSource &sender, bool force) {
bool FFmpegSource::close(MediaSource &sender) {
auto listener = getDelegate();
if(listener && !listener->close(sender,force)){
if (listener && !listener->close(sender)) {
//关闭失败
return false;
}
//该流无人观看,我们停止吧
if(_onClose){
if (_onClose) {
_onClose();
}
return true;

View File

@ -75,7 +75,7 @@ private:
///////MediaSourceEvent override///////
// 关闭
bool close(mediakit::MediaSource &sender,bool force) override;
bool close(mediakit::MediaSource &sender) override;
// 获取媒体源类型
mediakit::MediaOriginType getOriginType(mediakit::MediaSource &sender) const override;
//获取媒体源url或者文件路径

View File

@ -58,6 +58,12 @@ static int runChildProcess(string cmd, string log_file) {
log_file = StrPrinter << log_file << "." << getpid();
}
if (isatty(STDIN_FILENO)) {
/* bb_error_msg("ignoring input"); */
close(STDIN_FILENO);
open("/dev/null", O_RDONLY, 0666); /* will be fd 0 (STDIN_FILENO) */
}
//重定向shell日志至文件
auto fp = File::create_file(log_file.data(), "ab");
if (!fp) {

View File

@ -11,8 +11,10 @@
#ifndef ZLMEDIAKIT_PROCESS_H
#define ZLMEDIAKIT_PROCESS_H
#ifdef _WIN32
#if defined(_WIN32)
#if !defined(__MINGW32__)
typedef int pid_t;
#endif
#else
#include <sys/wait.h>
#endif // _WIN32

View File

@ -312,6 +312,14 @@ static inline string getPusherKey(const string &schema, const string &vhost, con
return schema + "/" + vhost + "/" + app + "/" + stream + "/" + MD5(dst_url).hexdigest();
}
static void fillSockInfo(Value& val, SockInfo* info) {
val["peer_ip"] = info->get_peer_ip();
val["peer_port"] = info->get_peer_port();
val["local_port"] = info->get_local_port();
val["local_ip"] = info->get_local_ip();
val["identifier"] = info->getIdentifier();
}
Value makeMediaSourceJson(MediaSource &media){
Value item;
item["schema"] = media.getSchema();
@ -330,11 +338,7 @@ Value makeMediaSourceJson(MediaSource &media){
item["isRecordingHLS"] = media.isRecording(Recorder::type_hls);
auto originSock = media.getOriginSock();
if (originSock) {
item["originSock"]["local_ip"] = originSock->get_local_ip();
item["originSock"]["local_port"] = originSock->get_local_port();
item["originSock"]["peer_ip"] = originSock->get_peer_ip();
item["originSock"]["peer_port"] = originSock->get_peer_port();
item["originSock"]["identifier"] = originSock->getIdentifier();
fillSockInfo(item["originSock"], originSock.get());
} else {
item["originSock"] = Json::nullValue;
}
@ -383,7 +387,7 @@ Value makeMediaSourceJson(MediaSource &media){
}
#if defined(ENABLE_RTPPROXY)
uint16_t openRtpServer(uint16_t local_port, const string &stream_id, bool enable_tcp, const string &local_ip, bool re_use_port, uint32_t ssrc) {
uint16_t openRtpServer(uint16_t local_port, const string &stream_id, int tcp_mode, const string &local_ip, bool re_use_port, uint32_t ssrc) {
lock_guard<recursive_mutex> lck(s_rtpServerMapMtx);
if (s_rtpServerMap.find(stream_id) != s_rtpServerMap.end()) {
//为了防止RtpProcess所有权限混乱的问题不允许重复添加相同的stream_id
@ -391,12 +395,12 @@ uint16_t openRtpServer(uint16_t local_port, const string &stream_id, bool enable
}
RtpServer::Ptr server = std::make_shared<RtpServer>();
server->start(local_port, stream_id, enable_tcp, local_ip.c_str(), re_use_port, ssrc);
server->start(local_port, stream_id, (RtpServer::TcpMode)tcp_mode, local_ip.c_str(), re_use_port, ssrc);
server->setOnDetach([stream_id]() {
//设置rtp超时移除事件
lock_guard<recursive_mutex> lck(s_rtpServerMapMtx);
s_rtpServerMap.erase(stream_id);
});
});
//保存对象
s_rtpServerMap.emplace(stream_id, server);
@ -404,6 +408,16 @@ uint16_t openRtpServer(uint16_t local_port, const string &stream_id, bool enable
return server->getPort();
}
void connectRtpServer(const string &stream_id, const string &dst_url, uint16_t dst_port, const function<void(const SockException &ex)> &cb) {
lock_guard<recursive_mutex> lck(s_rtpServerMapMtx);
auto it = s_rtpServerMap.find(stream_id);
if (it == s_rtpServerMap.end()) {
cb(SockException(Err_other, "未找到rtp服务"));
return;
}
it->second->connectToServer(dst_url, dst_port, cb);
}
bool closeRtpServer(const string &stream_id) {
lock_guard<recursive_mutex> lck(s_rtpServerMapMtx);
auto it = s_rtpServerMap.find(stream_id);
@ -772,9 +786,7 @@ void installWebApi() {
[](std::shared_ptr<void> &&info) -> std::shared_ptr<void> {
auto obj = std::make_shared<Value>();
auto session = static_pointer_cast<Session>(info);
(*obj)["peer_ip"] = session->get_peer_ip();
(*obj)["peer_port"] = session->get_peer_port();
(*obj)["id"] = session->getIdentifier();
fillSockInfo(*obj, session.get());
(*obj)["typeid"] = toolkit::demangle(typeid(*session).name());
return obj;
});
@ -842,7 +854,7 @@ void installWebApi() {
val["count_closed"] = count_closed;
});
//获取所有TcpSession列表信息
//获取所有Session列表信息
//可以根据本地端口和远端ip来筛选
//测试url(筛选某端口下的tcp会话) http://127.0.0.1/index/api/getAllSession?local_port=1935
api_regist("/index/api/getAllSession",[](API_ARGS_MAP){
@ -858,10 +870,7 @@ void installWebApi() {
if(!peer_ip.empty() && peer_ip != session->get_peer_ip()){
return;
}
jsession["peer_ip"] = session->get_peer_ip();
jsession["peer_port"] = session->get_peer_port();
jsession["local_ip"] = session->get_local_ip();
jsession["local_port"] = session->get_local_port();
fillSockInfo(jsession, session.get());
jsession["id"] = id;
jsession["typeid"] = toolkit::demangle(typeid(*session).name());
val["data"].append(jsession);
@ -1002,20 +1011,7 @@ void installWebApi() {
CHECK_SECRET();
CHECK_ARGS("vhost","app","stream","url");
ProtocolOption option;
getArgsValue(allArgs, "enable_hls", option.enable_hls);
getArgsValue(allArgs, "enable_mp4", option.enable_mp4);
getArgsValue(allArgs, "mp4_as_player", option.mp4_as_player);
getArgsValue(allArgs, "enable_rtsp", option.enable_rtsp);
getArgsValue(allArgs, "enable_rtmp", option.enable_rtmp);
getArgsValue(allArgs, "enable_ts", option.enable_ts);
getArgsValue(allArgs, "enable_fmp4", option.enable_fmp4);
getArgsValue(allArgs, "enable_audio", option.enable_audio);
getArgsValue(allArgs, "add_mute_audio", option.add_mute_audio);
getArgsValue(allArgs, "mp4_save_path", option.mp4_save_path);
getArgsValue(allArgs, "mp4_max_second", option.mp4_max_second);
getArgsValue(allArgs, "hls_save_path", option.hls_save_path);
getArgsValue(allArgs, "modify_stamp", option.modify_stamp);
ProtocolOption option(allArgs);
addStreamProxy(allArgs["vhost"],
allArgs["app"],
@ -1127,25 +1123,41 @@ void installWebApi() {
return;
}
val["exist"] = true;
val["peer_ip"] = process->get_peer_ip();
val["peer_port"] = process->get_peer_port();
val["local_port"] = process->get_local_port();
val["local_ip"] = process->get_local_ip();
fillSockInfo(val, process.get());
});
api_regist("/index/api/openRtpServer",[](API_ARGS_MAP){
CHECK_SECRET();
CHECK_ARGS("port", "enable_tcp", "stream_id");
CHECK_ARGS("port", "stream_id");
auto stream_id = allArgs["stream_id"];
auto port = openRtpServer(allArgs["port"], stream_id, allArgs["enable_tcp"].as<bool>(), "::",
allArgs["re_use_port"].as<bool>(), allArgs["ssrc"].as<uint32_t>());
if(port == 0) {
auto tcp_mode = allArgs["tcp_mode"].as<int>();
if (allArgs["enable_tcp"].as<int>() && !tcp_mode) {
//兼容老版本请求新版本去除enable_tcp参数并新增tcp_mode参数
tcp_mode = 1;
}
auto port = openRtpServer(allArgs["port"], stream_id, tcp_mode, "::", allArgs["re_use_port"].as<bool>(),
allArgs["ssrc"].as<uint32_t>());
if (port == 0) {
throw InvalidArgsException("该stream_id已存在");
}
//回复json
val["port"] = port;
});
api_regist("/index/api/connectRtpServer", [](API_ARGS_MAP_ASYNC) {
CHECK_SECRET();
CHECK_ARGS("stream_id", "dst_url", "dst_port");
connectRtpServer(
allArgs["stream_id"], allArgs["dst_url"], allArgs["dst_port"],
[val, headerOut, invoker](const SockException &ex) mutable {
if (ex) {
val["code"] = API::OtherFailed;
val["msg"] = ex.what();
}
invoker(200, headerOut, val.toStyledString());
});
});
api_regist("/index/api/closeRtpServer",[](API_ARGS_MAP){
CHECK_SECRET();
CHECK_ARGS("stream_id");
@ -1567,9 +1579,9 @@ void installWebApi() {
auto offer = allArgs.getArgs();
CHECK(!offer.empty(), "http body(webrtc offer sdp) is empty");
WebRtcPluginManager::Instance().getAnswerSdp(
*(static_cast<Session *>(&sender)), type, offer, WebRtcArgsImp(allArgs, sender.getIdentifier()),
[invoker, val, offer, headerOut](const WebRtcInterface &exchanger) mutable {
WebRtcPluginManager::Instance().getAnswerSdp(*(static_cast<Session *>(&sender)), type,
WebRtcArgsImp(allArgs, sender.getIdentifier()),
[invoker, val, offer, headerOut](const WebRtcInterface &exchanger) mutable {
//设置返回类型
headerOut["Content-Type"] = HttpFileManager::getContentType(".json");
//设置跨域
@ -1605,9 +1617,9 @@ void installWebApi() {
api_regist("/index/hook/on_publish",[](API_ARGS_JSON){
//开始推流事件
//转换hls
val["enableHls"] = true;
val["enable_hls"] = true;
//不录制mp4
val["enableMP4"] = false;
val["enable_mp4"] = false;
});
api_regist("/index/hook/on_play",[](API_ARGS_JSON){
@ -1750,6 +1762,11 @@ void installWebApi() {
api_regist("/index/hook/on_server_keepalive",[](API_ARGS_JSON){
//心跳hook
});
api_regist("/index/hook/on_rtp_server_timeout",[](API_ARGS_JSON){
//rtp server 超时
TraceL <<allArgs.getArgs().toStyledString();
});
}
void unInstallWebApi(){

View File

@ -13,7 +13,7 @@
#include <string>
#include <functional>
#include "jsoncpp/json.h"
#include "json/json.h"
#include "Common/Parser.h"
#include "Network/Socket.h"
#include "Http/HttpSession.h"
@ -231,7 +231,8 @@ bool checkArgs(Args &args, const First &first, const KeyTypes &...keys) {
void installWebApi();
void unInstallWebApi();
uint16_t openRtpServer(uint16_t local_port, const std::string &stream_id, bool enable_tcp, const std::string &local_ip, bool re_use_port, uint32_t ssrc);
uint16_t openRtpServer(uint16_t local_port, const std::string &stream_id, int tcp_mode, const std::string &local_ip, bool re_use_port, uint32_t ssrc);
void connectRtpServer(const std::string &stream_id, const std::string &dst_url, uint16_t dst_port, const std::function<void(const toolkit::SockException &ex)> &cb);
bool closeRtpServer(const std::string &stream_id);
Json::Value makeMediaSourceJson(mediakit::MediaSource &media);
void getStatisticJson(const std::function<void(Json::Value &val)> &cb);

View File

@ -15,7 +15,7 @@
#include "Common/config.h"
#include "Common/MediaSource.h"
#include "Http/HttpRequester.h"
#include "Network/TcpSession.h"
#include "Network/Session.h"
#include "Rtsp/RtspSession.h"
#include "Http/HttpSession.h"
#include "WebHook.h"
@ -46,6 +46,7 @@ const string kOnHttpAccess = HOOK_FIELD"on_http_access";
const string kOnServerStarted = HOOK_FIELD"on_server_started";
const string kOnServerKeepalive = HOOK_FIELD"on_server_keepalive";
const string kOnSendRtpStopped = HOOK_FIELD"on_send_rtp_stopped";
const string kOnRtpServerTimeout = HOOK_FIELD"on_rtp_server_timeout";
const string kAdminParams = HOOK_FIELD"admin_params";
const string kAliveInterval = HOOK_FIELD"alive_interval";
const string kRetry = HOOK_FIELD"retry";
@ -70,6 +71,7 @@ onceToken token([](){
mINI::Instance()[kOnServerStarted] = "";
mINI::Instance()[kOnServerKeepalive] = "";
mINI::Instance()[kOnSendRtpStopped] = "";
mINI::Instance()[kOnRtpServerTimeout] = "";
mINI::Instance()[kAdminParams] = "secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc";
mINI::Instance()[kAliveInterval] = 30.0;
mINI::Instance()[kRetry] = 1;
@ -292,6 +294,21 @@ static void pullStreamFromOrigin(const vector<string>& urls, size_t index, size_
static void *web_hook_tag = nullptr;
static mINI jsonToMini(const Value &obj) {
mINI ret;
if (obj.isObject()) {
for (auto it = obj.begin(); it != obj.end(); ++it) {
try {
auto str = (*it).asString();
ret[it.name()] = std::move(str);
} catch (std::exception &) {
WarnL << "Json is not convertible to string, key: " << it.name() << ", value: " << (*it);
}
}
}
return ret;
}
void installWebHook(){
GET_CONFIG(bool,hook_enable,Hook::kEnable);
GET_CONFIG(string,hook_adminparams,Hook::kAdminParams);
@ -311,55 +328,12 @@ void installWebHook(){
body["originTypeStr"] = getOriginTypeString(type);
//执行hook
do_http_hook(hook_publish, body, [invoker](const Value &obj, const string &err) mutable {
ProtocolOption option;
if (err.empty()) {
//推流鉴权成功
if (obj.isMember("enable_hls")) {
option.enable_hls = obj["enable_hls"].asBool();
}
if (obj.isMember("enable_mp4")) {
option.enable_mp4 = obj["enable_mp4"].asBool();
}
if (obj.isMember("enable_audio")) {
option.enable_audio = obj["enable_audio"].asBool();
}
if (obj.isMember("add_mute_audio")) {
option.add_mute_audio = obj["add_mute_audio"].asBool();
}
if (obj.isMember("mp4_save_path")) {
option.mp4_save_path = obj["mp4_save_path"].asString();
}
if (obj.isMember("mp4_max_second")) {
option.mp4_max_second = obj["mp4_max_second"].asUInt();
}
if (obj.isMember("hls_save_path")) {
option.hls_save_path = obj["hls_save_path"].asString();
}
if (obj.isMember("enable_rtsp")) {
option.enable_rtsp = obj["enable_rtsp"].asBool();
}
if (obj.isMember("enable_rtmp")) {
option.enable_rtmp = obj["enable_rtmp"].asBool();
}
if (obj.isMember("enable_ts")) {
option.enable_ts = obj["enable_ts"].asBool();
}
if (obj.isMember("enable_fmp4")) {
option.enable_fmp4 = obj["enable_fmp4"].asBool();
}
if (obj.isMember("continue_push_ms")) {
option.continue_push_ms = obj["continue_push_ms"].asUInt();
}
if (obj.isMember("mp4_as_player")) {
option.mp4_as_player = obj["mp4_as_player"].asBool();
}
if (obj.isMember("modify_stamp")) {
option.modify_stamp = obj["modify_stamp"].asBool();
}
invoker(err, option);
invoker(err, ProtocolOption(jsonToMini(obj)));
} else {
//推流鉴权失败
invoker(err, option);
invoker(err, ProtocolOption());
}
});
});
@ -505,8 +479,17 @@ void installWebHook(){
body["ip"] = sender.get_peer_ip();
body["port"] = sender.get_peer_port();
body["id"] = sender.getIdentifier();
// Hook回复立即关闭流
auto res_cb = [closePlayer](const Value &res, const string &err) {
bool flag = res["close"].asBool();
if (flag) {
closePlayer();
}
};
//执行hook
do_http_hook(hook_stream_not_found, body, nullptr);
do_http_hook(hook_stream_not_found, body, res_cb);
});
static auto getRecordInfo = [](const RecordInfo &info) {
@ -667,6 +650,21 @@ void installWebHook(){
});
});
NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::KBroadcastRtpServerTimeout, [](BroadcastRtpServerTimeout) {
GET_CONFIG(string, rtp_server_timeout, Hook::kOnRtpServerTimeout);
if (!hook_enable || rtp_server_timeout.empty()) {
return;
}
ArgsType body;
body["local_port"] = local_port;
body["stream_id"] = stream_id;
body["tcp_mode"] = tcp_mode;
body["re_use_port"] = re_use_port;
body["ssrc"] = ssrc;
do_http_hook(rtp_server_timeout, body);
});
//汇报服务器重新启动
reportServerStarted();

View File

@ -13,7 +13,7 @@
#include <string>
#include <functional>
#include "jsoncpp/json.h"
#include "json/json.h"
//支持json或urlencoded方式传输参数
#define JSON_ARGS

View File

@ -165,6 +165,7 @@ public:
[](const std::shared_ptr<ostream> &stream, const string &arg) -> bool {
//版本信息
*stream << "编译日期: " << BUILD_TIME << std::endl;
*stream << "代码日期: " << COMMIT_TIME << std::endl;
*stream << "当前git分支: " << BRANCH_NAME << std::endl;
*stream << "当前git hash值: " << COMMIT_HASH << std::endl;
throw ExitException();
@ -222,6 +223,9 @@ int start_main(int argc,char *argv[]) {
//启动异步日志线程
Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());
InfoL << kServerName;
//加载配置文件,如果配置文件不存在就创建一个
loadIniConfig(g_ini_file.data());
@ -273,9 +277,10 @@ int start_main(int argc,char *argv[]) {
#endif//defined(ENABLE_RTPPROXY)
#if defined(ENABLE_WEBRTC)
auto rtcSrv_tcp = std::make_shared<TcpServer>();
//webrtc udp服务器
auto rtcSrv = std::make_shared<UdpServer>();
rtcSrv->setOnCreateSocket([](const EventPoller::Ptr &poller, const Buffer::Ptr &buf, struct sockaddr *, int) {
auto rtcSrv_udp = std::make_shared<UdpServer>();
rtcSrv_udp->setOnCreateSocket([](const EventPoller::Ptr &poller, const Buffer::Ptr &buf, struct sockaddr *, int) {
if (!buf) {
return Socket::createSocket(poller, false);
}
@ -286,7 +291,8 @@ int start_main(int argc,char *argv[]) {
}
return Socket::createSocket(new_poller, false);
});
uint16_t rtcPort = mINI::Instance()[RTC::kPort];
uint16_t rtcPort = mINI::Instance()[Rtc::kPort];
uint16_t rtcTcpPort = mINI::Instance()[Rtc::kTcpPort];
#endif//defined(ENABLE_WEBRTC)
@ -333,15 +339,15 @@ int start_main(int argc,char *argv[]) {
#if defined(ENABLE_WEBRTC)
//webrtc udp服务器
if (rtcPort) { rtcSrv->start<WebRtcSession>(rtcPort); }
#endif//defined(ENABLE_WEBRTC)
if (rtcPort) { rtcSrv_udp->start<WebRtcSession>(rtcPort);}
if (rtcTcpPort) { rtcSrv_tcp->start<WebRtcSession>(rtcTcpPort);}
#endif//defined(ENABLE_WEBRTC)
#if defined(ENABLE_SRT)
// srt udp服务器
if(srtPort){
srtSrv->start<SRT::SrtSession>(srtPort);
}
if(srtPort) { srtSrv->start<SRT::SrtSession>(srtPort); }
#endif//defined(ENABLE_SRT)
} catch (std::exception &ex) {

View File

@ -420,9 +420,7 @@ FFmpegDecoder::FFmpegDecoder(const Track::Ptr &track, int thread_num) {
FFmpegDecoder::~FFmpegDecoder() {
stopThread(true);
if (_do_merger) {
_merger.inputFrame(nullptr, [&](uint64_t dts, uint64_t pts, const Buffer::Ptr &buffer, bool have_idr) {
decodeFrame(buffer->data(), buffer->size(), dts, pts, false);
});
_merger.flush();
}
flush();
}
@ -452,7 +450,7 @@ const AVCodecContext *FFmpegDecoder::getContext() const {
bool FFmpegDecoder::inputFrame_l(const Frame::Ptr &frame, bool live, bool enable_merge) {
if (_do_merger && enable_merge) {
return _merger.inputFrame(frame, [&](uint64_t dts, uint64_t pts, const Buffer::Ptr &buffer, bool have_idr) {
return _merger.inputFrame(frame, [this, live](uint64_t dts, uint64_t pts, const Buffer::Ptr &buffer, bool have_idr) {
decodeFrame(buffer->data(), buffer->size(), dts, pts, live);
});
}

View File

@ -37,7 +37,7 @@ bool MediaSink::addTrack(const Track::Ptr &track_in) {
};
_ticker.resetTime();
track->addDelegate(std::make_shared<FrameWriterInterfaceHelper>([this](const Frame::Ptr &frame) {
track->addDelegate([this](const Frame::Ptr &frame) {
if (_all_track_ready) {
return onTrackFrame(frame);
}
@ -52,7 +52,7 @@ bool MediaSink::addTrack(const Track::Ptr &track_in) {
//还有Track未就绪先缓存之
frame_unread.emplace_back(Frame::getCacheAbleFrame(frame));
return true;
}));
});
return true;
}
@ -223,7 +223,7 @@ static uint8_t s_mute_adts[] = {0xff, 0xf1, 0x6c, 0x40, 0x2d, 0x3f, 0xfc, 0x00,
#define MUTE_ADTS_DATA s_mute_adts
#define MUTE_ADTS_DATA_LEN sizeof(s_mute_adts)
#define MUTE_ADTS_DATA_MS 130
#define MUTE_ADTS_DATA_MS 128
bool MuteAudioMaker::inputFrame(const Frame::Ptr &frame) {
if (frame->getTrackType() == TrackVideo) {
@ -247,13 +247,13 @@ bool MediaSink::addMuteAudioTrack() {
}
auto audio = std::make_shared<AACTrack>(makeAacConfig(MUTE_ADTS_DATA, ADTS_HEADER_LEN));
_track_map[audio->getTrackType()] = std::make_pair(audio, true);
audio->addDelegate(std::make_shared<FrameWriterInterfaceHelper>([this](const Frame::Ptr &frame) {
audio->addDelegate([this](const Frame::Ptr &frame) {
return onTrackFrame(frame);
}));
});
_mute_audio_maker = std::make_shared<MuteAudioMaker>();
_mute_audio_maker->addDelegate(std::make_shared<FrameWriterInterfaceHelper>([audio](const Frame::Ptr &frame) {
_mute_audio_maker->addDelegate([audio](const Frame::Ptr &frame) {
return audio->inputFrame(frame);
}));
});
onTrackReady(audio);
TraceL << "mute aac track added";
return true;

View File

@ -61,7 +61,7 @@ public:
bool inputFrame(const Frame::Ptr &frame) override;
private:
uint32_t _audio_idx = 0;
uint64_t _audio_idx = 0;
};
/**

View File

@ -8,11 +8,13 @@
* may be found in the AUTHORS file in the root of the source tree.
*/
#include "MediaSource.h"
#include "Record/MP4Reader.h"
#include "Util/util.h"
#include "Util/NoticeCenter.h"
#include "Network/sockutil.h"
#include "Network/TcpSession.h"
#include "Network/Session.h"
#include "MediaSource.h"
#include "Common/config.h"
#include "Record/MP4Reader.h"
using namespace std;
using namespace toolkit;
@ -24,7 +26,11 @@ namespace toolkit {
namespace mediakit {
static recursive_mutex s_media_source_mtx;
static MediaSource::SchemaVhostAppStreamMap s_media_source_map;
using StreamMap = unordered_map<string/*strema_id*/, weak_ptr<MediaSource> >;
using AppStreamMap = unordered_map<string/*app*/, StreamMap>;
using VhostAppStreamMap = unordered_map<string/*vhost*/, AppStreamMap>;
using SchemaVhostAppStreamMap = unordered_map<string/*schema*/, VhostAppStreamMap>;
static SchemaVhostAppStreamMap s_media_source_map;
string getOriginTypeString(MediaOriginType type){
#define SWITCH_CASE(type) case MediaOriginType::type : return #type
@ -43,11 +49,60 @@ string getOriginTypeString(MediaOriginType type){
}
}
static string getOriginUrl_l(const MediaSource *thiz) {
return thiz->getSchema() + "://" + thiz->getVhost() + "/" + thiz->getApp() + "/" + thiz->getId();
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
ProtocolOption::ProtocolOption() {
GET_CONFIG(bool, s_modify_stamp, Protocol::kModifyStamp);
GET_CONFIG(bool, s_enabel_audio, Protocol::kEnableAudio);
GET_CONFIG(bool, s_add_mute_audio, Protocol::kAddMuteAudio);
GET_CONFIG(uint32_t, s_continue_push_ms, Protocol::kContinuePushMS);
GET_CONFIG(bool, s_enable_hls, Protocol::kEnableHls);
GET_CONFIG(bool, s_enable_mp4, Protocol::kEnableMP4);
GET_CONFIG(bool, s_enable_rtsp, Protocol::kEnableRtsp);
GET_CONFIG(bool, s_enable_rtmp, Protocol::kEnableRtmp);
GET_CONFIG(bool, s_enable_ts, Protocol::kEnableTS);
GET_CONFIG(bool, s_enable_fmp4, Protocol::kEnableFMP4);
GET_CONFIG(bool, s_hls_demand, Protocol::kHlsDemand);
GET_CONFIG(bool, s_rtsp_demand, Protocol::kRtspDemand);
GET_CONFIG(bool, s_rtmp_demand, Protocol::kRtmpDemand);
GET_CONFIG(bool, s_ts_demand, Protocol::kTSDemand);
GET_CONFIG(bool, s_fmp4_demand, Protocol::kFMP4Demand);
GET_CONFIG(bool, s_mp4_as_player, Protocol::kMP4AsPlayer);
GET_CONFIG(uint32_t, s_mp4_max_second, Protocol::kMP4MaxSecond);
GET_CONFIG(string, s_mp4_save_path, Protocol::kMP4SavePath);
GET_CONFIG(string, s_hls_save_path, Protocol::kHlsSavePath);
modify_stamp = s_modify_stamp;
enable_audio = s_enabel_audio;
add_mute_audio = s_add_mute_audio;
continue_push_ms = s_continue_push_ms;
enable_hls = s_enable_hls;
enable_mp4 = s_enable_mp4;
enable_rtsp = s_enable_rtsp;
enable_rtmp = s_enable_rtmp;
enable_ts = s_enable_ts;
enable_fmp4 = s_enable_fmp4;
hls_demand = s_hls_demand;
rtsp_demand = s_rtsp_demand;
rtmp_demand = s_rtmp_demand;
ts_demand = s_ts_demand;
fmp4_demand = s_fmp4_demand;
mp4_as_player = s_mp4_as_player;
mp4_max_second = s_mp4_max_second;
mp4_save_path = s_mp4_save_path;
hls_save_path = s_hls_save_path;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct MediaSourceNull : public MediaSource {
MediaSourceNull() : MediaSource("schema", "vhost", "app", "stream") {};
int readerCount() override { return 0; }
@ -109,16 +164,12 @@ std::shared_ptr<void> MediaSource::getOwnership() {
}
int MediaSource::getBytesSpeed(TrackType type){
if(type == TrackInvalid){
if(type == TrackInvalid || type == TrackMax){
return _speed[TrackVideo].getSpeed() + _speed[TrackAudio].getSpeed();
}
return _speed[type].getSpeed();
}
uint64_t MediaSource::getCreateStamp() const {
return _create_stamp;
}
uint64_t MediaSource::getAliveSecond() const {
//使用Ticker对象获取存活时间的目的是防止修改系统时间导致回退
return _ticker.createdTime() / 1000;
@ -140,6 +191,7 @@ std::weak_ptr<MediaSourceEvent> MediaSource::getListener(bool next) const{
if (!next) {
return _listener;
}
auto listener = dynamic_pointer_cast<MediaSourceEventInterceptor>(_listener.lock());
if (!listener) {
//不是MediaSourceEventInterceptor对象或者对象已经销毁
@ -170,13 +222,13 @@ MediaOriginType MediaSource::getOriginType() const {
string MediaSource::getOriginUrl() const {
auto listener = _listener.lock();
if (!listener) {
return getOriginUrl_l(this);
return getUrl();
}
auto ret = listener->getOriginUrl(const_cast<MediaSource &>(*this));
if (!ret.empty()) {
return ret;
}
return getOriginUrl_l(this);
return getUrl();
}
std::shared_ptr<SockInfo> MediaSource::getOriginSock() const {
@ -213,10 +265,14 @@ bool MediaSource::speed(float speed) {
bool MediaSource::close(bool force) {
auto listener = _listener.lock();
if(!listener){
if (!listener) {
return false;
}
return listener->close(*this,force);
if (!force && totalReaderCount()) {
//有人观看,不强制关闭
return false;
}
return listener->close(*this);
}
float MediaSource::getLossRate(mediakit::TrackType type) {
@ -231,29 +287,31 @@ toolkit::EventPoller::Ptr MediaSource::getOwnerPoller() {
toolkit::EventPoller::Ptr ret;
auto listener = _listener.lock();
if (listener) {
ret = listener->getOwnerPoller(*this);
return listener->getOwnerPoller(*this);
}
return ret ? ret : _default_poller;
WarnL << toolkit::demangle(typeid(*this).name()) + "::getOwnerPoller failed, now return default poller: " + getUrl();
return _default_poller;
}
void MediaSource::onReaderChanged(int size) {
weak_ptr<MediaSource> weak_self = shared_from_this();
getOwnerPoller()->async([weak_self, size]() {
auto listener = _listener.lock();
if (!listener) {
return;
}
getOwnerPoller()->async([weak_self, size, listener]() {
auto strong_self = weak_self.lock();
if (!strong_self) {
return;
}
auto listener = strong_self->_listener.lock();
if (listener) {
listener->onReaderChanged(*strong_self, size);
}
listener->onReaderChanged(*strong_self, size);
});
}
bool MediaSource::setupRecord(Recorder::type type, bool start, const string &custom_path, size_t max_second){
auto listener = _listener.lock();
if (!listener) {
WarnL << "未设置MediaSource的事件监听者setupRecord失败:" << getSchema() << "/" << getVhost() << "/" << getApp() << "/" << getId();
WarnL << "未设置MediaSource的事件监听者setupRecord失败:" << getUrl();
return false;
}
return listener->setupRecord(*this, type, start, custom_path, max_second);
@ -337,7 +395,7 @@ void MediaSource::for_each_media(const function<void(const Ptr &src)> &cb,
static MediaSource::Ptr find_l(const string &schema, const string &vhost_in, const string &app, const string &id, bool from_mp4) {
string vhost = vhost_in;
GET_CONFIG(bool,enableVhost,General::kEnableVhost);
GET_CONFIG(bool, enableVhost, General::kEnableVhost);
if(vhost.empty() || !enableVhost){
vhost = DEFAULT_VHOST;
}
@ -351,7 +409,7 @@ static MediaSource::Ptr find_l(const string &schema, const string &vhost_in, con
MediaSource::for_each_media([&](const MediaSource::Ptr &src) { ret = std::move(const_cast<MediaSource::Ptr &>(src)); }, schema, vhost, app, id);
if(!ret && from_mp4 && schema != HLS_SCHEMA){
//未找媒体源则读取mp4创建一个
//未媒体源则读取mp4创建一个
//播放hls不触发mp4点播(因为HLS也可以用于录像不是纯粹的直播)
ret = MediaSource::createFromMP4(schema, vhost, app, id);
}
@ -379,7 +437,7 @@ static void findAsync_l(const MediaInfo &info, const std::shared_ptr<Session> &s
};
auto on_timeout = poller->doDelayTask(maxWaitMS, [cb_once, listener_tag]() {
//最多等待一定时间,如果这个时间内,流未注册上,那么返回未找到流
// 最多等待一定时间,如在这个时间内,流还未注册上,则返回空
NoticeCenter::Instance().delListener(listener_tag, Broadcast::kBroadcastMediaChanged);
cb_once(nullptr);
return 0;
@ -402,17 +460,15 @@ static void findAsync_l(const MediaInfo &info, const std::shared_ptr<Session> &s
//不是自己感兴趣的事件,忽略之
return;
}
poller->async([weak_session, cancel_all, info, cb_once]() {
cancel_all();
auto strong_session = weak_session.lock();
if (!strong_session) {
//自己已经销毁
return;
if (auto strong_session = weak_session.lock()) {
//播发器请求的流终于注册上了,切换到自己的线程再回复
DebugL << "收到媒体注册事件,回复播放器:" << info.getUrl();
//再找一遍媒体源,一般能找到
findAsync_l(info, strong_session, false, cb_once);
}
//播发器请求的流终于注册上了,切换到自己的线程再回复
DebugL << "收到媒体注册事件,回复播放器:" << info._schema << "/" << info._vhost << "/" << info._app << "/" << info._streamid;
//再找一遍媒体源,一般能找到
findAsync_l(info, strong_session, false, cb_once);
}, false);
};
@ -458,7 +514,7 @@ void MediaSource::emitEvent(bool regist){
}
//触发广播
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaChanged, regist, *this);
InfoL << (regist ? "媒体注册:" : "媒体注销:") << _schema << " " << _vhost << " " << _app << " " << _stream_id;
InfoL << (regist ? "媒体注册:" : "媒体注销:") << getUrl();
}
void MediaSource::regist() {
@ -472,7 +528,7 @@ void MediaSource::regist() {
return;
}
//增加判断, 防止当前流已注册时再次注册
throw std::invalid_argument("media source already existed:" + _schema + "/" + _vhost + "/" + _app + "/" + _stream_id);
throw std::invalid_argument("media source already existed:" + getUrl());
}
ref = shared_from_this();
}
@ -519,9 +575,9 @@ bool MediaSource::unregist() {
/////////////////////////////////////MediaInfo//////////////////////////////////////
void MediaInfo::parse(const string &url_in){
void MediaInfo::parse(const std::string &url_in){
_full_url = url_in;
string url = url_in;
auto url = url_in;
auto pos = url.find("?");
if (pos != string::npos) {
_param_strs = url.substr(pos + 1);
@ -621,11 +677,7 @@ void MediaSourceEvent::onReaderChanged(MediaSource &sender, int size){
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastStreamNoneReader, *strong_sender);
} else {
//这个是mp4点播我们自动关闭
WarnL << "MP4点播无人观看,自动关闭:"
<< strong_sender->getSchema() << "/"
<< strong_sender->getVhost() << "/"
<< strong_sender->getApp() << "/"
<< strong_sender->getId();
WarnL << "MP4点播无人观看,自动关闭:" << strong_sender->getUrl();
strong_sender->close(false);
}
return false;
@ -633,7 +685,7 @@ void MediaSourceEvent::onReaderChanged(MediaSource &sender, int size){
}
string MediaSourceEvent::getOriginUrl(MediaSource &sender) const {
return getOriginUrl_l(&sender);
return sender.getUrl();
}
MediaOriginType MediaSourceEventInterceptor::getOriginType(MediaSource &sender) const {
@ -688,12 +740,12 @@ bool MediaSourceEventInterceptor::speed(MediaSource &sender, float speed) {
return listener->speed(sender, speed);
}
bool MediaSourceEventInterceptor::close(MediaSource &sender, bool force) {
bool MediaSourceEventInterceptor::close(MediaSource &sender) {
auto listener = _listener.lock();
if (!listener) {
return false;
}
return listener->close(sender, force);
return listener->close(sender);
}
int MediaSourceEventInterceptor::totalReaderCount(MediaSource &sender) {
@ -733,7 +785,7 @@ toolkit::EventPoller::Ptr MediaSourceEventInterceptor::getOwnerPoller(MediaSourc
if (listener) {
return listener->getOwnerPoller(sender);
}
return EventPollerPool::Instance().getPoller();
throw std::runtime_error(toolkit::demangle(typeid(*this).name()) + "::getOwnerPoller failed");
}
bool MediaSourceEventInterceptor::setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path, size_t max_second) {

View File

@ -19,19 +19,14 @@
#include <unordered_map>
#include "Common/config.h"
#include "Common/Parser.h"
#include "Util/logger.h"
#include "Util/TimeTicker.h"
#include "Util/NoticeCenter.h"
#include "Util/List.h"
#include "Network/Socket.h"
#include "Rtsp/Rtsp.h"
#include "Rtmp/Rtmp.h"
#include "Extension/Track.h"
#include "Record/Recorder.h"
namespace toolkit{
class Session;
}// namespace toolkit
namespace toolkit {
class Session;
} // namespace toolkit
namespace mediakit {
@ -62,8 +57,8 @@ public:
~NotImplemented() override = default;
};
MediaSourceEvent(){};
virtual ~MediaSourceEvent(){};
MediaSourceEvent() = default;
virtual ~MediaSourceEvent() = default;
// 获取媒体源类型
virtual MediaOriginType getOriginType(MediaSource &sender) const { return MediaOriginType::unknown; }
@ -79,13 +74,13 @@ public:
// 通知倍数
virtual bool speed(MediaSource &sender, float speed) { return false; }
// 通知其停止产生流
virtual bool close(MediaSource &sender, bool force) { return false; }
virtual bool close(MediaSource &sender) { return false; }
// 获取观看总人数,此函数一般强制重载
virtual int totalReaderCount(MediaSource &sender) { throw NotImplemented(toolkit::demangle(typeid(*this).name()) + "::totalReaderCount not implemented"); }
// 通知观看人数变化
virtual void onReaderChanged(MediaSource &sender, int size);
//流注册或注销事件
virtual void onRegist(MediaSource &sender, bool regist) {};
virtual void onRegist(MediaSource &sender, bool regist) {}
// 获取丢包率
virtual float getLossRate(MediaSource &sender, TrackType type) { return -1; }
// 获取所在线程, 此函数一般强制重载
@ -95,7 +90,7 @@ public:
// 开启或关闭录制
virtual bool setupRecord(MediaSource &sender, Recorder::type type, bool start, const std::string &custom_path, size_t max_second) { return false; };
// 获取录制状态
virtual bool isRecording(MediaSource &sender, Recorder::type type) { return false; };
virtual bool isRecording(MediaSource &sender, Recorder::type type) { return false; }
// 获取所有track相关信息
virtual std::vector<Track::Ptr> getMediaTracks(MediaSource &sender, bool trackReady = true) const { return std::vector<Track::Ptr>(); };
@ -139,11 +134,96 @@ private:
toolkit::Timer::Ptr _async_close_timer;
};
//该对象用于拦截感兴趣的MediaSourceEvent事件
class MediaSourceEventInterceptor : public MediaSourceEvent{
class ProtocolOption {
public:
MediaSourceEventInterceptor(){}
~MediaSourceEventInterceptor() override {}
ProtocolOption();
//时间戳修复这一路流标志位
bool modify_stamp;
//转协议是否开启音频
bool enable_audio;
//添加静音音频,在关闭音频时,此开关无效
bool add_mute_audio;
//断连续推延时,单位毫秒,默认采用配置文件
uint32_t continue_push_ms;
//是否开启转换为hls
bool enable_hls;
//是否开启MP4录制
bool enable_mp4;
//是否开启转换为rtsp/webrtc
bool enable_rtsp;
//是否开启转换为rtmp/flv
bool enable_rtmp;
//是否开启转换为http-ts/ws-ts
bool enable_ts;
//是否开启转换为http-fmp4/ws-fmp4
bool enable_fmp4;
// hls协议是否按需生成如果hls.segNum配置为0(意味着hls录制)那么hls将一直生成(不管此开关)
bool hls_demand;
// rtsp[s]协议是否按需生成
bool rtsp_demand;
// rtmp[s]、http[s]-flv、ws[s]-flv协议是否按需生成
bool rtmp_demand;
// http[s]-ts协议是否按需生成
bool ts_demand;
// http[s]-fmp4、ws[s]-fmp4协议是否按需生成
bool fmp4_demand;
//是否将mp4录制当做观看者
bool mp4_as_player;
//mp4切片大小单位秒
size_t mp4_max_second;
//mp4录制保存路径
std::string mp4_save_path;
//hls录制保存路径
std::string hls_save_path;
template <typename MAP>
ProtocolOption(const MAP &allArgs) : ProtocolOption() {
#define GET_OPT_VALUE(key) getArgsValue(allArgs, #key, key)
GET_OPT_VALUE(modify_stamp);
GET_OPT_VALUE(enable_audio);
GET_OPT_VALUE(add_mute_audio);
GET_OPT_VALUE(continue_push_ms);
GET_OPT_VALUE(enable_hls);
GET_OPT_VALUE(enable_mp4);
GET_OPT_VALUE(enable_rtsp);
GET_OPT_VALUE(enable_rtmp);
GET_OPT_VALUE(enable_ts);
GET_OPT_VALUE(enable_fmp4);
GET_OPT_VALUE(hls_demand);
GET_OPT_VALUE(rtsp_demand);
GET_OPT_VALUE(rtmp_demand);
GET_OPT_VALUE(ts_demand);
GET_OPT_VALUE(fmp4_demand);
GET_OPT_VALUE(mp4_max_second);
GET_OPT_VALUE(mp4_as_player);
GET_OPT_VALUE(mp4_save_path);
GET_OPT_VALUE(hls_save_path);
}
private:
template <typename MAP, typename KEY, typename TYPE>
static void getArgsValue(const MAP &allArgs, const KEY &key, TYPE &value) {
auto val = ((MAP &)allArgs)[key];
if (!val.empty()) {
value = (TYPE)val;
}
}
};
//该对象用于拦截感兴趣的MediaSourceEvent事件
class MediaSourceEventInterceptor : public MediaSourceEvent {
public:
MediaSourceEventInterceptor() = default;
~MediaSourceEventInterceptor() override = default;
void setDelegate(const std::weak_ptr<MediaSourceEvent> &listener);
std::shared_ptr<MediaSourceEvent> getDelegate() const;
@ -155,7 +235,7 @@ public:
bool seekTo(MediaSource &sender, uint32_t stamp) override;
bool pause(MediaSource &sender, bool pause) override;
bool speed(MediaSource &sender, float speed) override;
bool close(MediaSource &sender, bool force) override;
bool close(MediaSource &sender) override;
int totalReaderCount(MediaSource &sender) override;
void onReaderChanged(MediaSource &sender, int size) override;
void onRegist(MediaSource &sender, bool regist) override;
@ -174,18 +254,20 @@ private:
/**
* url获取媒体相关信息
*/
class MediaInfo{
class MediaInfo {
public:
~MediaInfo() {}
MediaInfo() {}
~MediaInfo() = default;
MediaInfo() = default;
MediaInfo(const std::string &url) { parse(url); }
void parse(const std::string &url);
std::string shortUrl() const { return _vhost + "/" + _app + "/" + _streamid; }
std::string getUrl() const { return _schema + "://" + shortUrl(); }
public:
uint16_t _port = 0;
std::string _full_url;
std::string _schema;
std::string _host;
uint16_t _port = 0;
std::string _vhost;
std::string _app;
std::string _streamid;
@ -199,12 +281,8 @@ class MediaSource: public TrackSource, public std::enable_shared_from_this<Media
public:
static MediaSource& NullMediaSource();
using Ptr = std::shared_ptr<MediaSource>;
using StreamMap = std::unordered_map<std::string/*stream_id*/, std::weak_ptr<MediaSource> >;
using AppStreamMap = std::unordered_map<std::string/*app*/, StreamMap>;
using VhostAppStreamMap = std::unordered_map<std::string/*vhost*/, AppStreamMap>;
using SchemaVhostAppStreamMap = std::unordered_map<std::string/*schema*/, VhostAppStreamMap>;
MediaSource(const std::string &schema, const std::string &vhost, const std::string &app, const std::string &stream_id) ;
MediaSource(const std::string &schema, const std::string &vhost, const std::string &app, const std::string &stream_id);
virtual ~MediaSource();
////////////////获取MediaSource相关信息////////////////
@ -218,6 +296,10 @@ public:
// 流id
const std::string& getId() const;
std::string shortUrl() const { return _vhost + "/" + _app + "/" + _stream_id; }
std::string getUrl() const { return _schema + "://" + shortUrl(); }
//获取对象所有权
std::shared_ptr<void> getOwnership();
@ -232,7 +314,7 @@ public:
// 获取数据速率单位bytes/s
int getBytesSpeed(TrackType type = TrackInvalid);
// 获取流创建GMT unix时间戳单位秒
uint64_t getCreateStamp() const;
uint64_t getCreateStamp() const { return _create_stamp; }
// 获取流上线时间,单位秒
uint64_t getAliveSecond() const;
@ -263,9 +345,9 @@ public:
// 拖动进度条
bool seekTo(uint32_t stamp);
//暂停
// 暂停
bool pause(bool pause);
//倍数播放
// 倍数播放
bool speed(float speed);
// 关闭该流
bool close(bool force);
@ -288,8 +370,11 @@ public:
// 同步查找流
static Ptr find(const std::string &schema, const std::string &vhost, const std::string &app, const std::string &id, bool from_mp4 = false);
static Ptr find(const MediaInfo &info, bool from_mp4 = false) {
return find(info._schema, info._vhost, info._app, info._streamid, from_mp4);
}
// 忽略类型同步查找流可能返回rtmp/rtsp/hls类型
// 忽略schema同步查找流可能返回rtmp/rtsp/hls类型
static Ptr find(const std::string &vhost, const std::string &app, const std::string &stream_id, bool from_mp4 = false);
// 异步查找流
@ -304,9 +389,9 @@ protected:
void regist();
private:
//媒体注销
// 媒体注销
bool unregist();
//触发媒体事件
// 触发媒体事件
void emitEvent(bool regist);
protected:
@ -322,11 +407,11 @@ private:
std::string _stream_id;
std::weak_ptr<MediaSourceEvent> _listener;
toolkit::EventPoller::Ptr _default_poller;
//对象个数统计
// 对象个数统计
toolkit::ObjectStatistic<MediaSource> _statistic;
};
///缓存刷新策略类
/// 缓存刷新策略类
class FlushPolicy {
public:
FlushPolicy() = default;
@ -335,7 +420,8 @@ public:
bool isFlushAble(bool is_video, bool is_key, uint64_t new_stamp, size_t cache_size);
private:
uint64_t _last_stamp[2] = {0, 0};
// 音视频的最后时间戳
uint64_t _last_stamp[2] = { 0, 0 };
};
/// 合并写缓存模板
@ -345,16 +431,14 @@ private:
template<typename packet, typename policy = FlushPolicy, typename packet_list = toolkit::List<std::shared_ptr<packet> > >
class PacketCache {
public:
PacketCache(){
_cache = std::make_shared<packet_list>();
}
PacketCache() { _cache = std::make_shared<packet_list>(); }
virtual ~PacketCache() = default;
void inputPacket(uint64_t stamp, bool is_video, std::shared_ptr<packet> pkt, bool key_pos) {
bool flush = flushImmediatelyWhenCloseMerge();
if (!flush && _policy.isFlushAble(is_video, key_pos, stamp, _cache->size())) {
flushAll();
bool flag = flushImmediatelyWhenCloseMerge();
if (!flag && _policy.isFlushAble(is_video, key_pos, stamp, _cache->size())) {
flush();
}
//追加数据到最后
@ -363,11 +447,20 @@ public:
_key_pos = key_pos;
}
if (flush) {
flushAll();
if (flag) {
flush();
}
}
void flush() {
if (_cache->empty()) {
return;
}
onFlush(std::move(_cache), _key_pos);
_cache = std::make_shared<packet_list>();
_key_pos = false;
}
virtual void clearCache() {
_cache->clear();
}
@ -375,22 +468,19 @@ public:
virtual void onFlush(std::shared_ptr<packet_list>, bool key_pos) = 0;
private:
void flushAll() {
if (_cache->empty()) {
return;
}
onFlush(std::move(_cache), _key_pos);
_cache = std::make_shared<packet_list>();
_key_pos = false;
}
bool flushImmediatelyWhenCloseMerge() {
//一般的协议关闭合并写时立即刷新缓存这样可以减少一帧的延时但是rtp例外
//因为rtp的包很小一个RtpPacket包中也不是完整的一帧图像所以在关闭合并写时
//还是有必要缓冲一帧的rtp(也就是时间戳相同的rtp)再输出,这样虽然会增加一帧的延时
//但是却对性能提升很大,这样做还是比较划算的
// 一般的协议关闭合并写时立即刷新缓存这样可以减少一帧的延时但是rtp例外
// 因为rtp的包很小一个RtpPacket包中也不是完整的一帧图像所以在关闭合并写时
// 还是有必要缓冲一帧的rtp(也就是时间戳相同的rtp)再输出,这样虽然会增加一帧的延时
// 但是却对性能提升很大,这样做还是比较划算的
GET_CONFIG(int, mergeWriteMS, General::kMergeWriteMS);
GET_CONFIG(int, rtspLowLatency, Rtsp::kLowLatency);
if (std::is_same<packet, RtpPacket>::value && rtspLowLatency) {
return true;
}
return std::is_same<packet, RtpPacket>::value ? false : (mergeWriteMS <= 0);
}

View File

@ -1,4 +1,4 @@
/*
/*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
@ -21,26 +21,8 @@ namespace toolkit {
namespace mediakit {
ProtocolOption::ProtocolOption() {
GET_CONFIG(bool, s_to_hls, General::kPublishToHls);
GET_CONFIG(bool, s_to_mp4, General::kPublishToMP4);
GET_CONFIG(bool, s_enabel_audio, General::kEnableAudio);
GET_CONFIG(bool, s_add_mute_audio, General::kAddMuteAudio);
GET_CONFIG(bool, s_mp4_as_player, Record::kMP4AsPlayer);
GET_CONFIG(uint32_t, s_continue_push_ms, General::kContinuePushMS);
GET_CONFIG(bool, s_modify_stamp, General::kModifyStamp);
enable_hls = s_to_hls;
enable_mp4 = s_to_mp4;
enable_audio = s_enabel_audio;
add_mute_audio = s_add_mute_audio;
continue_push_ms = s_continue_push_ms;
mp4_as_player = s_mp4_as_player;
modify_stamp = s_modify_stamp;
}
static std::shared_ptr<MediaSinkInterface> makeRecorder(MediaSource &sender, const vector<Track::Ptr> &tracks, Recorder::type type, const string &custom_path, size_t max_second){
auto recorder = Recorder::createRecorder(type, sender.getVhost(), sender.getApp(), sender.getId(), custom_path, max_second);
static std::shared_ptr<MediaSinkInterface> makeRecorder(MediaSource &sender, const vector<Track::Ptr> &tracks, Recorder::type type, const ProtocolOption &option){
auto recorder = Recorder::createRecorder(type, sender.getVhost(), sender.getApp(), sender.getId(), option);
for (auto &track : tracks) {
recorder->addTrack(track);
}
@ -89,38 +71,40 @@ const std::string &MultiMediaSourceMuxer::getStreamId() const {
return _stream_id;
}
std::string MultiMediaSourceMuxer::shortUrl() const {
auto ret = getOriginUrl(MediaSource::NullMediaSource());
if (!ret.empty()) {
return ret;
}
return _vhost + "/" + _app + "/" + _stream_id;
}
MultiMediaSourceMuxer::MultiMediaSourceMuxer(const string &vhost, const string &app, const string &stream, float dur_sec, const ProtocolOption &option) {
_poller = EventPollerPool::Instance().getPoller();
_create_in_poller = _poller->isCurrentThread();
_vhost = vhost;
_app = app;
_stream_id = stream;
_option = option;
_get_origin_url = [this, vhost, app, stream]() {
auto ret = getOriginUrl(MediaSource::NullMediaSource());
if (!ret.empty()) {
return ret;
}
return vhost + "/" + app + "/" + stream;
};
if (option.enable_rtmp) {
_rtmp = std::make_shared<RtmpMediaSourceMuxer>(vhost, app, stream, std::make_shared<TitleMeta>(dur_sec));
_rtmp = std::make_shared<RtmpMediaSourceMuxer>(vhost, app, stream, option, std::make_shared<TitleMeta>(dur_sec));
}
if (option.enable_rtsp) {
_rtsp = std::make_shared<RtspMediaSourceMuxer>(vhost, app, stream, std::make_shared<TitleSdp>(dur_sec));
_rtsp = std::make_shared<RtspMediaSourceMuxer>(vhost, app, stream, option, std::make_shared<TitleSdp>(dur_sec));
}
if (option.enable_hls) {
_hls = dynamic_pointer_cast<HlsRecorder>(Recorder::createRecorder(Recorder::type_hls, vhost, app, stream, option.hls_save_path));
_hls = dynamic_pointer_cast<HlsRecorder>(Recorder::createRecorder(Recorder::type_hls, vhost, app, stream, option));
}
if (option.enable_mp4) {
_mp4 = Recorder::createRecorder(Recorder::type_mp4, vhost, app, stream, option.mp4_save_path, option.mp4_max_second);
_mp4 = Recorder::createRecorder(Recorder::type_mp4, vhost, app, stream, option);
}
if (option.enable_ts) {
_ts = std::make_shared<TSMediaSourceMuxer>(vhost, app, stream);
_ts = std::make_shared<TSMediaSourceMuxer>(vhost, app, stream, option);
}
#if defined(ENABLE_MP4)
if (option.enable_fmp4) {
_fmp4 = std::make_shared<FMP4MediaSourceMuxer>(vhost, app, stream);
_fmp4 = std::make_shared<FMP4MediaSourceMuxer>(vhost, app, stream, option);
}
#endif
@ -200,12 +184,19 @@ int MultiMediaSourceMuxer::totalReaderCount(MediaSource &sender) {
//此函数可能跨线程调用
bool MultiMediaSourceMuxer::setupRecord(MediaSource &sender, Recorder::type type, bool start, const string &custom_path, size_t max_second) {
onceToken token(nullptr, [&]() {
if (_option.mp4_as_player && type == Recorder::type_mp4) {
//开启关闭mp4录制触发观看人数变化相关事件
onReaderChanged(sender, totalReaderCount());
}
});
switch (type) {
case Recorder::type_hls : {
if (!_hls)
{
//创建hls对象
auto hls = dynamic_pointer_cast<HlsRecorder>(makeRecorder(sender, getTracks(), type, custom_path, max_second));
_option.hls_save_path = custom_path;
auto hls = dynamic_pointer_cast<HlsRecorder>(makeRecorder(sender, getTracks(), type, _option));
if (hls) {
//设置HlsMediaSource的事件监听器
hls->setListener(shared_from_this());
@ -218,7 +209,9 @@ bool MultiMediaSourceMuxer::setupRecord(MediaSource &sender, Recorder::type type
case Recorder::type_mp4 : {
if (start && !_mp4) {
//开始录制
_mp4 = makeRecorder(sender, getTracks(), type, custom_path, max_second);
_option.mp4_save_path = custom_path;
_option.mp4_max_second = max_second;
_mp4 = makeRecorder(sender, getTracks(), type, _option);
} else if (!start && _mp4) {
//停止录制
_mp4 = nullptr;
@ -264,7 +257,7 @@ void MultiMediaSourceMuxer::startSendRtp(MediaSource &sender, const MediaSourceE
auto ssrc = args.ssrc;
rtp_sender->setOnClose([weak_self, ssrc](const toolkit::SockException &ex) {
if (auto strong_self = weak_self.lock()) {
WarnL << "stream:" << strong_self->_get_origin_url() << " stop send rtp:" << ssrc << ", reason:" << ex.what();
WarnL << "stream:" << strong_self->shortUrl() << " stop send rtp:" << ssrc << ", reason:" << ex.what();
strong_self->_rtp_sender.erase(ssrc);
//触发观看人数统计
strong_self->onReaderChanged(MediaSource::NullMediaSource(), strong_self->totalReaderCount());
@ -308,7 +301,12 @@ EventPoller::Ptr MultiMediaSourceMuxer::getOwnerPoller(MediaSource &sender) {
return _poller;
}
try {
return listener->getOwnerPoller(sender);
auto ret = listener->getOwnerPoller(sender);
if (ret != _poller) {
WarnL << "OwnerPoller changed:" << shortUrl();
_poller = ret;
}
return ret;
} catch (MediaSourceEvent::NotImplemented &) {
// listener未重载getOwnerPoller
return _poller;
@ -316,10 +314,6 @@ EventPoller::Ptr MultiMediaSourceMuxer::getOwnerPoller(MediaSource &sender) {
}
bool MultiMediaSourceMuxer::onTrackReady(const Track::Ptr &track) {
if (CodecL16 == track->getCodecId()) {
WarnL << "L16音频格式目前只支持RTSP协议推流拉流!!!";
return false;
}
bool ret = false;
if (_rtmp) {
@ -350,6 +344,7 @@ bool MultiMediaSourceMuxer::onTrackReady(const Track::Ptr &track) {
}
void MultiMediaSourceMuxer::onAllTrackReady() {
CHECK(!_create_in_poller || getOwnerPoller(MediaSource::NullMediaSource())->isCurrentThread());
setMediaListener(getDelegate());
if (_rtmp) {
@ -367,7 +362,7 @@ void MultiMediaSourceMuxer::onAllTrackReady() {
if (listener) {
listener->onAllTrackReady();
}
InfoL << "stream: " << _get_origin_url() << " , codec info: " << getTrackInfoStr(this);
InfoL << "stream: " << shortUrl() << " , codec info: " << getTrackInfoStr(this);
}
void MultiMediaSourceMuxer::resetTracks() {

View File

@ -23,50 +23,11 @@
namespace mediakit {
class ProtocolOption {
public:
ProtocolOption();
//是否开启转换为hls
bool enable_hls = false;
//是否开启MP4录制
bool enable_mp4 = false;
//是否将mp4录制当做观看者
bool mp4_as_player = false;
//是否开启转换为rtsp/webrtc
bool enable_rtsp = true;
//是否开启转换为rtmp/flv
bool enable_rtmp = true;
//是否开启转换为http-ts/ws-ts
bool enable_ts = true;
//是否开启转换为http-fmp4/ws-fmp4
bool enable_fmp4 = true;
//转协议是否开启音频
bool enable_audio = true;
//添加静音音频,在关闭音频时,此开关无效
bool add_mute_audio = true;
//mp4录制保存路径
std::string mp4_save_path;
//mp4切片大小单位秒
size_t mp4_max_second = 0;
//hls录制保存路径
std::string hls_save_path;
//断连续推延时,单位毫秒,默认采用配置文件
uint32_t continue_push_ms;
//时间戳修复这一路流标志位
bool modify_stamp;
};
class MultiMediaSourceMuxer : public MediaSourceEventInterceptor, public MediaSink, public std::enable_shared_from_this<MultiMediaSourceMuxer>{
public:
typedef std::shared_ptr<MultiMediaSourceMuxer> Ptr;
class Listener{
class Listener {
public:
Listener() = default;
virtual ~Listener() = default;
@ -165,6 +126,7 @@ public:
const std::string& getVhost() const;
const std::string& getApp() const;
const std::string& getStreamId() const;
std::string shortUrl() const;
protected:
/////////////////////////////////MediaSink override/////////////////////////////////
@ -189,6 +151,7 @@ protected:
private:
bool _is_enable = false;
bool _create_in_poller = false;
std::string _vhost;
std::string _app;
std::string _stream_id;
@ -196,7 +159,6 @@ private:
toolkit::Ticker _last_check;
Stamp _stamp[2];
std::weak_ptr<Listener> _track_listener;
std::function<std::string()> _get_origin_url;
#if defined(ENABLE_RTPPROXY)
std::unordered_map<std::string, RtpSender::Ptr> _rtp_sender;
#endif //ENABLE_RTPPROXY

View File

@ -56,6 +56,7 @@ const string kBroadcastNotFoundStream = "kBroadcastNotFoundStream";
const string kBroadcastStreamNoneReader = "kBroadcastStreamNoneReader";
const string kBroadcastHttpBeforeAccess = "kBroadcastHttpBeforeAccess";
const string kBroadcastSendRtpStopped = "kBroadcastSendRtpStopped";
const string KBroadcastRtpServerTimeout = "KBroadcastRtpServerTimeout";
} // namespace Broadcast
@ -67,53 +68,84 @@ const string kFlowThreshold = GENERAL_FIELD "flowThreshold";
const string kStreamNoneReaderDelayMS = GENERAL_FIELD "streamNoneReaderDelayMS";
const string kMaxStreamWaitTimeMS = GENERAL_FIELD "maxStreamWaitMS";
const string kEnableVhost = GENERAL_FIELD "enableVhost";
const string kAddMuteAudio = GENERAL_FIELD "addMuteAudio";
const string kResetWhenRePlay = GENERAL_FIELD "resetWhenRePlay";
const string kPublishToHls = GENERAL_FIELD "publishToHls";
const string kPublishToMP4 = GENERAL_FIELD "publishToMP4";
const string kMergeWriteMS = GENERAL_FIELD "mergeWriteMS";
const string kModifyStamp = GENERAL_FIELD "modifyStamp";
const string kHlsDemand = GENERAL_FIELD "hls_demand";
const string kRtspDemand = GENERAL_FIELD "rtsp_demand";
const string kRtmpDemand = GENERAL_FIELD "rtmp_demand";
const string kTSDemand = GENERAL_FIELD "ts_demand";
const string kFMP4Demand = GENERAL_FIELD "fmp4_demand";
const string kEnableAudio = GENERAL_FIELD "enable_audio";
const string kCheckNvidiaDev = GENERAL_FIELD "check_nvidia_dev";
const string kEnableFFmpegLog = GENERAL_FIELD "enable_ffmpeg_log";
const string kWaitTrackReadyMS = GENERAL_FIELD "wait_track_ready_ms";
const string kWaitAddTrackMS = GENERAL_FIELD "wait_add_track_ms";
const string kUnreadyFrameCache = GENERAL_FIELD "unready_frame_cache";
const string kContinuePushMS = GENERAL_FIELD "continue_push_ms";
static onceToken token([]() {
mINI::Instance()[kFlowThreshold] = 1024;
mINI::Instance()[kStreamNoneReaderDelayMS] = 20 * 1000;
mINI::Instance()[kMaxStreamWaitTimeMS] = 15 * 1000;
mINI::Instance()[kEnableVhost] = 0;
mINI::Instance()[kAddMuteAudio] = 1;
mINI::Instance()[kResetWhenRePlay] = 1;
mINI::Instance()[kPublishToHls] = 1;
mINI::Instance()[kPublishToMP4] = 0;
mINI::Instance()[kMergeWriteMS] = 0;
mINI::Instance()[kModifyStamp] = 0;
mINI::Instance()[kMediaServerId] = makeRandStr(16);
mINI::Instance()[kHlsDemand] = 0;
mINI::Instance()[kRtspDemand] = 0;
mINI::Instance()[kRtmpDemand] = 0;
mINI::Instance()[kTSDemand] = 0;
mINI::Instance()[kFMP4Demand] = 0;
mINI::Instance()[kEnableAudio] = 1;
mINI::Instance()[kCheckNvidiaDev] = 1;
mINI::Instance()[kEnableFFmpegLog] = 0;
mINI::Instance()[kWaitTrackReadyMS] = 10000;
mINI::Instance()[kWaitAddTrackMS] = 3000;
mINI::Instance()[kUnreadyFrameCache] = 100;
mINI::Instance()[kContinuePushMS] = 15 * 1000;
});
} // namespace General
namespace Protocol {
#define PROTOCOL_FIELD "protocol."
const string kModifyStamp = PROTOCOL_FIELD "modify_stamp";
const string kEnableAudio = PROTOCOL_FIELD "enable_audio";
const string kAddMuteAudio = PROTOCOL_FIELD "add_mute_audio";
const string kContinuePushMS = PROTOCOL_FIELD "continue_push_ms";
const string kEnableHls = PROTOCOL_FIELD "enable_hls";
const string kEnableMP4 = PROTOCOL_FIELD "enable_mp4";
const string kEnableRtsp = PROTOCOL_FIELD "enable_rtsp";
const string kEnableRtmp = PROTOCOL_FIELD "enable_rtmp";
const string kEnableTS = PROTOCOL_FIELD "enable_ts";
const string kEnableFMP4 = PROTOCOL_FIELD "enable_fmp4";
const string kMP4AsPlayer = PROTOCOL_FIELD "mp4_as_player";
const string kMP4MaxSecond = PROTOCOL_FIELD "mp4_max_second";
const string kMP4SavePath = PROTOCOL_FIELD "mp4_save_path";
const string kHlsSavePath = PROTOCOL_FIELD "hls_save_path";
const string kHlsDemand = PROTOCOL_FIELD "hls_demand";
const string kRtspDemand = PROTOCOL_FIELD "rtsp_demand";
const string kRtmpDemand = PROTOCOL_FIELD "rtmp_demand";
const string kTSDemand = PROTOCOL_FIELD "ts_demand";
const string kFMP4Demand = PROTOCOL_FIELD "fmp4_demand";
static onceToken token([]() {
mINI::Instance()[kModifyStamp] = 0;
mINI::Instance()[kEnableAudio] = 1;
mINI::Instance()[kAddMuteAudio] = 1;
mINI::Instance()[kContinuePushMS] = 15000;
mINI::Instance()[kEnableHls] = 1;
mINI::Instance()[kEnableMP4] = 0;
mINI::Instance()[kEnableRtsp] = 1;
mINI::Instance()[kEnableRtmp] = 1;
mINI::Instance()[kEnableTS] = 1;
mINI::Instance()[kEnableFMP4] = 1;
mINI::Instance()[kMP4AsPlayer] = 0;
mINI::Instance()[kMP4MaxSecond] = 3600;
mINI::Instance()[kMP4SavePath] = "./www";
mINI::Instance()[kHlsSavePath] = "./www";
mINI::Instance()[kHlsDemand] = 0;
mINI::Instance()[kRtspDemand] = 0;
mINI::Instance()[kRtmpDemand] = 0;
mINI::Instance()[kTSDemand] = 0;
mINI::Instance()[kFMP4Demand] = 0;
});
} // !Protocol
////////////HTTP配置///////////
namespace Http {
#define HTTP_FIELD "http."
@ -173,6 +205,7 @@ const string kAuthBasic = RTSP_FIELD "authBasic";
const string kHandshakeSecond = RTSP_FIELD "handshakeSecond";
const string kKeepAliveSecond = RTSP_FIELD "keepAliveSecond";
const string kDirectProxy = RTSP_FIELD "directProxy";
const string kLowLatency = RTSP_FIELD"lowLatency";
static onceToken token([]() {
// 默认Md5方式认证
@ -180,6 +213,7 @@ static onceToken token([]() {
mINI::Instance()[kHandshakeSecond] = 15;
mINI::Instance()[kKeepAliveSecond] = 15;
mINI::Instance()[kDirectProxy] = 1;
mINI::Instance()[kLowLatency] = 0;
});
} // namespace Rtsp
@ -206,10 +240,14 @@ const string kAudioMtuSize = RTP_FIELD "audioMtuSize";
// rtp包最大长度限制单位是KB
const string kRtpMaxSize = RTP_FIELD "rtpMaxSize";
const string kLowLatency = RTP_FIELD "lowLatency";
static onceToken token([]() {
mINI::Instance()[kVideoMtuSize] = 1400;
mINI::Instance()[kAudioMtuSize] = 600;
mINI::Instance()[kRtpMaxSize] = 10;
mINI::Instance()[kLowLatency] = 0;
});
} // namespace Rtp
@ -235,22 +273,16 @@ namespace Record {
#define RECORD_FIELD "record."
const string kAppName = RECORD_FIELD "appName";
const string kSampleMS = RECORD_FIELD "sampleMS";
const string kFileSecond = RECORD_FIELD "fileSecond";
const string kFilePath = RECORD_FIELD "filePath";
const string kFileBufSize = RECORD_FIELD "fileBufSize";
const string kFastStart = RECORD_FIELD "fastStart";
const string kFileRepeat = RECORD_FIELD "fileRepeat";
const string kMP4AsPlayer = RECORD_FIELD "mp4_as_player";
static onceToken token([]() {
mINI::Instance()[kAppName] = "record";
mINI::Instance()[kSampleMS] = 500;
mINI::Instance()[kFileSecond] = 60 * 60;
mINI::Instance()[kFilePath] = "./www";
mINI::Instance()[kFileBufSize] = 64 * 1024;
mINI::Instance()[kFastStart] = false;
mINI::Instance()[kFileRepeat] = false;
mINI::Instance()[kMP4AsPlayer] = false;
});
} // namespace Record
@ -262,7 +294,6 @@ const string kSegmentNum = HLS_FIELD "segNum";
const string kSegmentKeep = HLS_FIELD "segKeep";
const string kSegmentRetain = HLS_FIELD "segRetain";
const string kFileBufSize = HLS_FIELD "fileBufSize";
const string kFilePath = HLS_FIELD "filePath";
const string kBroadcastRecordTs = HLS_FIELD "broadcastRecordTs";
const string kDeleteDelaySec = HLS_FIELD "deleteDelaySec";
@ -272,9 +303,8 @@ static onceToken token([]() {
mINI::Instance()[kSegmentKeep] = false;
mINI::Instance()[kSegmentRetain] = 5;
mINI::Instance()[kFileBufSize] = 64 * 1024;
mINI::Instance()[kFilePath] = "./www";
mINI::Instance()[kBroadcastRecordTs] = false;
mINI::Instance()[kDeleteDelaySec] = 0;
mINI::Instance()[kDeleteDelaySec] = 10;
});
} // namespace Hls
@ -317,6 +347,7 @@ const string kMediaTimeoutMS = "media_timeout_ms";
const string kBeatIntervalMS = "beat_interval_ms";
const string kBenchmarkMode = "benchmark_mode";
const string kWaitTrackReady = "wait_track_ready";
const string kPlayTrack = "play_track";
} // namespace Client
} // namespace mediakit

View File

@ -45,14 +45,11 @@ extern const std::string kBroadcastRecordTs;
// 收到http api请求广播
extern const std::string kBroadcastHttpRequest;
#define BroadcastHttpRequestArgs \
const Parser &parser, const HttpSession::HttpResponseInvoker &invoker, bool &consumed, SockInfo &sender
#define BroadcastHttpRequestArgs const Parser &parser, const HttpSession::HttpResponseInvoker &invoker, bool &consumed, SockInfo &sender
// 在http文件服务器中,收到http访问文件或目录的广播,通过该事件控制访问http目录的权限
extern const std::string kBroadcastHttpAccess;
#define BroadcastHttpAccessArgs \
const Parser &parser, const std::string &path, const bool &is_dir, \
const HttpSession::HttpAccessPathInvoker &invoker, SockInfo &sender
#define BroadcastHttpAccessArgs const Parser &parser, const std::string &path, const bool &is_dir, const HttpSession::HttpAccessPathInvoker &invoker, SockInfo &sender
// 在http文件服务器中,收到http访问文件或目录前的广播,通过该事件可以控制http url到文件路径的映射
// 在该事件中通过自行覆盖path参数可以做到譬如根据虚拟主机或者app选择不同http根目录的目的
@ -66,9 +63,7 @@ extern const std::string kBroadcastOnGetRtspRealm;
// 请求认证用户密码事件user_name为用户名must_no_encrypt如果为true则必须提供明文密码(因为此时是base64认证方式),否则会导致认证失败
// 获取到密码后请调用invoker并输入对应类型的密码和密码类型invoker执行时会匹配密码
extern const std::string kBroadcastOnRtspAuth;
#define BroadcastOnRtspAuthArgs \
const MediaInfo &args, const std::string &realm, const std::string &user_name, const bool &must_no_encrypt, \
const RtspSession::onAuth &invoker, SockInfo &sender
#define BroadcastOnRtspAuthArgs const MediaInfo &args, const std::string &realm, const std::string &user_name, const bool &must_no_encrypt, const RtspSession::onAuth &invoker, SockInfo &sender
// 推流鉴权结果回调对象
// 如果err为空则代表鉴权成功
@ -76,8 +71,7 @@ using PublishAuthInvoker = std::function<void(const std::string &err, const Prot
// 收到rtsp/rtmp推流事件广播通过该事件控制推流鉴权
extern const std::string kBroadcastMediaPublish;
#define BroadcastMediaPublishArgs \
const MediaOriginType &type, const MediaInfo &args, const Broadcast::PublishAuthInvoker &invoker, SockInfo &sender
#define BroadcastMediaPublishArgs const MediaOriginType &type, const MediaInfo &args, const Broadcast::PublishAuthInvoker &invoker, SockInfo &sender
// 播放鉴权结果回调对象
// 如果err为空则代表鉴权成功
@ -89,14 +83,11 @@ extern const std::string kBroadcastMediaPlayed;
// shell登录鉴权
extern const std::string kBroadcastShellLogin;
#define BroadcastShellLoginArgs \
const std::string &user_name, const std::string &passwd, const Broadcast::AuthInvoker &invoker, SockInfo &sender
#define BroadcastShellLoginArgs const std::string &user_name, const std::string &passwd, const Broadcast::AuthInvoker &invoker, SockInfo &sender
// 停止rtsp/rtmp/http-flv会话后流量汇报事件广播
extern const std::string kBroadcastFlowReport;
#define BroadcastFlowReportArgs \
const MediaInfo &args, const uint64_t &totalBytes, const uint64_t &totalDuration, const bool &isPlayer, \
SockInfo &sender
#define BroadcastFlowReportArgs const MediaInfo &args, const uint64_t &totalBytes, const uint64_t &totalDuration, const bool &isPlayer, SockInfo &sender
// 未找到流后会广播该事件,请在监听该事件后去拉流或其他方式产生流,这样就能按需拉流了
extern const std::string kBroadcastNotFoundStream;
@ -114,6 +105,10 @@ extern const std::string kBroadcastSendRtpStopped;
extern const std::string kBroadcastReloadConfig;
#define BroadcastReloadConfigArgs void
// rtp server 超时
extern const std::string KBroadcastRtpServerTimeout;
#define BroadcastRtpServerTimeout uint16_t &local_port, const string &stream_id,int &tcp_mode, bool &re_use_port, uint32_t &ssrc
#define ReloadConfigTag ((void *)(0xFF))
#define RELOAD_KEY(arg, key) \
do { \
@ -169,28 +164,12 @@ extern const std::string kStreamNoneReaderDelayMS;
extern const std::string kMaxStreamWaitTimeMS;
// 是否启动虚拟主机
extern const std::string kEnableVhost;
// 拉流代理时是否添加静音音频
extern const std::string kAddMuteAudio;
// 拉流代理时如果断流再重连成功是否删除前一次的媒体流数据,如果删除将重新开始,
// 如果不删除将会接着上一次的数据继续写(录制hls/mp4时会继续在前一个文件后面写)
extern const std::string kResetWhenRePlay;
// 是否默认推流时转换成hlshook接口(on_publish)中可以覆盖该设置
extern const std::string kPublishToHls;
// 是否默认推流时mp4录像hook接口(on_publish)中可以覆盖该设置
extern const std::string kPublishToMP4;
// 合并写缓存大小(单位毫秒)合并写指服务器缓存一定的数据后才会一次性写入socket这样能提高性能但是会提高延时
// 开启后会同时关闭TCP_NODELAY并开启MSG_MORE
extern const std::string kMergeWriteMS;
// 全局的时间戳覆盖开关在转协议时对frame进行时间戳覆盖
extern const std::string kModifyStamp;
// 按需转协议的开关
extern const std::string kHlsDemand;
extern const std::string kRtspDemand;
extern const std::string kRtmpDemand;
extern const std::string kTSDemand;
extern const std::string kFMP4Demand;
// 转协议是否全局开启或忽略音频
extern const std::string kEnableAudio;
// 在docker环境下不能通过英伟达驱动是否存在来判断是否支持硬件转码
extern const std::string kCheckNvidiaDev;
// 是否开启ffmpeg日志
@ -202,11 +181,49 @@ extern const std::string kWaitTrackReadyMS;
extern const std::string kWaitAddTrackMS;
// 如果track未就绪我们先缓存帧数据但是有最大个数限制(100帧时大约4秒),防止内存溢出
extern const std::string kUnreadyFrameCache;
// 推流断开后可以在超时时间内重新连接上继续推流,这样播放器会接着播放。
// 置0关闭此特性(推流断开会导致立即断开播放器)
extern const std::string kContinuePushMS;
} // namespace General
namespace Protocol {
//时间戳修复这一路流标志位
extern const std::string kModifyStamp;
//转协议是否开启音频
extern const std::string kEnableAudio;
//添加静音音频,在关闭音频时,此开关无效
extern const std::string kAddMuteAudio;
//断连续推延时,单位毫秒,默认采用配置文件
extern const std::string kContinuePushMS;
//是否开启转换为hls
extern const std::string kEnableHls;
//是否开启MP4录制
extern const std::string kEnableMP4;
//是否开启转换为rtsp/webrtc
extern const std::string kEnableRtsp;
//是否开启转换为rtmp/flv
extern const std::string kEnableRtmp;
//是否开启转换为http-ts/ws-ts
extern const std::string kEnableTS;
//是否开启转换为http-fmp4/ws-fmp4
extern const std::string kEnableFMP4;
//是否将mp4录制当做观看者
extern const std::string kMP4AsPlayer;
//mp4切片大小单位秒
extern const std::string kMP4MaxSecond;
//mp4录制保存路径
extern const std::string kMP4SavePath;
//hls录制保存路径
extern const std::string kHlsSavePath;
// 按需转协议的开关
extern const std::string kHlsDemand;
extern const std::string kRtspDemand;
extern const std::string kRtmpDemand;
extern const std::string kTSDemand;
extern const std::string kFMP4Demand;
} // !Protocol
////////////HTTP配置///////////
namespace Http {
// http 文件发送缓存大小
@ -251,12 +268,13 @@ extern const std::string kKeepAliveSecond;
// 假定您的拉流源地址不是264或265或AAC那么你可以使用直接代理的方式来支持rtsp代理
// 默认开启rtsp直接代理rtmp由于没有这些问题是强制开启直接代理的
extern const std::string kDirectProxy;
// rtsp 转发是否使用低延迟模式当开启时不会缓存rtp包来提高并发可以降低一帧的延迟
extern const std::string kLowLatency;
} // namespace Rtsp
////////////RTMP服务器配置///////////
namespace Rtmp {
// rtmp推流时间戳覆盖开关
extern const std::string kModifyStamp;
// 握手超时时间默认15秒
extern const std::string kHandshakeSecond;
// 维持链接超时时间默认15秒
@ -271,6 +289,8 @@ extern const std::string kVideoMtuSize;
extern const std::string kAudioMtuSize;
// rtp包最大长度限制, 单位KB
extern const std::string kRtpMaxSize;
// rtp 打包时低延迟开关默认关闭为0h264存在一帧多个sliceNAL的情况在这种情况下如果开启可能会导致画面花屏
extern const std::string kLowLatency;
} // namespace Rtp
////////////组播配置///////////
@ -289,18 +309,12 @@ namespace Record {
extern const std::string kAppName;
// 每次流化MP4文件的时长,单位毫秒
extern const std::string kSampleMS;
// MP4文件录制大小,默认一个小时
extern const std::string kFileSecond;
// 录制文件路径
extern const std::string kFilePath;
// mp4文件写缓存大小
extern const std::string kFileBufSize;
// mp4录制完成后是否进行二次关键帧索引写入头部
extern const std::string kFastStart;
// mp4文件是否重头循环读取
extern const std::string kFileRepeat;
// MP4录制是否当做播放器参与播放人数统计
extern const std::string kMP4AsPlayer;
} // namespace Record
////////////HLS相关配置///////////
@ -315,8 +329,6 @@ extern const std::string kSegmentKeep;
extern const std::string kSegmentRetain;
// HLS文件写缓存大小
extern const std::string kFileBufSize;
// 录制文件路径
extern const std::string kFilePath;
// 是否广播 ts 切片完成通知
extern const std::string kBroadcastRecordTs;
// hls直播文件删除延时单位秒
@ -375,6 +387,9 @@ extern const std::string kBeatIntervalMS;
extern const std::string kBenchmarkMode;
// 播放器在触发播放成功事件时是否等待所有track ready时再回调
extern const std::string kWaitTrackReady;
// rtsp播放指定track可选项有0(不指定,默认)、1(视频)、2(音频)
// 设置方法:player[Client::kPlayTrack] = 0/1/2;
extern const std::string kPlayTrack;
} // namespace Client
} // namespace mediakit

View File

@ -35,9 +35,9 @@ namespace mediakit {
//请遵循MIT协议勿修改服务器声明
#if !defined(ENABLE_VERSION)
const char kServerName[] = "ZLMediaKit-6.0(build in " __DATE__ " " __TIME__ ")";
const char kServerName[] = "ZLMediaKit-7.0(build in " __DATE__ " " __TIME__ ")";
#else
const char kServerName[] = "ZLMediaKit(git hash:" COMMIT_HASH ",branch:" BRANCH_NAME ",build time:" __DATE__ " " __TIME__ ")";
const char kServerName[] = "ZLMediaKit(git hash:" COMMIT_HASH "/" COMMIT_TIME ",branch:" BRANCH_NAME ",build time:" BUILD_TIME ")";
#endif
}//namespace mediakit

View File

@ -41,7 +41,7 @@
#endif
#ifndef CHECK
#define CHECK(exp, ...) mediakit::Assert_ThrowCpp(!(exp), #exp, __FUNCTION__, __FILE__, __LINE__, ##__VA_ARGS__)
#define CHECK(exp, ...) ::mediakit::Assert_ThrowCpp(!(exp), #exp, __FUNCTION__, __FILE__, __LINE__, ##__VA_ARGS__)
#endif // CHECK
#ifndef MAX

View File

@ -155,7 +155,7 @@ void FrameMerger::doMerge(BufferLikeString &merged, const Frame::Ptr &frame) con
}
}
bool FrameMerger::inputFrame(const Frame::Ptr &frame, const onOutput &cb, BufferLikeString *buffer) {
bool FrameMerger::inputFrame(const Frame::Ptr &frame, onOutput cb, BufferLikeString *buffer) {
if (willFlush(frame)) {
Frame::Ptr back = _frame_cache.back();
Buffer::Ptr merged_frame = back;
@ -190,6 +190,7 @@ bool FrameMerger::inputFrame(const Frame::Ptr &frame, const onOutput &cb, Buffer
if (frame->decodeAble()) {
_have_decode_able_frame = true;
}
_cb = std::move(cb);
_frame_cache.emplace_back(Frame::getCacheAbleFrame(frame));
return true;
}
@ -203,4 +204,38 @@ void FrameMerger::clear() {
_have_decode_able_frame = false;
}
void FrameMerger::flush() {
if (_cb) {
inputFrame(nullptr, std::move(_cb), nullptr);
}
clear();
}
/**
* function
*/
class FrameWriterInterfaceHelper : public FrameWriterInterface {
public:
using Ptr = std::shared_ptr<FrameWriterInterfaceHelper>;
using onWriteFrame = std::function<bool(const Frame::Ptr &frame)>;
/**
* inputFrame后触发onWriteFrame回调
*/
FrameWriterInterfaceHelper(onWriteFrame cb) { _callback = std::move(cb); }
virtual ~FrameWriterInterfaceHelper() = default;
/**
*
*/
bool inputFrame(const Frame::Ptr &frame) override { return _callback(frame); }
private:
onWriteFrame _callback;
};
FrameWriterInterface* FrameDispatcher::addDelegate(std::function<bool(const Frame::Ptr &frame)> cb) {
return addDelegate(std::make_shared<FrameWriterInterfaceHelper>(std::move(cb)));
}
}//namespace mediakit

View File

@ -38,7 +38,7 @@ typedef enum {
XX(CodecL16, TrackAudio, 6, "L16", PSI_STREAM_RESERVED) \
XX(CodecVP8, TrackVideo, 7, "VP8", PSI_STREAM_VP8) \
XX(CodecVP9, TrackVideo, 8, "VP9", PSI_STREAM_VP9) \
XX(CodecAV1, TrackVideo, 9, "AV1X", PSI_STREAM_AV1)
XX(CodecAV1, TrackVideo, 9, "AV1", PSI_STREAM_AV1)
typedef enum {
CodecInvalid = -1,
@ -271,29 +271,11 @@ public:
*
*/
virtual bool inputFrame(const Frame::Ptr &frame) = 0;
};
/**
* function
*/
class FrameWriterInterfaceHelper : public FrameWriterInterface {
public:
typedef std::shared_ptr<FrameWriterInterfaceHelper> Ptr;
typedef std::function<bool(const Frame::Ptr &frame)> onWriteFrame;
/**
* inputFrame后触发onWriteFrame回调
* frame缓存
*/
FrameWriterInterfaceHelper(const onWriteFrame &cb) { _writeCallback = cb; }
virtual ~FrameWriterInterfaceHelper() = default;
/**
*
*/
bool inputFrame(const Frame::Ptr &frame) override { return _writeCallback(frame); }
private:
onWriteFrame _writeCallback;
virtual void flush() {};
};
/**
@ -308,11 +290,13 @@ public:
/**
*
*/
void addDelegate(const FrameWriterInterface::Ptr &delegate) {
FrameWriterInterface* addDelegate(FrameWriterInterface::Ptr delegate) {
std::lock_guard<std::mutex> lck(_mtx);
_delegates.emplace(delegate.get(), delegate);
return _delegates.emplace(delegate.get(), std::move(delegate)).first->second.get();
}
FrameWriterInterface* addDelegate(std::function<bool(const Frame::Ptr &frame)> cb);
/**
*
*/
@ -542,8 +526,13 @@ public:
FrameMerger(int type);
~FrameMerger() = default;
/**
* FrameMerger::inputFrame传入的onOutput回调
*
*/
void flush();
void clear();
bool inputFrame(const Frame::Ptr &frame, const onOutput &cb, toolkit::BufferLikeString *buffer = nullptr);
bool inputFrame(const Frame::Ptr &frame, onOutput cb, toolkit::BufferLikeString *buffer = nullptr);
private:
bool willFlush(const Frame::Ptr &frame) const;
@ -552,6 +541,7 @@ private:
private:
int _type;
bool _have_decode_able_frame = false;
onOutput _cb;
toolkit::List<Frame::Ptr> _frame_cache;
};

View File

@ -245,23 +245,19 @@ public:
_printer << "a=rtpmap:" << payload_type << " " << getCodecName() << "/" << 90000 << "\r\n";
_printer << "a=fmtp:" << payload_type << " packetization-mode=1; profile-level-id=";
char strTemp[1024];
uint32_t profile_level_id = 0;
if (strSPS.length() >= 4) { // sanity check
profile_level_id = (uint8_t(strSPS[1]) << 16) |
(uint8_t(strSPS[2]) << 8) |
(uint8_t(strSPS[3])); // profile_idc|constraint_setN_flag|level_idc
}
memset(strTemp, 0, sizeof(strTemp));
snprintf(strTemp, sizeof(strTemp), "%06X", profile_level_id);
_printer << strTemp;
char profile[8];
snprintf(profile, sizeof(profile), "%06X", profile_level_id);
_printer << profile;
_printer << "; sprop-parameter-sets=";
memset(strTemp, 0, sizeof(strTemp));
av_base64_encode(strTemp, sizeof(strTemp), (uint8_t *)strSPS.data(), (int)strSPS.size());
_printer << strTemp << ",";
memset(strTemp, 0, sizeof(strTemp));
av_base64_encode(strTemp, sizeof(strTemp), (uint8_t *)strPPS.data(), (int)strPPS.size());
_printer << strTemp << "\r\n";
_printer << encodeBase64(strSPS) << ",";
_printer << encodeBase64(strPPS) << "\r\n";
_printer << "a=control:trackID=" << (int)TrackVideo << "\r\n";
}

View File

@ -124,26 +124,32 @@ void H264RtmpEncoder::makeConfigPacket(){
}
}
void H264RtmpEncoder::flush() {
inputFrame(nullptr);
}
bool H264RtmpEncoder::inputFrame(const Frame::Ptr &frame) {
auto data = frame->data() + frame->prefixSize();
auto len = frame->size() - frame->prefixSize();
auto type = H264_TYPE(data[0]);
switch (type) {
case H264Frame::NAL_SPS: {
if (!_got_config_frame) {
_sps = string(data, len);
makeConfigPacket();
if (frame) {
auto data = frame->data() + frame->prefixSize();
auto len = frame->size() - frame->prefixSize();
auto type = H264_TYPE(data[0]);
switch (type) {
case H264Frame::NAL_SPS: {
if (!_got_config_frame) {
_sps = string(data, len);
makeConfigPacket();
}
break;
}
break;
}
case H264Frame::NAL_PPS: {
if (!_got_config_frame) {
_pps = string(data, len);
makeConfigPacket();
case H264Frame::NAL_PPS: {
if (!_got_config_frame) {
_pps = string(data, len);
makeConfigPacket();
}
break;
}
break;
default: break;
}
default : break;
}
if (!_rtmp_packet) {

View File

@ -62,7 +62,7 @@ public:
* @param track
*/
H264RtmpEncoder(const Track::Ptr &track);
~H264RtmpEncoder() {}
~H264RtmpEncoder() = default;
/**
* 264sps pps
@ -70,6 +70,11 @@ public:
*/
bool inputFrame(const Frame::Ptr &frame) override;
/**
* frame缓存
*/
void flush() override;
/**
* config包
*/

View File

@ -283,14 +283,30 @@ bool H264RtpEncoder::inputFrame(const Frame::Ptr &frame) {
default: break;
}
if (_last_frame) {
//如果时间戳发生了变化那么markbit才置true
inputFrame_l(_last_frame, _last_frame->pts() != frame->pts());
GET_CONFIG(int,lowLatency,Rtp::kLowLatency);
if (lowLatency) { // 低延迟模式
if (_last_frame) {
flush();
}
inputFrame_l(frame, true);
} else {
if (_last_frame) {
//如果时间戳发生了变化那么markbit才置true
inputFrame_l(_last_frame, _last_frame->pts() != frame->pts());
}
_last_frame = Frame::getCacheAbleFrame(frame);
}
_last_frame = Frame::getCacheAbleFrame(frame);
return true;
}
void H264RtpEncoder::flush() {
if (_last_frame) {
// 如果时间戳发生了变化那么markbit才置true
inputFrame_l(_last_frame, true);
_last_frame = nullptr;
}
}
bool H264RtpEncoder::inputFrame_l(const Frame::Ptr &frame, bool is_mark){
if (frame->keyFrame()) {
//保证每一个关键帧前都有SPS与PPS

View File

@ -85,6 +85,11 @@ public:
*/
bool inputFrame(const Frame::Ptr &frame) override;
/**
* frame缓存
*/
void flush() override;
private:
void insertConfigFrame(uint64_t pts);
bool inputFrame_l(const Frame::Ptr &frame, bool is_mark);

View File

@ -138,33 +138,39 @@ void H265RtmpEncoder::makeConfigPacket(){
}
}
void H265RtmpEncoder::flush() {
inputFrame(nullptr);
}
bool H265RtmpEncoder::inputFrame(const Frame::Ptr &frame) {
auto data = frame->data() + frame->prefixSize();
auto len = frame->size() - frame->prefixSize();
auto type = H265_TYPE(data[0]);
switch (type) {
case H265Frame::NAL_SPS: {
if (!_got_config_frame) {
_sps = string(data, len);
makeConfigPacket();
if (frame) {
auto data = frame->data() + frame->prefixSize();
auto len = frame->size() - frame->prefixSize();
auto type = H265_TYPE(data[0]);
switch (type) {
case H265Frame::NAL_SPS: {
if (!_got_config_frame) {
_sps = string(data, len);
makeConfigPacket();
}
break;
}
break;
}
case H265Frame::NAL_PPS: {
if (!_got_config_frame) {
_pps = string(data, len);
makeConfigPacket();
case H265Frame::NAL_PPS: {
if (!_got_config_frame) {
_pps = string(data, len);
makeConfigPacket();
}
break;
}
break;
}
case H265Frame::NAL_VPS: {
if (!_got_config_frame) {
_vps = string(data, len);
makeConfigPacket();
case H265Frame::NAL_VPS: {
if (!_got_config_frame) {
_vps = string(data, len);
makeConfigPacket();
}
break;
}
break;
default: break;
}
default: break;
}
if (!_rtmp_packet) {

View File

@ -60,7 +60,7 @@ public:
* @param track
*/
H265RtmpEncoder(const Track::Ptr &track);
~H265RtmpEncoder() {}
~H265RtmpEncoder() = default;
/**
* 265sps pps
@ -68,6 +68,11 @@ public:
*/
bool inputFrame(const Frame::Ptr &frame) override;
/**
* frame缓存
*/
void flush() override;
/**
* config包
*/

View File

@ -31,7 +31,7 @@ public:
};
//FMP4直播源
class FMP4MediaSource : public MediaSource, public toolkit::RingDelegate<FMP4Packet::Ptr>, private PacketCache<FMP4Packet>{
class FMP4MediaSource final : public MediaSource, public toolkit::RingDelegate<FMP4Packet::Ptr>, private PacketCache<FMP4Packet>{
public:
using Ptr = std::shared_ptr<FMP4MediaSource>;
using RingDataType = std::shared_ptr<toolkit::List<FMP4Packet::Ptr> >;
@ -42,7 +42,7 @@ public:
const std::string &stream_id,
int ring_size = FMP4_GOP_SIZE) : MediaSource(FMP4_SCHEMA, vhost, app, stream_id), _ring_size(ring_size) {}
~FMP4MediaSource() override = default;
~FMP4MediaSource() override { flush(); }
/**
*

View File

@ -18,18 +18,20 @@
namespace mediakit {
class FMP4MediaSourceMuxer : public MP4MuxerMemory, public MediaSourceEventInterceptor,
public std::enable_shared_from_this<FMP4MediaSourceMuxer> {
class FMP4MediaSourceMuxer final : public MP4MuxerMemory, public MediaSourceEventInterceptor,
public std::enable_shared_from_this<FMP4MediaSourceMuxer> {
public:
using Ptr = std::shared_ptr<FMP4MediaSourceMuxer>;
FMP4MediaSourceMuxer(const std::string &vhost,
const std::string &app,
const std::string &stream_id) {
const std::string &stream_id,
const ProtocolOption &option) {
_option = option;
_media_src = std::make_shared<FMP4MediaSource>(vhost, app, stream_id);
}
~FMP4MediaSourceMuxer() override = default;
~FMP4MediaSourceMuxer() override { MP4MuxerMemory::flush(); };
void setListener(const std::weak_ptr<MediaSourceEvent> &listener){
setDelegate(listener);
@ -41,30 +43,27 @@ public:
}
void onReaderChanged(MediaSource &sender, int size) override {
GET_CONFIG(bool, fmp4_demand, General::kFMP4Demand);
_enabled = fmp4_demand ? size : true;
if (!size && fmp4_demand) {
_enabled = _option.fmp4_demand ? size : true;
if (!size && _option.fmp4_demand) {
_clear_cache = true;
}
MediaSourceEventInterceptor::onReaderChanged(sender, size);
}
bool inputFrame(const Frame::Ptr &frame) override {
GET_CONFIG(bool, fmp4_demand, General::kFMP4Demand);
if (_clear_cache && fmp4_demand) {
if (_clear_cache && _option.fmp4_demand) {
_clear_cache = false;
_media_src->clearCache();
}
if (_enabled || !fmp4_demand) {
if (_enabled || !_option.fmp4_demand) {
return MP4MuxerMemory::inputFrame(frame);
}
return false;
}
bool isEnabled() {
GET_CONFIG(bool, fmp4_demand, General::kFMP4Demand);
//缓存尚未清空时还允许触发inputFrame函数以便及时清空缓存
return fmp4_demand ? (_clear_cache ? true : _enabled) : true;
return _option.fmp4_demand ? (_clear_cache ? true : _enabled) : true;
}
void onAllTrackReady() {
@ -84,6 +83,7 @@ protected:
private:
bool _enabled = true;
bool _clear_cache = false;
ProtocolOption _option;
FMP4MediaSource::Ptr _media_src;
};

View File

@ -380,10 +380,14 @@ void HlsPlayerImp::onPlayResult(const SockException &ex) {
void HlsPlayerImp::onShutdown(const SockException &ex) {
while (_demuxer) {
try {
//shared_from_this()可能抛异常
std::weak_ptr<HlsPlayerImp> weak_self = static_pointer_cast<HlsPlayerImp>(shared_from_this());
if (_decoder) {
_decoder->flush();
}
//等待所有frame flush输出后再触发onShutdown事件
static_pointer_cast<HlsDemuxer>(_demuxer)->pushTask([weak_self, ex]() {
auto strong_self = weak_self.lock();
if (strong_self) {
if (auto strong_self = weak_self.lock()) {
strong_self->_demuxer = nullptr;
strong_self->onShutdown(ex);
}

View File

@ -91,8 +91,8 @@ public:
*
*/
template <class T>
const T& getAttach() const {
return *static_cast<const T *>(_attach.get());
T& getAttach() {
return *static_cast<T *>(_attach.get());
}
private:

View File

@ -33,8 +33,9 @@ static int kHlsCookieSecond = 60;
static const string kCookieName = "ZL_COOKIE";
static const string kHlsSuffix = "/hls.m3u8";
class HttpCookieAttachment {
public:
struct HttpCookieAttachment {
//是否已经查找到过MediaSource
bool _find_src = false;
//cookie生效作用域本cookie只对该目录下的文件生效
string _path;
//上次鉴权失败信息,为空则上次鉴权成功
@ -180,7 +181,7 @@ static bool makeFolderMenu(const string &httpPath, const string &strFullPath, st
}
//拦截hls的播放请求
static bool emitHlsPlayed(const Parser &parser, const MediaInfo &media_info, const HttpSession::HttpAccessPathInvoker &invoker,TcpSession &sender){
static bool emitHlsPlayed(const Parser &parser, const MediaInfo &media_info, const HttpSession::HttpAccessPathInvoker &invoker,Session &sender){
//访问的hls.m3u8结尾我们转换成kBroadcastMediaPlayed事件
Broadcast::AuthInvoker auth_invoker = [invoker](const string &err) {
//cookie有效期为kHlsCookieSecond
@ -235,7 +236,7 @@ public:
* 4cookie中记录的url参数是否跟本次url参数一致
* 5kBroadcastHttpAccess事件
*/
static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaInfo &media_info, bool is_dir,
static void canAccessPath(Session &sender, const Parser &parser, const MediaInfo &media_info, bool is_dir,
const function<void(const string &err_msg, const HttpServerCookie::Ptr &cookie)> &callback) {
//获取用户唯一id
auto uid = parser.Params();
@ -352,7 +353,7 @@ static string pathCat(const string &a, const string &b){
* @param file_path
* @param cb
*/
static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo &media_info, const string &file_path, const HttpFileManager::invoker &cb) {
static void accessFile(Session &sender, const Parser &parser, const MediaInfo &media_info, const string &file_path, const HttpFileManager::invoker &cb) {
bool is_hls = end_with(file_path, kHlsSuffix);
if (!is_hls && !File::fileExist(file_path.data())) {
//文件不存在且不是hls,那么直接返回404
@ -365,7 +366,7 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo
replace(const_cast<string &>(media_info._streamid), kHlsSuffix, "");
}
weak_ptr<TcpSession> weakSession = sender.shared_from_this();
weak_ptr<Session> weakSession = sender.shared_from_this();
//判断是否有权限访问该文件
canAccessPath(sender, parser, media_info, false, [cb, file_path, parser, is_hls, media_info, weakSession](const string &err_msg, const HttpServerCookie::Ptr &cookie) {
auto strongSession = weakSession.lock();
@ -425,6 +426,11 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo
response_file(cookie, cb, file_path, parser, src->getIndexFile());
return;
}
if (cookie->getAttach<HttpCookieAttachment>()._find_src) {
//查找过MediaSource但是流已经注销了不用再查找
response_file(cookie, cb, file_path, parser);
return;
}
//hls流可能未注册MediaSource::findAsync可以触发not_found事件然后再按需推拉流
MediaSource::findAsync(media_info, strongSession, [response_file, cookie, cb, file_path, parser](const MediaSource::Ptr &src) {
@ -439,6 +445,8 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo
attach._hls_data->setMediaSource(hls);
//添加HlsMediaSource的观看人数(HLS是按需生成的这样可以触发HLS文件的生成)
attach._hls_data->addByteUsage(0);
//标记找到MediaSource
attach._find_src = true;
// m3u8文件可能不存在, 等待m3u8索引文件按需生成
hls->getIndexFile([response_file, file_path, cookie, cb, parser](const string &file) {
@ -448,7 +456,7 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo
});
}
static string getFilePath(const Parser &parser,const MediaInfo &media_info, TcpSession &sender){
static string getFilePath(const Parser &parser,const MediaInfo &media_info, Session &sender){
GET_CONFIG(bool, enableVhost, General::kEnableVhost);
GET_CONFIG(string, rootPath, Http::kRootPath);
GET_CONFIG_FUNC(StrCaseMap, virtualPathMap, Http::kVirtualPath, [](const string &str) {
@ -483,7 +491,7 @@ static string getFilePath(const Parser &parser,const MediaInfo &media_info, TcpS
* @param parser http请求
* @param cb
*/
void HttpFileManager::onAccessPath(TcpSession &sender, Parser &parser, const HttpFileManager::invoker &cb) {
void HttpFileManager::onAccessPath(Session &sender, Parser &parser, const HttpFileManager::invoker &cb) {
auto fullUrl = string(HTTP_SCHEMA) + "://" + parser["Host"] + parser.FullUrl();
MediaInfo media_info(fullUrl);
auto file_path = getFilePath(parser, media_info, sender);

View File

@ -14,7 +14,7 @@
#include "HttpBody.h"
#include "HttpCookie.h"
#include "Common/Parser.h"
#include "Network/TcpSession.h"
#include "Network/Session.h"
#include "Util/function_traits.h"
namespace mediakit {
@ -54,7 +54,7 @@ public:
* @param parser http请求
* @param cb
*/
static void onAccessPath(toolkit::TcpSession &sender, Parser &parser, const invoker &cb);
static void onAccessPath(toolkit::Session &sender, Parser &parser, const invoker &cb);
/**
* mime值

View File

@ -14,8 +14,8 @@
using namespace toolkit;
using namespace std;
//协议解析最大缓存1兆数据
static constexpr size_t kMaxCacheSize = 1 * 1024 * 1024;
//协议解析最大缓存4兆数据
static constexpr size_t kMaxCacheSize = 4 * 1024 * 1024;
namespace mediakit {

View File

@ -23,7 +23,7 @@ using namespace toolkit;
namespace mediakit {
HttpSession::HttpSession(const Socket::Ptr &pSock) : TcpSession(pSock) {
HttpSession::HttpSession(const Socket::Ptr &pSock) : Session(pSock) {
TraceP(this);
GET_CONFIG(uint32_t,keep_alive_sec,Http::kKeepAliveSecond);
pSock->setSendTimeOutSecond(keep_alive_sec);
@ -103,9 +103,7 @@ void HttpSession::onError(const SockException& err) {
//flv/ts播放器
uint64_t duration = _ticker.createdTime() / 1000;
WarnP(this) << "FLV/TS/FMP4播放器("
<< _mediaInfo._vhost << "/"
<< _mediaInfo._app << "/"
<< _mediaInfo._streamid
<< _mediaInfo.shortUrl()
<< ")断开:" << err.what()
<< ",耗时(s):" << duration;
@ -442,7 +440,7 @@ class AsyncSenderData {
public:
friend class AsyncSender;
typedef std::shared_ptr<AsyncSenderData> Ptr;
AsyncSenderData(const TcpSession::Ptr &session, const HttpBody::Ptr &body, bool close_when_complete) {
AsyncSenderData(const Session::Ptr &session, const HttpBody::Ptr &body, bool close_when_complete) {
_session = dynamic_pointer_cast<HttpSession>(session);
_body = body;
_close_when_complete = close_when_complete;
@ -674,7 +672,10 @@ bool HttpSession::emitHttpEvent(bool doInvoke){
std::string HttpSession::get_peer_ip() {
GET_CONFIG(string, forwarded_ip_header, Http::kForwardedIpHeader);
return forwarded_ip_header.empty() ? TcpSession::get_peer_ip() : _parser.getHeader()[forwarded_ip_header];
if(!forwarded_ip_header.empty() && !_parser.getHeader()[forwarded_ip_header].empty()){
return _parser.getHeader()[forwarded_ip_header];
}
return Session::get_peer_ip();
}
void HttpSession::Handle_Req_POST(ssize_t &content_len) {

View File

@ -12,7 +12,7 @@
#define SRC_HTTP_HTTPSESSION_H_
#include <functional>
#include "Network/TcpSession.h"
#include "Network/Session.h"
#include "Rtmp/RtmpMediaSource.h"
#include "Rtmp/FlvMuxer.h"
#include "HttpRequestSplitter.h"
@ -24,7 +24,7 @@
namespace mediakit {
class HttpSession: public toolkit::TcpSession,
class HttpSession: public toolkit::Session,
public FlvMuxer,
public HttpRequestSplitter,
public WebSocketSplitter {
@ -139,7 +139,7 @@ private:
std::function<bool (const char *data,size_t len) > _contentCallBack;
};
using HttpsSession = toolkit::TcpSessionWithSSL<HttpSession>;
using HttpsSession = toolkit::SessionWithSSL<HttpSession>;
} /* namespace mediakit */

View File

@ -47,10 +47,14 @@ void TsPlayerImp::onPlayResult(const SockException &ex) {
void TsPlayerImp::onShutdown(const SockException &ex) {
while (_demuxer) {
try {
//shared_from_this()可能抛异常
std::weak_ptr<TsPlayerImp> weak_self = static_pointer_cast<TsPlayerImp>(shared_from_this());
if (_decoder) {
_decoder->flush();
}
//等待所有frame flush输出后再触发onShutdown事件
static_pointer_cast<HlsDemuxer>(_demuxer)->pushTask([weak_self, ex]() {
auto strong_self = weak_self.lock();
if (strong_self) {
if (auto strong_self = weak_self.lock()) {
strong_self->_demuxer = nullptr;
strong_self->onShutdown(ex);
}

View File

@ -27,18 +27,18 @@ public:
};
/**
* TcpSession派生类发送数据的截取
* Session派生类发送数据的截取
* websocket协议的打包
*/
template <typename TcpSessionType>
class TcpSessionTypeImp : public TcpSessionType, public SendInterceptor{
template <typename SessionType>
class SessionTypeImp : public SessionType, public SendInterceptor{
public:
typedef std::shared_ptr<TcpSessionTypeImp> Ptr;
using Ptr = std::shared_ptr<SessionTypeImp>;
TcpSessionTypeImp(const mediakit::Parser &header, const mediakit::HttpSession &parent, const toolkit::Socket::Ptr &pSock) :
TcpSessionType(pSock), _identifier(parent.getIdentifier()) {}
SessionTypeImp(const mediakit::Parser &header, const mediakit::HttpSession &parent, const toolkit::Socket::Ptr &pSock) :
SessionType(pSock), _identifier(parent.getIdentifier()) {}
~TcpSessionTypeImp() {}
~SessionTypeImp() = default;
/**
*
@ -58,7 +58,7 @@ protected:
if (_beforeSendCB) {
return _beforeSendCB(buf);
}
return TcpSessionType::send(std::move(buf));
return SessionType::send(std::move(buf));
}
std::string getIdentifier() const override {
@ -70,12 +70,12 @@ private:
onBeforeSendCB _beforeSendCB;
};
template <typename TcpSessionType>
class TcpSessionCreator {
template <typename SessionType>
class SessionCreator {
public:
//返回的TcpSession必须派生于SendInterceptor可以返回null
toolkit::TcpSession::Ptr operator()(const mediakit::Parser &header, const mediakit::HttpSession &parent, const toolkit::Socket::Ptr &pSock){
return std::make_shared<TcpSessionTypeImp<TcpSessionType> >(header,parent,pSock);
//返回的Session必须派生于SendInterceptor可以返回null
toolkit::Session::Ptr operator()(const mediakit::Parser &header, const mediakit::HttpSession &parent, const toolkit::Socket::Ptr &pSock){
return std::make_shared<SessionTypeImp<SessionType> >(header,parent,pSock);
}
};
@ -228,15 +228,15 @@ private:
std::string _payload_cache;
std::string _payload_section;
std::weak_ptr<toolkit::Server> _weak_server;
toolkit::TcpSession::Ptr _session;
toolkit::Session::Ptr _session;
Creator _creator;
};
template<typename TcpSessionType,typename HttpSessionType = mediakit::HttpSession, mediakit::WebSocketHeader::Type DataType = mediakit::WebSocketHeader::TEXT>
class WebSocketSession : public WebSocketSessionBase<TcpSessionCreator<TcpSessionType>,HttpSessionType,DataType>{
template<typename SessionType,typename HttpSessionType = mediakit::HttpSession, mediakit::WebSocketHeader::Type DataType = mediakit::WebSocketHeader::TEXT>
class WebSocketSession : public WebSocketSessionBase<SessionCreator<SessionType>,HttpSessionType,DataType>{
public:
WebSocketSession(const toolkit::Socket::Ptr &pSock) : WebSocketSessionBase<TcpSessionCreator<TcpSessionType>,HttpSessionType,DataType>(pSock){}
WebSocketSession(const toolkit::Socket::Ptr &pSock) : WebSocketSessionBase<SessionCreator<SessionType>,HttpSessionType,DataType>(pSock){}
virtual ~WebSocketSession(){}
};

View File

@ -112,9 +112,9 @@ bool Demuxer::addTrack(const Track::Ptr &track) {
}
if (_sink->addTrack(track)) {
track->addDelegate(std::make_shared<FrameWriterInterfaceHelper>([this](const Frame::Ptr &frame) {
track->addDelegate([this](const Frame::Ptr &frame) {
return _sink->inputFrame(frame);
}));
});
return true;
}
return false;

View File

@ -143,11 +143,7 @@ void PlayerProxy::rePlay(const string &strUrl, int iFailedCnt) {
}, getPoller());
}
bool PlayerProxy::close(MediaSource &sender, bool force) {
if (!force && totalReaderCount()) {
return false;
}
bool PlayerProxy::close(MediaSource &sender) {
//通知其停止推流
weak_ptr<PlayerProxy> weakSelf = dynamic_pointer_cast<PlayerProxy>(shared_from_this());
getPoller()->async_first([weakSelf]() {
@ -160,7 +156,7 @@ bool PlayerProxy::close(MediaSource &sender, bool force) {
strongSelf->teardown();
});
_on_close(SockException(Err_shutdown, "closed by user"));
WarnL << sender.getSchema() << "/" << sender.getVhost() << "/" << sender.getApp() << "/" << sender.getId() << " " << force;
WarnL << "close media: " << sender.getUrl();
return true;
}

View File

@ -54,7 +54,7 @@ public:
private:
//MediaSourceEvent override
bool close(MediaSource &sender,bool force) override;
bool close(MediaSource &sender) override;
int totalReaderCount(MediaSource &sender) override;
MediaOriginType getOriginType(MediaSource &sender) const override;
std::string getOriginUrl(MediaSource &sender) const override;

View File

@ -40,8 +40,7 @@ HlsCookieData::~HlsCookieData() {
if (*_added) {
uint64_t duration = (_ticker.createdTime() - _ticker.elapsedTime()) / 1000;
WarnL << _sock_info->getIdentifier() << "(" << _sock_info->get_peer_ip() << ":" << _sock_info->get_peer_port()
<< ") " << "HLS播放器(" << _info._vhost << "/" << _info._app << "/" << _info._streamid
<< ")断开,耗时(s):" << duration;
<< ") " << "HLS播放器(" << _info.shortUrl() << ")断开,耗时(s):" << duration;
GET_CONFIG(uint32_t, iFlowThreshold, General::kFlowThreshold);
uint64_t bytes = _bytes.load();

View File

@ -1,4 +1,4 @@
/*
/*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
@ -17,22 +17,23 @@
namespace mediakit {
class HlsRecorder : public MediaSourceEventInterceptor, public MpegMuxer, public std::enable_shared_from_this<HlsRecorder> {
class HlsRecorder final : public MediaSourceEventInterceptor, public MpegMuxer, public std::enable_shared_from_this<HlsRecorder> {
public:
using Ptr = std::shared_ptr<HlsRecorder>;
HlsRecorder(const std::string &m3u8_file, const std::string &params) : MpegMuxer(false) {
HlsRecorder(const std::string &m3u8_file, const std::string &params, const ProtocolOption &option) : MpegMuxer(false) {
GET_CONFIG(uint32_t, hlsNum, Hls::kSegmentNum);
GET_CONFIG(bool, hlsKeep, Hls::kSegmentKeep);
GET_CONFIG(uint32_t, hlsBufSize, Hls::kFileBufSize);
GET_CONFIG(float, hlsDuration, Hls::kSegmentDuration);
// _hls = std::make_shared<HlsMakerImp>(m3u8_file, params, hlsBufSize, hlsDuration, hlsNum, hlsKeep);
// _hls = std::make_shared<HlsMakerImp>(m3u8_file, params, hlsBufSize, hlsDuration, hlsNum, hlsKeep);
_option = option;
_hls = std::make_shared<HlsMakerImpSub>(m3u8_file, params, hlsBufSize, hlsDuration, hlsNum, hlsKeep);
//清空上次的残余文件
_hls->clearCache();
}
~HlsRecorder() = default;
~HlsRecorder() { MpegMuxer::flush(); };
void setMediaSource(const std::string &vhost, const std::string &app, const std::string &stream_id) {
_hls->setMediaSource(vhost, app, stream_id);
@ -46,10 +47,9 @@ public:
int readerCount() { return _hls->getMediaSource()->readerCount(); }
void onReaderChanged(MediaSource &sender, int size) override {
GET_CONFIG(bool, hls_demand, General::kHlsDemand);
// hls保留切片个数为0时代表为hls录制(不删除切片)那么不管有无观看者都一直生成hls
_enabled = hls_demand ? (_hls->isLive() ? size : true) : true;
if (!size && _hls->isLive() && hls_demand) {
_enabled = _option.hls_demand ? (_hls->isLive() ? size : true) : true;
if (!size && _hls->isLive() && _option.hls_demand) {
// hls直播时如果无人观看就删除视频缓存目的是为了防止视频跳跃
_clear_cache = true;
}
@ -57,23 +57,21 @@ public:
}
bool inputFrame(const Frame::Ptr &frame) override {
GET_CONFIG(bool, hls_demand, General::kHlsDemand);
if (_clear_cache && hls_demand) {
if (_clear_cache && _option.hls_demand) {
_clear_cache = false;
//清空旧的m3u8索引文件于ts切片
_hls->clearCache();
_hls->getMediaSource()->setIndexFile("");
}
if (_enabled || !hls_demand) {
if (_enabled || !_option.hls_demand) {
return MpegMuxer::inputFrame(frame);
}
return false;
}
bool isEnabled() {
GET_CONFIG(bool, hls_demand, General::kHlsDemand);
//缓存尚未清空时还允许触发inputFrame函数以便及时清空缓存
return hls_demand ? (_clear_cache ? true : _enabled) : true;
return _option.hls_demand ? (_clear_cache ? true : _enabled) : true;
}
void startRecord(bool flag) {
_hls->startRecord(flag);
@ -94,6 +92,7 @@ private:
bool _enabled = true;
bool _clear_cache = false;
//std::shared_ptr<HlsMakerImp> _hls;
ProtocolOption _option;
std::shared_ptr<HlsMakerImpSub> _hls;
bool _isRecord = false;
};

View File

@ -19,8 +19,6 @@ using namespace toolkit;
namespace mediakit {
MP4Muxer::MP4Muxer() {}
MP4Muxer::~MP4Muxer() {
closeMP4();
}
@ -61,6 +59,16 @@ bool MP4MuxerInterface::haveVideo() const {
return _have_video;
}
uint64_t MP4MuxerInterface::getDuration() const {
uint64_t ret = 0;
for (auto &pr : _codec_to_trackid) {
if (pr.second.stamp.getRelativeStamp() > (int64_t)ret) {
ret = pr.second.stamp.getRelativeStamp();
}
}
return ret;
}
void MP4MuxerInterface::resetTracks() {
_started = false;
_have_video = false;
@ -69,6 +77,10 @@ void MP4MuxerInterface::resetTracks() {
_codec_to_trackid.clear();
}
void MP4MuxerInterface::flush() {
_frame_merger.flush();
}
bool MP4MuxerInterface::inputFrame(const Frame::Ptr &frame) {
auto it = _codec_to_trackid.find(frame->getCodecId());
if (it == _codec_to_trackid.end()) {
@ -88,12 +100,12 @@ bool MP4MuxerInterface::inputFrame(const Frame::Ptr &frame) {
//mp4文件时间戳需要从0开始
auto &track_info = it->second;
int64_t dts_out, pts_out;
switch (frame->getCodecId()) {
case CodecH264:
case CodecH265: {
//这里的代码逻辑是让SPS、PPS、IDR这些时间戳相同的帧打包到一起当做一个帧处理
_frame_merger.inputFrame(frame, [&](uint64_t dts, uint64_t pts, const Buffer::Ptr &buffer, bool have_idr) {
_frame_merger.inputFrame(frame, [this, &track_info](uint64_t dts, uint64_t pts, const Buffer::Ptr &buffer, bool have_idr) {
int64_t dts_out, pts_out;
track_info.stamp.revise(dts, pts, dts_out, pts_out);
mp4_writer_write(_mov_writter.get(),
track_info.track_id,
@ -107,6 +119,7 @@ bool MP4MuxerInterface::inputFrame(const Frame::Ptr &frame) {
}
default: {
int64_t dts_out, pts_out;
track_info.stamp.revise(frame->dts(), frame->pts(), dts_out, pts_out);
mp4_writer_write(_mov_writter.get(),
track_info.track_id,

View File

@ -43,6 +43,11 @@ public:
*/
void resetTracks() override;
/**
* frame缓存
*/
void flush() override;
/**
*
*/
@ -58,6 +63,11 @@ public:
*/
void initSegment();
/**
* mp4时长,
*/
uint64_t getDuration() const;
protected:
virtual MP4FileIO::Writer createWriter() = 0;
@ -73,14 +83,14 @@ private:
Stamp stamp;
};
std::unordered_map<int, track_info> _codec_to_trackid;
FrameMerger _frame_merger{FrameMerger::mp4_nal_size};
FrameMerger _frame_merger { FrameMerger::mp4_nal_size };
};
class MP4Muxer : public MP4MuxerInterface{
public:
typedef std::shared_ptr<MP4Muxer> Ptr;
MP4Muxer();
MP4Muxer() = default;
~MP4Muxer() override;
/**

Some files were not shown because too many files have changed in this diff Show More