mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2024-11-22 10:40:05 +08:00
tab统一替换为4个空格键:#242
This commit is contained in:
parent
2a0d78fd12
commit
1168174c2b
@ -1 +1 @@
|
||||
Subproject commit 34b42499ce9ee055b5c7cac9a270239984d0e839
|
||||
Subproject commit d985d49f28b441216bf50020b7c6587edb5f43dc
|
@ -31,12 +31,12 @@
|
||||
|
||||
#if defined(_WIN32)
|
||||
#if defined(MediaKitApi_EXPORTS)
|
||||
#define API_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define API_EXPORT __declspec(dllimport)
|
||||
#endif
|
||||
#define API_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define API_EXPORT __declspec(dllimport)
|
||||
#endif
|
||||
|
||||
#define API_CALL __cdecl
|
||||
#define API_CALL __cdecl
|
||||
#else
|
||||
#define API_EXPORT
|
||||
#define API_CALL
|
||||
|
@ -32,41 +32,41 @@ using namespace toolkit;
|
||||
using namespace mediakit;
|
||||
|
||||
API_EXPORT mk_player API_CALL mk_player_create() {
|
||||
MediaPlayer::Ptr *obj = new MediaPlayer::Ptr(new MediaPlayer());
|
||||
return obj;
|
||||
MediaPlayer::Ptr *obj = new MediaPlayer::Ptr(new MediaPlayer());
|
||||
return obj;
|
||||
}
|
||||
API_EXPORT void API_CALL mk_player_release(mk_player ctx) {
|
||||
assert(ctx);
|
||||
MediaPlayer::Ptr *obj = (MediaPlayer::Ptr *)ctx;
|
||||
delete obj;
|
||||
assert(ctx);
|
||||
MediaPlayer::Ptr *obj = (MediaPlayer::Ptr *)ctx;
|
||||
delete obj;
|
||||
}
|
||||
|
||||
API_EXPORT void API_CALL mk_player_set_option(mk_player ctx,const char* key,const char *val){
|
||||
assert(ctx && key && val);
|
||||
MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx);
|
||||
string key_str(key), val_str(val);
|
||||
player->getPoller()->async([key_str,val_str,player](){
|
||||
//切换线程后再操作
|
||||
(*player)[key_str] = val_str;
|
||||
});
|
||||
assert(ctx && key && val);
|
||||
MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx);
|
||||
string key_str(key), val_str(val);
|
||||
player->getPoller()->async([key_str,val_str,player](){
|
||||
//切换线程后再操作
|
||||
(*player)[key_str] = val_str;
|
||||
});
|
||||
}
|
||||
API_EXPORT void API_CALL mk_player_play(mk_player ctx, const char *url) {
|
||||
assert(ctx && url);
|
||||
MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx);
|
||||
string url_str(url);
|
||||
player->getPoller()->async([url_str,player](){
|
||||
//切换线程后再操作
|
||||
player->play(url_str);
|
||||
});
|
||||
assert(ctx && url);
|
||||
MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx);
|
||||
string url_str(url);
|
||||
player->getPoller()->async([url_str,player](){
|
||||
//切换线程后再操作
|
||||
player->play(url_str);
|
||||
});
|
||||
}
|
||||
|
||||
API_EXPORT void API_CALL mk_player_pause(mk_player ctx, int pause) {
|
||||
assert(ctx);
|
||||
MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx);
|
||||
player->getPoller()->async([pause,player](){
|
||||
//切换线程后再操作
|
||||
player->pause(pause);
|
||||
});
|
||||
assert(ctx);
|
||||
MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx);
|
||||
player->getPoller()->async([pause,player](){
|
||||
//切换线程后再操作
|
||||
player->pause(pause);
|
||||
});
|
||||
}
|
||||
|
||||
API_EXPORT void API_CALL mk_player_seekto(mk_player ctx, float progress) {
|
||||
@ -79,100 +79,100 @@ API_EXPORT void API_CALL mk_player_seekto(mk_player ctx, float progress) {
|
||||
}
|
||||
|
||||
static void mk_player_set_on_event(mk_player ctx, on_mk_play_event cb, void *user_data, int type) {
|
||||
assert(ctx && cb);
|
||||
MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx);
|
||||
player->getPoller()->async([cb,user_data,type,player](){
|
||||
//切换线程后再操作
|
||||
if(type == 0){
|
||||
player->setOnPlayResult([cb,user_data](const SockException &ex){
|
||||
cb(user_data,ex.getErrCode(),ex.what());
|
||||
});
|
||||
}else{
|
||||
player->setOnShutdown([cb,user_data](const SockException &ex){
|
||||
cb(user_data,ex.getErrCode(),ex.what());
|
||||
});
|
||||
}
|
||||
});
|
||||
assert(ctx && cb);
|
||||
MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx);
|
||||
player->getPoller()->async([cb,user_data,type,player](){
|
||||
//切换线程后再操作
|
||||
if(type == 0){
|
||||
player->setOnPlayResult([cb,user_data](const SockException &ex){
|
||||
cb(user_data,ex.getErrCode(),ex.what());
|
||||
});
|
||||
}else{
|
||||
player->setOnShutdown([cb,user_data](const SockException &ex){
|
||||
cb(user_data,ex.getErrCode(),ex.what());
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
API_EXPORT void API_CALL mk_player_set_on_result(mk_player ctx, on_mk_play_event cb, void *user_data) {
|
||||
mk_player_set_on_event(ctx,cb,user_data,0);
|
||||
mk_player_set_on_event(ctx,cb,user_data,0);
|
||||
}
|
||||
|
||||
API_EXPORT void API_CALL mk_player_set_on_shutdown(mk_player ctx, on_mk_play_event cb, void *user_data) {
|
||||
mk_player_set_on_event(ctx,cb,user_data,1);
|
||||
mk_player_set_on_event(ctx,cb,user_data,1);
|
||||
}
|
||||
|
||||
API_EXPORT void API_CALL mk_player_set_on_data(mk_player ctx, on_mk_play_data cb, void *user_data) {
|
||||
assert(ctx && cb);
|
||||
MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx);
|
||||
player->getPoller()->async([player,cb,user_data](){
|
||||
//切换线程后再操作
|
||||
auto delegate = std::make_shared<FrameWriterInterfaceHelper>([cb,user_data](const Frame::Ptr &frame){
|
||||
cb(user_data,frame->getTrackType(),frame->getCodecId(),frame->data(),frame->size(),frame->dts(),frame->pts());
|
||||
});
|
||||
for(auto &track : player->getTracks()){
|
||||
track->addDelegate(delegate);
|
||||
}
|
||||
});
|
||||
assert(ctx && cb);
|
||||
MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx);
|
||||
player->getPoller()->async([player,cb,user_data](){
|
||||
//切换线程后再操作
|
||||
auto delegate = std::make_shared<FrameWriterInterfaceHelper>([cb,user_data](const Frame::Ptr &frame){
|
||||
cb(user_data,frame->getTrackType(),frame->getCodecId(),frame->data(),frame->size(),frame->dts(),frame->pts());
|
||||
});
|
||||
for(auto &track : player->getTracks()){
|
||||
track->addDelegate(delegate);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
API_EXPORT int API_CALL mk_player_video_width(mk_player ctx) {
|
||||
assert(ctx);
|
||||
MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx);
|
||||
auto track = dynamic_pointer_cast<VideoTrack>(player->getTrack(TrackVideo));
|
||||
return track ? track->getVideoWidth() : 0;
|
||||
assert(ctx);
|
||||
MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx);
|
||||
auto track = dynamic_pointer_cast<VideoTrack>(player->getTrack(TrackVideo));
|
||||
return track ? track->getVideoWidth() : 0;
|
||||
}
|
||||
|
||||
API_EXPORT int API_CALL mk_player_video_height(mk_player ctx) {
|
||||
assert(ctx);
|
||||
MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx);
|
||||
auto track = dynamic_pointer_cast<VideoTrack>(player->getTrack(TrackVideo));
|
||||
return track ? track->getVideoHeight() : 0;
|
||||
assert(ctx);
|
||||
MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx);
|
||||
auto track = dynamic_pointer_cast<VideoTrack>(player->getTrack(TrackVideo));
|
||||
return track ? track->getVideoHeight() : 0;
|
||||
}
|
||||
|
||||
API_EXPORT int API_CALL mk_player_video_fps(mk_player ctx) {
|
||||
assert(ctx);
|
||||
MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx);
|
||||
auto track = dynamic_pointer_cast<VideoTrack>(player->getTrack(TrackVideo));
|
||||
return track ? track->getVideoFps() : 0;
|
||||
assert(ctx);
|
||||
MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx);
|
||||
auto track = dynamic_pointer_cast<VideoTrack>(player->getTrack(TrackVideo));
|
||||
return track ? track->getVideoFps() : 0;
|
||||
}
|
||||
|
||||
API_EXPORT int API_CALL mk_player_audio_samplerate(mk_player ctx) {
|
||||
assert(ctx);
|
||||
MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx);
|
||||
auto track = dynamic_pointer_cast<AudioTrack>(player->getTrack(TrackAudio));
|
||||
return track ? track->getAudioSampleRate() : 0;
|
||||
assert(ctx);
|
||||
MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx);
|
||||
auto track = dynamic_pointer_cast<AudioTrack>(player->getTrack(TrackAudio));
|
||||
return track ? track->getAudioSampleRate() : 0;
|
||||
}
|
||||
|
||||
API_EXPORT int API_CALL mk_player_audio_bit(mk_player ctx) {
|
||||
assert(ctx);
|
||||
MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx);
|
||||
auto track = dynamic_pointer_cast<AudioTrack>(player->getTrack(TrackAudio));
|
||||
return track ? track->getAudioSampleBit() : 0;
|
||||
assert(ctx);
|
||||
MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx);
|
||||
auto track = dynamic_pointer_cast<AudioTrack>(player->getTrack(TrackAudio));
|
||||
return track ? track->getAudioSampleBit() : 0;
|
||||
}
|
||||
|
||||
API_EXPORT int API_CALL mk_player_audio_channel(mk_player ctx) {
|
||||
assert(ctx);
|
||||
MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx);
|
||||
auto track = dynamic_pointer_cast<AudioTrack>(player->getTrack(TrackAudio));
|
||||
return track ? track->getAudioChannel() : 0;
|
||||
assert(ctx);
|
||||
MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx);
|
||||
auto track = dynamic_pointer_cast<AudioTrack>(player->getTrack(TrackAudio));
|
||||
return track ? track->getAudioChannel() : 0;
|
||||
}
|
||||
|
||||
API_EXPORT float API_CALL mk_player_duration(mk_player ctx) {
|
||||
assert(ctx);
|
||||
MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx);
|
||||
return player->getDuration();
|
||||
assert(ctx);
|
||||
MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx);
|
||||
return player->getDuration();
|
||||
}
|
||||
|
||||
API_EXPORT float API_CALL mk_player_progress(mk_player ctx) {
|
||||
assert(ctx);
|
||||
MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx);
|
||||
return player->getProgress();
|
||||
assert(ctx);
|
||||
MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx);
|
||||
return player->getProgress();
|
||||
}
|
||||
|
||||
API_EXPORT float API_CALL mk_player_loss_rate(mk_player ctx, int track_type) {
|
||||
assert(ctx);
|
||||
MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx);
|
||||
return player->getPacketLossRate((TrackType)track_type);
|
||||
assert(ctx);
|
||||
MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx);
|
||||
return player->getPacketLossRate((TrackType)track_type);
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ onceToken token([]() {
|
||||
//windows下先关闭FFmpeg日志(目前不支持日志重定向)
|
||||
mINI::Instance()[kCmd] = "%s -re -i \"%s\" -loglevel quiet -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s ";
|
||||
#else
|
||||
string ffmpeg_bin = System::execute("which ffmpeg");
|
||||
string ffmpeg_bin = System::execute("which ffmpeg");
|
||||
mINI::Instance()[kCmd] = "%s -re -i \"%s\" -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s ";
|
||||
#endif
|
||||
//默认ffmpeg命令路径为环境变量中路径
|
||||
|
@ -46,92 +46,92 @@
|
||||
using namespace toolkit;
|
||||
|
||||
void Process::run(const string &cmd, const string &log_file_tmp) {
|
||||
kill(2000);
|
||||
kill(2000);
|
||||
#ifdef _WIN32
|
||||
STARTUPINFO si;
|
||||
PROCESS_INFORMATION pi;
|
||||
ZeroMemory(&si, sizeof(si)); //结构体初始化;
|
||||
ZeroMemory(&pi, sizeof(pi));
|
||||
STARTUPINFO si;
|
||||
PROCESS_INFORMATION pi;
|
||||
ZeroMemory(&si, sizeof(si)); //结构体初始化;
|
||||
ZeroMemory(&pi, sizeof(pi));
|
||||
|
||||
LPTSTR lpDir = const_cast<char*>(cmd.data());
|
||||
LPTSTR lpDir = const_cast<char*>(cmd.data());
|
||||
|
||||
if (CreateProcess(NULL, lpDir, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)){
|
||||
//下面两行关闭句柄,解除本进程和新进程的关系,不然有可能 不小心调用TerminateProcess函数关掉子进程
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
if (CreateProcess(NULL, lpDir, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)){
|
||||
//下面两行关闭句柄,解除本进程和新进程的关系,不然有可能 不小心调用TerminateProcess函数关掉子进程
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
|
||||
_pid = pi.dwProcessId;
|
||||
InfoL << "start child proces " << _pid;
|
||||
} else {
|
||||
WarnL << "start child proces fail: " << GetLastError();
|
||||
}
|
||||
_pid = pi.dwProcessId;
|
||||
InfoL << "start child proces " << _pid;
|
||||
} else {
|
||||
WarnL << "start child proces fail: " << GetLastError();
|
||||
}
|
||||
#else
|
||||
_pid = fork();
|
||||
if (_pid < 0) {
|
||||
throw std::runtime_error(StrPrinter << "fork child process falied,err:" << get_uv_errmsg());
|
||||
}
|
||||
if (_pid == 0) {
|
||||
//子进程关闭core文件生成
|
||||
struct rlimit rlim = { 0,0 };
|
||||
setrlimit(RLIMIT_CORE, &rlim);
|
||||
_pid = fork();
|
||||
if (_pid < 0) {
|
||||
throw std::runtime_error(StrPrinter << "fork child process falied,err:" << get_uv_errmsg());
|
||||
}
|
||||
if (_pid == 0) {
|
||||
//子进程关闭core文件生成
|
||||
struct rlimit rlim = { 0,0 };
|
||||
setrlimit(RLIMIT_CORE, &rlim);
|
||||
|
||||
//在启动子进程时,暂时禁用SIGINT、SIGTERM信号
|
||||
// ignore the SIGINT and SIGTERM
|
||||
signal(SIGINT, SIG_IGN);
|
||||
signal(SIGTERM, SIG_IGN);
|
||||
//在启动子进程时,暂时禁用SIGINT、SIGTERM信号
|
||||
// ignore the SIGINT and SIGTERM
|
||||
signal(SIGINT, SIG_IGN);
|
||||
signal(SIGTERM, SIG_IGN);
|
||||
|
||||
string log_file;
|
||||
if (log_file_tmp.empty()) {
|
||||
log_file = "/dev/null";
|
||||
}
|
||||
else {
|
||||
log_file = StrPrinter << log_file_tmp << "." << getpid();
|
||||
}
|
||||
string log_file;
|
||||
if (log_file_tmp.empty()) {
|
||||
log_file = "/dev/null";
|
||||
}
|
||||
else {
|
||||
log_file = StrPrinter << log_file_tmp << "." << getpid();
|
||||
}
|
||||
|
||||
int log_fd = -1;
|
||||
int flags = O_CREAT | O_WRONLY | O_APPEND;
|
||||
mode_t mode = S_IRWXO | S_IRWXG | S_IRWXU;// S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
|
||||
File::createfile_path(log_file.data(), mode);
|
||||
if ((log_fd = ::open(log_file.c_str(), flags, mode)) < 0) {
|
||||
fprintf(stderr, "open log file %s failed:%d(%s)\r\n", log_file.data(), errno, strerror(errno));
|
||||
}
|
||||
else {
|
||||
// dup to stdout and stderr.
|
||||
if (dup2(log_fd, STDOUT_FILENO) < 0) {
|
||||
fprintf(stderr, "dup2 stdout file %s failed:%d(%s)\r\n", log_file.data(), errno, strerror(errno));
|
||||
}
|
||||
if (dup2(log_fd, STDERR_FILENO) < 0) {
|
||||
fprintf(stderr, "dup2 stderr file %s failed:%d(%s)\r\n", log_file.data(), errno, strerror(errno));
|
||||
}
|
||||
// close log fd
|
||||
::close(log_fd);
|
||||
}
|
||||
fprintf(stderr, "\r\n\r\n#### pid=%d,cmd=%s #####\r\n\r\n", getpid(), cmd.data());
|
||||
int log_fd = -1;
|
||||
int flags = O_CREAT | O_WRONLY | O_APPEND;
|
||||
mode_t mode = S_IRWXO | S_IRWXG | S_IRWXU;// S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
|
||||
File::createfile_path(log_file.data(), mode);
|
||||
if ((log_fd = ::open(log_file.c_str(), flags, mode)) < 0) {
|
||||
fprintf(stderr, "open log file %s failed:%d(%s)\r\n", log_file.data(), errno, strerror(errno));
|
||||
}
|
||||
else {
|
||||
// dup to stdout and stderr.
|
||||
if (dup2(log_fd, STDOUT_FILENO) < 0) {
|
||||
fprintf(stderr, "dup2 stdout file %s failed:%d(%s)\r\n", log_file.data(), errno, strerror(errno));
|
||||
}
|
||||
if (dup2(log_fd, STDERR_FILENO) < 0) {
|
||||
fprintf(stderr, "dup2 stderr file %s failed:%d(%s)\r\n", log_file.data(), errno, strerror(errno));
|
||||
}
|
||||
// close log fd
|
||||
::close(log_fd);
|
||||
}
|
||||
fprintf(stderr, "\r\n\r\n#### pid=%d,cmd=%s #####\r\n\r\n", getpid(), cmd.data());
|
||||
|
||||
// close other fds
|
||||
// TODO: do in right way.
|
||||
for (int i = 3; i < 1024; i++) {
|
||||
::close(i);
|
||||
}
|
||||
// close other fds
|
||||
// TODO: do in right way.
|
||||
for (int i = 3; i < 1024; i++) {
|
||||
::close(i);
|
||||
}
|
||||
|
||||
auto params = split(cmd, " ");
|
||||
// memory leak in child process, it's ok.
|
||||
char **charpv_params = new char *[params.size() + 1];
|
||||
for (int i = 0; i < (int)params.size(); i++) {
|
||||
std::string &p = params[i];
|
||||
charpv_params[i] = (char *)p.data();
|
||||
}
|
||||
// EOF: NULL
|
||||
charpv_params[params.size()] = NULL;
|
||||
auto params = split(cmd, " ");
|
||||
// memory leak in child process, it's ok.
|
||||
char **charpv_params = new char *[params.size() + 1];
|
||||
for (int i = 0; i < (int)params.size(); i++) {
|
||||
std::string &p = params[i];
|
||||
charpv_params[i] = (char *)p.data();
|
||||
}
|
||||
// EOF: NULL
|
||||
charpv_params[params.size()] = NULL;
|
||||
|
||||
// TODO: execv or execvp
|
||||
auto ret = execv(params[0].c_str(), charpv_params);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "fork process failed, errno=%d(%s)\r\n", errno, strerror(errno));
|
||||
}
|
||||
exit(ret);
|
||||
}
|
||||
InfoL << "start child proces " << _pid;
|
||||
// TODO: execv or execvp
|
||||
auto ret = execv(params[0].c_str(), charpv_params);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "fork process failed, errno=%d(%s)\r\n", errno, strerror(errno));
|
||||
}
|
||||
exit(ret);
|
||||
}
|
||||
InfoL << "start child proces " << _pid;
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
@ -148,27 +148,27 @@ static bool s_wait(pid_t pid,int *exit_code_ptr,bool block) {
|
||||
}
|
||||
int status = 0;
|
||||
#ifdef _WIN32
|
||||
HANDLE hProcess = NULL;
|
||||
hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid); //打开目标进程
|
||||
if (hProcess == NULL) {
|
||||
return false;
|
||||
}
|
||||
HANDLE hProcess = NULL;
|
||||
hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid); //打开目标进程
|
||||
if (hProcess == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CloseHandle(hProcess);
|
||||
CloseHandle(hProcess);
|
||||
#else
|
||||
pid_t p = waitpid(pid, &status, block ? 0 : WNOHANG);
|
||||
int exit_code = (status & 0xFF00) >> 8;
|
||||
if (exit_code_ptr) {
|
||||
*exit_code_ptr = (status & 0xFF00) >> 8;
|
||||
}
|
||||
if (p < 0) {
|
||||
WarnL << "waitpid failed, pid=" << pid << ", err=" << get_uv_errmsg();
|
||||
return false;
|
||||
}
|
||||
if (p > 0) {
|
||||
InfoL << "process terminated, pid=" << pid << ", exit code=" << exit_code;
|
||||
return false;
|
||||
}
|
||||
pid_t p = waitpid(pid, &status, block ? 0 : WNOHANG);
|
||||
int exit_code = (status & 0xFF00) >> 8;
|
||||
if (exit_code_ptr) {
|
||||
*exit_code_ptr = (status & 0xFF00) >> 8;
|
||||
}
|
||||
if (p < 0) {
|
||||
WarnL << "waitpid failed, pid=" << pid << ", err=" << get_uv_errmsg();
|
||||
return false;
|
||||
}
|
||||
if (p > 0) {
|
||||
InfoL << "process terminated, pid=" << pid << ", exit code=" << exit_code;
|
||||
return false;
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
return true;
|
||||
@ -180,22 +180,22 @@ static void s_kill(pid_t pid,int max_delay,bool force){
|
||||
return;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
HANDLE hProcess = NULL;
|
||||
hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid); //打开目标进程
|
||||
if (hProcess == NULL) {
|
||||
WarnL << "\nOpen Process fAiled: " << GetLastError();
|
||||
return;
|
||||
}
|
||||
DWORD ret = TerminateProcess(hProcess, 0); //结束目标进程
|
||||
if (ret == 0) {
|
||||
WarnL << GetLastError;
|
||||
}
|
||||
HANDLE hProcess = NULL;
|
||||
hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid); //打开目标进程
|
||||
if (hProcess == NULL) {
|
||||
WarnL << "\nOpen Process fAiled: " << GetLastError();
|
||||
return;
|
||||
}
|
||||
DWORD ret = TerminateProcess(hProcess, 0); //结束目标进程
|
||||
if (ret == 0) {
|
||||
WarnL << GetLastError;
|
||||
}
|
||||
#else
|
||||
if (::kill(pid, force ? SIGKILL : SIGTERM) == -1) {
|
||||
//进程可能已经退出了
|
||||
WarnL << "kill process " << pid << " failed:" << get_uv_errmsg();
|
||||
return;
|
||||
}
|
||||
if (::kill(pid, force ? SIGKILL : SIGTERM) == -1) {
|
||||
//进程可能已经退出了
|
||||
WarnL << "kill process " << pid << " failed:" << get_uv_errmsg();
|
||||
return;
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
|
||||
|
@ -774,9 +774,9 @@ void installWebApi() {
|
||||
val["status"] = (int)status;
|
||||
});
|
||||
|
||||
//获取录像文件夹列表或mp4文件列表
|
||||
//http://127.0.0.1/index/api/getMp4RecordFile?vhost=__defaultVhost__&app=live&stream=ss&period=2020-01
|
||||
api_regist1("/index/api/getMp4RecordFile", [](API_ARGS1){
|
||||
//获取录像文件夹列表或mp4文件列表
|
||||
//http://127.0.0.1/index/api/getMp4RecordFile?vhost=__defaultVhost__&app=live&stream=ss&period=2020-01
|
||||
api_regist1("/index/api/getMp4RecordFile", [](API_ARGS1){
|
||||
CHECK_SECRET();
|
||||
CHECK_ARGS("vhost", "app", "stream");
|
||||
auto record_path = Recorder::getRecordPath(Recorder::type_mp4, allArgs["vhost"], allArgs["app"],allArgs["stream"]);
|
||||
@ -809,7 +809,7 @@ void installWebApi() {
|
||||
|
||||
val["data"]["rootPath"] = record_path;
|
||||
val["data"]["paths"] = paths;
|
||||
});
|
||||
});
|
||||
|
||||
////////////以下是注册的Hook API////////////
|
||||
api_regist1("/index/hook/on_publish",[](API_ARGS1){
|
||||
|
@ -359,7 +359,7 @@ int start_main(int argc,char *argv[]) {
|
||||
InfoL << "程序退出中,请等待...";
|
||||
sleep(1);
|
||||
InfoL << "程序退出完毕!";
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef DISABLE_MAIN
|
||||
|
@ -47,73 +47,73 @@ AACEncoder::AACEncoder() {
|
||||
}
|
||||
|
||||
AACEncoder::~AACEncoder() {
|
||||
if (_hEncoder != nullptr) {
|
||||
faacEncClose(_hEncoder);
|
||||
_hEncoder = nullptr;
|
||||
}
|
||||
if (_pucAacBuf != nullptr) {
|
||||
delete[] _pucAacBuf;
|
||||
_pucAacBuf = nullptr;
|
||||
}
|
||||
if (_pucPcmBuf != nullptr) {
|
||||
delete[] _pucPcmBuf;
|
||||
_pucPcmBuf = nullptr;
|
||||
}
|
||||
if (_hEncoder != nullptr) {
|
||||
faacEncClose(_hEncoder);
|
||||
_hEncoder = nullptr;
|
||||
}
|
||||
if (_pucAacBuf != nullptr) {
|
||||
delete[] _pucAacBuf;
|
||||
_pucAacBuf = nullptr;
|
||||
}
|
||||
if (_pucPcmBuf != nullptr) {
|
||||
delete[] _pucPcmBuf;
|
||||
_pucPcmBuf = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool AACEncoder::init(int iSampleRate, int iChannels, int iSampleBit) {
|
||||
if (iSampleBit != 16) {
|
||||
return false;
|
||||
}
|
||||
// (1) Open FAAC engine
|
||||
_hEncoder = faacEncOpen(iSampleRate, iChannels, &_ulInputSamples,
|
||||
&_ulMaxOutputBytes);
|
||||
if (_hEncoder == NULL) {
|
||||
return false;
|
||||
}
|
||||
_pucAacBuf = new unsigned char[_ulMaxOutputBytes];
|
||||
_ulMaxInputBytes = _ulInputSamples * iSampleBit / 8;
|
||||
_pucPcmBuf = new unsigned char[_ulMaxInputBytes * 4];
|
||||
if (iSampleBit != 16) {
|
||||
return false;
|
||||
}
|
||||
// (1) Open FAAC engine
|
||||
_hEncoder = faacEncOpen(iSampleRate, iChannels, &_ulInputSamples,
|
||||
&_ulMaxOutputBytes);
|
||||
if (_hEncoder == NULL) {
|
||||
return false;
|
||||
}
|
||||
_pucAacBuf = new unsigned char[_ulMaxOutputBytes];
|
||||
_ulMaxInputBytes = _ulInputSamples * iSampleBit / 8;
|
||||
_pucPcmBuf = new unsigned char[_ulMaxInputBytes * 4];
|
||||
|
||||
// (2.1) Get current encoding configuration
|
||||
faacEncConfigurationPtr pConfiguration = faacEncGetCurrentConfiguration(_hEncoder);
|
||||
if (pConfiguration == NULL) {
|
||||
faacEncClose(_hEncoder);
|
||||
return false;
|
||||
}
|
||||
pConfiguration->aacObjectType =LOW;
|
||||
pConfiguration->mpegVersion = 4;
|
||||
pConfiguration->useTns = 1;
|
||||
pConfiguration->shortctl = SHORTCTL_NORMAL;
|
||||
pConfiguration->useLfe = 1;
|
||||
pConfiguration->allowMidside = 1;
|
||||
pConfiguration->bitRate = 0;
|
||||
pConfiguration->bandWidth = 0;
|
||||
pConfiguration->quantqual = 50;
|
||||
pConfiguration->outputFormat = 1;
|
||||
pConfiguration->inputFormat = FAAC_INPUT_16BIT;
|
||||
// (2.1) Get current encoding configuration
|
||||
faacEncConfigurationPtr pConfiguration = faacEncGetCurrentConfiguration(_hEncoder);
|
||||
if (pConfiguration == NULL) {
|
||||
faacEncClose(_hEncoder);
|
||||
return false;
|
||||
}
|
||||
pConfiguration->aacObjectType =LOW;
|
||||
pConfiguration->mpegVersion = 4;
|
||||
pConfiguration->useTns = 1;
|
||||
pConfiguration->shortctl = SHORTCTL_NORMAL;
|
||||
pConfiguration->useLfe = 1;
|
||||
pConfiguration->allowMidside = 1;
|
||||
pConfiguration->bitRate = 0;
|
||||
pConfiguration->bandWidth = 0;
|
||||
pConfiguration->quantqual = 50;
|
||||
pConfiguration->outputFormat = 1;
|
||||
pConfiguration->inputFormat = FAAC_INPUT_16BIT;
|
||||
|
||||
// (2.2) Set encoding configuration
|
||||
if(!faacEncSetConfiguration(_hEncoder, pConfiguration)){
|
||||
ErrorL << "faacEncSetConfiguration failed";
|
||||
faacEncClose(_hEncoder);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
// (2.2) Set encoding configuration
|
||||
if(!faacEncSetConfiguration(_hEncoder, pConfiguration)){
|
||||
ErrorL << "faacEncSetConfiguration failed";
|
||||
faacEncClose(_hEncoder);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int AACEncoder::inputData(char *pcPcmBufr, int iLen, unsigned char **ppucOutBuffer) {
|
||||
memcpy(_pucPcmBuf + _uiPcmLen, pcPcmBufr, iLen);
|
||||
_uiPcmLen += iLen;
|
||||
if (_uiPcmLen < _ulMaxInputBytes) {
|
||||
return 0;
|
||||
}
|
||||
memcpy(_pucPcmBuf + _uiPcmLen, pcPcmBufr, iLen);
|
||||
_uiPcmLen += iLen;
|
||||
if (_uiPcmLen < _ulMaxInputBytes) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nRet = faacEncEncode(_hEncoder, (int32_t *) (_pucPcmBuf), _ulInputSamples, _pucAacBuf, _ulMaxOutputBytes);
|
||||
_uiPcmLen -= _ulMaxInputBytes;
|
||||
memmove(_pucPcmBuf, _pucPcmBuf + _ulMaxInputBytes, _uiPcmLen);
|
||||
*ppucOutBuffer = _pucAacBuf;
|
||||
return nRet;
|
||||
int nRet = faacEncEncode(_hEncoder, (int32_t *) (_pucPcmBuf), _ulInputSamples, _pucAacBuf, _ulMaxOutputBytes);
|
||||
_uiPcmLen -= _ulMaxInputBytes;
|
||||
memmove(_pucPcmBuf, _pucPcmBuf + _ulMaxInputBytes, _uiPcmLen);
|
||||
*ppucOutBuffer = _pucAacBuf;
|
||||
return nRet;
|
||||
}
|
||||
|
||||
} /* namespace mediakit */
|
||||
|
@ -32,21 +32,21 @@ namespace mediakit {
|
||||
|
||||
class AACEncoder {
|
||||
public:
|
||||
AACEncoder(void);
|
||||
virtual ~AACEncoder(void);
|
||||
bool init(int iSampleRate, int iAudioChannel, int iAudioSampleBit);
|
||||
int inputData(char *pcData, int iLen, unsigned char **ppucOutBuffer);
|
||||
AACEncoder(void);
|
||||
virtual ~AACEncoder(void);
|
||||
bool init(int iSampleRate, int iAudioChannel, int iAudioSampleBit);
|
||||
int inputData(char *pcData, int iLen, unsigned char **ppucOutBuffer);
|
||||
|
||||
private:
|
||||
unsigned char *_pucPcmBuf = nullptr;
|
||||
unsigned int _uiPcmLen = 0;
|
||||
unsigned char *_pucPcmBuf = nullptr;
|
||||
unsigned int _uiPcmLen = 0;
|
||||
|
||||
unsigned char *_pucAacBuf = nullptr;
|
||||
void *_hEncoder = nullptr;
|
||||
unsigned char *_pucAacBuf = nullptr;
|
||||
void *_hEncoder = nullptr;
|
||||
|
||||
unsigned long _ulInputSamples = 0;
|
||||
unsigned long _ulMaxInputBytes = 0;
|
||||
unsigned long _ulMaxOutputBytes = 0;
|
||||
unsigned long _ulInputSamples = 0;
|
||||
unsigned long _ulMaxInputBytes = 0;
|
||||
unsigned long _ulMaxOutputBytes = 0;
|
||||
|
||||
};
|
||||
|
||||
|
@ -38,21 +38,21 @@ H264Encoder::H264Encoder() {
|
||||
}
|
||||
|
||||
H264Encoder::~H264Encoder() {
|
||||
//* 清除图像区域
|
||||
if (_pPicIn) {
|
||||
delete _pPicIn;
|
||||
_pPicIn = nullptr;
|
||||
}
|
||||
if (_pPicOut) {
|
||||
delete _pPicOut;
|
||||
_pPicOut = nullptr;
|
||||
}
|
||||
//* 清除图像区域
|
||||
if (_pPicIn) {
|
||||
delete _pPicIn;
|
||||
_pPicIn = nullptr;
|
||||
}
|
||||
if (_pPicOut) {
|
||||
delete _pPicOut;
|
||||
_pPicOut = nullptr;
|
||||
}
|
||||
|
||||
//* 关闭编码器句柄
|
||||
if (_pX264Handle) {
|
||||
x264_encoder_close(_pX264Handle);
|
||||
_pX264Handle = nullptr;
|
||||
}
|
||||
//* 关闭编码器句柄
|
||||
if (_pX264Handle) {
|
||||
x264_encoder_close(_pX264Handle);
|
||||
_pX264Handle = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -229,122 +229,122 @@ Value的值就是fps。
|
||||
} x264_param_t;*/
|
||||
|
||||
bool H264Encoder::init(int iWidth, int iHeight, int iFps) {
|
||||
if (_pX264Handle) {
|
||||
return true;
|
||||
}
|
||||
x264_param_t X264Param, *pX264Param = &X264Param;
|
||||
//* 配置参数
|
||||
//* 使用默认参数
|
||||
x264_param_default_preset(pX264Param, "ultrafast", "zerolatency");
|
||||
if (_pX264Handle) {
|
||||
return true;
|
||||
}
|
||||
x264_param_t X264Param, *pX264Param = &X264Param;
|
||||
//* 配置参数
|
||||
//* 使用默认参数
|
||||
x264_param_default_preset(pX264Param, "ultrafast", "zerolatency");
|
||||
|
||||
//* cpuFlags
|
||||
pX264Param->i_threads = X264_SYNC_LOOKAHEAD_AUTO; //* 取空缓冲区继续使用不死锁的保证.
|
||||
//* video Properties
|
||||
pX264Param->i_width = iWidth; //* 宽度.
|
||||
pX264Param->i_height = iHeight; //* 高度
|
||||
pX264Param->i_frame_total = 0; //* 编码总帧数.不知道用0.
|
||||
pX264Param->i_keyint_max = iFps * 3; //ffmpeg:gop_size 关键帧最大间隔
|
||||
pX264Param->i_keyint_min = iFps * 1; //ffmpeg:keyint_min 关键帧最小间隔
|
||||
//* Rate control Parameters
|
||||
pX264Param->rc.i_bitrate = 5000; //* 码率(比特率,单位Kbps)
|
||||
pX264Param->rc.i_qp_step = 1; //最大的在帧与帧之间进行切变的量化因子的变化量。ffmpeg:max_qdiff
|
||||
pX264Param->rc.i_qp_min = 10; //ffmpeg:qmin;最小的量化因子。取值范围1-51。建议在10-30之间。
|
||||
pX264Param->rc.i_qp_max = 41; //ffmpeg:qmax;最大的量化因子。取值范围1-51。建议在10-30之间。
|
||||
pX264Param->rc.f_qcompress = 0.6;//ffmpeg:qcompress 量化器压缩比率0-1.越小则比特率越区域固定,但是越高越使量化器参数越固定
|
||||
pX264Param->analyse.i_me_range = 16; //ffmpeg:me_range 运动侦测的半径
|
||||
pX264Param->i_frame_reference = 3; //ffmpeg:refsB和P帧向前预测参考的帧数。取值范围1-16。
|
||||
//该值不影响解码的速度,但是越大解码
|
||||
//所需的内存越大。这个值在一般情况下
|
||||
//越大效果越好,但是超过6以后效果就
|
||||
//不明显了。
|
||||
//* cpuFlags
|
||||
pX264Param->i_threads = X264_SYNC_LOOKAHEAD_AUTO; //* 取空缓冲区继续使用不死锁的保证.
|
||||
//* video Properties
|
||||
pX264Param->i_width = iWidth; //* 宽度.
|
||||
pX264Param->i_height = iHeight; //* 高度
|
||||
pX264Param->i_frame_total = 0; //* 编码总帧数.不知道用0.
|
||||
pX264Param->i_keyint_max = iFps * 3; //ffmpeg:gop_size 关键帧最大间隔
|
||||
pX264Param->i_keyint_min = iFps * 1; //ffmpeg:keyint_min 关键帧最小间隔
|
||||
//* Rate control Parameters
|
||||
pX264Param->rc.i_bitrate = 5000; //* 码率(比特率,单位Kbps)
|
||||
pX264Param->rc.i_qp_step = 1; //最大的在帧与帧之间进行切变的量化因子的变化量。ffmpeg:max_qdiff
|
||||
pX264Param->rc.i_qp_min = 10; //ffmpeg:qmin;最小的量化因子。取值范围1-51。建议在10-30之间。
|
||||
pX264Param->rc.i_qp_max = 41; //ffmpeg:qmax;最大的量化因子。取值范围1-51。建议在10-30之间。
|
||||
pX264Param->rc.f_qcompress = 0.6;//ffmpeg:qcompress 量化器压缩比率0-1.越小则比特率越区域固定,但是越高越使量化器参数越固定
|
||||
pX264Param->analyse.i_me_range = 16; //ffmpeg:me_range 运动侦测的半径
|
||||
pX264Param->i_frame_reference = 3; //ffmpeg:refsB和P帧向前预测参考的帧数。取值范围1-16。
|
||||
//该值不影响解码的速度,但是越大解码
|
||||
//所需的内存越大。这个值在一般情况下
|
||||
//越大效果越好,但是超过6以后效果就
|
||||
//不明显了。
|
||||
|
||||
pX264Param->analyse.i_trellis = 1; //ffmpeg:trellis
|
||||
//pX264Param->analyse.i_me_method=X264_ME_DIA;//ffmpeg:me_method ME_ZERO 运动侦测的方式
|
||||
pX264Param->rc.f_qblur = 0.5; //ffmpeg:qblur
|
||||
pX264Param->analyse.i_trellis = 1; //ffmpeg:trellis
|
||||
//pX264Param->analyse.i_me_method=X264_ME_DIA;//ffmpeg:me_method ME_ZERO 运动侦测的方式
|
||||
pX264Param->rc.f_qblur = 0.5; //ffmpeg:qblur
|
||||
|
||||
//* bitstream parameters
|
||||
/*open-GOP
|
||||
码流里面包含B帧的时候才会出现open-GOP。
|
||||
一个GOP里面的某一帧在解码时要依赖于前一个GOP的某些帧,
|
||||
这个GOP就称为open-GOP。
|
||||
有些解码器不能完全支持open-GOP码流,
|
||||
例如蓝光解码器,因此在x264里面open-GOP是默认关闭的。
|
||||
对于解码端,接收到的码流如果如下:I0 B0 B1 P0 B2 B3...这就是一个open-GOP码流(I帧后面紧跟B帧)。
|
||||
因此B0 B1的解码需要用到I0前面一个GOP的数据,B0 B1的dts是小于I0的。
|
||||
如果码流如下: I0 P0 B0 B1 P1 B2 B3...这就是一个close-GOP码流,
|
||||
I0后面所有帧的解码不依赖于I0前面的帧,I0后面所有帧的dts都比I0的大。
|
||||
如果码流是IDR0 B0 B1 P0 B2 B3...那个这个GOP是close-GOP,B0,B1虽然dst比IDR0小,
|
||||
但编解码端都刷新了参考缓冲,B0,B1参考不到前向GOP帧。
|
||||
对于编码端,如果编码帧类型决定如下: ...P0 B1 B2 P3 B4 B5 I6这就会输出open-Gop码流 (P0 P3 B1 B2 I6 B4 B5...),
|
||||
B4 B5的解码依赖P3。
|
||||
如果编码帧类型决定如下...P0 B1 B2 P3 B4 P5 I6这样就不会输出open-GOP码流(P0 P3 B1 B2 P5 B4 I6...)。
|
||||
两者区别在于I6前面的第5帧是设置为B帧还是P帧,
|
||||
如果一个GOP的最后一帧(上例中是第5帧)设置为B帧,
|
||||
这个码流就是open-GOP,设置为P帧就是close-GOP。
|
||||
由于B帧压缩性能好于P帧,因此open-GOP在编码性能上稍微优于close-GOP,
|
||||
但为了兼容性和少一些麻烦,还是把opne-GOP关闭的好。*/
|
||||
pX264Param->b_open_gop = 0;
|
||||
pX264Param->i_bframe = 0; //最大B帧数.
|
||||
pX264Param->i_bframe_pyramid = 0;
|
||||
pX264Param->i_bframe_adaptive = X264_B_ADAPT_TRELLIS;
|
||||
//* Log
|
||||
pX264Param->i_log_level = X264_LOG_ERROR;
|
||||
//* bitstream parameters
|
||||
/*open-GOP
|
||||
码流里面包含B帧的时候才会出现open-GOP。
|
||||
一个GOP里面的某一帧在解码时要依赖于前一个GOP的某些帧,
|
||||
这个GOP就称为open-GOP。
|
||||
有些解码器不能完全支持open-GOP码流,
|
||||
例如蓝光解码器,因此在x264里面open-GOP是默认关闭的。
|
||||
对于解码端,接收到的码流如果如下:I0 B0 B1 P0 B2 B3...这就是一个open-GOP码流(I帧后面紧跟B帧)。
|
||||
因此B0 B1的解码需要用到I0前面一个GOP的数据,B0 B1的dts是小于I0的。
|
||||
如果码流如下: I0 P0 B0 B1 P1 B2 B3...这就是一个close-GOP码流,
|
||||
I0后面所有帧的解码不依赖于I0前面的帧,I0后面所有帧的dts都比I0的大。
|
||||
如果码流是IDR0 B0 B1 P0 B2 B3...那个这个GOP是close-GOP,B0,B1虽然dst比IDR0小,
|
||||
但编解码端都刷新了参考缓冲,B0,B1参考不到前向GOP帧。
|
||||
对于编码端,如果编码帧类型决定如下: ...P0 B1 B2 P3 B4 B5 I6这就会输出open-Gop码流 (P0 P3 B1 B2 I6 B4 B5...),
|
||||
B4 B5的解码依赖P3。
|
||||
如果编码帧类型决定如下...P0 B1 B2 P3 B4 P5 I6这样就不会输出open-GOP码流(P0 P3 B1 B2 P5 B4 I6...)。
|
||||
两者区别在于I6前面的第5帧是设置为B帧还是P帧,
|
||||
如果一个GOP的最后一帧(上例中是第5帧)设置为B帧,
|
||||
这个码流就是open-GOP,设置为P帧就是close-GOP。
|
||||
由于B帧压缩性能好于P帧,因此open-GOP在编码性能上稍微优于close-GOP,
|
||||
但为了兼容性和少一些麻烦,还是把opne-GOP关闭的好。*/
|
||||
pX264Param->b_open_gop = 0;
|
||||
pX264Param->i_bframe = 0; //最大B帧数.
|
||||
pX264Param->i_bframe_pyramid = 0;
|
||||
pX264Param->i_bframe_adaptive = X264_B_ADAPT_TRELLIS;
|
||||
//* Log
|
||||
pX264Param->i_log_level = X264_LOG_ERROR;
|
||||
|
||||
//* muxing parameters
|
||||
pX264Param->i_fps_den = 1; //* 帧率分母
|
||||
pX264Param->i_fps_num = iFps; //* 帧率分子
|
||||
pX264Param->i_timebase_den = pX264Param->i_fps_num;
|
||||
pX264Param->i_timebase_num = pX264Param->i_fps_den;
|
||||
//* muxing parameters
|
||||
pX264Param->i_fps_den = 1; //* 帧率分母
|
||||
pX264Param->i_fps_num = iFps; //* 帧率分子
|
||||
pX264Param->i_timebase_den = pX264Param->i_fps_num;
|
||||
pX264Param->i_timebase_num = pX264Param->i_fps_den;
|
||||
|
||||
pX264Param->analyse.i_subpel_refine = 1; //这个参数控制在运动估算过程中质量和速度的权衡。Subq=5可以压缩>10%于subq=1。1-7
|
||||
pX264Param->analyse.b_fast_pskip = 1; //在P帧内执行早期快速跳跃探测。这个经常在没有任何损失的前提下提高了速度。
|
||||
pX264Param->analyse.i_subpel_refine = 1; //这个参数控制在运动估算过程中质量和速度的权衡。Subq=5可以压缩>10%于subq=1。1-7
|
||||
pX264Param->analyse.b_fast_pskip = 1; //在P帧内执行早期快速跳跃探测。这个经常在没有任何损失的前提下提高了速度。
|
||||
|
||||
pX264Param->b_annexb = 1; //1前面为0x00000001,0为nal长度
|
||||
pX264Param->b_repeat_headers = 1; //关键帧前面是否放sps跟pps帧,0 否 1,放
|
||||
pX264Param->b_annexb = 1; //1前面为0x00000001,0为nal长度
|
||||
pX264Param->b_repeat_headers = 1; //关键帧前面是否放sps跟pps帧,0 否 1,放
|
||||
|
||||
//* 设置Profile.使用baseline
|
||||
x264_param_apply_profile(pX264Param, "high");
|
||||
//* 设置Profile.使用baseline
|
||||
x264_param_apply_profile(pX264Param, "high");
|
||||
|
||||
//* 打开编码器句柄,通过x264_encoder_parameters得到设置给X264
|
||||
//* 的参数.通过x264_encoder_reconfig更新X264的参数
|
||||
_pX264Handle = x264_encoder_open(pX264Param);
|
||||
if (!_pX264Handle) {
|
||||
return false;
|
||||
}
|
||||
_pPicIn = new x264_picture_t;
|
||||
_pPicOut = new x264_picture_t;
|
||||
x264_picture_init(_pPicIn);
|
||||
x264_picture_init(_pPicOut);
|
||||
_pPicIn->img.i_csp = X264_CSP_I420;
|
||||
_pPicIn->img.i_plane = 3;
|
||||
return true;
|
||||
//* 打开编码器句柄,通过x264_encoder_parameters得到设置给X264
|
||||
//* 的参数.通过x264_encoder_reconfig更新X264的参数
|
||||
_pX264Handle = x264_encoder_open(pX264Param);
|
||||
if (!_pX264Handle) {
|
||||
return false;
|
||||
}
|
||||
_pPicIn = new x264_picture_t;
|
||||
_pPicOut = new x264_picture_t;
|
||||
x264_picture_init(_pPicIn);
|
||||
x264_picture_init(_pPicOut);
|
||||
_pPicIn->img.i_csp = X264_CSP_I420;
|
||||
_pPicIn->img.i_plane = 3;
|
||||
return true;
|
||||
}
|
||||
|
||||
int H264Encoder::inputData(char* apcYuv[3], int aiYuvLen[3], int64_t i64Pts, H264Frame** ppFrame) {
|
||||
//TimeTicker1(5);
|
||||
_pPicIn->img.i_stride[0] = aiYuvLen[0];
|
||||
_pPicIn->img.i_stride[1] = aiYuvLen[1];
|
||||
_pPicIn->img.i_stride[2] = aiYuvLen[2];
|
||||
_pPicIn->img.plane[0] = (uint8_t *) apcYuv[0];
|
||||
_pPicIn->img.plane[1] = (uint8_t *) apcYuv[1];
|
||||
_pPicIn->img.plane[2] = (uint8_t *) apcYuv[2];
|
||||
_pPicIn->i_pts = i64Pts;
|
||||
int iNal;
|
||||
x264_nal_t* pNals;
|
||||
//TimeTicker1(5);
|
||||
_pPicIn->img.i_stride[0] = aiYuvLen[0];
|
||||
_pPicIn->img.i_stride[1] = aiYuvLen[1];
|
||||
_pPicIn->img.i_stride[2] = aiYuvLen[2];
|
||||
_pPicIn->img.plane[0] = (uint8_t *) apcYuv[0];
|
||||
_pPicIn->img.plane[1] = (uint8_t *) apcYuv[1];
|
||||
_pPicIn->img.plane[2] = (uint8_t *) apcYuv[2];
|
||||
_pPicIn->i_pts = i64Pts;
|
||||
int iNal;
|
||||
x264_nal_t* pNals;
|
||||
|
||||
int iResult = x264_encoder_encode(_pX264Handle, &pNals, &iNal, _pPicIn,
|
||||
_pPicOut);
|
||||
if (iResult <= 0) {
|
||||
return 0;
|
||||
}
|
||||
for (int i = 0; i < iNal; i++) {
|
||||
x264_nal_t pNal = pNals[i];
|
||||
_aFrames[i].iType = pNal.i_type;
|
||||
_aFrames[i].iLength = pNal.i_payload;
|
||||
_aFrames[i].pucData = pNal.p_payload;
|
||||
}
|
||||
*ppFrame = _aFrames;
|
||||
return iNal;
|
||||
int iResult = x264_encoder_encode(_pX264Handle, &pNals, &iNal, _pPicIn,
|
||||
_pPicOut);
|
||||
if (iResult <= 0) {
|
||||
return 0;
|
||||
}
|
||||
for (int i = 0; i < iNal; i++) {
|
||||
x264_nal_t pNal = pNals[i];
|
||||
_aFrames[i].iType = pNal.i_type;
|
||||
_aFrames[i].iLength = pNal.i_payload;
|
||||
_aFrames[i].pucData = pNal.p_payload;
|
||||
}
|
||||
*ppFrame = _aFrames;
|
||||
return iNal;
|
||||
}
|
||||
|
||||
} /* namespace mediakit */
|
||||
|
@ -42,21 +42,21 @@ namespace mediakit {
|
||||
|
||||
class H264Encoder {
|
||||
public:
|
||||
typedef struct {
|
||||
int iType;
|
||||
int iLength;
|
||||
uint8_t *pucData;
|
||||
} H264Frame;
|
||||
typedef struct {
|
||||
int iType;
|
||||
int iLength;
|
||||
uint8_t *pucData;
|
||||
} H264Frame;
|
||||
|
||||
H264Encoder(void);
|
||||
virtual ~H264Encoder(void);
|
||||
bool init(int iWidth, int iHeight, int iFps);
|
||||
int inputData(char *apcYuv[3], int aiYuvLen[3], int64_t i64Pts, H264Frame **ppFrame);
|
||||
H264Encoder(void);
|
||||
virtual ~H264Encoder(void);
|
||||
bool init(int iWidth, int iHeight, int iFps);
|
||||
int inputData(char *apcYuv[3], int aiYuvLen[3], int64_t i64Pts, H264Frame **ppFrame);
|
||||
private:
|
||||
x264_t* _pX264Handle = nullptr;
|
||||
x264_picture_t* _pPicIn = nullptr;
|
||||
x264_picture_t* _pPicOut = nullptr;
|
||||
H264Frame _aFrames[10];
|
||||
x264_t* _pX264Handle = nullptr;
|
||||
x264_picture_t* _pPicIn = nullptr;
|
||||
x264_picture_t* _pPicOut = nullptr;
|
||||
H264Frame _aFrames[10];
|
||||
};
|
||||
|
||||
} /* namespace mediakit */
|
||||
|
@ -42,8 +42,8 @@ DevChannel::DevChannel(const string &strVhost,
|
||||
const string &strApp,
|
||||
const string &strId,
|
||||
float fDuration,
|
||||
bool bEanbleRtsp,
|
||||
bool bEanbleRtmp,
|
||||
bool bEanbleRtsp,
|
||||
bool bEanbleRtmp,
|
||||
bool bEanbleHls,
|
||||
bool bEnableMp4) :
|
||||
MultiMediaSourceMuxer(strVhost, strApp, strId, fDuration, bEanbleRtsp, bEanbleRtmp, bEanbleHls, bEnableMp4) {}
|
||||
@ -52,50 +52,50 @@ DevChannel::~DevChannel() {}
|
||||
|
||||
#ifdef ENABLE_X264
|
||||
void DevChannel::inputYUV(char* apcYuv[3], int aiYuvLen[3], uint32_t uiStamp) {
|
||||
//TimeTicker1(50);
|
||||
if (!_pH264Enc) {
|
||||
_pH264Enc.reset(new H264Encoder());
|
||||
if (!_pH264Enc->init(_video->iWidth, _video->iHeight, _video->iFrameRate)) {
|
||||
_pH264Enc.reset();
|
||||
WarnL << "H264Encoder init failed!";
|
||||
}
|
||||
}
|
||||
if (_pH264Enc) {
|
||||
H264Encoder::H264Frame *pOut;
|
||||
int iFrames = _pH264Enc->inputData(apcYuv, aiYuvLen, uiStamp, &pOut);
|
||||
for (int i = 0; i < iFrames; i++) {
|
||||
inputH264((char *) pOut[i].pucData, pOut[i].iLength, uiStamp);
|
||||
}
|
||||
}
|
||||
//TimeTicker1(50);
|
||||
if (!_pH264Enc) {
|
||||
_pH264Enc.reset(new H264Encoder());
|
||||
if (!_pH264Enc->init(_video->iWidth, _video->iHeight, _video->iFrameRate)) {
|
||||
_pH264Enc.reset();
|
||||
WarnL << "H264Encoder init failed!";
|
||||
}
|
||||
}
|
||||
if (_pH264Enc) {
|
||||
H264Encoder::H264Frame *pOut;
|
||||
int iFrames = _pH264Enc->inputData(apcYuv, aiYuvLen, uiStamp, &pOut);
|
||||
for (int i = 0; i < iFrames; i++) {
|
||||
inputH264((char *) pOut[i].pucData, pOut[i].iLength, uiStamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif //ENABLE_X264
|
||||
|
||||
#ifdef ENABLE_FAAC
|
||||
void DevChannel::inputPCM(char* pcData, int iDataLen, uint32_t uiStamp) {
|
||||
if (!_pAacEnc) {
|
||||
_pAacEnc.reset(new AACEncoder());
|
||||
if (!_pAacEnc->init(_audio->iSampleRate, _audio->iChannel, _audio->iSampleBit)) {
|
||||
_pAacEnc.reset();
|
||||
WarnL << "AACEncoder init failed!";
|
||||
}
|
||||
}
|
||||
if (_pAacEnc) {
|
||||
unsigned char *pucOut;
|
||||
int iRet = _pAacEnc->inputData(pcData, iDataLen, &pucOut);
|
||||
if (iRet > 0) {
|
||||
inputAAC((char *) pucOut, iRet, uiStamp);
|
||||
}
|
||||
}
|
||||
if (!_pAacEnc) {
|
||||
_pAacEnc.reset(new AACEncoder());
|
||||
if (!_pAacEnc->init(_audio->iSampleRate, _audio->iChannel, _audio->iSampleBit)) {
|
||||
_pAacEnc.reset();
|
||||
WarnL << "AACEncoder init failed!";
|
||||
}
|
||||
}
|
||||
if (_pAacEnc) {
|
||||
unsigned char *pucOut;
|
||||
int iRet = _pAacEnc->inputData(pcData, iDataLen, &pucOut);
|
||||
if (iRet > 0) {
|
||||
inputAAC((char *) pucOut, iRet, uiStamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif //ENABLE_FAAC
|
||||
|
||||
void DevChannel::inputH264(const char* pcData, int iDataLen, uint32_t dts,uint32_t pts) {
|
||||
if(dts == 0){
|
||||
dts = (uint32_t)_aTicker[0].elapsedTime();
|
||||
dts = (uint32_t)_aTicker[0].elapsedTime();
|
||||
}
|
||||
if(pts == 0){
|
||||
pts = dts;
|
||||
}
|
||||
if(pts == 0){
|
||||
pts = dts;
|
||||
}
|
||||
int prefixeSize;
|
||||
if (memcmp("\x00\x00\x00\x01", pcData, 4) == 0) {
|
||||
prefixeSize = 4;
|
||||
@ -105,46 +105,46 @@ void DevChannel::inputH264(const char* pcData, int iDataLen, uint32_t dts,uint32
|
||||
prefixeSize = 0;
|
||||
}
|
||||
|
||||
H264Frame::Ptr frame = std::make_shared<H264Frame>();
|
||||
frame->_dts = dts;
|
||||
frame->_pts = pts;
|
||||
frame->_buffer.assign("\x00\x00\x00\x01",4);
|
||||
frame->_buffer.append(pcData + prefixeSize, iDataLen - prefixeSize);
|
||||
frame->_prefix_size = 4;
|
||||
H264Frame::Ptr frame = std::make_shared<H264Frame>();
|
||||
frame->_dts = dts;
|
||||
frame->_pts = pts;
|
||||
frame->_buffer.assign("\x00\x00\x00\x01",4);
|
||||
frame->_buffer.append(pcData + prefixeSize, iDataLen - prefixeSize);
|
||||
frame->_prefix_size = 4;
|
||||
inputFrame(frame);
|
||||
}
|
||||
|
||||
void DevChannel::inputH265(const char* pcData, int iDataLen, uint32_t dts,uint32_t pts) {
|
||||
if(dts == 0){
|
||||
dts = (uint32_t)_aTicker[0].elapsedTime();
|
||||
}
|
||||
if(pts == 0){
|
||||
pts = dts;
|
||||
}
|
||||
int prefixeSize;
|
||||
if (memcmp("\x00\x00\x00\x01", pcData, 4) == 0) {
|
||||
prefixeSize = 4;
|
||||
} else if (memcmp("\x00\x00\x01", pcData, 3) == 0) {
|
||||
prefixeSize = 3;
|
||||
} else {
|
||||
prefixeSize = 0;
|
||||
}
|
||||
if(dts == 0){
|
||||
dts = (uint32_t)_aTicker[0].elapsedTime();
|
||||
}
|
||||
if(pts == 0){
|
||||
pts = dts;
|
||||
}
|
||||
int prefixeSize;
|
||||
if (memcmp("\x00\x00\x00\x01", pcData, 4) == 0) {
|
||||
prefixeSize = 4;
|
||||
} else if (memcmp("\x00\x00\x01", pcData, 3) == 0) {
|
||||
prefixeSize = 3;
|
||||
} else {
|
||||
prefixeSize = 0;
|
||||
}
|
||||
|
||||
H265Frame::Ptr frame = std::make_shared<H265Frame>();
|
||||
frame->_dts = dts;
|
||||
frame->_pts = pts;
|
||||
frame->_buffer.assign("\x00\x00\x00\x01",4);
|
||||
frame->_buffer.append(pcData + prefixeSize, iDataLen - prefixeSize);
|
||||
frame->_prefix_size = 4;
|
||||
inputFrame(frame);
|
||||
H265Frame::Ptr frame = std::make_shared<H265Frame>();
|
||||
frame->_dts = dts;
|
||||
frame->_pts = pts;
|
||||
frame->_buffer.assign("\x00\x00\x00\x01",4);
|
||||
frame->_buffer.append(pcData + prefixeSize, iDataLen - prefixeSize);
|
||||
frame->_prefix_size = 4;
|
||||
inputFrame(frame);
|
||||
}
|
||||
|
||||
void DevChannel::inputAAC(const char* pcData, int iDataLen, uint32_t uiStamp,bool withAdtsHeader) {
|
||||
if(withAdtsHeader){
|
||||
inputAAC(pcData+7,iDataLen-7,uiStamp,pcData);
|
||||
} else if(_audio) {
|
||||
inputAAC(pcData,iDataLen,uiStamp,(char *)_adtsHeader);
|
||||
}
|
||||
if(withAdtsHeader){
|
||||
inputAAC(pcData+7,iDataLen-7,uiStamp,pcData);
|
||||
} else if(_audio) {
|
||||
inputAAC(pcData,iDataLen,uiStamp,(char *)_adtsHeader);
|
||||
}
|
||||
}
|
||||
|
||||
void DevChannel::inputAAC(const char *pcDataWithoutAdts,int iDataLen, uint32_t uiStamp,const char *pcAdtsHeader){
|
||||
@ -152,54 +152,54 @@ void DevChannel::inputAAC(const char *pcDataWithoutAdts,int iDataLen, uint32_t u
|
||||
uiStamp = (uint32_t)_aTicker[1].elapsedTime();
|
||||
}
|
||||
if(pcAdtsHeader + 7 == pcDataWithoutAdts){
|
||||
inputFrame(std::make_shared<AACFrameNoCacheAble>((char *)pcDataWithoutAdts - 7,iDataLen + 7,uiStamp,7));
|
||||
} else {
|
||||
char *dataWithAdts = new char[iDataLen + 7];
|
||||
memcpy(dataWithAdts,pcAdtsHeader,7);
|
||||
memcpy(dataWithAdts + 7 , pcDataWithoutAdts , iDataLen);
|
||||
inputFrame(std::make_shared<AACFrameNoCacheAble>(dataWithAdts,iDataLen + 7,uiStamp,7));
|
||||
delete [] dataWithAdts;
|
||||
}
|
||||
inputFrame(std::make_shared<AACFrameNoCacheAble>((char *)pcDataWithoutAdts - 7,iDataLen + 7,uiStamp,7));
|
||||
} else {
|
||||
char *dataWithAdts = new char[iDataLen + 7];
|
||||
memcpy(dataWithAdts,pcAdtsHeader,7);
|
||||
memcpy(dataWithAdts + 7 , pcDataWithoutAdts , iDataLen);
|
||||
inputFrame(std::make_shared<AACFrameNoCacheAble>(dataWithAdts,iDataLen + 7,uiStamp,7));
|
||||
delete [] dataWithAdts;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DevChannel::initVideo(const VideoInfo& info) {
|
||||
_video = std::make_shared<VideoInfo>(info);
|
||||
addTrack(std::make_shared<H264Track>());
|
||||
_video = std::make_shared<VideoInfo>(info);
|
||||
addTrack(std::make_shared<H264Track>());
|
||||
}
|
||||
|
||||
void DevChannel::initH265Video(const VideoInfo &info){
|
||||
_video = std::make_shared<VideoInfo>(info);
|
||||
addTrack(std::make_shared<H265Track>());
|
||||
_video = std::make_shared<VideoInfo>(info);
|
||||
addTrack(std::make_shared<H265Track>());
|
||||
}
|
||||
|
||||
void DevChannel::initAudio(const AudioInfo& info) {
|
||||
_audio = std::make_shared<AudioInfo>(info);
|
||||
addTrack(std::make_shared<AACTrack>());
|
||||
_audio = std::make_shared<AudioInfo>(info);
|
||||
addTrack(std::make_shared<AACTrack>());
|
||||
|
||||
AACFrame adtsHeader;
|
||||
adtsHeader.syncword = 0x0FFF;
|
||||
adtsHeader.id = 0;
|
||||
adtsHeader.layer = 0;
|
||||
adtsHeader.protection_absent = 1;
|
||||
adtsHeader.profile = info.iProfile;//audioObjectType - 1;
|
||||
int i = 0;
|
||||
for(auto rate : samplingFrequencyTable){
|
||||
if(rate == info.iSampleRate){
|
||||
adtsHeader.sf_index = i;
|
||||
};
|
||||
++i;
|
||||
}
|
||||
adtsHeader.private_bit = 0;
|
||||
adtsHeader.channel_configuration = info.iChannel;
|
||||
adtsHeader.original = 0;
|
||||
adtsHeader.home = 0;
|
||||
adtsHeader.copyright_identification_bit = 0;
|
||||
adtsHeader.copyright_identification_start = 0;
|
||||
adtsHeader.aac_frame_length = 7;
|
||||
adtsHeader.adts_buffer_fullness = 2047;
|
||||
adtsHeader.no_raw_data_blocks_in_frame = 0;
|
||||
writeAdtsHeader(adtsHeader,_adtsHeader);
|
||||
AACFrame adtsHeader;
|
||||
adtsHeader.syncword = 0x0FFF;
|
||||
adtsHeader.id = 0;
|
||||
adtsHeader.layer = 0;
|
||||
adtsHeader.protection_absent = 1;
|
||||
adtsHeader.profile = info.iProfile;//audioObjectType - 1;
|
||||
int i = 0;
|
||||
for(auto rate : samplingFrequencyTable){
|
||||
if(rate == info.iSampleRate){
|
||||
adtsHeader.sf_index = i;
|
||||
};
|
||||
++i;
|
||||
}
|
||||
adtsHeader.private_bit = 0;
|
||||
adtsHeader.channel_configuration = info.iChannel;
|
||||
adtsHeader.original = 0;
|
||||
adtsHeader.home = 0;
|
||||
adtsHeader.copyright_identification_bit = 0;
|
||||
adtsHeader.copyright_identification_start = 0;
|
||||
adtsHeader.aac_frame_length = 7;
|
||||
adtsHeader.adts_buffer_fullness = 2047;
|
||||
adtsHeader.no_raw_data_blocks_in_frame = 0;
|
||||
writeAdtsHeader(adtsHeader,_adtsHeader);
|
||||
}
|
||||
|
||||
} /* namespace mediakit */
|
||||
|
@ -51,16 +51,16 @@ namespace mediakit {
|
||||
|
||||
class VideoInfo {
|
||||
public:
|
||||
int iWidth;
|
||||
int iHeight;
|
||||
float iFrameRate;
|
||||
int iWidth;
|
||||
int iHeight;
|
||||
float iFrameRate;
|
||||
};
|
||||
class AudioInfo {
|
||||
public:
|
||||
int iChannel;
|
||||
int iSampleBit;
|
||||
int iSampleRate;
|
||||
int iProfile;
|
||||
int iChannel;
|
||||
int iSampleBit;
|
||||
int iSampleRate;
|
||||
int iProfile;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -68,82 +68,82 @@ public:
|
||||
*/
|
||||
class DevChannel : public MultiMediaSourceMuxer{
|
||||
public:
|
||||
typedef std::shared_ptr<DevChannel> Ptr;
|
||||
typedef std::shared_ptr<DevChannel> Ptr;
|
||||
//fDuration<=0为直播,否则为点播
|
||||
DevChannel(const string &strVhost,
|
||||
const string &strApp,
|
||||
const string &strId,
|
||||
float fDuration = 0,
|
||||
bool bEanbleRtsp = true,
|
||||
bool bEanbleRtmp = true,
|
||||
bool bEanbleRtsp = true,
|
||||
bool bEanbleRtmp = true,
|
||||
bool bEanbleHls = true,
|
||||
bool bEnableMp4 = false);
|
||||
|
||||
virtual ~DevChannel();
|
||||
virtual ~DevChannel();
|
||||
|
||||
/**
|
||||
* 初始化h264视频Track
|
||||
* 相当于MultiMediaSourceMuxer::addTrack(H264Track::Ptr );
|
||||
* @param info
|
||||
*/
|
||||
/**
|
||||
* 初始化h264视频Track
|
||||
* 相当于MultiMediaSourceMuxer::addTrack(H264Track::Ptr );
|
||||
* @param info
|
||||
*/
|
||||
void initVideo(const VideoInfo &info);
|
||||
|
||||
/**
|
||||
* 初始化h265视频Track
|
||||
* @param info
|
||||
*/
|
||||
void initH265Video(const VideoInfo &info);
|
||||
/**
|
||||
* 初始化h265视频Track
|
||||
* @param info
|
||||
*/
|
||||
void initH265Video(const VideoInfo &info);
|
||||
|
||||
/**
|
||||
* 初始化aac音频Track
|
||||
* 相当于MultiMediaSourceMuxer::addTrack(AACTrack::Ptr );
|
||||
* @param info
|
||||
*/
|
||||
void initAudio(const AudioInfo &info);
|
||||
void initAudio(const AudioInfo &info);
|
||||
|
||||
/**
|
||||
* 输入264帧
|
||||
* @param pcData 264单帧数据指针
|
||||
* @param iDataLen 数据指针长度
|
||||
* @param dts 解码时间戳,单位毫秒;等于0时内部会自动生成时间戳
|
||||
* @param pts 播放时间戳,单位毫秒;等于0时内部会赋值为dts
|
||||
*/
|
||||
void inputH264(const char *pcData, int iDataLen, uint32_t dts,uint32_t pts = 0);
|
||||
/**
|
||||
* 输入264帧
|
||||
* @param pcData 264单帧数据指针
|
||||
* @param iDataLen 数据指针长度
|
||||
* @param dts 解码时间戳,单位毫秒;等于0时内部会自动生成时间戳
|
||||
* @param pts 播放时间戳,单位毫秒;等于0时内部会赋值为dts
|
||||
*/
|
||||
void inputH264(const char *pcData, int iDataLen, uint32_t dts,uint32_t pts = 0);
|
||||
|
||||
/**
|
||||
* 输入265帧
|
||||
* @param pcData 265单帧数据指针
|
||||
* @param iDataLen 数据指针长度
|
||||
* @param dts 解码时间戳,单位毫秒;等于0时内部会自动生成时间戳
|
||||
* @param pts 播放时间戳,单位毫秒;等于0时内部会赋值为dts
|
||||
*/
|
||||
void inputH265(const char *pcData, int iDataLen, uint32_t dts,uint32_t pts = 0);
|
||||
/**
|
||||
* 输入265帧
|
||||
* @param pcData 265单帧数据指针
|
||||
* @param iDataLen 数据指针长度
|
||||
* @param dts 解码时间戳,单位毫秒;等于0时内部会自动生成时间戳
|
||||
* @param pts 播放时间戳,单位毫秒;等于0时内部会赋值为dts
|
||||
*/
|
||||
void inputH265(const char *pcData, int iDataLen, uint32_t dts,uint32_t pts = 0);
|
||||
|
||||
/**
|
||||
* 输入可能带adts头的aac帧
|
||||
* @param pcDataWithAdts 可能带adts头的aac帧
|
||||
* @param iDataLen 帧数据长度
|
||||
* @param uiStamp 时间戳,单位毫秒,等于0时内部会自动生成时间戳
|
||||
* @param withAdtsHeader 是否带adts头
|
||||
*/
|
||||
void inputAAC(const char *pcDataWithAdts, int iDataLen, uint32_t uiStamp, bool withAdtsHeader = true);
|
||||
/**
|
||||
* 输入可能带adts头的aac帧
|
||||
* @param pcDataWithAdts 可能带adts头的aac帧
|
||||
* @param iDataLen 帧数据长度
|
||||
* @param uiStamp 时间戳,单位毫秒,等于0时内部会自动生成时间戳
|
||||
* @param withAdtsHeader 是否带adts头
|
||||
*/
|
||||
void inputAAC(const char *pcDataWithAdts, int iDataLen, uint32_t uiStamp, bool withAdtsHeader = true);
|
||||
|
||||
/**
|
||||
* 输入不带adts头的aac帧
|
||||
* @param pcDataWithoutAdts 不带adts头的aac帧
|
||||
* @param iDataLen 帧数据长度
|
||||
* @param uiStamp 时间戳,单位毫秒
|
||||
* @param pcAdtsHeader adts头
|
||||
*/
|
||||
void inputAAC(const char *pcDataWithoutAdts,int iDataLen, uint32_t uiStamp,const char *pcAdtsHeader);
|
||||
/**
|
||||
* 输入不带adts头的aac帧
|
||||
* @param pcDataWithoutAdts 不带adts头的aac帧
|
||||
* @param iDataLen 帧数据长度
|
||||
* @param uiStamp 时间戳,单位毫秒
|
||||
* @param pcAdtsHeader adts头
|
||||
*/
|
||||
void inputAAC(const char *pcDataWithoutAdts,int iDataLen, uint32_t uiStamp,const char *pcAdtsHeader);
|
||||
|
||||
#ifdef ENABLE_X264
|
||||
/**
|
||||
* 输入yuv420p视频帧,内部会完成编码并调用inputH264方法
|
||||
* @param apcYuv
|
||||
* @param aiYuvLen
|
||||
* @param uiStamp
|
||||
*/
|
||||
/**
|
||||
* 输入yuv420p视频帧,内部会完成编码并调用inputH264方法
|
||||
* @param apcYuv
|
||||
* @param aiYuvLen
|
||||
* @param uiStamp
|
||||
*/
|
||||
void inputYUV(char *apcYuv[3], int aiYuvLen[3], uint32_t uiStamp);
|
||||
#endif //ENABLE_X264
|
||||
|
||||
@ -160,11 +160,11 @@ public:
|
||||
|
||||
private:
|
||||
#ifdef ENABLE_X264
|
||||
std::shared_ptr<H264Encoder> _pH264Enc;
|
||||
std::shared_ptr<H264Encoder> _pH264Enc;
|
||||
#endif //ENABLE_X264
|
||||
|
||||
#ifdef ENABLE_FAAC
|
||||
std::shared_ptr<AACEncoder> _pAacEnc;
|
||||
std::shared_ptr<AACEncoder> _pAacEnc;
|
||||
#endif //ENABLE_FAAC
|
||||
std::shared_ptr<VideoInfo> _video;
|
||||
std::shared_ptr<AudioInfo> _audio;
|
||||
|
@ -42,15 +42,15 @@ bool loadIniConfig(const char *ini_path){
|
||||
}else{
|
||||
ini = exePath() + ".ini";
|
||||
}
|
||||
try{
|
||||
try{
|
||||
mINI::Instance().parseFile(ini);
|
||||
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastReloadConfig);
|
||||
return true;
|
||||
}catch (std::exception &ex) {
|
||||
InfoL << "dump ini file to:" << ini;
|
||||
}catch (std::exception &ex) {
|
||||
InfoL << "dump ini file to:" << ini;
|
||||
mINI::Instance().dumpFile(ini);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
////////////广播名称///////////
|
||||
namespace Broadcast {
|
||||
@ -90,12 +90,12 @@ onceToken token([](){
|
||||
mINI::Instance()[kStreamNoneReaderDelayMS] = 20 * 1000;
|
||||
mINI::Instance()[kMaxStreamWaitTimeMS] = 15 * 1000;
|
||||
mINI::Instance()[kEnableVhost] = 0;
|
||||
mINI::Instance()[kUltraLowDelay] = 1;
|
||||
mINI::Instance()[kAddMuteAudio] = 1;
|
||||
mINI::Instance()[kResetWhenRePlay] = 1;
|
||||
mINI::Instance()[kPublishToRtxp] = 1;
|
||||
mINI::Instance()[kPublishToHls] = 1;
|
||||
mINI::Instance()[kPublishToMP4] = 0;
|
||||
mINI::Instance()[kUltraLowDelay] = 1;
|
||||
mINI::Instance()[kAddMuteAudio] = 1;
|
||||
mINI::Instance()[kResetWhenRePlay] = 1;
|
||||
mINI::Instance()[kPublishToRtxp] = 1;
|
||||
mINI::Instance()[kPublishToHls] = 1;
|
||||
mINI::Instance()[kPublishToMP4] = 0;
|
||||
},nullptr);
|
||||
|
||||
}//namespace General
|
||||
@ -117,26 +117,26 @@ const string kRootPath = HTTP_FIELD"rootPath";
|
||||
const string kNotFound = HTTP_FIELD"notFound";
|
||||
|
||||
onceToken token([](){
|
||||
mINI::Instance()[kSendBufSize] = 64 * 1024;
|
||||
mINI::Instance()[kMaxReqSize] = 4*1024;
|
||||
mINI::Instance()[kKeepAliveSecond] = 15;
|
||||
mINI::Instance()[kSendBufSize] = 64 * 1024;
|
||||
mINI::Instance()[kMaxReqSize] = 4*1024;
|
||||
mINI::Instance()[kKeepAliveSecond] = 15;
|
||||
#if defined(_WIN32)
|
||||
mINI::Instance()[kCharSet] = "gb2312";
|
||||
mINI::Instance()[kCharSet] = "gb2312";
|
||||
#else
|
||||
mINI::Instance()[kCharSet] ="utf-8";
|
||||
mINI::Instance()[kCharSet] ="utf-8";
|
||||
#endif
|
||||
|
||||
mINI::Instance()[kRootPath] = "./www";
|
||||
mINI::Instance()[kNotFound] =
|
||||
"<html>"
|
||||
"<head><title>404 Not Found</title></head>"
|
||||
"<body bgcolor=\"white\">"
|
||||
"<center><h1>您访问的资源不存在!</h1></center>"
|
||||
"<hr><center>"
|
||||
SERVER_NAME
|
||||
"</center>"
|
||||
"</body>"
|
||||
"</html>";
|
||||
mINI::Instance()[kRootPath] = "./www";
|
||||
mINI::Instance()[kNotFound] =
|
||||
"<html>"
|
||||
"<head><title>404 Not Found</title></head>"
|
||||
"<body bgcolor=\"white\">"
|
||||
"<center><h1>您访问的资源不存在!</h1></center>"
|
||||
"<hr><center>"
|
||||
SERVER_NAME
|
||||
"</center>"
|
||||
"</body>"
|
||||
"</html>";
|
||||
},nullptr);
|
||||
|
||||
}//namespace Http
|
||||
@ -147,7 +147,7 @@ namespace Shell {
|
||||
const string kMaxReqSize = SHELL_FIELD"maxReqSize";
|
||||
|
||||
onceToken token([](){
|
||||
mINI::Instance()[kMaxReqSize] = 1024;
|
||||
mINI::Instance()[kMaxReqSize] = 1024;
|
||||
},nullptr);
|
||||
} //namespace Shell
|
||||
|
||||
@ -160,11 +160,11 @@ const string kKeepAliveSecond = RTSP_FIELD"keepAliveSecond";
|
||||
const string kDirectProxy = RTSP_FIELD"directProxy";
|
||||
|
||||
onceToken token([](){
|
||||
//默认Md5方式认证
|
||||
mINI::Instance()[kAuthBasic] = 0;
|
||||
//默认Md5方式认证
|
||||
mINI::Instance()[kAuthBasic] = 0;
|
||||
mINI::Instance()[kHandshakeSecond] = 15;
|
||||
mINI::Instance()[kKeepAliveSecond] = 15;
|
||||
mINI::Instance()[kDirectProxy] = 1;
|
||||
mINI::Instance()[kDirectProxy] = 1;
|
||||
},nullptr);
|
||||
} //namespace Rtsp
|
||||
|
||||
@ -176,7 +176,7 @@ const string kHandshakeSecond = RTMP_FIELD"handshakeSecond";
|
||||
const string kKeepAliveSecond = RTMP_FIELD"keepAliveSecond";
|
||||
|
||||
onceToken token([](){
|
||||
mINI::Instance()[kModifyStamp] = false;
|
||||
mINI::Instance()[kModifyStamp] = false;
|
||||
mINI::Instance()[kHandshakeSecond] = 15;
|
||||
mINI::Instance()[kKeepAliveSecond] = 15;
|
||||
},nullptr);
|
||||
@ -197,11 +197,11 @@ const string kClearCount = RTP_FIELD"clearCount";
|
||||
const string kCycleMS = RTP_FIELD"cycleMS";
|
||||
|
||||
onceToken token([](){
|
||||
mINI::Instance()[kVideoMtuSize] = 1400;
|
||||
mINI::Instance()[kAudioMtuSize] = 600;
|
||||
mINI::Instance()[kMaxRtpCount] = 50;
|
||||
mINI::Instance()[kClearCount] = 10;
|
||||
mINI::Instance()[kCycleMS] = 13*60*60*1000;
|
||||
mINI::Instance()[kVideoMtuSize] = 1400;
|
||||
mINI::Instance()[kAudioMtuSize] = 600;
|
||||
mINI::Instance()[kMaxRtpCount] = 50;
|
||||
mINI::Instance()[kClearCount] = 10;
|
||||
mINI::Instance()[kCycleMS] = 13*60*60*1000;
|
||||
},nullptr);
|
||||
} //namespace Rtsp
|
||||
|
||||
@ -216,9 +216,9 @@ const string kAddrMax = MULTI_FIELD"addrMax";
|
||||
const string kUdpTTL = MULTI_FIELD"udpTTL";
|
||||
|
||||
onceToken token([](){
|
||||
mINI::Instance()[kAddrMin] = "239.0.0.0";
|
||||
mINI::Instance()[kAddrMax] = "239.255.255.255";
|
||||
mINI::Instance()[kUdpTTL] = 64;
|
||||
mINI::Instance()[kAddrMin] = "239.0.0.0";
|
||||
mINI::Instance()[kAddrMax] = "239.255.255.255";
|
||||
mINI::Instance()[kUdpTTL] = 64;
|
||||
},nullptr);
|
||||
} //namespace MultiCast
|
||||
|
||||
@ -241,13 +241,13 @@ const string kFastStart = RECORD_FIELD"fastStart";
|
||||
const string kFileRepeat = RECORD_FIELD"fileRepeat";
|
||||
|
||||
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()[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;
|
||||
},nullptr);
|
||||
} //namespace Record
|
||||
|
||||
@ -266,11 +266,11 @@ const string kFileBufSize = HLS_FIELD"fileBufSize";
|
||||
const string kFilePath = HLS_FIELD"filePath";
|
||||
|
||||
onceToken token([](){
|
||||
mINI::Instance()[kSegmentDuration] = 2;
|
||||
mINI::Instance()[kSegmentNum] = 3;
|
||||
mINI::Instance()[kSegmentRetain] = 5;
|
||||
mINI::Instance()[kFileBufSize] = 64 * 1024;
|
||||
mINI::Instance()[kFilePath] = "./www";
|
||||
mINI::Instance()[kSegmentDuration] = 2;
|
||||
mINI::Instance()[kSegmentNum] = 3;
|
||||
mINI::Instance()[kSegmentRetain] = 5;
|
||||
mINI::Instance()[kFileBufSize] = 64 * 1024;
|
||||
mINI::Instance()[kFilePath] = "./www";
|
||||
},nullptr);
|
||||
} //namespace Hls
|
||||
|
||||
@ -286,9 +286,9 @@ const string kCheckSource = RTP_PROXY_FIELD"checkSource";
|
||||
const string kTimeoutSec = RTP_PROXY_FIELD"timeoutSec";
|
||||
|
||||
onceToken token([](){
|
||||
mINI::Instance()[kDumpDir] = "";
|
||||
mINI::Instance()[kCheckSource] = 1;
|
||||
mINI::Instance()[kTimeoutSec] = 15;
|
||||
mINI::Instance()[kDumpDir] = "";
|
||||
mINI::Instance()[kCheckSource] = 1;
|
||||
mINI::Instance()[kTimeoutSec] = 15;
|
||||
},nullptr);
|
||||
} //namespace RtpProxy
|
||||
|
||||
|
@ -88,24 +88,24 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) {
|
||||
* Type==7:SPS frame
|
||||
* Type==8:PPS frame
|
||||
*/
|
||||
/*
|
||||
RTF3984 5.2节 Common Structure of the RTP Payload Format
|
||||
/*
|
||||
RTF3984 5.2节 Common Structure of the RTP Payload Format
|
||||
Table 1. Summary of NAL unit types and their payload structures
|
||||
|
||||
Type Packet Type name Section
|
||||
---------------------------------------------------------
|
||||
0 undefined -
|
||||
1-23 NAL unit Single NAL unit packet per H.264 5.6
|
||||
24 STAP-A Single-time aggregation packet 5.7.1
|
||||
25 STAP-B Single-time aggregation packet 5.7.1
|
||||
26 MTAP16 Multi-time aggregation packet 5.7.2
|
||||
27 MTAP24 Multi-time aggregation packet 5.7.2
|
||||
28 FU-A Fragmentation unit 5.8
|
||||
29 FU-B Fragmentation unit 5.8
|
||||
30-31 undefined -
|
||||
Type Packet Type name Section
|
||||
---------------------------------------------------------
|
||||
0 undefined -
|
||||
1-23 NAL unit Single NAL unit packet per H.264 5.6
|
||||
24 STAP-A Single-time aggregation packet 5.7.1
|
||||
25 STAP-B Single-time aggregation packet 5.7.1
|
||||
26 MTAP16 Multi-time aggregation packet 5.7.2
|
||||
27 MTAP24 Multi-time aggregation packet 5.7.2
|
||||
28 FU-A Fragmentation unit 5.8
|
||||
29 FU-B Fragmentation unit 5.8
|
||||
30-31 undefined -
|
||||
|
||||
|
||||
*/
|
||||
*/
|
||||
const uint8_t *frame = (uint8_t *) rtppack->data() + rtppack->offset;
|
||||
int length = rtppack->size() - rtppack->offset;
|
||||
NALU nal;
|
||||
|
@ -34,36 +34,36 @@ bool getHEVCInfo(const char * vps, int vps_len,const char * sps,int sps_len,int
|
||||
T_GetBitContext tGetBitBuf;
|
||||
T_HEVCSPS tH265SpsInfo;
|
||||
T_HEVCVPS tH265VpsInfo;
|
||||
if ( vps_len > 2 ){
|
||||
memset(&tGetBitBuf,0,sizeof(tGetBitBuf));
|
||||
memset(&tH265VpsInfo,0,sizeof(tH265VpsInfo));
|
||||
tGetBitBuf.pu8Buf = (uint8_t*)vps+2;
|
||||
tGetBitBuf.iBufSize = vps_len-2;
|
||||
if(0 != h265DecVideoParameterSet((void *) &tGetBitBuf, &tH265VpsInfo)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if ( vps_len > 2 ){
|
||||
memset(&tGetBitBuf,0,sizeof(tGetBitBuf));
|
||||
memset(&tH265VpsInfo,0,sizeof(tH265VpsInfo));
|
||||
tGetBitBuf.pu8Buf = (uint8_t*)vps+2;
|
||||
tGetBitBuf.iBufSize = vps_len-2;
|
||||
if(0 != h265DecVideoParameterSet((void *) &tGetBitBuf, &tH265VpsInfo)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( sps_len > 2 ){
|
||||
memset(&tGetBitBuf,0,sizeof(tGetBitBuf));
|
||||
memset(&tH265SpsInfo,0,sizeof(tH265SpsInfo));
|
||||
tGetBitBuf.pu8Buf = (uint8_t*)sps+2;
|
||||
tGetBitBuf.iBufSize = sps_len-2;
|
||||
if(0 != h265DecSeqParameterSet((void *) &tGetBitBuf, &tH265SpsInfo)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
return false;
|
||||
if ( sps_len > 2 ){
|
||||
memset(&tGetBitBuf,0,sizeof(tGetBitBuf));
|
||||
memset(&tH265SpsInfo,0,sizeof(tH265SpsInfo));
|
||||
tGetBitBuf.pu8Buf = (uint8_t*)sps+2;
|
||||
tGetBitBuf.iBufSize = sps_len-2;
|
||||
if(0 != h265DecSeqParameterSet((void *) &tGetBitBuf, &tH265SpsInfo)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
return false;
|
||||
h265GetWidthHeight(&tH265SpsInfo, &iVideoWidth, &iVideoHeight);
|
||||
iVideoFps = 0;
|
||||
iVideoFps = 0;
|
||||
h265GeFramerate(&tH265VpsInfo, &tH265SpsInfo, &iVideoFps);
|
||||
// ErrorL << iVideoWidth << " " << iVideoHeight << " " << iVideoFps;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getHEVCInfo(const string &strVps, const string &strSps, int &iVideoWidth, int &iVideoHeight, float &iVideoFps) {
|
||||
return getHEVCInfo(strVps.data(),strVps.size(),strSps.data(),strSps.size(),iVideoWidth,iVideoHeight,iVideoFps);
|
||||
return getHEVCInfo(strVps.data(),strVps.size(),strSps.data(),strSps.size(),iVideoWidth,iVideoHeight,iVideoFps);
|
||||
}
|
||||
|
||||
Sdp::Ptr H265Track::getSdp() {
|
||||
|
@ -202,7 +202,7 @@ public:
|
||||
_vps = vps.substr(vps_prefix_len);
|
||||
_sps = sps.substr(sps_prefix_len);
|
||||
_pps = pps.substr(pps_prefix_len);
|
||||
onReady();
|
||||
onReady();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -267,10 +267,10 @@ public:
|
||||
* @param frame 数据帧
|
||||
*/
|
||||
void inputFrame(const Frame::Ptr &frame) override{
|
||||
int type = H265_TYPE(*((uint8_t *)frame->data() + frame->prefixSize()));
|
||||
int type = H265_TYPE(*((uint8_t *)frame->data() + frame->prefixSize()));
|
||||
if(type == H265Frame::NAL_VPS){
|
||||
bool first_frame = true;
|
||||
splitH264(frame->data() + frame->prefixSize(),
|
||||
bool first_frame = true;
|
||||
splitH264(frame->data() + frame->prefixSize(),
|
||||
frame->size() - frame->prefixSize(),
|
||||
[&](const char *ptr, int len){
|
||||
if(first_frame){
|
||||
@ -288,9 +288,9 @@ public:
|
||||
inputFrame_l(sub_frame);
|
||||
}
|
||||
});
|
||||
}else{
|
||||
inputFrame_l(frame);
|
||||
}
|
||||
}else{
|
||||
inputFrame_l(frame);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
@ -336,7 +336,7 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* 解析sps获取宽高fps
|
||||
*/
|
||||
void onReady(){
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -434,7 +434,7 @@ typedef struct T_HEVCSPS {
|
||||
|
||||
int iQpBdOffset;
|
||||
|
||||
int iVuiPresent;
|
||||
int iVuiPresent;
|
||||
}T_HEVCSPS;
|
||||
|
||||
|
||||
|
@ -137,10 +137,10 @@ public:
|
||||
virtual ~TrackSource(){}
|
||||
|
||||
/**
|
||||
* 获取全部的Track
|
||||
* @param trackReady 是否获取全部已经准备好的Track
|
||||
* @return
|
||||
*/
|
||||
* 获取全部的Track
|
||||
* @param trackReady 是否获取全部已经准备好的Track
|
||||
* @return
|
||||
*/
|
||||
virtual vector<Track::Ptr> getTracks(bool trackReady = true) const = 0;
|
||||
|
||||
/**
|
||||
|
@ -148,7 +148,7 @@ protected:
|
||||
* 接收http回复完毕,
|
||||
*/
|
||||
virtual void onResponseCompleted(){
|
||||
DebugL;
|
||||
DebugL;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -29,11 +29,11 @@
|
||||
namespace mediakit {
|
||||
|
||||
void HttpClientImp::onConnect(const SockException &ex) {
|
||||
if(!_isHttps){
|
||||
HttpClient::onConnect(ex);
|
||||
} else {
|
||||
TcpClientWithSSL<HttpClient>::onConnect(ex);
|
||||
}
|
||||
if(!_isHttps){
|
||||
HttpClient::onConnect(ex);
|
||||
} else {
|
||||
TcpClientWithSSL<HttpClient>::onConnect(ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -36,11 +36,11 @@ namespace mediakit {
|
||||
|
||||
class HttpClientImp: public TcpClientWithSSL<HttpClient> {
|
||||
public:
|
||||
typedef std::shared_ptr<HttpClientImp> Ptr;
|
||||
HttpClientImp() {}
|
||||
virtual ~HttpClientImp() {}
|
||||
typedef std::shared_ptr<HttpClientImp> Ptr;
|
||||
HttpClientImp() {}
|
||||
virtual ~HttpClientImp() {}
|
||||
protected:
|
||||
void onConnect(const SockException &ex) override ;
|
||||
void onConnect(const SockException &ex) override ;
|
||||
};
|
||||
|
||||
} /* namespace mediakit */
|
||||
|
@ -36,75 +36,75 @@ HttpDownloader::HttpDownloader() {
|
||||
}
|
||||
|
||||
HttpDownloader::~HttpDownloader() {
|
||||
closeFile();
|
||||
closeFile();
|
||||
}
|
||||
|
||||
void HttpDownloader::startDownload(const string& url, const string& filePath,bool bAppend,float timeOutSecond) {
|
||||
_filePath = filePath;
|
||||
if(_filePath.empty()){
|
||||
_filePath = exeDir() + "HttpDownloader/" + MD5(url).hexdigest();
|
||||
}
|
||||
_saveFile = File::createfile_file(_filePath.data(),bAppend ? "ab" : "wb");
|
||||
if(!_saveFile){
|
||||
auto strErr = StrPrinter << "打开文件失败:" << filePath << endl;
|
||||
throw std::runtime_error(strErr);
|
||||
}
|
||||
_bDownloadSuccess = false;
|
||||
if(bAppend){
|
||||
auto currentLen = ftell(_saveFile);
|
||||
if(currentLen){
|
||||
//最少续传一个字节,怕遇到http 416的错误
|
||||
currentLen -= 1;
|
||||
fseek(_saveFile,-1,SEEK_CUR);
|
||||
}
|
||||
addHeader("Range", StrPrinter << "bytes=" << currentLen << "-" << endl);
|
||||
}
|
||||
setMethod("GET");
|
||||
sendRequest(url,timeOutSecond);
|
||||
_filePath = filePath;
|
||||
if(_filePath.empty()){
|
||||
_filePath = exeDir() + "HttpDownloader/" + MD5(url).hexdigest();
|
||||
}
|
||||
_saveFile = File::createfile_file(_filePath.data(),bAppend ? "ab" : "wb");
|
||||
if(!_saveFile){
|
||||
auto strErr = StrPrinter << "打开文件失败:" << filePath << endl;
|
||||
throw std::runtime_error(strErr);
|
||||
}
|
||||
_bDownloadSuccess = false;
|
||||
if(bAppend){
|
||||
auto currentLen = ftell(_saveFile);
|
||||
if(currentLen){
|
||||
//最少续传一个字节,怕遇到http 416的错误
|
||||
currentLen -= 1;
|
||||
fseek(_saveFile,-1,SEEK_CUR);
|
||||
}
|
||||
addHeader("Range", StrPrinter << "bytes=" << currentLen << "-" << endl);
|
||||
}
|
||||
setMethod("GET");
|
||||
sendRequest(url,timeOutSecond);
|
||||
}
|
||||
|
||||
int64_t HttpDownloader::onResponseHeader(const string& status,const HttpHeader& headers) {
|
||||
if(status != "200" && status != "206"){
|
||||
//失败
|
||||
shutdown(SockException(Err_shutdown,StrPrinter << "Http Status:" << status));
|
||||
}
|
||||
//后续全部是content
|
||||
return -1;
|
||||
//失败
|
||||
shutdown(SockException(Err_shutdown,StrPrinter << "Http Status:" << status));
|
||||
}
|
||||
//后续全部是content
|
||||
return -1;
|
||||
}
|
||||
|
||||
void HttpDownloader::onResponseBody(const char* buf, int64_t size, int64_t recvedSize, int64_t totalSize) {
|
||||
if(_saveFile){
|
||||
fwrite(buf,size,1,_saveFile);
|
||||
}
|
||||
fwrite(buf,size,1,_saveFile);
|
||||
}
|
||||
}
|
||||
|
||||
void HttpDownloader::onResponseCompleted() {
|
||||
closeFile();
|
||||
//InfoL << "md5Sum:" << getMd5Sum(_filePath);
|
||||
_bDownloadSuccess = true;
|
||||
if(_onResult){
|
||||
_onResult(Err_success,"success",_filePath);
|
||||
_onResult = nullptr;
|
||||
}
|
||||
closeFile();
|
||||
//InfoL << "md5Sum:" << getMd5Sum(_filePath);
|
||||
_bDownloadSuccess = true;
|
||||
if(_onResult){
|
||||
_onResult(Err_success,"success",_filePath);
|
||||
_onResult = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void HttpDownloader::onDisconnect(const SockException &ex) {
|
||||
closeFile();
|
||||
if(!_bDownloadSuccess){
|
||||
File::delete_file(_filePath.data());
|
||||
}
|
||||
if(_onResult){
|
||||
_onResult(ex.getErrCode(),ex.what(),_filePath);
|
||||
_onResult = nullptr;
|
||||
}
|
||||
closeFile();
|
||||
if(!_bDownloadSuccess){
|
||||
File::delete_file(_filePath.data());
|
||||
}
|
||||
if(_onResult){
|
||||
_onResult(ex.getErrCode(),ex.what(),_filePath);
|
||||
_onResult = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void HttpDownloader::closeFile() {
|
||||
if(_saveFile){
|
||||
fflush(_saveFile);
|
||||
fclose(_saveFile);
|
||||
_saveFile = nullptr;
|
||||
}
|
||||
if(_saveFile){
|
||||
fflush(_saveFile);
|
||||
fclose(_saveFile);
|
||||
_saveFile = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -33,30 +33,30 @@ namespace mediakit {
|
||||
|
||||
class HttpDownloader: public HttpClientImp {
|
||||
public:
|
||||
typedef std::shared_ptr<HttpDownloader> Ptr;
|
||||
typedef std::function<void(ErrCode code,const string &errMsg,const string &filePath)> onDownloadResult;
|
||||
HttpDownloader();
|
||||
virtual ~HttpDownloader();
|
||||
//开始下载文件,默认断点续传方式下载
|
||||
void startDownload(const string &url,const string &filePath = "",bool bAppend = false, float timeOutSecond = 10 );
|
||||
void startDownload(const string &url,const onDownloadResult &cb,float timeOutSecond = 10){
|
||||
setOnResult(cb);
|
||||
startDownload(url,"",false,timeOutSecond);
|
||||
}
|
||||
void setOnResult(const onDownloadResult &cb){
|
||||
_onResult = cb;
|
||||
}
|
||||
typedef std::shared_ptr<HttpDownloader> Ptr;
|
||||
typedef std::function<void(ErrCode code,const string &errMsg,const string &filePath)> onDownloadResult;
|
||||
HttpDownloader();
|
||||
virtual ~HttpDownloader();
|
||||
//开始下载文件,默认断点续传方式下载
|
||||
void startDownload(const string &url,const string &filePath = "",bool bAppend = false, float timeOutSecond = 10 );
|
||||
void startDownload(const string &url,const onDownloadResult &cb,float timeOutSecond = 10){
|
||||
setOnResult(cb);
|
||||
startDownload(url,"",false,timeOutSecond);
|
||||
}
|
||||
void setOnResult(const onDownloadResult &cb){
|
||||
_onResult = cb;
|
||||
}
|
||||
private:
|
||||
int64_t onResponseHeader(const string &status,const HttpHeader &headers) override;
|
||||
void onResponseBody(const char *buf,int64_t size,int64_t recvedSize,int64_t totalSize) override;
|
||||
void onResponseCompleted() override;
|
||||
void onDisconnect(const SockException &ex) override;
|
||||
int64_t onResponseHeader(const string &status,const HttpHeader &headers) override;
|
||||
void onResponseBody(const char *buf,int64_t size,int64_t recvedSize,int64_t totalSize) override;
|
||||
void onResponseCompleted() override;
|
||||
void onDisconnect(const SockException &ex) override;
|
||||
void closeFile();
|
||||
private:
|
||||
FILE *_saveFile = nullptr;
|
||||
string _filePath;
|
||||
onDownloadResult _onResult;
|
||||
bool _bDownloadSuccess = false;
|
||||
FILE *_saveFile = nullptr;
|
||||
string _filePath;
|
||||
onDownloadResult _onResult;
|
||||
bool _bDownloadSuccess = false;
|
||||
};
|
||||
|
||||
} /* namespace mediakit */
|
||||
|
@ -45,8 +45,8 @@ HttpSession::HttpSession(const Socket::Ptr &pSock) : TcpSession(pSock) {
|
||||
TraceP(this);
|
||||
GET_CONFIG(uint32_t,keep_alive_sec,Http::kKeepAliveSecond);
|
||||
pSock->setSendTimeOutSecond(keep_alive_sec);
|
||||
//起始接收buffer缓存设置为4K,节省内存
|
||||
pSock->setReadBuffer(std::make_shared<BufferRaw>(4 * 1024));
|
||||
//起始接收buffer缓存设置为4K,节省内存
|
||||
pSock->setReadBuffer(std::make_shared<BufferRaw>(4 * 1024));
|
||||
}
|
||||
|
||||
HttpSession::~HttpSession() {
|
||||
@ -61,48 +61,48 @@ void HttpSession::Handle_Req_HEAD(int64_t &content_len){
|
||||
}
|
||||
|
||||
int64_t HttpSession::onRecvHeader(const char *header,uint64_t len) {
|
||||
typedef void (HttpSession::*HttpCMDHandle)(int64_t &);
|
||||
static unordered_map<string, HttpCMDHandle> s_func_map;
|
||||
static onceToken token([]() {
|
||||
s_func_map.emplace("GET",&HttpSession::Handle_Req_GET);
|
||||
s_func_map.emplace("POST",&HttpSession::Handle_Req_POST);
|
||||
typedef void (HttpSession::*HttpCMDHandle)(int64_t &);
|
||||
static unordered_map<string, HttpCMDHandle> s_func_map;
|
||||
static onceToken token([]() {
|
||||
s_func_map.emplace("GET",&HttpSession::Handle_Req_GET);
|
||||
s_func_map.emplace("POST",&HttpSession::Handle_Req_POST);
|
||||
s_func_map.emplace("HEAD",&HttpSession::Handle_Req_HEAD);
|
||||
}, nullptr);
|
||||
|
||||
_parser.Parse(header);
|
||||
urlDecode(_parser);
|
||||
string cmd = _parser.Method();
|
||||
auto it = s_func_map.find(cmd);
|
||||
if (it == s_func_map.end()) {
|
||||
WarnL << "不支持该命令:" << cmd;
|
||||
sendResponse("405 Not Allowed", true);
|
||||
_parser.Parse(header);
|
||||
urlDecode(_parser);
|
||||
string cmd = _parser.Method();
|
||||
auto it = s_func_map.find(cmd);
|
||||
if (it == s_func_map.end()) {
|
||||
WarnL << "不支持该命令:" << cmd;
|
||||
sendResponse("405 Not Allowed", true);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
//跨域
|
||||
_origin = _parser["Origin"];
|
||||
|
||||
//默认后面数据不是content而是header
|
||||
int64_t content_len = 0;
|
||||
auto &fun = it->second;
|
||||
int64_t content_len = 0;
|
||||
auto &fun = it->second;
|
||||
try {
|
||||
(this->*fun)(content_len);
|
||||
}catch (exception &ex){
|
||||
shutdown(SockException(Err_shutdown,ex.what()));
|
||||
}
|
||||
|
||||
//清空解析器节省内存
|
||||
_parser.Clear();
|
||||
//返回content长度
|
||||
return content_len;
|
||||
//清空解析器节省内存
|
||||
_parser.Clear();
|
||||
//返回content长度
|
||||
return content_len;
|
||||
}
|
||||
|
||||
void HttpSession::onRecvContent(const char *data,uint64_t len) {
|
||||
if(_contentCallBack){
|
||||
if(!_contentCallBack(data,len)){
|
||||
_contentCallBack = nullptr;
|
||||
}
|
||||
}
|
||||
if(_contentCallBack){
|
||||
if(!_contentCallBack(data,len)){
|
||||
_contentCallBack = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HttpSession::onRecv(const Buffer::Ptr &pBuf) {
|
||||
@ -140,25 +140,25 @@ void HttpSession::onManager() {
|
||||
GET_CONFIG(uint32_t,keepAliveSec,Http::kKeepAliveSecond);
|
||||
|
||||
if(_ticker.elapsedTime() > keepAliveSec * 1000){
|
||||
//1分钟超时
|
||||
shutdown(SockException(Err_timeout,"session timeouted"));
|
||||
}
|
||||
//1分钟超时
|
||||
shutdown(SockException(Err_timeout,"session timeouted"));
|
||||
}
|
||||
}
|
||||
|
||||
bool HttpSession::checkWebSocket(){
|
||||
auto Sec_WebSocket_Key = _parser["Sec-WebSocket-Key"];
|
||||
if(Sec_WebSocket_Key.empty()){
|
||||
return false;
|
||||
}
|
||||
auto Sec_WebSocket_Accept = encodeBase64(SHA1::encode_bin(Sec_WebSocket_Key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));
|
||||
auto Sec_WebSocket_Key = _parser["Sec-WebSocket-Key"];
|
||||
if(Sec_WebSocket_Key.empty()){
|
||||
return false;
|
||||
}
|
||||
auto Sec_WebSocket_Accept = encodeBase64(SHA1::encode_bin(Sec_WebSocket_Key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));
|
||||
|
||||
KeyValue headerOut;
|
||||
headerOut["Upgrade"] = "websocket";
|
||||
headerOut["Connection"] = "Upgrade";
|
||||
headerOut["Sec-WebSocket-Accept"] = Sec_WebSocket_Accept;
|
||||
if(!_parser["Sec-WebSocket-Protocol"].empty()){
|
||||
headerOut["Sec-WebSocket-Protocol"] = _parser["Sec-WebSocket-Protocol"];
|
||||
}
|
||||
KeyValue headerOut;
|
||||
headerOut["Upgrade"] = "websocket";
|
||||
headerOut["Connection"] = "Upgrade";
|
||||
headerOut["Sec-WebSocket-Accept"] = Sec_WebSocket_Accept;
|
||||
if(!_parser["Sec-WebSocket-Protocol"].empty()){
|
||||
headerOut["Sec-WebSocket-Protocol"] = _parser["Sec-WebSocket-Protocol"];
|
||||
}
|
||||
|
||||
auto res_cb = [this,headerOut](){
|
||||
_flv_over_websocket = true;
|
||||
@ -177,28 +177,28 @@ bool HttpSession::checkWebSocket(){
|
||||
return true;
|
||||
}
|
||||
sendResponse("101 Switching Protocols",false, nullptr,headerOut);
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
//http-flv 链接格式:http://vhost-url:port/app/streamid.flv?key1=value1&key2=value2
|
||||
//如果url(除去?以及后面的参数)后缀是.flv,那么表明该url是一个http-flv直播。
|
||||
bool HttpSession::checkLiveFlvStream(const function<void()> &cb){
|
||||
auto pos = strrchr(_parser.Url().data(),'.');
|
||||
if(!pos){
|
||||
//未找到".flv"后缀
|
||||
return false;
|
||||
}
|
||||
if(strcasecmp(pos,".flv") != 0){
|
||||
//未找到".flv"后缀
|
||||
return false;
|
||||
}
|
||||
|
||||
//这是个.flv的流
|
||||
_mediaInfo.parse(string(RTMP_SCHEMA) + "://" + _parser["Host"] + _parser.FullUrl());
|
||||
if(_mediaInfo._app.empty() || _mediaInfo._streamid.size() < 5){
|
||||
//url不合法
|
||||
auto pos = strrchr(_parser.Url().data(),'.');
|
||||
if(!pos){
|
||||
//未找到".flv"后缀
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(strcasecmp(pos,".flv") != 0){
|
||||
//未找到".flv"后缀
|
||||
return false;
|
||||
}
|
||||
|
||||
//这是个.flv的流
|
||||
_mediaInfo.parse(string(RTMP_SCHEMA) + "://" + _parser["Host"] + _parser.FullUrl());
|
||||
if(_mediaInfo._app.empty() || _mediaInfo._streamid.size() < 5){
|
||||
//url不合法
|
||||
return false;
|
||||
}
|
||||
_mediaInfo._streamid.erase(_mediaInfo._streamid.size() - 4);//去除.flv后缀
|
||||
bool bClose = !strcasecmp(_parser["Connection"].data(),"close");
|
||||
|
||||
@ -270,21 +270,21 @@ void HttpSession::Handle_Req_GET(int64_t &content_len) {
|
||||
}
|
||||
|
||||
void HttpSession::Handle_Req_GET_l(int64_t &content_len, bool sendBody) {
|
||||
//先看看是否为WebSocket请求
|
||||
if(checkWebSocket()){
|
||||
content_len = -1;
|
||||
_contentCallBack = [this](const char *data,uint64_t len){
|
||||
//先看看是否为WebSocket请求
|
||||
if(checkWebSocket()){
|
||||
content_len = -1;
|
||||
_contentCallBack = [this](const char *data,uint64_t len){
|
||||
WebSocketSplitter::decode((uint8_t *)data,len);
|
||||
//_contentCallBack是可持续的,后面还要处理后续数据
|
||||
return true;
|
||||
};
|
||||
return;
|
||||
}
|
||||
//_contentCallBack是可持续的,后面还要处理后续数据
|
||||
return true;
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
if(emitHttpEvent(false)){
|
||||
if(emitHttpEvent(false)){
|
||||
//拦截http api事件
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(checkLiveFlvStream()){
|
||||
//拦截http-flv播放器
|
||||
@ -498,111 +498,111 @@ void HttpSession::sendResponse(const char *pcStatus,
|
||||
}
|
||||
|
||||
string HttpSession::urlDecode(const string &str){
|
||||
auto ret = strCoding::UrlDecode(str);
|
||||
auto ret = strCoding::UrlDecode(str);
|
||||
#ifdef _WIN32
|
||||
GET_CONFIG(string,charSet,Http::kCharSet);
|
||||
bool isGb2312 = !strcasecmp(charSet.data(), "gb2312");
|
||||
if (isGb2312) {
|
||||
ret = strCoding::UTF8ToGB2312(ret);
|
||||
}
|
||||
bool isGb2312 = !strcasecmp(charSet.data(), "gb2312");
|
||||
if (isGb2312) {
|
||||
ret = strCoding::UTF8ToGB2312(ret);
|
||||
}
|
||||
#endif // _WIN32
|
||||
return ret;
|
||||
}
|
||||
|
||||
void HttpSession::urlDecode(Parser &parser){
|
||||
parser.setUrl(urlDecode(parser.Url()));
|
||||
for(auto &pr : _parser.getUrlArgs()){
|
||||
const_cast<string &>(pr.second) = urlDecode(pr.second);
|
||||
}
|
||||
parser.setUrl(urlDecode(parser.Url()));
|
||||
for(auto &pr : _parser.getUrlArgs()){
|
||||
const_cast<string &>(pr.second) = urlDecode(pr.second);
|
||||
}
|
||||
}
|
||||
|
||||
bool HttpSession::emitHttpEvent(bool doInvoke){
|
||||
bool bClose = !strcasecmp(_parser["Connection"].data(),"close");
|
||||
/////////////////////异步回复Invoker///////////////////////////////
|
||||
weak_ptr<HttpSession> weakSelf = dynamic_pointer_cast<HttpSession>(shared_from_this());
|
||||
HttpResponseInvoker invoker = [weakSelf,bClose](const string &codeOut, const KeyValue &headerOut, const HttpBody::Ptr &body){
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return;
|
||||
}
|
||||
strongSelf->async([weakSelf,bClose,codeOut,headerOut,body]() {
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
/////////////////////异步回复Invoker///////////////////////////////
|
||||
weak_ptr<HttpSession> weakSelf = dynamic_pointer_cast<HttpSession>(shared_from_this());
|
||||
HttpResponseInvoker invoker = [weakSelf,bClose](const string &codeOut, const KeyValue &headerOut, const HttpBody::Ptr &body){
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return;
|
||||
}
|
||||
strongSelf->async([weakSelf,bClose,codeOut,headerOut,body]() {
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
//本对象已经销毁
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
strongSelf->sendResponse(codeOut.data(), bClose, nullptr, headerOut, body);
|
||||
});
|
||||
};
|
||||
///////////////////广播HTTP事件///////////////////////////
|
||||
bool consumed = false;//该事件是否被消费
|
||||
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastHttpRequest,_parser,invoker,consumed,*this);
|
||||
if(!consumed && doInvoke){
|
||||
//该事件无人消费,所以返回404
|
||||
invoker("404 Not Found",KeyValue(), HttpBody::Ptr());
|
||||
}
|
||||
return consumed;
|
||||
});
|
||||
};
|
||||
///////////////////广播HTTP事件///////////////////////////
|
||||
bool consumed = false;//该事件是否被消费
|
||||
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastHttpRequest,_parser,invoker,consumed,*this);
|
||||
if(!consumed && doInvoke){
|
||||
//该事件无人消费,所以返回404
|
||||
invoker("404 Not Found",KeyValue(), HttpBody::Ptr());
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void HttpSession::Handle_Req_POST(int64_t &content_len) {
|
||||
GET_CONFIG(uint64_t,maxReqSize,Http::kMaxReqSize);
|
||||
GET_CONFIG(uint64_t,maxReqSize,Http::kMaxReqSize);
|
||||
|
||||
int64_t totalContentLen = _parser["Content-Length"].empty() ? -1 : atoll(_parser["Content-Length"].data());
|
||||
|
||||
if(totalContentLen == 0){
|
||||
//content为空
|
||||
//emitHttpEvent内部会选择是否关闭连接
|
||||
emitHttpEvent(true);
|
||||
return;
|
||||
}
|
||||
if(totalContentLen == 0){
|
||||
//content为空
|
||||
//emitHttpEvent内部会选择是否关闭连接
|
||||
emitHttpEvent(true);
|
||||
return;
|
||||
}
|
||||
|
||||
//根据Content-Length设置接收缓存大小
|
||||
if(totalContentLen > 0){
|
||||
_sock->setReadBuffer(std::make_shared<BufferRaw>(MIN(totalContentLen + 1,256 * 1024)));
|
||||
}else{
|
||||
//不定长度的Content-Length
|
||||
//不定长度的Content-Length
|
||||
_sock->setReadBuffer(std::make_shared<BufferRaw>(256 * 1024));
|
||||
}
|
||||
}
|
||||
|
||||
if(totalContentLen > 0 && totalContentLen < maxReqSize ){
|
||||
//返回固定长度的content
|
||||
content_len = totalContentLen;
|
||||
auto parserCopy = _parser;
|
||||
_contentCallBack = [this,parserCopy](const char *data,uint64_t len){
|
||||
//恢复http头
|
||||
_parser = parserCopy;
|
||||
//设置content
|
||||
_parser.setContent(string(data,len));
|
||||
//触发http事件,emitHttpEvent内部会选择是否关闭连接
|
||||
emitHttpEvent(true);
|
||||
//清空数据,节省内存
|
||||
_parser.Clear();
|
||||
//content已经接收完毕
|
||||
return false;
|
||||
};
|
||||
}else{
|
||||
//返回不固定长度的content
|
||||
content_len = -1;
|
||||
auto parserCopy = _parser;
|
||||
std::shared_ptr<uint64_t> recvedContentLen = std::make_shared<uint64_t>(0);
|
||||
bool bClose = !strcasecmp(_parser["Connection"].data(),"close");
|
||||
//返回固定长度的content
|
||||
content_len = totalContentLen;
|
||||
auto parserCopy = _parser;
|
||||
_contentCallBack = [this,parserCopy](const char *data,uint64_t len){
|
||||
//恢复http头
|
||||
_parser = parserCopy;
|
||||
//设置content
|
||||
_parser.setContent(string(data,len));
|
||||
//触发http事件,emitHttpEvent内部会选择是否关闭连接
|
||||
emitHttpEvent(true);
|
||||
//清空数据,节省内存
|
||||
_parser.Clear();
|
||||
//content已经接收完毕
|
||||
return false;
|
||||
};
|
||||
}else{
|
||||
//返回不固定长度的content
|
||||
content_len = -1;
|
||||
auto parserCopy = _parser;
|
||||
std::shared_ptr<uint64_t> recvedContentLen = std::make_shared<uint64_t>(0);
|
||||
bool bClose = !strcasecmp(_parser["Connection"].data(),"close");
|
||||
|
||||
_contentCallBack = [this,parserCopy,totalContentLen,recvedContentLen,bClose](const char *data,uint64_t len){
|
||||
*(recvedContentLen) += len;
|
||||
_contentCallBack = [this,parserCopy,totalContentLen,recvedContentLen,bClose](const char *data,uint64_t len){
|
||||
*(recvedContentLen) += len;
|
||||
|
||||
onRecvUnlimitedContent(parserCopy,data,len,totalContentLen,*(recvedContentLen));
|
||||
onRecvUnlimitedContent(parserCopy,data,len,totalContentLen,*(recvedContentLen));
|
||||
|
||||
if(*(recvedContentLen) < totalContentLen){
|
||||
//数据还没接收完毕
|
||||
if(*(recvedContentLen) < totalContentLen){
|
||||
//数据还没接收完毕
|
||||
//_contentCallBack是可持续的,后面还要处理后续content数据
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//数据接收完毕
|
||||
//数据接收完毕
|
||||
if(!bClose){
|
||||
//keep-alive类型连接
|
||||
//content接收完毕,后续都是http header
|
||||
setContentLen(0);
|
||||
//keep-alive类型连接
|
||||
//content接收完毕,后续都是http header
|
||||
setContentLen(0);
|
||||
//content已经接收完毕
|
||||
return false;
|
||||
}
|
||||
@ -611,9 +611,9 @@ void HttpSession::Handle_Req_POST(int64_t &content_len) {
|
||||
shutdown(SockException(Err_shutdown,"recv http content completed"));
|
||||
//content已经接收完毕
|
||||
return false ;
|
||||
};
|
||||
}
|
||||
//有后续content数据要处理,暂时不关闭连接
|
||||
};
|
||||
}
|
||||
//有后续content数据要处理,暂时不关闭连接
|
||||
}
|
||||
|
||||
void HttpSession::sendNotFound(bool bClose) {
|
||||
@ -632,7 +632,7 @@ void HttpSession::setSocketFlags(){
|
||||
}
|
||||
|
||||
void HttpSession::onWrite(const Buffer::Ptr &buffer) {
|
||||
_ticker.resetTime();
|
||||
_ticker.resetTime();
|
||||
if(!_flv_over_websocket){
|
||||
_ui64TotalBytes += buffer->size();
|
||||
send(buffer);
|
||||
@ -653,11 +653,11 @@ void HttpSession::onWebSocketEncodeData(const Buffer::Ptr &buffer){
|
||||
}
|
||||
|
||||
void HttpSession::onDetach() {
|
||||
shutdown(SockException(Err_shutdown,"rtmp ring buffer detached"));
|
||||
shutdown(SockException(Err_shutdown,"rtmp ring buffer detached"));
|
||||
}
|
||||
|
||||
std::shared_ptr<FlvMuxer> HttpSession::getSharedPtr(){
|
||||
return dynamic_pointer_cast<FlvMuxer>(shared_from_this());
|
||||
return dynamic_pointer_cast<FlvMuxer>(shared_from_this());
|
||||
}
|
||||
|
||||
} /* namespace mediakit */
|
||||
|
@ -45,51 +45,51 @@ class HttpSession: public TcpSession,
|
||||
public HttpRequestSplitter,
|
||||
public WebSocketSplitter {
|
||||
public:
|
||||
typedef StrCaseMap KeyValue;
|
||||
typedef HttpResponseInvokerImp HttpResponseInvoker;
|
||||
friend class AsyncSender;
|
||||
/**
|
||||
* @param errMsg 如果为空,则代表鉴权通过,否则为错误提示
|
||||
* @param accessPath 运行或禁止访问的根目录
|
||||
* @param cookieLifeSecond 鉴权cookie有效期
|
||||
**/
|
||||
typedef std::function<void(const string &errMsg,const string &accessPath, int cookieLifeSecond)> HttpAccessPathInvoker;
|
||||
typedef StrCaseMap KeyValue;
|
||||
typedef HttpResponseInvokerImp HttpResponseInvoker;
|
||||
friend class AsyncSender;
|
||||
/**
|
||||
* @param errMsg 如果为空,则代表鉴权通过,否则为错误提示
|
||||
* @param accessPath 运行或禁止访问的根目录
|
||||
* @param cookieLifeSecond 鉴权cookie有效期
|
||||
**/
|
||||
typedef std::function<void(const string &errMsg,const string &accessPath, int cookieLifeSecond)> HttpAccessPathInvoker;
|
||||
|
||||
HttpSession(const Socket::Ptr &pSock);
|
||||
virtual ~HttpSession();
|
||||
HttpSession(const Socket::Ptr &pSock);
|
||||
virtual ~HttpSession();
|
||||
|
||||
virtual void onRecv(const Buffer::Ptr &) override;
|
||||
virtual void onError(const SockException &err) override;
|
||||
virtual void onManager() override;
|
||||
static string urlDecode(const string &str);
|
||||
virtual void onRecv(const Buffer::Ptr &) override;
|
||||
virtual void onError(const SockException &err) override;
|
||||
virtual void onManager() override;
|
||||
static string urlDecode(const string &str);
|
||||
protected:
|
||||
//FlvMuxer override
|
||||
void onWrite(const Buffer::Ptr &data) override ;
|
||||
void onDetach() override;
|
||||
std::shared_ptr<FlvMuxer> getSharedPtr() override;
|
||||
//FlvMuxer override
|
||||
void onWrite(const Buffer::Ptr &data) override ;
|
||||
void onDetach() override;
|
||||
std::shared_ptr<FlvMuxer> getSharedPtr() override;
|
||||
|
||||
//HttpRequestSplitter override
|
||||
int64_t onRecvHeader(const char *data,uint64_t len) override;
|
||||
void onRecvContent(const char *data,uint64_t len) override;
|
||||
//HttpRequestSplitter override
|
||||
int64_t onRecvHeader(const char *data,uint64_t len) override;
|
||||
void onRecvContent(const char *data,uint64_t len) override;
|
||||
|
||||
/**
|
||||
* 重载之用于处理不定长度的content
|
||||
* 这个函数可用于处理大文件上传、http-flv推流
|
||||
* @param header http请求头
|
||||
* @param data content分片数据
|
||||
* @param len content分片数据大小
|
||||
* @param totalSize content总大小,如果为0则是不限长度content
|
||||
* @param recvedSize 已收数据大小
|
||||
*/
|
||||
virtual void onRecvUnlimitedContent(const Parser &header,
|
||||
const char *data,
|
||||
uint64_t len,
|
||||
uint64_t totalSize,
|
||||
uint64_t recvedSize){
|
||||
/**
|
||||
* 重载之用于处理不定长度的content
|
||||
* 这个函数可用于处理大文件上传、http-flv推流
|
||||
* @param header http请求头
|
||||
* @param data content分片数据
|
||||
* @param len content分片数据大小
|
||||
* @param totalSize content总大小,如果为0则是不限长度content
|
||||
* @param recvedSize 已收数据大小
|
||||
*/
|
||||
virtual void onRecvUnlimitedContent(const Parser &header,
|
||||
const char *data,
|
||||
uint64_t len,
|
||||
uint64_t totalSize,
|
||||
uint64_t recvedSize){
|
||||
shutdown(SockException(Err_shutdown,"http post content is too huge,default closed"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* websocket客户端连接上事件
|
||||
* @param header http头
|
||||
* @return true代表允许websocket连接,否则拒绝
|
||||
@ -99,31 +99,31 @@ protected:
|
||||
return false;
|
||||
}
|
||||
|
||||
//WebSocketSplitter override
|
||||
/**
|
||||
//WebSocketSplitter override
|
||||
/**
|
||||
* 发送数据进行websocket协议打包后回调
|
||||
* @param buffer websocket协议数据
|
||||
*/
|
||||
void onWebSocketEncodeData(const Buffer::Ptr &buffer) override;
|
||||
void onWebSocketEncodeData(const Buffer::Ptr &buffer) override;
|
||||
private:
|
||||
void Handle_Req_GET(int64_t &content_len);
|
||||
void Handle_Req_GET(int64_t &content_len);
|
||||
void Handle_Req_GET_l(int64_t &content_len, bool sendBody);
|
||||
void Handle_Req_POST(int64_t &content_len);
|
||||
void Handle_Req_HEAD(int64_t &content_len);
|
||||
|
||||
bool checkLiveFlvStream(const function<void()> &cb = nullptr);
|
||||
bool checkWebSocket();
|
||||
bool emitHttpEvent(bool doInvoke);
|
||||
void urlDecode(Parser &parser);
|
||||
void sendNotFound(bool bClose);
|
||||
void sendResponse(const char *pcStatus, bool bClose, const char *pcContentType = nullptr,
|
||||
const HttpSession::KeyValue &header = HttpSession::KeyValue(),
|
||||
bool checkWebSocket();
|
||||
bool emitHttpEvent(bool doInvoke);
|
||||
void urlDecode(Parser &parser);
|
||||
void sendNotFound(bool bClose);
|
||||
void sendResponse(const char *pcStatus, bool bClose, const char *pcContentType = nullptr,
|
||||
const HttpSession::KeyValue &header = HttpSession::KeyValue(),
|
||||
const HttpBody::Ptr &body = nullptr,bool is_http_flv = false);
|
||||
|
||||
//设置socket标志
|
||||
void setSocketFlags();
|
||||
//设置socket标志
|
||||
void setSocketFlags();
|
||||
private:
|
||||
string _origin;
|
||||
string _origin;
|
||||
Parser _parser;
|
||||
Ticker _ticker;
|
||||
//消耗的总流量
|
||||
@ -132,8 +132,8 @@ private:
|
||||
MediaInfo _mediaInfo;
|
||||
//处理content数据的callback
|
||||
function<bool (const char *data,uint64_t len) > _contentCallBack;
|
||||
bool _flv_over_websocket = false;
|
||||
bool _is_flv_stream = false;
|
||||
bool _flv_over_websocket = false;
|
||||
bool _is_flv_stream = false;
|
||||
};
|
||||
|
||||
|
||||
|
@ -36,76 +36,76 @@ namespace mediakit {
|
||||
//////////////////////////通用///////////////////////
|
||||
void UTF8ToUnicode(wchar_t* pOut, const char *pText)
|
||||
{
|
||||
char* uchar = (char *)pOut;
|
||||
uchar[1] = ((pText[0] & 0x0F) << 4) + ((pText[1] >> 2) & 0x0F);
|
||||
uchar[0] = ((pText[1] & 0x03) << 6) + (pText[2] & 0x3F);
|
||||
return;
|
||||
char* uchar = (char *)pOut;
|
||||
uchar[1] = ((pText[0] & 0x0F) << 4) + ((pText[1] >> 2) & 0x0F);
|
||||
uchar[0] = ((pText[1] & 0x03) << 6) + (pText[2] & 0x3F);
|
||||
return;
|
||||
}
|
||||
void UnicodeToUTF8(char* pOut, const wchar_t* pText)
|
||||
{
|
||||
// 注意 WCHAR高低字的顺序,低字节在前,高字节在后
|
||||
const char* pchar = (const char *)pText;
|
||||
pOut[0] = (0xE0 | ((pchar[1] & 0xF0) >> 4));
|
||||
pOut[1] = (0x80 | ((pchar[1] & 0x0F) << 2)) + ((pchar[0] & 0xC0) >> 6);
|
||||
pOut[2] = (0x80 | (pchar[0] & 0x3F));
|
||||
return;
|
||||
// 注意 WCHAR高低字的顺序,低字节在前,高字节在后
|
||||
const char* pchar = (const char *)pText;
|
||||
pOut[0] = (0xE0 | ((pchar[1] & 0xF0) >> 4));
|
||||
pOut[1] = (0x80 | ((pchar[1] & 0x0F) << 2)) + ((pchar[0] & 0xC0) >> 6);
|
||||
pOut[2] = (0x80 | (pchar[0] & 0x3F));
|
||||
return;
|
||||
}
|
||||
|
||||
char CharToInt(char ch)
|
||||
{
|
||||
if (ch >= '0' && ch <= '9')return (char)(ch - '0');
|
||||
if (ch >= 'a' && ch <= 'f')return (char)(ch - 'a' + 10);
|
||||
if (ch >= 'A' && ch <= 'F')return (char)(ch - 'A' + 10);
|
||||
return -1;
|
||||
if (ch >= '0' && ch <= '9')return (char)(ch - '0');
|
||||
if (ch >= 'a' && ch <= 'f')return (char)(ch - 'a' + 10);
|
||||
if (ch >= 'A' && ch <= 'F')return (char)(ch - 'A' + 10);
|
||||
return -1;
|
||||
}
|
||||
char StrToBin(const char *str)
|
||||
{
|
||||
char tempWord[2];
|
||||
char chn;
|
||||
tempWord[0] = CharToInt(str[0]); //make the B to 11 -- 00001011
|
||||
tempWord[1] = CharToInt(str[1]); //make the 0 to 0 -- 00000000
|
||||
chn = (tempWord[0] << 4) | tempWord[1]; //to change the BO to 10110000
|
||||
return chn;
|
||||
char tempWord[2];
|
||||
char chn;
|
||||
tempWord[0] = CharToInt(str[0]); //make the B to 11 -- 00001011
|
||||
tempWord[1] = CharToInt(str[1]); //make the 0 to 0 -- 00000000
|
||||
chn = (tempWord[0] << 4) | tempWord[1]; //to change the BO to 10110000
|
||||
return chn;
|
||||
}
|
||||
|
||||
string strCoding::UrlEncode(const string &str) {
|
||||
string out;
|
||||
string out;
|
||||
size_t len = str.size();
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
char ch = str[i];
|
||||
if (isalnum((uint8_t)ch)) {
|
||||
if (isalnum((uint8_t)ch)) {
|
||||
out.push_back(ch);
|
||||
}else {
|
||||
char buf[4];
|
||||
sprintf(buf, "%%%X%X", (uint8_t)ch >> 4,(uint8_t)ch & 0x0F);
|
||||
}else {
|
||||
char buf[4];
|
||||
sprintf(buf, "%%%X%X", (uint8_t)ch >> 4,(uint8_t)ch & 0x0F);
|
||||
out.append(buf);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
string strCoding::UrlDecode(const string &str) {
|
||||
string output = "";
|
||||
char tmp[2];
|
||||
int i = 0, len = str.length();
|
||||
while (i < len) {
|
||||
if (str[i] == '%') {
|
||||
if(i > len - 3){
|
||||
//防止内存溢出
|
||||
string output = "";
|
||||
char tmp[2];
|
||||
int i = 0, len = str.length();
|
||||
while (i < len) {
|
||||
if (str[i] == '%') {
|
||||
if(i > len - 3){
|
||||
//防止内存溢出
|
||||
break;
|
||||
}
|
||||
tmp[0] = str[i + 1];
|
||||
tmp[1] = str[i + 2];
|
||||
output += StrToBin(tmp);
|
||||
i = i + 3;
|
||||
} else if (str[i] == '+') {
|
||||
output += ' ';
|
||||
i++;
|
||||
} else {
|
||||
output += str[i];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
tmp[0] = str[i + 1];
|
||||
tmp[1] = str[i + 2];
|
||||
output += StrToBin(tmp);
|
||||
i = i + 3;
|
||||
} else if (str[i] == '+') {
|
||||
output += ' ';
|
||||
i++;
|
||||
} else {
|
||||
output += str[i];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
@ -113,73 +113,73 @@ string strCoding::UrlDecode(const string &str) {
|
||||
#if defined(_WIN32)
|
||||
void UnicodeToGB2312(char* pOut, wchar_t uData)
|
||||
{
|
||||
WideCharToMultiByte(CP_ACP, NULL, &uData, 1, pOut, sizeof(wchar_t), NULL, NULL);
|
||||
WideCharToMultiByte(CP_ACP, NULL, &uData, 1, pOut, sizeof(wchar_t), NULL, NULL);
|
||||
}
|
||||
void Gb2312ToUnicode(wchar_t* pOut, const char *gbBuffer)
|
||||
{
|
||||
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, gbBuffer, 2, pOut, 1);
|
||||
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, gbBuffer, 2, pOut, 1);
|
||||
}
|
||||
|
||||
string strCoding::UTF8ToGB2312(const string &str) {
|
||||
auto len = str.size();
|
||||
auto pText = str.data();
|
||||
char Ctemp[4] = {0};
|
||||
char *pOut = new char[len + 1];
|
||||
memset(pOut, 0, len + 1);
|
||||
auto len = str.size();
|
||||
auto pText = str.data();
|
||||
char Ctemp[4] = {0};
|
||||
char *pOut = new char[len + 1];
|
||||
memset(pOut, 0, len + 1);
|
||||
|
||||
int i = 0, j = 0;
|
||||
while (i < len)
|
||||
{
|
||||
if (pText[i] >= 0)
|
||||
{
|
||||
pOut[j++] = pText[i++];
|
||||
}
|
||||
else
|
||||
{
|
||||
wchar_t Wtemp;
|
||||
UTF8ToUnicode(&Wtemp, pText + i);
|
||||
UnicodeToGB2312(Ctemp, Wtemp);
|
||||
pOut[j] = Ctemp[0];
|
||||
pOut[j + 1] = Ctemp[1];
|
||||
i += 3;
|
||||
j += 2;
|
||||
}
|
||||
}
|
||||
string ret = pOut;
|
||||
delete[] pOut;
|
||||
return ret;
|
||||
int i = 0, j = 0;
|
||||
while (i < len)
|
||||
{
|
||||
if (pText[i] >= 0)
|
||||
{
|
||||
pOut[j++] = pText[i++];
|
||||
}
|
||||
else
|
||||
{
|
||||
wchar_t Wtemp;
|
||||
UTF8ToUnicode(&Wtemp, pText + i);
|
||||
UnicodeToGB2312(Ctemp, Wtemp);
|
||||
pOut[j] = Ctemp[0];
|
||||
pOut[j + 1] = Ctemp[1];
|
||||
i += 3;
|
||||
j += 2;
|
||||
}
|
||||
}
|
||||
string ret = pOut;
|
||||
delete[] pOut;
|
||||
return ret;
|
||||
}
|
||||
|
||||
string strCoding::GB2312ToUTF8(const string &str) {
|
||||
auto len = str.size();
|
||||
auto pText = str.data();
|
||||
char buf[4] = { 0 };
|
||||
int nLength = len * 3;
|
||||
char* pOut = new char[nLength];
|
||||
memset(pOut, 0, nLength);
|
||||
int i = 0, j = 0;
|
||||
while (i < len)
|
||||
{
|
||||
//如果是英文直接复制就可以
|
||||
if (*(pText + i) >= 0)
|
||||
{
|
||||
pOut[j++] = pText[i++];
|
||||
}
|
||||
else
|
||||
{
|
||||
wchar_t pbuffer;
|
||||
Gb2312ToUnicode(&pbuffer, pText + i);
|
||||
UnicodeToUTF8(buf, &pbuffer);
|
||||
pOut[j] = buf[0];
|
||||
pOut[j + 1] = buf[1];
|
||||
pOut[j + 2] = buf[2];
|
||||
j += 3;
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
string ret = pOut;
|
||||
delete[] pOut;
|
||||
return ret;
|
||||
auto len = str.size();
|
||||
auto pText = str.data();
|
||||
char buf[4] = { 0 };
|
||||
int nLength = len * 3;
|
||||
char* pOut = new char[nLength];
|
||||
memset(pOut, 0, nLength);
|
||||
int i = 0, j = 0;
|
||||
while (i < len)
|
||||
{
|
||||
//如果是英文直接复制就可以
|
||||
if (*(pText + i) >= 0)
|
||||
{
|
||||
pOut[j++] = pText[i++];
|
||||
}
|
||||
else
|
||||
{
|
||||
wchar_t pbuffer;
|
||||
Gb2312ToUnicode(&pbuffer, pText + i);
|
||||
UnicodeToUTF8(buf, &pbuffer);
|
||||
pOut[j] = buf[0];
|
||||
pOut[j + 1] = buf[1];
|
||||
pOut[j + 2] = buf[2];
|
||||
j += 3;
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
string ret = pOut;
|
||||
delete[] pOut;
|
||||
return ret;
|
||||
}
|
||||
#endif//defined(_WIN32)
|
||||
|
||||
|
@ -36,15 +36,15 @@ namespace mediakit {
|
||||
|
||||
class strCoding {
|
||||
public:
|
||||
static string UrlEncode(const string &str); //urlutf8 编码
|
||||
static string UrlDecode(const string &str); //urlutf8解码
|
||||
static string UrlEncode(const string &str); //urlutf8 编码
|
||||
static string UrlDecode(const string &str); //urlutf8解码
|
||||
#if defined(_WIN32)
|
||||
static string UTF8ToGB2312(const string &str);//utf_8转为gb2312
|
||||
static string GB2312ToUTF8(const string &str); //gb2312 转utf_8
|
||||
static string UTF8ToGB2312(const string &str);//utf_8转为gb2312
|
||||
static string GB2312ToUTF8(const string &str); //gb2312 转utf_8
|
||||
#endif//defined(_WIN32)
|
||||
private:
|
||||
strCoding(void);
|
||||
virtual ~strCoding(void);
|
||||
strCoding(void);
|
||||
virtual ~strCoding(void);
|
||||
};
|
||||
|
||||
} /* namespace mediakit */
|
||||
|
@ -42,29 +42,29 @@ MediaPlayer::MediaPlayer(const EventPoller::Ptr &poller) {
|
||||
MediaPlayer::~MediaPlayer() {
|
||||
}
|
||||
void MediaPlayer::play(const string &strUrl) {
|
||||
_delegate = PlayerBase::createPlayer(_poller,strUrl);
|
||||
_delegate->setOnShutdown(_shutdownCB);
|
||||
_delegate->setOnPlayResult(_playResultCB);
|
||||
_delegate = PlayerBase::createPlayer(_poller,strUrl);
|
||||
_delegate->setOnShutdown(_shutdownCB);
|
||||
_delegate->setOnPlayResult(_playResultCB);
|
||||
_delegate->setOnResume(_resumeCB);
|
||||
_delegate->setMediaSouce(_pMediaSrc);
|
||||
_delegate->mINI::operator=(*this);
|
||||
_delegate->play(strUrl);
|
||||
_delegate->mINI::operator=(*this);
|
||||
_delegate->play(strUrl);
|
||||
}
|
||||
|
||||
EventPoller::Ptr MediaPlayer::getPoller(){
|
||||
return _poller;
|
||||
return _poller;
|
||||
}
|
||||
|
||||
void MediaPlayer::pause(bool bPause) {
|
||||
if (_delegate) {
|
||||
_delegate->pause(bPause);
|
||||
}
|
||||
if (_delegate) {
|
||||
_delegate->pause(bPause);
|
||||
}
|
||||
}
|
||||
|
||||
void MediaPlayer::teardown() {
|
||||
if (_delegate) {
|
||||
_delegate->teardown();
|
||||
}
|
||||
if (_delegate) {
|
||||
_delegate->teardown();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -39,16 +39,16 @@ namespace mediakit {
|
||||
|
||||
class MediaPlayer : public PlayerImp<PlayerBase,PlayerBase> {
|
||||
public:
|
||||
typedef std::shared_ptr<MediaPlayer> Ptr;
|
||||
typedef std::shared_ptr<MediaPlayer> Ptr;
|
||||
|
||||
MediaPlayer(const EventPoller::Ptr &poller = nullptr);
|
||||
virtual ~MediaPlayer();
|
||||
void play(const string &strUrl) override;
|
||||
void pause(bool bPause) override;
|
||||
void teardown() override;
|
||||
EventPoller::Ptr getPoller();
|
||||
MediaPlayer(const EventPoller::Ptr &poller = nullptr);
|
||||
virtual ~MediaPlayer();
|
||||
void play(const string &strUrl) override;
|
||||
void pause(bool bPause) override;
|
||||
void teardown() override;
|
||||
EventPoller::Ptr getPoller();
|
||||
private:
|
||||
EventPoller::Ptr _poller;
|
||||
EventPoller::Ptr _poller;
|
||||
};
|
||||
|
||||
} /* namespace mediakit */
|
||||
|
@ -33,92 +33,92 @@ using namespace toolkit;
|
||||
namespace mediakit {
|
||||
|
||||
PlayerBase::Ptr PlayerBase::createPlayer(const EventPoller::Ptr &poller,const string &strUrl) {
|
||||
static auto releasePlayer = [](PlayerBase *ptr){
|
||||
onceToken token(nullptr,[&](){
|
||||
delete ptr;
|
||||
});
|
||||
ptr->teardown();
|
||||
};
|
||||
string prefix = FindField(strUrl.data(), NULL, "://");
|
||||
static auto releasePlayer = [](PlayerBase *ptr){
|
||||
onceToken token(nullptr,[&](){
|
||||
delete ptr;
|
||||
});
|
||||
ptr->teardown();
|
||||
};
|
||||
string prefix = FindField(strUrl.data(), NULL, "://");
|
||||
|
||||
if (strcasecmp("rtsps",prefix.data()) == 0) {
|
||||
return PlayerBase::Ptr(new TcpClientWithSSL<RtspPlayerImp>(poller),releasePlayer);
|
||||
}
|
||||
if (strcasecmp("rtsps",prefix.data()) == 0) {
|
||||
return PlayerBase::Ptr(new TcpClientWithSSL<RtspPlayerImp>(poller),releasePlayer);
|
||||
}
|
||||
|
||||
if (strcasecmp("rtsp",prefix.data()) == 0) {
|
||||
return PlayerBase::Ptr(new RtspPlayerImp(poller),releasePlayer);
|
||||
}
|
||||
if (strcasecmp("rtsp",prefix.data()) == 0) {
|
||||
return PlayerBase::Ptr(new RtspPlayerImp(poller),releasePlayer);
|
||||
}
|
||||
|
||||
if (strcasecmp("rtmps",prefix.data()) == 0) {
|
||||
return PlayerBase::Ptr(new TcpClientWithSSL<RtmpPlayerImp>(poller),releasePlayer);
|
||||
}
|
||||
if (strcasecmp("rtmps",prefix.data()) == 0) {
|
||||
return PlayerBase::Ptr(new TcpClientWithSSL<RtmpPlayerImp>(poller),releasePlayer);
|
||||
}
|
||||
|
||||
if (strcasecmp("rtmp",prefix.data()) == 0) {
|
||||
return PlayerBase::Ptr(new RtmpPlayerImp(poller),releasePlayer);
|
||||
}
|
||||
if (strcasecmp("rtmp",prefix.data()) == 0) {
|
||||
return PlayerBase::Ptr(new RtmpPlayerImp(poller),releasePlayer);
|
||||
}
|
||||
|
||||
return PlayerBase::Ptr(new RtspPlayerImp(poller),releasePlayer);
|
||||
return PlayerBase::Ptr(new RtspPlayerImp(poller),releasePlayer);
|
||||
}
|
||||
|
||||
PlayerBase::PlayerBase() {
|
||||
this->mINI::operator[](kTimeoutMS) = 10000;
|
||||
this->mINI::operator[](kMediaTimeoutMS) = 5000;
|
||||
this->mINI::operator[](kBeatIntervalMS) = 5000;
|
||||
this->mINI::operator[](kMaxAnalysisMS) = 5000;
|
||||
this->mINI::operator[](kTimeoutMS) = 10000;
|
||||
this->mINI::operator[](kMediaTimeoutMS) = 5000;
|
||||
this->mINI::operator[](kBeatIntervalMS) = 5000;
|
||||
this->mINI::operator[](kMaxAnalysisMS) = 5000;
|
||||
}
|
||||
|
||||
///////////////////////////Demuxer//////////////////////////////
|
||||
bool Demuxer::isInited(int analysisMs) {
|
||||
if(analysisMs && _ticker.createdTime() > analysisMs){
|
||||
//analysisMs毫秒后强制初始化完毕
|
||||
return true;
|
||||
}
|
||||
if (_videoTrack && !_videoTrack->ready()) {
|
||||
//视频未准备好
|
||||
return false;
|
||||
}
|
||||
if (_audioTrack && !_audioTrack->ready()) {
|
||||
//音频未准备好
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
if(analysisMs && _ticker.createdTime() > analysisMs){
|
||||
//analysisMs毫秒后强制初始化完毕
|
||||
return true;
|
||||
}
|
||||
if (_videoTrack && !_videoTrack->ready()) {
|
||||
//视频未准备好
|
||||
return false;
|
||||
}
|
||||
if (_audioTrack && !_audioTrack->ready()) {
|
||||
//音频未准备好
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
vector<Track::Ptr> Demuxer::getTracks(bool trackReady) const {
|
||||
vector<Track::Ptr> ret;
|
||||
if(_videoTrack){
|
||||
if(trackReady){
|
||||
if(_videoTrack->ready()){
|
||||
ret.emplace_back(_videoTrack);
|
||||
}
|
||||
}else{
|
||||
ret.emplace_back(_videoTrack);
|
||||
}
|
||||
}
|
||||
if(_audioTrack){
|
||||
if(trackReady){
|
||||
if(_audioTrack->ready()){
|
||||
ret.emplace_back(_audioTrack);
|
||||
}
|
||||
}else{
|
||||
ret.emplace_back(_audioTrack);
|
||||
}
|
||||
}
|
||||
return std::move(ret);
|
||||
vector<Track::Ptr> ret;
|
||||
if(_videoTrack){
|
||||
if(trackReady){
|
||||
if(_videoTrack->ready()){
|
||||
ret.emplace_back(_videoTrack);
|
||||
}
|
||||
}else{
|
||||
ret.emplace_back(_videoTrack);
|
||||
}
|
||||
}
|
||||
if(_audioTrack){
|
||||
if(trackReady){
|
||||
if(_audioTrack->ready()){
|
||||
ret.emplace_back(_audioTrack);
|
||||
}
|
||||
}else{
|
||||
ret.emplace_back(_audioTrack);
|
||||
}
|
||||
}
|
||||
return std::move(ret);
|
||||
}
|
||||
|
||||
float Demuxer::getDuration() const {
|
||||
return _fDuration;
|
||||
return _fDuration;
|
||||
}
|
||||
|
||||
void Demuxer::onAddTrack(const Track::Ptr &track){
|
||||
if(_listener){
|
||||
_listener->onAddTrack(track);
|
||||
}
|
||||
if(_listener){
|
||||
_listener->onAddTrack(track);
|
||||
}
|
||||
}
|
||||
|
||||
void Demuxer::setTrackListener(Demuxer::Listener *listener) {
|
||||
_listener = listener;
|
||||
_listener = listener;
|
||||
}
|
||||
|
||||
} /* namespace mediakit */
|
||||
|
@ -43,59 +43,59 @@ namespace mediakit {
|
||||
|
||||
class DemuxerBase : public TrackSource{
|
||||
public:
|
||||
typedef std::shared_ptr<DemuxerBase> Ptr;
|
||||
typedef std::shared_ptr<DemuxerBase> Ptr;
|
||||
|
||||
/**
|
||||
* 获取节目总时长,单位秒
|
||||
* @return
|
||||
*/
|
||||
virtual float getDuration() const { return 0;}
|
||||
/**
|
||||
* 获取节目总时长,单位秒
|
||||
* @return
|
||||
*/
|
||||
virtual float getDuration() const { return 0;}
|
||||
|
||||
/**
|
||||
* 是否初始化完毕,完毕后方可调用getTrack方法
|
||||
* @param analysisMs 数据流最大分析时间 单位毫秒
|
||||
* @return
|
||||
*/
|
||||
virtual bool isInited(int analysisMs) { return true; }
|
||||
/**
|
||||
* 是否初始化完毕,完毕后方可调用getTrack方法
|
||||
* @param analysisMs 数据流最大分析时间 单位毫秒
|
||||
* @return
|
||||
*/
|
||||
virtual bool isInited(int analysisMs) { return true; }
|
||||
};
|
||||
|
||||
|
||||
class PlayerBase : public DemuxerBase, public mINI{
|
||||
public:
|
||||
typedef std::shared_ptr<PlayerBase> Ptr;
|
||||
typedef std::shared_ptr<PlayerBase> Ptr;
|
||||
static Ptr createPlayer(const EventPoller::Ptr &poller,const string &strUrl);
|
||||
|
||||
PlayerBase();
|
||||
virtual ~PlayerBase(){}
|
||||
PlayerBase();
|
||||
virtual ~PlayerBase(){}
|
||||
|
||||
/**
|
||||
* 开始播放
|
||||
* @param strUrl 视频url,支持rtsp/rtmp
|
||||
*/
|
||||
virtual void play(const string &strUrl) {}
|
||||
/**
|
||||
* 开始播放
|
||||
* @param strUrl 视频url,支持rtsp/rtmp
|
||||
*/
|
||||
virtual void play(const string &strUrl) {}
|
||||
|
||||
/**
|
||||
* 暂停或恢复
|
||||
* @param bPause
|
||||
*/
|
||||
virtual void pause(bool bPause) {}
|
||||
/**
|
||||
* 暂停或恢复
|
||||
* @param bPause
|
||||
*/
|
||||
virtual void pause(bool bPause) {}
|
||||
|
||||
/**
|
||||
* 中断播放
|
||||
*/
|
||||
virtual void teardown() {}
|
||||
/**
|
||||
* 中断播放
|
||||
*/
|
||||
virtual void teardown() {}
|
||||
|
||||
/**
|
||||
* 设置异常中断回调
|
||||
* @param cb
|
||||
*/
|
||||
virtual void setOnShutdown( const function<void(const SockException &)> &cb) {}
|
||||
/**
|
||||
* 设置异常中断回调
|
||||
* @param cb
|
||||
*/
|
||||
virtual void setOnShutdown( const function<void(const SockException &)> &cb) {}
|
||||
|
||||
/**
|
||||
* 设置播放结果回调
|
||||
* @param cb
|
||||
*/
|
||||
virtual void setOnPlayResult( const function<void(const SockException &ex)> &cb) {}
|
||||
/**
|
||||
* 设置播放结果回调
|
||||
* @param cb
|
||||
*/
|
||||
virtual void setOnPlayResult( const function<void(const SockException &ex)> &cb) {}
|
||||
|
||||
/**
|
||||
* 设置播放恢复回调
|
||||
@ -103,10 +103,10 @@ public:
|
||||
*/
|
||||
virtual void setOnResume( const function<void()> &cb) {}
|
||||
|
||||
/**
|
||||
* 获取播放进度,取值 0.0 ~ 1.0
|
||||
* @return
|
||||
*/
|
||||
/**
|
||||
* 获取播放进度,取值 0.0 ~ 1.0
|
||||
* @return
|
||||
*/
|
||||
virtual float getProgress() const { return 0;}
|
||||
|
||||
/**
|
||||
@ -126,7 +126,7 @@ public:
|
||||
* @param trackType 音频或视频,TrackInvalid时为总丢包率
|
||||
* @return
|
||||
*/
|
||||
virtual float getPacketLossRate(TrackType trackType) const {return 0; }
|
||||
virtual float getPacketLossRate(TrackType trackType) const {return 0; }
|
||||
|
||||
/**
|
||||
* 获取所有track
|
||||
@ -146,24 +146,24 @@ protected:
|
||||
template<typename Parent,typename Delegate>
|
||||
class PlayerImp : public Parent {
|
||||
public:
|
||||
typedef std::shared_ptr<PlayerImp> Ptr;
|
||||
typedef std::shared_ptr<PlayerImp> Ptr;
|
||||
|
||||
template<typename ...ArgsType>
|
||||
PlayerImp(ArgsType &&...args):Parent(std::forward<ArgsType>(args)...){}
|
||||
template<typename ...ArgsType>
|
||||
PlayerImp(ArgsType &&...args):Parent(std::forward<ArgsType>(args)...){}
|
||||
|
||||
virtual ~PlayerImp(){}
|
||||
void setOnShutdown(const function<void(const SockException &)> &cb) override {
|
||||
if (_delegate) {
|
||||
_delegate->setOnShutdown(cb);
|
||||
}
|
||||
_shutdownCB = cb;
|
||||
}
|
||||
void setOnPlayResult(const function<void(const SockException &ex)> &cb) override {
|
||||
if (_delegate) {
|
||||
_delegate->setOnPlayResult(cb);
|
||||
}
|
||||
_playResultCB = cb;
|
||||
}
|
||||
virtual ~PlayerImp(){}
|
||||
void setOnShutdown(const function<void(const SockException &)> &cb) override {
|
||||
if (_delegate) {
|
||||
_delegate->setOnShutdown(cb);
|
||||
}
|
||||
_shutdownCB = cb;
|
||||
}
|
||||
void setOnPlayResult(const function<void(const SockException &ex)> &cb) override {
|
||||
if (_delegate) {
|
||||
_delegate->setOnPlayResult(cb);
|
||||
}
|
||||
_playResultCB = cb;
|
||||
}
|
||||
|
||||
void setOnResume(const function<void()> &cb) override {
|
||||
if (_delegate) {
|
||||
@ -178,12 +178,12 @@ public:
|
||||
}
|
||||
return Parent::isInited(analysisMs);
|
||||
}
|
||||
float getDuration() const override {
|
||||
if (_delegate) {
|
||||
return _delegate->getDuration();
|
||||
}
|
||||
return Parent::getDuration();
|
||||
}
|
||||
float getDuration() const override {
|
||||
if (_delegate) {
|
||||
return _delegate->getDuration();
|
||||
}
|
||||
return Parent::getDuration();
|
||||
}
|
||||
float getProgress() const override{
|
||||
if (_delegate) {
|
||||
return _delegate->getProgress();
|
||||
@ -198,95 +198,95 @@ public:
|
||||
}
|
||||
|
||||
void setMediaSouce(const MediaSource::Ptr & src) override {
|
||||
if (_delegate) {
|
||||
_delegate->setMediaSouce(src);
|
||||
}
|
||||
_pMediaSrc = src;
|
||||
if (_delegate) {
|
||||
_delegate->setMediaSouce(src);
|
||||
}
|
||||
_pMediaSrc = src;
|
||||
}
|
||||
|
||||
vector<Track::Ptr> getTracks(bool trackReady = true) const override{
|
||||
if (_delegate) {
|
||||
return _delegate->getTracks(trackReady);
|
||||
}
|
||||
return Parent::getTracks(trackReady);
|
||||
}
|
||||
if (_delegate) {
|
||||
return _delegate->getTracks(trackReady);
|
||||
}
|
||||
return Parent::getTracks(trackReady);
|
||||
}
|
||||
protected:
|
||||
void onShutdown(const SockException &ex) override {
|
||||
if (_shutdownCB) {
|
||||
_shutdownCB(ex);
|
||||
_shutdownCB = nullptr;
|
||||
}
|
||||
}
|
||||
void onShutdown(const SockException &ex) override {
|
||||
if (_shutdownCB) {
|
||||
_shutdownCB(ex);
|
||||
_shutdownCB = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void onPlayResult(const SockException &ex) override {
|
||||
if(_playResultCB) {
|
||||
_playResultCB(ex);
|
||||
_playResultCB = nullptr;
|
||||
}
|
||||
}
|
||||
void onPlayResult(const SockException &ex) override {
|
||||
if(_playResultCB) {
|
||||
_playResultCB(ex);
|
||||
_playResultCB = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void onResume() override{
|
||||
void onResume() override{
|
||||
if(_resumeCB){
|
||||
_resumeCB();
|
||||
}
|
||||
}
|
||||
protected:
|
||||
function<void(const SockException &ex)> _shutdownCB;
|
||||
function<void(const SockException &ex)> _playResultCB;
|
||||
function<void(const SockException &ex)> _shutdownCB;
|
||||
function<void(const SockException &ex)> _playResultCB;
|
||||
function<void()> _resumeCB;
|
||||
std::shared_ptr<Delegate> _delegate;
|
||||
MediaSource::Ptr _pMediaSrc;
|
||||
MediaSource::Ptr _pMediaSrc;
|
||||
};
|
||||
|
||||
|
||||
class Demuxer : public PlayerBase{
|
||||
public:
|
||||
class Listener{
|
||||
public:
|
||||
Listener() = default;
|
||||
virtual ~Listener() = default;
|
||||
virtual void onAddTrack(const Track::Ptr &track) = 0;
|
||||
};
|
||||
class Listener{
|
||||
public:
|
||||
Listener() = default;
|
||||
virtual ~Listener() = default;
|
||||
virtual void onAddTrack(const Track::Ptr &track) = 0;
|
||||
};
|
||||
|
||||
Demuxer(){};
|
||||
virtual ~Demuxer(){};
|
||||
Demuxer(){};
|
||||
virtual ~Demuxer(){};
|
||||
|
||||
/**
|
||||
* 返回是否完成初始化完毕
|
||||
* 在构造RtspDemuxer对象时有些rtsp的sdp不包含sps pps信息
|
||||
* 所以要等待接收到到sps的rtp包后才能完成
|
||||
*
|
||||
* 在构造RtmpDemuxer对象时是无法获取sps pps aac_cfg等这些信息,
|
||||
* 所以要调用inputRtmp后才会获取到这些信息,这时才初始化成功
|
||||
* @param analysisMs 数据流最大分析时间 单位毫秒
|
||||
* @return
|
||||
*/
|
||||
bool isInited(int analysisMs) override;
|
||||
/**
|
||||
* 返回是否完成初始化完毕
|
||||
* 在构造RtspDemuxer对象时有些rtsp的sdp不包含sps pps信息
|
||||
* 所以要等待接收到到sps的rtp包后才能完成
|
||||
*
|
||||
* 在构造RtmpDemuxer对象时是无法获取sps pps aac_cfg等这些信息,
|
||||
* 所以要调用inputRtmp后才会获取到这些信息,这时才初始化成功
|
||||
* @param analysisMs 数据流最大分析时间 单位毫秒
|
||||
* @return
|
||||
*/
|
||||
bool isInited(int analysisMs) override;
|
||||
|
||||
/**
|
||||
* 获取所有Track
|
||||
* @return 所有Track
|
||||
*/
|
||||
vector<Track::Ptr> getTracks(bool trackReady = true) const override;
|
||||
/**
|
||||
* 获取所有Track
|
||||
* @return 所有Track
|
||||
*/
|
||||
vector<Track::Ptr> getTracks(bool trackReady = true) const override;
|
||||
|
||||
/**
|
||||
* 获取节目总时长
|
||||
* @return 节目总时长,单位秒
|
||||
*/
|
||||
float getDuration() const override;
|
||||
/**
|
||||
* 获取节目总时长
|
||||
* @return 节目总时长,单位秒
|
||||
*/
|
||||
float getDuration() const override;
|
||||
|
||||
/**
|
||||
* 设置track监听器
|
||||
*/
|
||||
void setTrackListener(Listener *listener);
|
||||
/**
|
||||
* 设置track监听器
|
||||
*/
|
||||
void setTrackListener(Listener *listener);
|
||||
protected:
|
||||
void onAddTrack(const Track::Ptr &track);
|
||||
void onAddTrack(const Track::Ptr &track);
|
||||
protected:
|
||||
Listener *_listener = nullptr;
|
||||
AudioTrack::Ptr _audioTrack;
|
||||
VideoTrack::Ptr _videoTrack;
|
||||
Ticker _ticker;
|
||||
float _fDuration = 0;
|
||||
Listener *_listener = nullptr;
|
||||
AudioTrack::Ptr _audioTrack;
|
||||
VideoTrack::Ptr _videoTrack;
|
||||
Ticker _ticker;
|
||||
float _fDuration = 0;
|
||||
};
|
||||
|
||||
} /* namespace mediakit */
|
||||
|
@ -65,17 +65,17 @@ static uint8_t s_mute_adts[] = {0xff, 0xf1, 0x6c, 0x40, 0x2d, 0x3f, 0xfc, 0x00,
|
||||
PlayerProxy::PlayerProxy(const string &strVhost,
|
||||
const string &strApp,
|
||||
const string &strSrc,
|
||||
bool bEnableRtsp,
|
||||
bool bEnableRtmp,
|
||||
bool bEnableRtsp,
|
||||
bool bEnableRtmp,
|
||||
bool bEnableHls,
|
||||
bool bEnableMp4,
|
||||
int iRetryCount,
|
||||
const EventPoller::Ptr &poller) : MediaPlayer(poller){
|
||||
_strVhost = strVhost;
|
||||
_strApp = strApp;
|
||||
_strSrc = strSrc;
|
||||
_bEnableRtsp = bEnableRtsp;
|
||||
_bEnableRtmp = bEnableRtmp;
|
||||
const EventPoller::Ptr &poller) : MediaPlayer(poller){
|
||||
_strVhost = strVhost;
|
||||
_strApp = strApp;
|
||||
_strSrc = strSrc;
|
||||
_bEnableRtsp = bEnableRtsp;
|
||||
_bEnableRtmp = bEnableRtmp;
|
||||
_bEnableHls = bEnableHls;
|
||||
_bEnableMp4 = bEnableMp4;
|
||||
_iRetryCount = iRetryCount;
|
||||
@ -90,88 +90,88 @@ void PlayerProxy::setOnClose(const function<void()> &cb){
|
||||
}
|
||||
|
||||
void PlayerProxy::play(const string &strUrlTmp) {
|
||||
weak_ptr<PlayerProxy> weakSelf = shared_from_this();
|
||||
std::shared_ptr<int> piFailedCnt(new int(0)); //连续播放失败次数
|
||||
setOnPlayResult([weakSelf,strUrlTmp,piFailedCnt](const SockException &err) {
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return;
|
||||
}
|
||||
weak_ptr<PlayerProxy> weakSelf = shared_from_this();
|
||||
std::shared_ptr<int> piFailedCnt(new int(0)); //连续播放失败次数
|
||||
setOnPlayResult([weakSelf,strUrlTmp,piFailedCnt](const SockException &err) {
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(strongSelf->_playCB) {
|
||||
if(strongSelf->_playCB) {
|
||||
strongSelf->_playCB(err);
|
||||
strongSelf->_playCB = nullptr;
|
||||
}
|
||||
|
||||
if(!err) {
|
||||
// 播放成功
|
||||
*piFailedCnt = 0;//连续播放失败次数清0
|
||||
strongSelf->onPlaySuccess();
|
||||
}else if(*piFailedCnt < strongSelf->_iRetryCount || strongSelf->_iRetryCount < 0) {
|
||||
// 播放失败,延时重试播放
|
||||
strongSelf->rePlay(strUrlTmp,(*piFailedCnt)++);
|
||||
}
|
||||
});
|
||||
setOnShutdown([weakSelf,strUrlTmp,piFailedCnt](const SockException &err) {
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return;
|
||||
}
|
||||
if(strongSelf->_mediaMuxer) {
|
||||
auto tracks = strongSelf->getTracks(false);
|
||||
for (auto & track : tracks){
|
||||
track->delDelegate(strongSelf->_mediaMuxer.get());
|
||||
}
|
||||
if(!err) {
|
||||
// 播放成功
|
||||
*piFailedCnt = 0;//连续播放失败次数清0
|
||||
strongSelf->onPlaySuccess();
|
||||
}else if(*piFailedCnt < strongSelf->_iRetryCount || strongSelf->_iRetryCount < 0) {
|
||||
// 播放失败,延时重试播放
|
||||
strongSelf->rePlay(strUrlTmp,(*piFailedCnt)++);
|
||||
}
|
||||
});
|
||||
setOnShutdown([weakSelf,strUrlTmp,piFailedCnt](const SockException &err) {
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return;
|
||||
}
|
||||
if(strongSelf->_mediaMuxer) {
|
||||
auto tracks = strongSelf->getTracks(false);
|
||||
for (auto & track : tracks){
|
||||
track->delDelegate(strongSelf->_mediaMuxer.get());
|
||||
}
|
||||
|
||||
GET_CONFIG(bool,resetWhenRePlay,General::kResetWhenRePlay);
|
||||
if (resetWhenRePlay) {
|
||||
strongSelf->_mediaMuxer.reset();
|
||||
} else {
|
||||
strongSelf->_mediaMuxer->resetTracks();
|
||||
}
|
||||
}
|
||||
//播放异常中断,延时重试播放
|
||||
if(*piFailedCnt < strongSelf->_iRetryCount || strongSelf->_iRetryCount < 0) {
|
||||
strongSelf->rePlay(strUrlTmp,(*piFailedCnt)++);
|
||||
}
|
||||
});
|
||||
MediaPlayer::play(strUrlTmp);
|
||||
GET_CONFIG(bool,resetWhenRePlay,General::kResetWhenRePlay);
|
||||
if (resetWhenRePlay) {
|
||||
strongSelf->_mediaMuxer.reset();
|
||||
} else {
|
||||
strongSelf->_mediaMuxer->resetTracks();
|
||||
}
|
||||
}
|
||||
//播放异常中断,延时重试播放
|
||||
if(*piFailedCnt < strongSelf->_iRetryCount || strongSelf->_iRetryCount < 0) {
|
||||
strongSelf->rePlay(strUrlTmp,(*piFailedCnt)++);
|
||||
}
|
||||
});
|
||||
MediaPlayer::play(strUrlTmp);
|
||||
|
||||
MediaSource::Ptr mediaSource;
|
||||
if(dynamic_pointer_cast<RtspPlayer>(_delegate)){
|
||||
//rtsp拉流
|
||||
GET_CONFIG(bool,directProxy,Rtsp::kDirectProxy);
|
||||
if(directProxy && _bEnableRtsp){
|
||||
mediaSource = std::make_shared<RtspMediaSource>(_strVhost,_strApp,_strSrc);
|
||||
}
|
||||
} else if(dynamic_pointer_cast<RtmpPlayer>(_delegate)){
|
||||
//rtmp拉流
|
||||
if(_bEnableRtmp){
|
||||
mediaSource = std::make_shared<RtmpMediaSource>(_strVhost,_strApp,_strSrc);
|
||||
}
|
||||
}
|
||||
if(mediaSource){
|
||||
setMediaSouce(mediaSource);
|
||||
mediaSource->setListener(shared_from_this());
|
||||
}
|
||||
MediaSource::Ptr mediaSource;
|
||||
if(dynamic_pointer_cast<RtspPlayer>(_delegate)){
|
||||
//rtsp拉流
|
||||
GET_CONFIG(bool,directProxy,Rtsp::kDirectProxy);
|
||||
if(directProxy && _bEnableRtsp){
|
||||
mediaSource = std::make_shared<RtspMediaSource>(_strVhost,_strApp,_strSrc);
|
||||
}
|
||||
} else if(dynamic_pointer_cast<RtmpPlayer>(_delegate)){
|
||||
//rtmp拉流
|
||||
if(_bEnableRtmp){
|
||||
mediaSource = std::make_shared<RtmpMediaSource>(_strVhost,_strApp,_strSrc);
|
||||
}
|
||||
}
|
||||
if(mediaSource){
|
||||
setMediaSouce(mediaSource);
|
||||
mediaSource->setListener(shared_from_this());
|
||||
}
|
||||
}
|
||||
|
||||
PlayerProxy::~PlayerProxy() {
|
||||
_timer.reset();
|
||||
_timer.reset();
|
||||
}
|
||||
void PlayerProxy::rePlay(const string &strUrl,int iFailedCnt){
|
||||
auto iDelay = MAX(2 * 1000, MIN(iFailedCnt * 3000, 60*1000));
|
||||
weak_ptr<PlayerProxy> weakSelf = shared_from_this();
|
||||
_timer = std::make_shared<Timer>(iDelay / 1000.0f,[weakSelf,strUrl,iFailedCnt]() {
|
||||
//播放失败次数越多,则延时越长
|
||||
auto strongPlayer = weakSelf.lock();
|
||||
if(!strongPlayer) {
|
||||
return false;
|
||||
}
|
||||
WarnL << "重试播放[" << iFailedCnt << "]:" << strUrl;
|
||||
strongPlayer->MediaPlayer::play(strUrl);
|
||||
return false;
|
||||
}, getPoller());
|
||||
auto iDelay = MAX(2 * 1000, MIN(iFailedCnt * 3000, 60*1000));
|
||||
weak_ptr<PlayerProxy> weakSelf = shared_from_this();
|
||||
_timer = std::make_shared<Timer>(iDelay / 1000.0f,[weakSelf,strUrl,iFailedCnt]() {
|
||||
//播放失败次数越多,则延时越长
|
||||
auto strongPlayer = weakSelf.lock();
|
||||
if(!strongPlayer) {
|
||||
return false;
|
||||
}
|
||||
WarnL << "重试播放[" << iFailedCnt << "]:" << strUrl;
|
||||
strongPlayer->MediaPlayer::play(strUrl);
|
||||
return false;
|
||||
}, getPoller());
|
||||
}
|
||||
|
||||
bool PlayerProxy::close(MediaSource &sender,bool force) {
|
||||
@ -179,19 +179,19 @@ bool PlayerProxy::close(MediaSource &sender,bool force) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//通知其停止推流
|
||||
weak_ptr<PlayerProxy> weakSlef = dynamic_pointer_cast<PlayerProxy>(shared_from_this());
|
||||
getPoller()->async_first([weakSlef]() {
|
||||
auto stronSelf = weakSlef.lock();
|
||||
if (stronSelf) {
|
||||
stronSelf->_mediaMuxer.reset();
|
||||
stronSelf->setMediaSouce(nullptr);
|
||||
stronSelf->teardown();
|
||||
if(stronSelf->_onClose){
|
||||
//通知其停止推流
|
||||
weak_ptr<PlayerProxy> weakSlef = dynamic_pointer_cast<PlayerProxy>(shared_from_this());
|
||||
getPoller()->async_first([weakSlef]() {
|
||||
auto stronSelf = weakSlef.lock();
|
||||
if (stronSelf) {
|
||||
stronSelf->_mediaMuxer.reset();
|
||||
stronSelf->setMediaSouce(nullptr);
|
||||
stronSelf->teardown();
|
||||
if(stronSelf->_onClose){
|
||||
stronSelf->_onClose();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
WarnL << sender.getSchema() << "/" << sender.getVhost() << "/" << sender.getApp() << "/" << sender.getId() << " " << force;
|
||||
return true;
|
||||
}
|
||||
@ -208,90 +208,90 @@ int PlayerProxy::totalReaderCount(){
|
||||
}
|
||||
|
||||
int PlayerProxy::totalReaderCount(MediaSource &sender) {
|
||||
return totalReaderCount();
|
||||
return totalReaderCount();
|
||||
}
|
||||
|
||||
class MuteAudioMaker : public FrameDispatcher{
|
||||
public:
|
||||
typedef std::shared_ptr<MuteAudioMaker> Ptr;
|
||||
typedef std::shared_ptr<MuteAudioMaker> Ptr;
|
||||
|
||||
MuteAudioMaker(){};
|
||||
virtual ~MuteAudioMaker(){}
|
||||
void inputFrame(const Frame::Ptr &frame) override {
|
||||
if(frame->getTrackType() == TrackVideo){
|
||||
auto iAudioIndex = frame->dts() / MUTE_ADTS_DATA_MS;
|
||||
if(_iAudioIndex != iAudioIndex){
|
||||
_iAudioIndex = iAudioIndex;
|
||||
auto aacFrame = std::make_shared<AACFrameCacheAble>((char *)MUTE_ADTS_DATA, MUTE_ADTS_DATA_LEN, _iAudioIndex * MUTE_ADTS_DATA_MS);
|
||||
FrameDispatcher::inputFrame(aacFrame);
|
||||
}
|
||||
}
|
||||
}
|
||||
MuteAudioMaker(){};
|
||||
virtual ~MuteAudioMaker(){}
|
||||
void inputFrame(const Frame::Ptr &frame) override {
|
||||
if(frame->getTrackType() == TrackVideo){
|
||||
auto iAudioIndex = frame->dts() / MUTE_ADTS_DATA_MS;
|
||||
if(_iAudioIndex != iAudioIndex){
|
||||
_iAudioIndex = iAudioIndex;
|
||||
auto aacFrame = std::make_shared<AACFrameCacheAble>((char *)MUTE_ADTS_DATA, MUTE_ADTS_DATA_LEN, _iAudioIndex * MUTE_ADTS_DATA_MS);
|
||||
FrameDispatcher::inputFrame(aacFrame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
class AACFrameCacheAble : public AACFrameNoCacheAble{
|
||||
public:
|
||||
template <typename ... ARGS>
|
||||
AACFrameCacheAble(ARGS && ...args) : AACFrameNoCacheAble(std::forward<ARGS>(args)...){};
|
||||
virtual ~AACFrameCacheAble() = default;
|
||||
class AACFrameCacheAble : public AACFrameNoCacheAble{
|
||||
public:
|
||||
template <typename ... ARGS>
|
||||
AACFrameCacheAble(ARGS && ...args) : AACFrameNoCacheAble(std::forward<ARGS>(args)...){};
|
||||
virtual ~AACFrameCacheAble() = default;
|
||||
|
||||
bool cacheAble() const override {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
bool cacheAble() const override {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
private:
|
||||
int _iAudioIndex = 0;
|
||||
int _iAudioIndex = 0;
|
||||
};
|
||||
|
||||
void PlayerProxy::onPlaySuccess() {
|
||||
GET_CONFIG(bool,resetWhenRePlay,General::kResetWhenRePlay);
|
||||
if (dynamic_pointer_cast<RtspMediaSource>(_pMediaSrc)) {
|
||||
//rtsp拉流代理
|
||||
if (resetWhenRePlay || !_mediaMuxer) {
|
||||
_mediaMuxer.reset(new MultiMediaSourceMuxer(_strVhost, _strApp, _strSrc, getDuration(), false, _bEnableRtmp, _bEnableHls, _bEnableMp4));
|
||||
}
|
||||
} else if (dynamic_pointer_cast<RtmpMediaSource>(_pMediaSrc)) {
|
||||
//rtmp拉流代理
|
||||
if (resetWhenRePlay || !_mediaMuxer) {
|
||||
_mediaMuxer.reset(new MultiMediaSourceMuxer(_strVhost, _strApp, _strSrc, getDuration(), _bEnableRtsp, false, _bEnableHls, _bEnableMp4));
|
||||
}
|
||||
} else {
|
||||
//其他拉流代理
|
||||
if (resetWhenRePlay || !_mediaMuxer) {
|
||||
_mediaMuxer.reset(new MultiMediaSourceMuxer(_strVhost, _strApp, _strSrc, getDuration(), _bEnableRtsp, _bEnableRtmp, _bEnableHls, _bEnableMp4));
|
||||
}
|
||||
}
|
||||
_mediaMuxer->setListener(shared_from_this());
|
||||
GET_CONFIG(bool,resetWhenRePlay,General::kResetWhenRePlay);
|
||||
if (dynamic_pointer_cast<RtspMediaSource>(_pMediaSrc)) {
|
||||
//rtsp拉流代理
|
||||
if (resetWhenRePlay || !_mediaMuxer) {
|
||||
_mediaMuxer.reset(new MultiMediaSourceMuxer(_strVhost, _strApp, _strSrc, getDuration(), false, _bEnableRtmp, _bEnableHls, _bEnableMp4));
|
||||
}
|
||||
} else if (dynamic_pointer_cast<RtmpMediaSource>(_pMediaSrc)) {
|
||||
//rtmp拉流代理
|
||||
if (resetWhenRePlay || !_mediaMuxer) {
|
||||
_mediaMuxer.reset(new MultiMediaSourceMuxer(_strVhost, _strApp, _strSrc, getDuration(), _bEnableRtsp, false, _bEnableHls, _bEnableMp4));
|
||||
}
|
||||
} else {
|
||||
//其他拉流代理
|
||||
if (resetWhenRePlay || !_mediaMuxer) {
|
||||
_mediaMuxer.reset(new MultiMediaSourceMuxer(_strVhost, _strApp, _strSrc, getDuration(), _bEnableRtsp, _bEnableRtmp, _bEnableHls, _bEnableMp4));
|
||||
}
|
||||
}
|
||||
_mediaMuxer->setListener(shared_from_this());
|
||||
|
||||
auto videoTrack = getTrack(TrackVideo,false);
|
||||
if(videoTrack){
|
||||
//添加视频
|
||||
_mediaMuxer->addTrack(videoTrack);
|
||||
//视频数据写入_mediaMuxer
|
||||
videoTrack->addDelegate(_mediaMuxer);
|
||||
}
|
||||
auto videoTrack = getTrack(TrackVideo,false);
|
||||
if(videoTrack){
|
||||
//添加视频
|
||||
_mediaMuxer->addTrack(videoTrack);
|
||||
//视频数据写入_mediaMuxer
|
||||
videoTrack->addDelegate(_mediaMuxer);
|
||||
}
|
||||
|
||||
//是否添加静音音频
|
||||
GET_CONFIG(bool,addMuteAudio,General::kAddMuteAudio);
|
||||
//是否添加静音音频
|
||||
GET_CONFIG(bool,addMuteAudio,General::kAddMuteAudio);
|
||||
|
||||
auto audioTrack = getTrack(TrackAudio, false);
|
||||
if(audioTrack){
|
||||
//添加音频
|
||||
_mediaMuxer->addTrack(audioTrack);
|
||||
//音频数据写入_mediaMuxer
|
||||
auto audioTrack = getTrack(TrackAudio, false);
|
||||
if(audioTrack){
|
||||
//添加音频
|
||||
_mediaMuxer->addTrack(audioTrack);
|
||||
//音频数据写入_mediaMuxer
|
||||
audioTrack->addDelegate(_mediaMuxer);
|
||||
}else if(addMuteAudio && videoTrack){
|
||||
//没有音频信息,产生一个静音音频
|
||||
MuteAudioMaker::Ptr audioMaker = std::make_shared<MuteAudioMaker>();
|
||||
//videoTrack把数据写入MuteAudioMaker
|
||||
videoTrack->addDelegate(audioMaker);
|
||||
//添加一个静音Track至_mediaMuxer
|
||||
_mediaMuxer->addTrack(std::make_shared<AACTrack>());
|
||||
//MuteAudioMaker生成静音音频然后写入_mediaMuxer;
|
||||
audioMaker->addDelegate(_mediaMuxer);
|
||||
}
|
||||
//没有音频信息,产生一个静音音频
|
||||
MuteAudioMaker::Ptr audioMaker = std::make_shared<MuteAudioMaker>();
|
||||
//videoTrack把数据写入MuteAudioMaker
|
||||
videoTrack->addDelegate(audioMaker);
|
||||
//添加一个静音Track至_mediaMuxer
|
||||
_mediaMuxer->addTrack(std::make_shared<AACTrack>());
|
||||
//MuteAudioMaker生成静音音频然后写入_mediaMuxer;
|
||||
audioMaker->addDelegate(_mediaMuxer);
|
||||
}
|
||||
|
||||
//添加完毕所有track,防止单track情况下最大等待3秒
|
||||
//添加完毕所有track,防止单track情况下最大等待3秒
|
||||
_mediaMuxer->addTrackCompleted();
|
||||
|
||||
if(_pMediaSrc){
|
||||
|
@ -39,24 +39,24 @@ using namespace toolkit;
|
||||
namespace mediakit {
|
||||
|
||||
class PlayerProxy :public MediaPlayer,
|
||||
public std::enable_shared_from_this<PlayerProxy> ,
|
||||
public MediaSourceEvent{
|
||||
public std::enable_shared_from_this<PlayerProxy> ,
|
||||
public MediaSourceEvent{
|
||||
public:
|
||||
typedef std::shared_ptr<PlayerProxy> Ptr;
|
||||
typedef std::shared_ptr<PlayerProxy> Ptr;
|
||||
|
||||
//如果iRetryCount<0,则一直重试播放;否则重试iRetryCount次数
|
||||
//默认一直重试
|
||||
PlayerProxy(const string &strVhost,
|
||||
PlayerProxy(const string &strVhost,
|
||||
const string &strApp,
|
||||
const string &strSrc,
|
||||
bool bEnableRtsp = true,
|
||||
bool bEnableRtmp = true,
|
||||
bool bEnableRtsp = true,
|
||||
bool bEnableRtmp = true,
|
||||
bool bEnableHls = true,
|
||||
bool bEnableMp4 = false,
|
||||
int iRetryCount = -1,
|
||||
const EventPoller::Ptr &poller = nullptr);
|
||||
const EventPoller::Ptr &poller = nullptr);
|
||||
|
||||
virtual ~PlayerProxy();
|
||||
virtual ~PlayerProxy();
|
||||
|
||||
/**
|
||||
* 设置play结果回调,只触发一次;在play执行之前有效
|
||||
@ -81,19 +81,19 @@ public:
|
||||
*/
|
||||
int totalReaderCount() ;
|
||||
private:
|
||||
//MediaSourceEvent override
|
||||
bool close(MediaSource &sender,bool force) override;
|
||||
//MediaSourceEvent override
|
||||
bool close(MediaSource &sender,bool force) override;
|
||||
void onNoneReader(MediaSource &sender) override;
|
||||
int totalReaderCount(MediaSource &sender) override;
|
||||
void rePlay(const string &strUrl,int iFailedCnt);
|
||||
void onPlaySuccess();
|
||||
int totalReaderCount(MediaSource &sender) override;
|
||||
void rePlay(const string &strUrl,int iFailedCnt);
|
||||
void onPlaySuccess();
|
||||
private:
|
||||
bool _bEnableRtsp;
|
||||
bool _bEnableRtmp;
|
||||
bool _bEnableHls;
|
||||
bool _bEnableMp4;
|
||||
bool _bEnableHls;
|
||||
bool _bEnableMp4;
|
||||
int _iRetryCount;
|
||||
MultiMediaSourceMuxer::Ptr _mediaMuxer;
|
||||
MultiMediaSourceMuxer::Ptr _mediaMuxer;
|
||||
string _strVhost;
|
||||
string _strApp;
|
||||
string _strSrc;
|
||||
|
@ -44,16 +44,16 @@ public:
|
||||
virtual ~HlsMediaSource() = default;
|
||||
|
||||
/**
|
||||
* 获取媒体源的环形缓冲
|
||||
*/
|
||||
* 获取媒体源的环形缓冲
|
||||
*/
|
||||
const RingType::Ptr &getRing() const {
|
||||
return _ring;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取播放器个数
|
||||
* @return
|
||||
*/
|
||||
* 获取播放器个数
|
||||
* @return
|
||||
*/
|
||||
int readerCount() override {
|
||||
return _readerCount.load();
|
||||
}
|
||||
|
@ -192,7 +192,7 @@ void MP4Muxer::addTrack(const Track::Ptr &track) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct mpeg4_avc_t avc = {0};
|
||||
struct mpeg4_avc_t avc = {0};
|
||||
string sps_pps = string("\x00\x00\x00\x01", 4) + h264_track->getSps() +
|
||||
string("\x00\x00\x00\x01", 4) + h264_track->getPps();
|
||||
h264_annexbtomp4(&avc, sps_pps.data(), sps_pps.size(), NULL, 0, NULL, NULL);
|
||||
@ -230,7 +230,7 @@ void MP4Muxer::addTrack(const Track::Ptr &track) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct mpeg4_hevc_t hevc = {0};
|
||||
struct mpeg4_hevc_t hevc = {0};
|
||||
string vps_sps_pps = string("\x00\x00\x00\x01", 4) + h265_track->getVps() +
|
||||
string("\x00\x00\x00\x01", 4) + h265_track->getSps() +
|
||||
string("\x00\x00\x00\x01", 4) + h265_track->getPps();
|
||||
|
@ -39,118 +39,118 @@ namespace mediakit {
|
||||
|
||||
#ifdef ENABLE_MP4V2
|
||||
MP4Reader::MP4Reader(const string &strVhost,const string &strApp, const string &strId,const string &filePath ) {
|
||||
_poller = WorkThreadPool::Instance().getPoller();
|
||||
_poller = WorkThreadPool::Instance().getPoller();
|
||||
auto strFileName = filePath;
|
||||
if(strFileName.empty()){
|
||||
GET_CONFIG(string,recordPath,Record::kFilePath);
|
||||
GET_CONFIG(string,recordPath,Record::kFilePath);
|
||||
GET_CONFIG(bool,enableVhost,General::kEnableVhost);
|
||||
if(enableVhost){
|
||||
strFileName = strVhost + "/" + strApp + "/" + strId;
|
||||
}else{
|
||||
strFileName = strApp + "/" + strId;
|
||||
}
|
||||
strFileName = File::absolutePath(strFileName,recordPath);
|
||||
strFileName = File::absolutePath(strFileName,recordPath);
|
||||
}
|
||||
|
||||
_hMP4File = MP4Read(strFileName.data());
|
||||
if(_hMP4File == MP4_INVALID_FILE_HANDLE){
|
||||
throw runtime_error(StrPrinter << "打开MP4文件失败:" << strFileName << endl);
|
||||
}
|
||||
_video_trId = MP4FindTrackId(_hMP4File, 0, MP4_VIDEO_TRACK_TYPE, 0);
|
||||
if(_video_trId != MP4_INVALID_TRACK_ID){
|
||||
if(strcmp(MP4GetTrackMediaDataName(_hMP4File, _video_trId),"avc1") ==0){
|
||||
auto _video_timescale = MP4GetTrackTimeScale(_hMP4File, _video_trId);
|
||||
auto _video_duration = MP4GetTrackDuration(_hMP4File, _video_trId);
|
||||
_video_num_samples = MP4GetTrackNumberOfSamples(_hMP4File, _video_trId);
|
||||
_video_sample_max_size = MP4GetTrackMaxSampleSize(_hMP4File, _video_trId);
|
||||
_video_width = MP4GetTrackVideoWidth(_hMP4File, _video_trId);
|
||||
_video_height = MP4GetTrackVideoHeight(_hMP4File, _video_trId);
|
||||
_video_framerate = MP4GetTrackVideoFrameRate(_hMP4File, _video_trId);
|
||||
_pcVideoSample = std::shared_ptr<uint8_t> (new uint8_t[_video_sample_max_size],[](uint8_t *ptr){
|
||||
delete [] ptr;
|
||||
});
|
||||
uint8_t **seqheader;
|
||||
uint8_t **pictheader;
|
||||
uint32_t *pictheadersize;
|
||||
uint32_t *seqheadersize;
|
||||
uint32_t ix;
|
||||
if(MP4GetTrackH264SeqPictHeaders(_hMP4File, _video_trId, &seqheader, &seqheadersize, &pictheader, &pictheadersize)){
|
||||
for (ix = 0; seqheadersize[ix] != 0; ix++) {
|
||||
_strSps.assign((char *)(seqheader[ix]), seqheadersize[ix]);
|
||||
float framerate;
|
||||
getAVCInfo(_strSps, (int &)_video_width, (int &)_video_height, framerate);
|
||||
_video_framerate = framerate;
|
||||
_strSps = string("\x0\x0\x0\x1",4) + _strSps;
|
||||
MP4Free(seqheader[ix]);
|
||||
}
|
||||
MP4Free(seqheader);
|
||||
MP4Free(seqheadersize);
|
||||
for (ix = 0; pictheadersize[ix] != 0; ix++) {
|
||||
_strPps.assign("\x0\x0\x0\x1",4);
|
||||
_strPps.append((char *)(pictheader[ix]), pictheadersize[ix]);
|
||||
MP4Free(pictheader[ix]);
|
||||
}
|
||||
MP4Free(pictheader);
|
||||
MP4Free(pictheadersize);
|
||||
}
|
||||
_video_ms = 1000.0 * _video_duration / _video_timescale;
|
||||
/*InfoL << "\r\n"
|
||||
<< _video_ms << "\r\n"
|
||||
<< _video_num_samples << "\r\n"
|
||||
<< _video_framerate << "\r\n"
|
||||
<< _video_width << "\r\n"
|
||||
<< _video_height << "\r\n";*/
|
||||
} else {
|
||||
//如果不是h264,则忽略
|
||||
_video_trId = MP4_INVALID_TRACK_ID;
|
||||
}
|
||||
}
|
||||
_hMP4File = MP4Read(strFileName.data());
|
||||
if(_hMP4File == MP4_INVALID_FILE_HANDLE){
|
||||
throw runtime_error(StrPrinter << "打开MP4文件失败:" << strFileName << endl);
|
||||
}
|
||||
_video_trId = MP4FindTrackId(_hMP4File, 0, MP4_VIDEO_TRACK_TYPE, 0);
|
||||
if(_video_trId != MP4_INVALID_TRACK_ID){
|
||||
if(strcmp(MP4GetTrackMediaDataName(_hMP4File, _video_trId),"avc1") ==0){
|
||||
auto _video_timescale = MP4GetTrackTimeScale(_hMP4File, _video_trId);
|
||||
auto _video_duration = MP4GetTrackDuration(_hMP4File, _video_trId);
|
||||
_video_num_samples = MP4GetTrackNumberOfSamples(_hMP4File, _video_trId);
|
||||
_video_sample_max_size = MP4GetTrackMaxSampleSize(_hMP4File, _video_trId);
|
||||
_video_width = MP4GetTrackVideoWidth(_hMP4File, _video_trId);
|
||||
_video_height = MP4GetTrackVideoHeight(_hMP4File, _video_trId);
|
||||
_video_framerate = MP4GetTrackVideoFrameRate(_hMP4File, _video_trId);
|
||||
_pcVideoSample = std::shared_ptr<uint8_t> (new uint8_t[_video_sample_max_size],[](uint8_t *ptr){
|
||||
delete [] ptr;
|
||||
});
|
||||
uint8_t **seqheader;
|
||||
uint8_t **pictheader;
|
||||
uint32_t *pictheadersize;
|
||||
uint32_t *seqheadersize;
|
||||
uint32_t ix;
|
||||
if(MP4GetTrackH264SeqPictHeaders(_hMP4File, _video_trId, &seqheader, &seqheadersize, &pictheader, &pictheadersize)){
|
||||
for (ix = 0; seqheadersize[ix] != 0; ix++) {
|
||||
_strSps.assign((char *)(seqheader[ix]), seqheadersize[ix]);
|
||||
float framerate;
|
||||
getAVCInfo(_strSps, (int &)_video_width, (int &)_video_height, framerate);
|
||||
_video_framerate = framerate;
|
||||
_strSps = string("\x0\x0\x0\x1",4) + _strSps;
|
||||
MP4Free(seqheader[ix]);
|
||||
}
|
||||
MP4Free(seqheader);
|
||||
MP4Free(seqheadersize);
|
||||
for (ix = 0; pictheadersize[ix] != 0; ix++) {
|
||||
_strPps.assign("\x0\x0\x0\x1",4);
|
||||
_strPps.append((char *)(pictheader[ix]), pictheadersize[ix]);
|
||||
MP4Free(pictheader[ix]);
|
||||
}
|
||||
MP4Free(pictheader);
|
||||
MP4Free(pictheadersize);
|
||||
}
|
||||
_video_ms = 1000.0 * _video_duration / _video_timescale;
|
||||
/*InfoL << "\r\n"
|
||||
<< _video_ms << "\r\n"
|
||||
<< _video_num_samples << "\r\n"
|
||||
<< _video_framerate << "\r\n"
|
||||
<< _video_width << "\r\n"
|
||||
<< _video_height << "\r\n";*/
|
||||
} else {
|
||||
//如果不是h264,则忽略
|
||||
_video_trId = MP4_INVALID_TRACK_ID;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_audio_trId = MP4FindTrackId(_hMP4File, 0, MP4_AUDIO_TRACK_TYPE, 0);
|
||||
if (_audio_trId != MP4_INVALID_TRACK_ID) {
|
||||
if (strcmp(MP4GetTrackMediaDataName(_hMP4File, _audio_trId), "mp4a") == 0) {
|
||||
_audio_sample_rate = MP4GetTrackTimeScale(_hMP4File, _audio_trId);
|
||||
auto _audio_duration = MP4GetTrackDuration(_hMP4File, _audio_trId);
|
||||
_audio_num_samples = MP4GetTrackNumberOfSamples(_hMP4File,_audio_trId);
|
||||
_audio_num_channels = MP4GetTrackAudioChannels(_hMP4File, _audio_trId);
|
||||
_audio_sample_max_size = MP4GetTrackMaxSampleSize(_hMP4File,_audio_trId);
|
||||
uint8_t *ppConfig;
|
||||
uint32_t pConfigSize;
|
||||
if(MP4GetTrackESConfiguration(_hMP4File,_audio_trId,&ppConfig,&pConfigSize)){
|
||||
_strAacCfg.assign((char *)ppConfig, pConfigSize);
|
||||
makeAdtsHeader(_strAacCfg, _adts);
|
||||
writeAdtsHeader(_adts,_adts.buffer);
|
||||
getAACInfo(_adts, (int &)_audio_sample_rate, (int &)_audio_num_channels);
|
||||
MP4Free(ppConfig);
|
||||
}
|
||||
_audio_ms = 1000.0 * _audio_duration / _audio_sample_rate;
|
||||
/*InfoL << "\r\n"
|
||||
<< _audio_ms << "\r\n"
|
||||
<< _audio_num_samples << "\r\n"
|
||||
<< _audio_num_channels << "\r\n"
|
||||
<< _audio_sample_rate << "\r\n";*/
|
||||
}else{
|
||||
_audio_trId = MP4_INVALID_TRACK_ID;
|
||||
}
|
||||
}
|
||||
if(_audio_trId == MP4_INVALID_TRACK_ID && _video_trId == MP4_INVALID_TRACK_ID){
|
||||
MP4Close(_hMP4File);
|
||||
_hMP4File = MP4_INVALID_FILE_HANDLE;
|
||||
throw runtime_error(StrPrinter << "该MP4文件音视频格式不支持:" << strFileName << endl);
|
||||
}
|
||||
_audio_trId = MP4FindTrackId(_hMP4File, 0, MP4_AUDIO_TRACK_TYPE, 0);
|
||||
if (_audio_trId != MP4_INVALID_TRACK_ID) {
|
||||
if (strcmp(MP4GetTrackMediaDataName(_hMP4File, _audio_trId), "mp4a") == 0) {
|
||||
_audio_sample_rate = MP4GetTrackTimeScale(_hMP4File, _audio_trId);
|
||||
auto _audio_duration = MP4GetTrackDuration(_hMP4File, _audio_trId);
|
||||
_audio_num_samples = MP4GetTrackNumberOfSamples(_hMP4File,_audio_trId);
|
||||
_audio_num_channels = MP4GetTrackAudioChannels(_hMP4File, _audio_trId);
|
||||
_audio_sample_max_size = MP4GetTrackMaxSampleSize(_hMP4File,_audio_trId);
|
||||
uint8_t *ppConfig;
|
||||
uint32_t pConfigSize;
|
||||
if(MP4GetTrackESConfiguration(_hMP4File,_audio_trId,&ppConfig,&pConfigSize)){
|
||||
_strAacCfg.assign((char *)ppConfig, pConfigSize);
|
||||
makeAdtsHeader(_strAacCfg, _adts);
|
||||
writeAdtsHeader(_adts,_adts.buffer);
|
||||
getAACInfo(_adts, (int &)_audio_sample_rate, (int &)_audio_num_channels);
|
||||
MP4Free(ppConfig);
|
||||
}
|
||||
_audio_ms = 1000.0 * _audio_duration / _audio_sample_rate;
|
||||
/*InfoL << "\r\n"
|
||||
<< _audio_ms << "\r\n"
|
||||
<< _audio_num_samples << "\r\n"
|
||||
<< _audio_num_channels << "\r\n"
|
||||
<< _audio_sample_rate << "\r\n";*/
|
||||
}else{
|
||||
_audio_trId = MP4_INVALID_TRACK_ID;
|
||||
}
|
||||
}
|
||||
if(_audio_trId == MP4_INVALID_TRACK_ID && _video_trId == MP4_INVALID_TRACK_ID){
|
||||
MP4Close(_hMP4File);
|
||||
_hMP4File = MP4_INVALID_FILE_HANDLE;
|
||||
throw runtime_error(StrPrinter << "该MP4文件音视频格式不支持:" << strFileName << endl);
|
||||
}
|
||||
|
||||
_iDuration = MAX(_video_ms,_audio_ms);
|
||||
_mediaMuxer.reset(new MultiMediaSourceMuxer(strVhost, strApp, strId, _iDuration / 1000.0, true, true, false, false));
|
||||
if (_audio_trId != MP4_INVALID_TRACK_ID) {
|
||||
AACTrack::Ptr track = std::make_shared<AACTrack>(_strAacCfg);
|
||||
_mediaMuxer->addTrack(track);
|
||||
}
|
||||
_iDuration = MAX(_video_ms,_audio_ms);
|
||||
_mediaMuxer.reset(new MultiMediaSourceMuxer(strVhost, strApp, strId, _iDuration / 1000.0, true, true, false, false));
|
||||
if (_audio_trId != MP4_INVALID_TRACK_ID) {
|
||||
AACTrack::Ptr track = std::make_shared<AACTrack>(_strAacCfg);
|
||||
_mediaMuxer->addTrack(track);
|
||||
}
|
||||
|
||||
if (_video_trId != MP4_INVALID_TRACK_ID) {
|
||||
H264Track::Ptr track = std::make_shared<H264Track>(_strSps,_strPps);
|
||||
_mediaMuxer->addTrack(track);
|
||||
}
|
||||
if (_video_trId != MP4_INVALID_TRACK_ID) {
|
||||
H264Track::Ptr track = std::make_shared<H264Track>(_strSps,_strPps);
|
||||
_mediaMuxer->addTrack(track);
|
||||
}
|
||||
|
||||
//添加完毕所有track,防止单track情况下最大等待3秒
|
||||
_mediaMuxer->addTrackCompleted();
|
||||
@ -158,34 +158,34 @@ MP4Reader::MP4Reader(const string &strVhost,const string &strApp, const string &
|
||||
|
||||
|
||||
MP4Reader::~MP4Reader() {
|
||||
if (_hMP4File != MP4_INVALID_FILE_HANDLE) {
|
||||
MP4Close(_hMP4File);
|
||||
_hMP4File = MP4_INVALID_FILE_HANDLE;
|
||||
}
|
||||
if (_hMP4File != MP4_INVALID_FILE_HANDLE) {
|
||||
MP4Close(_hMP4File);
|
||||
_hMP4File = MP4_INVALID_FILE_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MP4Reader::startReadMP4() {
|
||||
auto strongSelf = shared_from_this();
|
||||
auto strongSelf = shared_from_this();
|
||||
GET_CONFIG(uint32_t,sampleMS,Record::kSampleMS);
|
||||
|
||||
_timer = std::make_shared<Timer>(sampleMS / 1000.0f,[strongSelf](){
|
||||
return strongSelf->readSample(0,false);
|
||||
}, _poller);
|
||||
_timer = std::make_shared<Timer>(sampleMS / 1000.0f,[strongSelf](){
|
||||
return strongSelf->readSample(0,false);
|
||||
}, _poller);
|
||||
|
||||
//先读sampleMS毫秒的数据用于产生MediaSouce
|
||||
readSample(sampleMS, false);
|
||||
_mediaMuxer->setListener(strongSelf);
|
||||
readSample(sampleMS, false);
|
||||
_mediaMuxer->setListener(strongSelf);
|
||||
}
|
||||
bool MP4Reader::seekTo(MediaSource &sender,uint32_t ui32Stamp){
|
||||
seek(ui32Stamp);
|
||||
return true;
|
||||
seek(ui32Stamp);
|
||||
return true;
|
||||
}
|
||||
bool MP4Reader::close(MediaSource &sender,bool force){
|
||||
if(!_mediaMuxer || (!force && _mediaMuxer->totalReaderCount())){
|
||||
return false;
|
||||
}
|
||||
_timer.reset();
|
||||
_timer.reset();
|
||||
WarnL << sender.getSchema() << "/" << sender.getVhost() << "/" << sender.getApp() << "/" << sender.getId() << " " << force;
|
||||
return true;
|
||||
}
|
||||
@ -198,145 +198,145 @@ void MP4Reader::onNoneReader(MediaSource &sender) {
|
||||
}
|
||||
|
||||
int MP4Reader::totalReaderCount(MediaSource &sender) {
|
||||
return _mediaMuxer ? _mediaMuxer->totalReaderCount() : sender.readerCount();
|
||||
return _mediaMuxer ? _mediaMuxer->totalReaderCount() : sender.readerCount();
|
||||
}
|
||||
|
||||
bool MP4Reader::readSample(int iTimeInc,bool justSeekSyncFrame) {
|
||||
TimeTicker();
|
||||
lock_guard<recursive_mutex> lck(_mtx);
|
||||
auto bFlag0 = readVideoSample(iTimeInc,justSeekSyncFrame);//数据没读完
|
||||
auto bFlag1 = readAudioSample(iTimeInc,justSeekSyncFrame);//数据没读完
|
||||
auto bFlag2 = _mediaMuxer->totalReaderCount() > 0;//读取者大于0
|
||||
if((bFlag0 || bFlag1) && bFlag2){
|
||||
_alive.resetTime();
|
||||
}
|
||||
//重头开始循环读取
|
||||
GET_CONFIG(bool,fileRepeat,Record::kFileRepeat);
|
||||
if (fileRepeat && !bFlag0 && !bFlag1) {
|
||||
seek(0);
|
||||
}
|
||||
//DebugL << "alive ...";
|
||||
//3秒延时关闭
|
||||
return _alive.elapsedTime() < 3 * 1000;
|
||||
TimeTicker();
|
||||
lock_guard<recursive_mutex> lck(_mtx);
|
||||
auto bFlag0 = readVideoSample(iTimeInc,justSeekSyncFrame);//数据没读完
|
||||
auto bFlag1 = readAudioSample(iTimeInc,justSeekSyncFrame);//数据没读完
|
||||
auto bFlag2 = _mediaMuxer->totalReaderCount() > 0;//读取者大于0
|
||||
if((bFlag0 || bFlag1) && bFlag2){
|
||||
_alive.resetTime();
|
||||
}
|
||||
//重头开始循环读取
|
||||
GET_CONFIG(bool,fileRepeat,Record::kFileRepeat);
|
||||
if (fileRepeat && !bFlag0 && !bFlag1) {
|
||||
seek(0);
|
||||
}
|
||||
//DebugL << "alive ...";
|
||||
//3秒延时关闭
|
||||
return _alive.elapsedTime() < 3 * 1000;
|
||||
}
|
||||
inline bool MP4Reader::readVideoSample(int iTimeInc,bool justSeekSyncFrame) {
|
||||
if (_video_trId != MP4_INVALID_TRACK_ID) {
|
||||
auto iNextSample = getVideoSampleId(iTimeInc);
|
||||
MP4SampleId iIdx = _video_current;
|
||||
for (; iIdx < iNextSample; iIdx++) {
|
||||
uint8_t *pBytes = _pcVideoSample.get();
|
||||
uint32_t numBytes = _video_sample_max_size;
|
||||
MP4Duration pRenderingOffset;
|
||||
if(MP4ReadSample(_hMP4File, _video_trId, iIdx + 1, &pBytes, &numBytes,NULL,NULL,&pRenderingOffset,&_bSyncSample)){
|
||||
if (_video_trId != MP4_INVALID_TRACK_ID) {
|
||||
auto iNextSample = getVideoSampleId(iTimeInc);
|
||||
MP4SampleId iIdx = _video_current;
|
||||
for (; iIdx < iNextSample; iIdx++) {
|
||||
uint8_t *pBytes = _pcVideoSample.get();
|
||||
uint32_t numBytes = _video_sample_max_size;
|
||||
MP4Duration pRenderingOffset;
|
||||
if(MP4ReadSample(_hMP4File, _video_trId, iIdx + 1, &pBytes, &numBytes,NULL,NULL,&pRenderingOffset,&_bSyncSample)){
|
||||
if (!justSeekSyncFrame) {
|
||||
uint32_t dts = (double) _video_ms * iIdx / _video_num_samples;
|
||||
uint32_t pts = dts + pRenderingOffset / 90;
|
||||
uint32_t iOffset = 0;
|
||||
while (iOffset < numBytes) {
|
||||
uint32_t iFrameLen;
|
||||
memcpy(&iFrameLen,pBytes + iOffset,4);
|
||||
iFrameLen = ntohl(iFrameLen);
|
||||
while (iOffset < numBytes) {
|
||||
uint32_t iFrameLen;
|
||||
memcpy(&iFrameLen,pBytes + iOffset,4);
|
||||
iFrameLen = ntohl(iFrameLen);
|
||||
if(iFrameLen + iOffset + 4> numBytes){
|
||||
break;
|
||||
}
|
||||
memcpy(pBytes + iOffset, "\x0\x0\x0\x1", 4);
|
||||
writeH264(pBytes + iOffset, iFrameLen + 4, dts, pts);
|
||||
iOffset += (iFrameLen + 4);
|
||||
}
|
||||
}else if(_bSyncSample){
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
ErrorL << "读取视频失败:" << iIdx + 1;
|
||||
}
|
||||
}
|
||||
_video_current = iIdx;
|
||||
return _video_current < _video_num_samples;
|
||||
}
|
||||
return false;
|
||||
memcpy(pBytes + iOffset, "\x0\x0\x0\x1", 4);
|
||||
writeH264(pBytes + iOffset, iFrameLen + 4, dts, pts);
|
||||
iOffset += (iFrameLen + 4);
|
||||
}
|
||||
}else if(_bSyncSample){
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
ErrorL << "读取视频失败:" << iIdx + 1;
|
||||
}
|
||||
}
|
||||
_video_current = iIdx;
|
||||
return _video_current < _video_num_samples;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool MP4Reader::readAudioSample(int iTimeInc,bool justSeekSyncFrame) {
|
||||
if (_audio_trId != MP4_INVALID_TRACK_ID) {
|
||||
auto iNextSample = getAudioSampleId(iTimeInc);
|
||||
for (auto i = _audio_current; i < iNextSample; i++) {
|
||||
uint32_t numBytes = _audio_sample_max_size;
|
||||
uint8_t *pBytes = _adts.buffer + 7;
|
||||
if(MP4ReadSample(_hMP4File, _audio_trId, i + 1, &pBytes, &numBytes)){
|
||||
if (!justSeekSyncFrame) {
|
||||
uint32_t dts = (double) _audio_ms * i / _audio_num_samples;
|
||||
_adts.aac_frame_length = 7 + numBytes;
|
||||
writeAdtsHeader(_adts, _adts.buffer);
|
||||
writeAAC(_adts.buffer, _adts.aac_frame_length, dts);
|
||||
}
|
||||
}else{
|
||||
ErrorL << "读取音频失败:" << i+ 1;
|
||||
}
|
||||
}
|
||||
_audio_current = iNextSample;
|
||||
return _audio_current < _audio_num_samples;
|
||||
}
|
||||
return false;
|
||||
if (_audio_trId != MP4_INVALID_TRACK_ID) {
|
||||
auto iNextSample = getAudioSampleId(iTimeInc);
|
||||
for (auto i = _audio_current; i < iNextSample; i++) {
|
||||
uint32_t numBytes = _audio_sample_max_size;
|
||||
uint8_t *pBytes = _adts.buffer + 7;
|
||||
if(MP4ReadSample(_hMP4File, _audio_trId, i + 1, &pBytes, &numBytes)){
|
||||
if (!justSeekSyncFrame) {
|
||||
uint32_t dts = (double) _audio_ms * i / _audio_num_samples;
|
||||
_adts.aac_frame_length = 7 + numBytes;
|
||||
writeAdtsHeader(_adts, _adts.buffer);
|
||||
writeAAC(_adts.buffer, _adts.aac_frame_length, dts);
|
||||
}
|
||||
}else{
|
||||
ErrorL << "读取音频失败:" << i+ 1;
|
||||
}
|
||||
}
|
||||
_audio_current = iNextSample;
|
||||
return _audio_current < _audio_num_samples;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void MP4Reader::writeH264(uint8_t *pucData,int iLen,uint32_t dts,uint32_t pts) {
|
||||
_mediaMuxer->inputFrame(std::make_shared<H264FrameNoCacheAble>((char*)pucData,iLen,dts,pts));
|
||||
_mediaMuxer->inputFrame(std::make_shared<H264FrameNoCacheAble>((char*)pucData,iLen,dts,pts));
|
||||
}
|
||||
|
||||
inline void MP4Reader::writeAAC(uint8_t *pucData,int iLen,uint32_t uiStamp) {
|
||||
_mediaMuxer->inputFrame(std::make_shared<AACFrameNoCacheAble>((char*)pucData,iLen,uiStamp));
|
||||
_mediaMuxer->inputFrame(std::make_shared<AACFrameNoCacheAble>((char*)pucData,iLen,uiStamp));
|
||||
}
|
||||
|
||||
inline MP4SampleId MP4Reader::getVideoSampleId(int iTimeInc ) {
|
||||
MP4SampleId video_current = (double)_video_num_samples * (_iSeekTime + _ticker.elapsedTime() + iTimeInc) / _video_ms;
|
||||
video_current = MAX(0,MIN(_video_num_samples, video_current));
|
||||
return video_current;
|
||||
MP4SampleId video_current = (double)_video_num_samples * (_iSeekTime + _ticker.elapsedTime() + iTimeInc) / _video_ms;
|
||||
video_current = MAX(0,MIN(_video_num_samples, video_current));
|
||||
return video_current;
|
||||
|
||||
}
|
||||
|
||||
inline MP4SampleId MP4Reader::getAudioSampleId(int iTimeInc) {
|
||||
MP4SampleId audio_current = (double)_audio_num_samples * (_iSeekTime + _ticker.elapsedTime() + iTimeInc) / _audio_ms ;
|
||||
audio_current = MAX(0,MIN(_audio_num_samples,audio_current));
|
||||
return audio_current;
|
||||
MP4SampleId audio_current = (double)_audio_num_samples * (_iSeekTime + _ticker.elapsedTime() + iTimeInc) / _audio_ms ;
|
||||
audio_current = MAX(0,MIN(_audio_num_samples,audio_current));
|
||||
return audio_current;
|
||||
}
|
||||
inline void MP4Reader::setSeekTime(uint32_t iSeekTime){
|
||||
_iSeekTime = MAX(0, MIN(iSeekTime,_iDuration));
|
||||
_ticker.resetTime();
|
||||
if (_audio_trId != MP4_INVALID_TRACK_ID) {
|
||||
_audio_current = getAudioSampleId();
|
||||
}
|
||||
if (_video_trId != MP4_INVALID_TRACK_ID) {
|
||||
_video_current = getVideoSampleId();
|
||||
}
|
||||
_iSeekTime = MAX(0, MIN(iSeekTime,_iDuration));
|
||||
_ticker.resetTime();
|
||||
if (_audio_trId != MP4_INVALID_TRACK_ID) {
|
||||
_audio_current = getAudioSampleId();
|
||||
}
|
||||
if (_video_trId != MP4_INVALID_TRACK_ID) {
|
||||
_video_current = getVideoSampleId();
|
||||
}
|
||||
}
|
||||
|
||||
inline uint32_t MP4Reader::getVideoCurrentTime(){
|
||||
return (double)_video_current * _video_ms /_video_num_samples;
|
||||
return (double)_video_current * _video_ms /_video_num_samples;
|
||||
}
|
||||
void MP4Reader::seek(uint32_t iSeekTime,bool bReStart){
|
||||
lock_guard<recursive_mutex> lck(_mtx);
|
||||
if(iSeekTime == 0 || _video_trId == MP4_INVALID_TRACK_ID){
|
||||
setSeekTime(iSeekTime);
|
||||
}else{
|
||||
setSeekTime(iSeekTime - 5000);
|
||||
//在之后的10秒查找关键帧
|
||||
readVideoSample(10000, true);
|
||||
if (_bSyncSample) {
|
||||
//找到关键帧
|
||||
auto iIdr = _video_current;
|
||||
setSeekTime(getVideoCurrentTime());
|
||||
_video_current = iIdr;
|
||||
}else{
|
||||
//未找到关键帧
|
||||
setSeekTime(iSeekTime);
|
||||
}
|
||||
}
|
||||
_mediaMuxer->setTimeStamp(_iSeekTime);
|
||||
lock_guard<recursive_mutex> lck(_mtx);
|
||||
if(iSeekTime == 0 || _video_trId == MP4_INVALID_TRACK_ID){
|
||||
setSeekTime(iSeekTime);
|
||||
}else{
|
||||
setSeekTime(iSeekTime - 5000);
|
||||
//在之后的10秒查找关键帧
|
||||
readVideoSample(10000, true);
|
||||
if (_bSyncSample) {
|
||||
//找到关键帧
|
||||
auto iIdr = _video_current;
|
||||
setSeekTime(getVideoCurrentTime());
|
||||
_video_current = iIdr;
|
||||
}else{
|
||||
//未找到关键帧
|
||||
setSeekTime(iSeekTime);
|
||||
}
|
||||
}
|
||||
_mediaMuxer->setTimeStamp(_iSeekTime);
|
||||
|
||||
if(bReStart){
|
||||
_timer.reset();
|
||||
startReadMP4();
|
||||
}
|
||||
if(bReStart){
|
||||
_timer.reset();
|
||||
startReadMP4();
|
||||
}
|
||||
}
|
||||
|
||||
#endif //ENABLE_MP4V2
|
||||
@ -344,26 +344,26 @@ void MP4Reader::seek(uint32_t iSeekTime,bool bReStart){
|
||||
|
||||
|
||||
MediaSource::Ptr MP4Reader::onMakeMediaSource(const string &strSchema,
|
||||
const string &strVhost,
|
||||
const string &strApp,
|
||||
const string &strId,
|
||||
const string &filePath,
|
||||
bool checkApp ){
|
||||
const string &strVhost,
|
||||
const string &strApp,
|
||||
const string &strId,
|
||||
const string &filePath,
|
||||
bool checkApp ){
|
||||
#ifdef ENABLE_MP4V2
|
||||
GET_CONFIG(string,appName,Record::kAppName);
|
||||
if (checkApp && strApp != appName) {
|
||||
return nullptr;
|
||||
}
|
||||
try {
|
||||
MP4Reader::Ptr pReader(new MP4Reader(strVhost,strApp, strId,filePath));
|
||||
pReader->startReadMP4();
|
||||
return MediaSource::find(strSchema,strVhost,strApp, strId, false);
|
||||
} catch (std::exception &ex) {
|
||||
WarnL << ex.what();
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
try {
|
||||
MP4Reader::Ptr pReader(new MP4Reader(strVhost,strApp, strId,filePath));
|
||||
pReader->startReadMP4();
|
||||
return MediaSource::find(strSchema,strVhost,strApp, strId, false);
|
||||
} catch (std::exception &ex) {
|
||||
WarnL << ex.what();
|
||||
return nullptr;
|
||||
}
|
||||
#else
|
||||
return nullptr;
|
||||
return nullptr;
|
||||
#endif //ENABLE_MP4V2
|
||||
}
|
||||
|
||||
|
@ -39,91 +39,91 @@ namespace mediakit {
|
||||
|
||||
class MP4Reader : public std::enable_shared_from_this<MP4Reader> ,public MediaSourceEvent{
|
||||
public:
|
||||
typedef std::shared_ptr<MP4Reader> Ptr;
|
||||
virtual ~MP4Reader();
|
||||
typedef std::shared_ptr<MP4Reader> Ptr;
|
||||
virtual ~MP4Reader();
|
||||
|
||||
/**
|
||||
* 流化一个mp4文件,使之转换成RtspMediaSource和RtmpMediaSource
|
||||
* @param strVhost 虚拟主机
|
||||
* @param strApp 应用名
|
||||
* @param strId 流id
|
||||
* @param filePath 文件路径,如果为空则根据配置文件和上面参数自动生成,否则使用指定的文件
|
||||
*/
|
||||
MP4Reader(const string &strVhost,const string &strApp, const string &strId,const string &filePath = "");
|
||||
/**
|
||||
* 开始流化MP4文件,需要指出的是,MP4Reader对象一经过调用startReadMP4方法,它的强引用会自持有,
|
||||
* 意思是在文件流化结束之前或中断之前,MP4Reader对象是不会被销毁的(不管有没有被外部对象持有)
|
||||
*/
|
||||
void startReadMP4();
|
||||
/**
|
||||
* 流化一个mp4文件,使之转换成RtspMediaSource和RtmpMediaSource
|
||||
* @param strVhost 虚拟主机
|
||||
* @param strApp 应用名
|
||||
* @param strId 流id
|
||||
* @param filePath 文件路径,如果为空则根据配置文件和上面参数自动生成,否则使用指定的文件
|
||||
*/
|
||||
MP4Reader(const string &strVhost,const string &strApp, const string &strId,const string &filePath = "");
|
||||
/**
|
||||
* 开始流化MP4文件,需要指出的是,MP4Reader对象一经过调用startReadMP4方法,它的强引用会自持有,
|
||||
* 意思是在文件流化结束之前或中断之前,MP4Reader对象是不会被销毁的(不管有没有被外部对象持有)
|
||||
*/
|
||||
void startReadMP4();
|
||||
|
||||
/**
|
||||
* 自动生成MP4Reader对象然后查找相关的MediaSource对象
|
||||
* @param strSchema 协议名
|
||||
* @param strVhost 虚拟主机
|
||||
* @param strApp 应用名
|
||||
* @param strId 流id
|
||||
* @param filePath 文件路径,如果为空则根据配置文件和上面参数自动生成,否则使用指定的文件
|
||||
* @param checkApp 是否检查app,防止服务器上文件被乱访问
|
||||
* @return MediaSource
|
||||
*/
|
||||
static MediaSource::Ptr onMakeMediaSource(const string &strSchema,
|
||||
const string &strVhost,
|
||||
const string &strApp,
|
||||
const string &strId,
|
||||
const string &filePath = "",
|
||||
bool checkApp = true);
|
||||
/**
|
||||
* 自动生成MP4Reader对象然后查找相关的MediaSource对象
|
||||
* @param strSchema 协议名
|
||||
* @param strVhost 虚拟主机
|
||||
* @param strApp 应用名
|
||||
* @param strId 流id
|
||||
* @param filePath 文件路径,如果为空则根据配置文件和上面参数自动生成,否则使用指定的文件
|
||||
* @param checkApp 是否检查app,防止服务器上文件被乱访问
|
||||
* @return MediaSource
|
||||
*/
|
||||
static MediaSource::Ptr onMakeMediaSource(const string &strSchema,
|
||||
const string &strVhost,
|
||||
const string &strApp,
|
||||
const string &strId,
|
||||
const string &filePath = "",
|
||||
bool checkApp = true);
|
||||
|
||||
private:
|
||||
//MediaSourceEvent override
|
||||
bool seekTo(MediaSource &sender,uint32_t ui32Stamp) override;
|
||||
bool close(MediaSource &sender,bool force) override;
|
||||
//MediaSourceEvent override
|
||||
bool seekTo(MediaSource &sender,uint32_t ui32Stamp) override;
|
||||
bool close(MediaSource &sender,bool force) override;
|
||||
void onNoneReader(MediaSource &sender) override;
|
||||
int totalReaderCount(MediaSource &sender) override;
|
||||
int totalReaderCount(MediaSource &sender) override;
|
||||
#ifdef ENABLE_MP4V2
|
||||
void seek(uint32_t iSeekTime,bool bReStart = true);
|
||||
inline void setSeekTime(uint32_t iSeekTime);
|
||||
inline uint32_t getVideoCurrentTime();
|
||||
inline MP4SampleId getVideoSampleId(int iTimeInc = 0);
|
||||
inline MP4SampleId getAudioSampleId(int iTimeInc = 0);
|
||||
bool readSample(int iTimeInc, bool justSeekSyncFrame);
|
||||
inline bool readVideoSample(int iTimeInc,bool justSeekSyncFrame);
|
||||
inline bool readAudioSample(int iTimeInc,bool justSeekSyncFrame);
|
||||
inline void writeH264(uint8_t *pucData,int iLen,uint32_t dts,uint32_t pts);
|
||||
inline void writeAAC(uint8_t *pucData,int iLen,uint32_t uiStamp);
|
||||
inline void setSeekTime(uint32_t iSeekTime);
|
||||
inline uint32_t getVideoCurrentTime();
|
||||
inline MP4SampleId getVideoSampleId(int iTimeInc = 0);
|
||||
inline MP4SampleId getAudioSampleId(int iTimeInc = 0);
|
||||
bool readSample(int iTimeInc, bool justSeekSyncFrame);
|
||||
inline bool readVideoSample(int iTimeInc,bool justSeekSyncFrame);
|
||||
inline bool readAudioSample(int iTimeInc,bool justSeekSyncFrame);
|
||||
inline void writeH264(uint8_t *pucData,int iLen,uint32_t dts,uint32_t pts);
|
||||
inline void writeAAC(uint8_t *pucData,int iLen,uint32_t uiStamp);
|
||||
private:
|
||||
MP4FileHandle _hMP4File = MP4_INVALID_FILE_HANDLE;
|
||||
MP4TrackId _video_trId = MP4_INVALID_TRACK_ID;
|
||||
uint32_t _video_ms = 0;
|
||||
uint32_t _video_num_samples = 0;
|
||||
uint32_t _video_sample_max_size = 0;
|
||||
uint32_t _video_width = 0;
|
||||
uint32_t _video_height = 0;
|
||||
uint32_t _video_framerate = 0;
|
||||
string _strPps;
|
||||
string _strSps;
|
||||
bool _bSyncSample = false;
|
||||
MP4FileHandle _hMP4File = MP4_INVALID_FILE_HANDLE;
|
||||
MP4TrackId _video_trId = MP4_INVALID_TRACK_ID;
|
||||
uint32_t _video_ms = 0;
|
||||
uint32_t _video_num_samples = 0;
|
||||
uint32_t _video_sample_max_size = 0;
|
||||
uint32_t _video_width = 0;
|
||||
uint32_t _video_height = 0;
|
||||
uint32_t _video_framerate = 0;
|
||||
string _strPps;
|
||||
string _strSps;
|
||||
bool _bSyncSample = false;
|
||||
|
||||
MP4TrackId _audio_trId = MP4_INVALID_TRACK_ID;
|
||||
uint32_t _audio_ms = 0;
|
||||
uint32_t _audio_num_samples = 0;
|
||||
uint32_t _audio_sample_max_size = 0;
|
||||
uint32_t _audio_sample_rate = 0;
|
||||
uint32_t _audio_num_channels = 0;
|
||||
string _strAacCfg;
|
||||
AACFrame _adts;
|
||||
MP4TrackId _audio_trId = MP4_INVALID_TRACK_ID;
|
||||
uint32_t _audio_ms = 0;
|
||||
uint32_t _audio_num_samples = 0;
|
||||
uint32_t _audio_sample_max_size = 0;
|
||||
uint32_t _audio_sample_rate = 0;
|
||||
uint32_t _audio_num_channels = 0;
|
||||
string _strAacCfg;
|
||||
AACFrame _adts;
|
||||
|
||||
int _iDuration = 0;
|
||||
MultiMediaSourceMuxer::Ptr _mediaMuxer;
|
||||
MP4SampleId _video_current = 0;
|
||||
MP4SampleId _audio_current = 0;
|
||||
std::shared_ptr<uint8_t> _pcVideoSample;
|
||||
int _iDuration = 0;
|
||||
MultiMediaSourceMuxer::Ptr _mediaMuxer;
|
||||
MP4SampleId _video_current = 0;
|
||||
MP4SampleId _audio_current = 0;
|
||||
std::shared_ptr<uint8_t> _pcVideoSample;
|
||||
|
||||
int _iSeekTime = 0 ;
|
||||
Ticker _ticker;
|
||||
Ticker _alive;
|
||||
recursive_mutex _mtx;
|
||||
Timer::Ptr _timer;
|
||||
EventPoller::Ptr _poller;
|
||||
int _iSeekTime = 0 ;
|
||||
Ticker _ticker;
|
||||
Ticker _alive;
|
||||
recursive_mutex _mtx;
|
||||
Timer::Ptr _timer;
|
||||
EventPoller::Ptr _poller;
|
||||
#endif //ENABLE_MP4V2
|
||||
};
|
||||
|
||||
|
@ -36,31 +36,31 @@ using namespace toolkit;
|
||||
namespace mediakit {
|
||||
|
||||
MP4Recorder::MP4Recorder(const string& strPath,
|
||||
const string &strVhost,
|
||||
const string &strApp,
|
||||
const string &strStreamId) {
|
||||
_strPath = strPath;
|
||||
/////record 业务逻辑//////
|
||||
_info.strAppName = strApp;
|
||||
_info.strStreamId = strStreamId;
|
||||
_info.strVhost = strVhost;
|
||||
_info.strFolder = strPath;
|
||||
const string &strVhost,
|
||||
const string &strApp,
|
||||
const string &strStreamId) {
|
||||
_strPath = strPath;
|
||||
/////record 业务逻辑//////
|
||||
_info.strAppName = strApp;
|
||||
_info.strStreamId = strStreamId;
|
||||
_info.strVhost = strVhost;
|
||||
_info.strFolder = strPath;
|
||||
}
|
||||
MP4Recorder::~MP4Recorder() {
|
||||
closeFile();
|
||||
closeFile();
|
||||
}
|
||||
|
||||
void MP4Recorder::createFile() {
|
||||
closeFile();
|
||||
auto strDate = getTimeStr("%Y-%m-%d");
|
||||
auto strTime = getTimeStr("%H-%M-%S");
|
||||
auto strFileTmp = _strPath + strDate + "/." + strTime + ".mp4";
|
||||
auto strFile = _strPath + strDate + "/" + strTime + ".mp4";
|
||||
closeFile();
|
||||
auto strDate = getTimeStr("%Y-%m-%d");
|
||||
auto strTime = getTimeStr("%H-%M-%S");
|
||||
auto strFileTmp = _strPath + strDate + "/." + strTime + ".mp4";
|
||||
auto strFile = _strPath + strDate + "/" + strTime + ".mp4";
|
||||
|
||||
/////record 业务逻辑//////
|
||||
_info.ui64StartedTime = ::time(NULL);
|
||||
_info.strFileName = strTime + ".mp4";
|
||||
_info.strFilePath = strFile;
|
||||
/////record 业务逻辑//////
|
||||
_info.ui64StartedTime = ::time(NULL);
|
||||
_info.strFileName = strTime + ".mp4";
|
||||
_info.strFilePath = strFile;
|
||||
GET_CONFIG(string,appName,Record::kAppName);
|
||||
_info.strUrl = appName + "/"
|
||||
+ _info.strAppName + "/"
|
||||
@ -68,78 +68,78 @@ void MP4Recorder::createFile() {
|
||||
+ strDate + "/"
|
||||
+ strTime + ".mp4";
|
||||
|
||||
try {
|
||||
_muxer = std::make_shared<MP4MuxerFile>(strFileTmp.data());
|
||||
for(auto &track :_tracks){
|
||||
try {
|
||||
_muxer = std::make_shared<MP4MuxerFile>(strFileTmp.data());
|
||||
for(auto &track :_tracks){
|
||||
//添加track
|
||||
_muxer->addTrack(track);
|
||||
}
|
||||
_strFileTmp = strFileTmp;
|
||||
_strFile = strFile;
|
||||
_createFileTicker.resetTime();
|
||||
}catch(std::exception &ex) {
|
||||
WarnL << ex.what();
|
||||
}
|
||||
}
|
||||
_strFileTmp = strFileTmp;
|
||||
_strFile = strFile;
|
||||
_createFileTicker.resetTime();
|
||||
}catch(std::exception &ex) {
|
||||
WarnL << ex.what();
|
||||
}
|
||||
}
|
||||
|
||||
void MP4Recorder::asyncClose() {
|
||||
auto muxer = _muxer;
|
||||
auto strFileTmp = _strFileTmp;
|
||||
auto strFile = _strFile;
|
||||
auto info = _info;
|
||||
WorkThreadPool::Instance().getExecutor()->async([muxer,strFileTmp,strFile,info]() {
|
||||
//获取文件录制时间,放在关闭mp4之前是为了忽略关闭mp4执行时间
|
||||
const_cast<MP4Info&>(info).ui64TimeLen = ::time(NULL) - info.ui64StartedTime;
|
||||
//关闭mp4非常耗时,所以要放在后台线程执行
|
||||
const_cast<MP4MuxerFile::Ptr &>(muxer).reset();
|
||||
//临时文件名改成正式文件名,防止mp4未完成时被访问
|
||||
rename(strFileTmp.data(),strFile.data());
|
||||
//获取文件大小
|
||||
struct stat fileData;
|
||||
stat(strFile.data(), &fileData);
|
||||
const_cast<MP4Info&>(info).ui64FileSize = fileData.st_size;
|
||||
/////record 业务逻辑//////
|
||||
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastRecordMP4,info);
|
||||
});
|
||||
auto muxer = _muxer;
|
||||
auto strFileTmp = _strFileTmp;
|
||||
auto strFile = _strFile;
|
||||
auto info = _info;
|
||||
WorkThreadPool::Instance().getExecutor()->async([muxer,strFileTmp,strFile,info]() {
|
||||
//获取文件录制时间,放在关闭mp4之前是为了忽略关闭mp4执行时间
|
||||
const_cast<MP4Info&>(info).ui64TimeLen = ::time(NULL) - info.ui64StartedTime;
|
||||
//关闭mp4非常耗时,所以要放在后台线程执行
|
||||
const_cast<MP4MuxerFile::Ptr &>(muxer).reset();
|
||||
//临时文件名改成正式文件名,防止mp4未完成时被访问
|
||||
rename(strFileTmp.data(),strFile.data());
|
||||
//获取文件大小
|
||||
struct stat fileData;
|
||||
stat(strFile.data(), &fileData);
|
||||
const_cast<MP4Info&>(info).ui64FileSize = fileData.st_size;
|
||||
/////record 业务逻辑//////
|
||||
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastRecordMP4,info);
|
||||
});
|
||||
}
|
||||
|
||||
void MP4Recorder::closeFile() {
|
||||
if (_muxer) {
|
||||
asyncClose();
|
||||
_muxer = nullptr;
|
||||
}
|
||||
if (_muxer) {
|
||||
asyncClose();
|
||||
_muxer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void MP4Recorder::inputFrame(const Frame::Ptr &frame) {
|
||||
GET_CONFIG(uint32_t,recordSec,Record::kFileSecond);
|
||||
if(!_muxer || ((_createFileTicker.elapsedTime() > recordSec * 1000) &&
|
||||
(!_haveVideo || (_haveVideo && frame->keyFrame()))) ){
|
||||
//成立条件
|
||||
//1、_muxer为空
|
||||
//2、到了切片时间,并且只有音频
|
||||
//3、到了切片时间,有视频并且遇到视频的关键帧
|
||||
createFile();
|
||||
}
|
||||
GET_CONFIG(uint32_t,recordSec,Record::kFileSecond);
|
||||
if(!_muxer || ((_createFileTicker.elapsedTime() > recordSec * 1000) &&
|
||||
(!_haveVideo || (_haveVideo && frame->keyFrame()))) ){
|
||||
//成立条件
|
||||
//1、_muxer为空
|
||||
//2、到了切片时间,并且只有音频
|
||||
//3、到了切片时间,有视频并且遇到视频的关键帧
|
||||
createFile();
|
||||
}
|
||||
|
||||
if(_muxer){
|
||||
//生成mp4文件
|
||||
_muxer->inputFrame(frame);
|
||||
}
|
||||
if(_muxer){
|
||||
//生成mp4文件
|
||||
_muxer->inputFrame(frame);
|
||||
}
|
||||
}
|
||||
|
||||
void MP4Recorder::addTrack(const Track::Ptr & track){
|
||||
//保存所有的track,为创建MP4MuxerFile做准备
|
||||
_tracks.emplace_back(track);
|
||||
if(track->getTrackType() == TrackVideo){
|
||||
_haveVideo = true;
|
||||
}
|
||||
//保存所有的track,为创建MP4MuxerFile做准备
|
||||
_tracks.emplace_back(track);
|
||||
if(track->getTrackType() == TrackVideo){
|
||||
_haveVideo = true;
|
||||
}
|
||||
}
|
||||
|
||||
void MP4Recorder::resetTracks() {
|
||||
closeFile();
|
||||
_tracks.clear();
|
||||
_haveVideo = false;
|
||||
_createFileTicker.resetTime();
|
||||
closeFile();
|
||||
_tracks.clear();
|
||||
_haveVideo = false;
|
||||
_createFileTicker.resetTime();
|
||||
}
|
||||
|
||||
} /* namespace mediakit */
|
||||
|
@ -42,38 +42,38 @@ namespace mediakit {
|
||||
|
||||
class MP4Info {
|
||||
public:
|
||||
time_t ui64StartedTime; //GMT标准时间,单位秒
|
||||
time_t ui64TimeLen;//录像长度,单位秒
|
||||
off_t ui64FileSize;//文件大小,单位BYTE
|
||||
string strFilePath;//文件路径
|
||||
string strFileName;//文件名称
|
||||
string strFolder;//文件夹路径
|
||||
string strUrl;//播放路径
|
||||
string strAppName;//应用名称
|
||||
string strStreamId;//流ID
|
||||
string strVhost;//vhost
|
||||
time_t ui64StartedTime; //GMT标准时间,单位秒
|
||||
time_t ui64TimeLen;//录像长度,单位秒
|
||||
off_t ui64FileSize;//文件大小,单位BYTE
|
||||
string strFilePath;//文件路径
|
||||
string strFileName;//文件名称
|
||||
string strFolder;//文件夹路径
|
||||
string strUrl;//播放路径
|
||||
string strAppName;//应用名称
|
||||
string strStreamId;//流ID
|
||||
string strVhost;//vhost
|
||||
};
|
||||
|
||||
#ifdef ENABLE_MP4RECORD
|
||||
class MP4Recorder : public MediaSinkInterface{
|
||||
public:
|
||||
typedef std::shared_ptr<MP4Recorder> Ptr;
|
||||
typedef std::shared_ptr<MP4Recorder> Ptr;
|
||||
|
||||
MP4Recorder(const string &strPath,
|
||||
const string &strVhost,
|
||||
const string &strApp,
|
||||
const string &strStreamId);
|
||||
virtual ~MP4Recorder();
|
||||
MP4Recorder(const string &strPath,
|
||||
const string &strVhost,
|
||||
const string &strApp,
|
||||
const string &strStreamId);
|
||||
virtual ~MP4Recorder();
|
||||
|
||||
/**
|
||||
* 重置所有Track
|
||||
*/
|
||||
void resetTracks() override;
|
||||
/**
|
||||
* 重置所有Track
|
||||
*/
|
||||
void resetTracks() override;
|
||||
|
||||
/**
|
||||
/**
|
||||
* 输入frame
|
||||
*/
|
||||
void inputFrame(const Frame::Ptr &frame) override;
|
||||
void inputFrame(const Frame::Ptr &frame) override;
|
||||
|
||||
/**
|
||||
* 添加ready状态的track
|
||||
@ -84,14 +84,14 @@ private:
|
||||
void closeFile();
|
||||
void asyncClose();
|
||||
private:
|
||||
string _strPath;
|
||||
string _strFile;
|
||||
string _strFileTmp;
|
||||
Ticker _createFileTicker;
|
||||
MP4Info _info;
|
||||
bool _haveVideo = false;
|
||||
MP4MuxerFile::Ptr _muxer;
|
||||
list<Track::Ptr> _tracks;
|
||||
string _strPath;
|
||||
string _strFile;
|
||||
string _strFileTmp;
|
||||
Ticker _createFileTicker;
|
||||
MP4Info _info;
|
||||
bool _haveVideo = false;
|
||||
MP4MuxerFile::Ptr _muxer;
|
||||
list<Track::Ptr> _tracks;
|
||||
};
|
||||
|
||||
#endif ///ENABLE_MP4RECORD
|
||||
|
@ -37,32 +37,32 @@ class MediaSinkInterface;
|
||||
|
||||
class Recorder{
|
||||
public:
|
||||
typedef enum {
|
||||
// 未录制
|
||||
status_not_record = 0,
|
||||
// 等待MediaSource注册,注册成功后立即开始录制
|
||||
status_wait_record = 1,
|
||||
// MediaSource已注册,并且正在录制
|
||||
status_recording = 2,
|
||||
} status;
|
||||
typedef enum {
|
||||
// 未录制
|
||||
status_not_record = 0,
|
||||
// 等待MediaSource注册,注册成功后立即开始录制
|
||||
status_wait_record = 1,
|
||||
// MediaSource已注册,并且正在录制
|
||||
status_recording = 2,
|
||||
} status;
|
||||
|
||||
typedef enum {
|
||||
// 录制hls
|
||||
type_hls = 0,
|
||||
// 录制MP4
|
||||
type_mp4 = 1
|
||||
} type;
|
||||
typedef enum {
|
||||
// 录制hls
|
||||
type_hls = 0,
|
||||
// 录制MP4
|
||||
type_mp4 = 1
|
||||
} type;
|
||||
|
||||
/**
|
||||
* 获取录制文件绝对路径
|
||||
* @param type hls还是MP4录制
|
||||
/**
|
||||
* 获取录制文件绝对路径
|
||||
* @param type hls还是MP4录制
|
||||
* @param vhost 虚拟主机
|
||||
* @param app 应用名
|
||||
* @param stream_id 流id
|
||||
* @param customized_path 录像文件保存自定义目录,默认为空则自动生成
|
||||
* @return 录制文件绝对路径
|
||||
*/
|
||||
static string getRecordPath(type type, const string &vhost, const string &app, const string &stream_id,const string &customized_path = "");
|
||||
* @return 录制文件绝对路径
|
||||
*/
|
||||
static string getRecordPath(type type, const string &vhost, const string &app, const string &stream_id,const string &customized_path = "");
|
||||
|
||||
/**
|
||||
* 获取录制状态
|
||||
@ -72,57 +72,57 @@ public:
|
||||
* @param stream_id 流id
|
||||
* @return 录制状态
|
||||
*/
|
||||
static status getRecordStatus(type type, const string &vhost, const string &app, const string &stream_id);
|
||||
static status getRecordStatus(type type, const string &vhost, const string &app, const string &stream_id);
|
||||
|
||||
/**
|
||||
* 开始录制
|
||||
* @param type hls还是MP4录制
|
||||
/**
|
||||
* 开始录制
|
||||
* @param type hls还是MP4录制
|
||||
* @param vhost 虚拟主机
|
||||
* @param app 应用名
|
||||
* @param stream_id 流id
|
||||
* @param customized_path 录像文件保存自定义目录,默认为空则自动生成
|
||||
* @param waitForRecord 是否等待流注册后再录制,未注册时,置false将返回失败
|
||||
* @param continueRecord 流注销时是否继续等待录制还是立即停止录制
|
||||
* @return 0代表成功,负数代表失败
|
||||
*/
|
||||
static int startRecord(type type, const string &vhost, const string &app, const string &stream_id,const string &customized_path,bool waitForRecord, bool continueRecord);
|
||||
* @param waitForRecord 是否等待流注册后再录制,未注册时,置false将返回失败
|
||||
* @param continueRecord 流注销时是否继续等待录制还是立即停止录制
|
||||
* @return 0代表成功,负数代表失败
|
||||
*/
|
||||
static int startRecord(type type, const string &vhost, const string &app, const string &stream_id,const string &customized_path,bool waitForRecord, bool continueRecord);
|
||||
|
||||
/**
|
||||
* 停止录制
|
||||
* @param type hls还是MP4录制
|
||||
/**
|
||||
* 停止录制
|
||||
* @param type hls还是MP4录制
|
||||
* @param vhost 虚拟主机
|
||||
* @param app 应用名
|
||||
* @param stream_id 流id
|
||||
*/
|
||||
static bool stopRecord(type type, const string &vhost, const string &app, const string &stream_id);
|
||||
*/
|
||||
static bool stopRecord(type type, const string &vhost, const string &app, const string &stream_id);
|
||||
|
||||
/**
|
||||
* 停止所有录制,一般程序退出时调用
|
||||
*/
|
||||
static void stopAll();
|
||||
/**
|
||||
* 停止所有录制,一般程序退出时调用
|
||||
*/
|
||||
static void stopAll();
|
||||
|
||||
/**
|
||||
* 获取录制对象
|
||||
* @param type hls还是MP4录制
|
||||
/**
|
||||
* 获取录制对象
|
||||
* @param type hls还是MP4录制
|
||||
* @param vhost 虚拟主机
|
||||
* @param app 应用名
|
||||
* @param stream_id 流id
|
||||
*/
|
||||
static std::shared_ptr<MediaSinkInterface> getRecorder(type type, const string &vhost, const string &app, const string &stream_id);
|
||||
*/
|
||||
static std::shared_ptr<MediaSinkInterface> getRecorder(type type, const string &vhost, const string &app, const string &stream_id);
|
||||
|
||||
/**
|
||||
* 创建录制器对象
|
||||
* @param type hls还是MP4录制
|
||||
/**
|
||||
* 创建录制器对象
|
||||
* @param type hls还是MP4录制
|
||||
* @param vhost 虚拟主机
|
||||
* @param app 应用名
|
||||
* @param stream_id 流id
|
||||
* @param customized_path 录像文件保存自定义目录,默认为空则自动生成
|
||||
* @return 对象指针,可能为nullptr
|
||||
*/
|
||||
* @return 对象指针,可能为nullptr
|
||||
*/
|
||||
static std::shared_ptr<MediaSinkInterface> createRecorder(type type, const string &vhost, const string &app, const string &stream_id, const string &customized_path);
|
||||
private:
|
||||
Recorder() = delete;
|
||||
~Recorder() = delete;
|
||||
Recorder() = delete;
|
||||
~Recorder() = delete;
|
||||
};
|
||||
|
||||
} /* namespace mediakit */
|
||||
|
@ -41,27 +41,27 @@ namespace mediakit {
|
||||
|
||||
class RtmpDemuxer : public Demuxer{
|
||||
public:
|
||||
typedef std::shared_ptr<RtmpDemuxer> Ptr;
|
||||
typedef std::shared_ptr<RtmpDemuxer> Ptr;
|
||||
|
||||
RtmpDemuxer() = default;
|
||||
virtual ~RtmpDemuxer() = default;
|
||||
RtmpDemuxer() = default;
|
||||
virtual ~RtmpDemuxer() = default;
|
||||
|
||||
void loadMetaData(const AMFValue &metadata);
|
||||
void loadMetaData(const AMFValue &metadata);
|
||||
|
||||
/**
|
||||
* 开始解复用
|
||||
* @param pkt rtmp包
|
||||
* @return true 代表是i帧
|
||||
*/
|
||||
bool inputRtmp(const RtmpPacket::Ptr &pkt);
|
||||
/**
|
||||
* 开始解复用
|
||||
* @param pkt rtmp包
|
||||
* @return true 代表是i帧
|
||||
*/
|
||||
bool inputRtmp(const RtmpPacket::Ptr &pkt);
|
||||
private:
|
||||
void makeVideoTrack(const AMFValue &val);
|
||||
void makeAudioTrack(const AMFValue &val);
|
||||
void makeVideoTrack(const AMFValue &val);
|
||||
void makeAudioTrack(const AMFValue &val);
|
||||
private:
|
||||
bool _tryedGetVideoTrack = false;
|
||||
bool _tryedGetAudioTrack = false;
|
||||
RtmpCodec::Ptr _audioRtmpDecoder;
|
||||
RtmpCodec::Ptr _videoRtmpDecoder;
|
||||
bool _tryedGetVideoTrack = false;
|
||||
bool _tryedGetAudioTrack = false;
|
||||
RtmpCodec::Ptr _audioRtmpDecoder;
|
||||
RtmpCodec::Ptr _videoRtmpDecoder;
|
||||
};
|
||||
|
||||
} /* namespace mediakit */
|
||||
|
@ -58,163 +58,163 @@ namespace mediakit {
|
||||
*/
|
||||
class RtmpMediaSource : public MediaSource, public RingDelegate<RtmpPacket::Ptr> {
|
||||
public:
|
||||
typedef std::shared_ptr<RtmpMediaSource> Ptr;
|
||||
typedef RingBuffer<RtmpPacket::Ptr> RingType;
|
||||
typedef std::shared_ptr<RtmpMediaSource> Ptr;
|
||||
typedef RingBuffer<RtmpPacket::Ptr> RingType;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param vhost 虚拟主机名
|
||||
* @param app 应用名
|
||||
* @param stream_id 流id
|
||||
* @param ring_size 可以设置固定的环形缓冲大小,0则自适应
|
||||
*/
|
||||
RtmpMediaSource(const string &vhost,
|
||||
const string &app,
|
||||
const string &stream_id,
|
||||
int ring_size = RTMP_GOP_SIZE) :
|
||||
MediaSource(RTMP_SCHEMA, vhost, app, stream_id), _ring_size(ring_size) {
|
||||
}
|
||||
/**
|
||||
* 构造函数
|
||||
* @param vhost 虚拟主机名
|
||||
* @param app 应用名
|
||||
* @param stream_id 流id
|
||||
* @param ring_size 可以设置固定的环形缓冲大小,0则自适应
|
||||
*/
|
||||
RtmpMediaSource(const string &vhost,
|
||||
const string &app,
|
||||
const string &stream_id,
|
||||
int ring_size = RTMP_GOP_SIZE) :
|
||||
MediaSource(RTMP_SCHEMA, vhost, app, stream_id), _ring_size(ring_size) {
|
||||
}
|
||||
|
||||
virtual ~RtmpMediaSource() {}
|
||||
virtual ~RtmpMediaSource() {}
|
||||
|
||||
/**
|
||||
* 获取媒体源的环形缓冲
|
||||
*/
|
||||
const RingType::Ptr &getRing() const {
|
||||
return _ring;
|
||||
}
|
||||
/**
|
||||
* 获取媒体源的环形缓冲
|
||||
*/
|
||||
const RingType::Ptr &getRing() const {
|
||||
return _ring;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取播放器个数
|
||||
* @return
|
||||
*/
|
||||
int readerCount() override {
|
||||
return _ring ? _ring->readerCount() : 0;
|
||||
}
|
||||
/**
|
||||
* 获取播放器个数
|
||||
* @return
|
||||
*/
|
||||
int readerCount() override {
|
||||
return _ring ? _ring->readerCount() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取metadata
|
||||
*/
|
||||
const AMFValue &getMetaData() const {
|
||||
lock_guard<recursive_mutex> lock(_mtx);
|
||||
return _metadata;
|
||||
}
|
||||
/**
|
||||
* 获取metadata
|
||||
*/
|
||||
const AMFValue &getMetaData() const {
|
||||
lock_guard<recursive_mutex> lock(_mtx);
|
||||
return _metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有的config帧
|
||||
*/
|
||||
template<typename FUNC>
|
||||
void getConfigFrame(const FUNC &f) {
|
||||
lock_guard<recursive_mutex> lock(_mtx);
|
||||
for (auto &pr : _config_frame_map) {
|
||||
f(pr.second);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 获取所有的config帧
|
||||
*/
|
||||
template<typename FUNC>
|
||||
void getConfigFrame(const FUNC &f) {
|
||||
lock_guard<recursive_mutex> lock(_mtx);
|
||||
for (auto &pr : _config_frame_map) {
|
||||
f(pr.second);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置metadata
|
||||
*/
|
||||
virtual void setMetaData(const AMFValue &metadata) {
|
||||
lock_guard<recursive_mutex> lock(_mtx);
|
||||
_metadata = metadata;
|
||||
if(_ring){
|
||||
regist();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 设置metadata
|
||||
*/
|
||||
virtual void setMetaData(const AMFValue &metadata) {
|
||||
lock_guard<recursive_mutex> lock(_mtx);
|
||||
_metadata = metadata;
|
||||
if(_ring){
|
||||
regist();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 输入rtmp包
|
||||
* @param pkt rtmp包
|
||||
* @param key 是否为关键帧
|
||||
*/
|
||||
void onWrite(const RtmpPacket::Ptr &pkt, bool key = true) override {
|
||||
lock_guard<recursive_mutex> lock(_mtx);
|
||||
if(pkt->typeId == MSG_VIDEO){
|
||||
//有视频,那么启用GOP缓存
|
||||
/**
|
||||
* 输入rtmp包
|
||||
* @param pkt rtmp包
|
||||
* @param key 是否为关键帧
|
||||
*/
|
||||
void onWrite(const RtmpPacket::Ptr &pkt, bool key = true) override {
|
||||
lock_guard<recursive_mutex> lock(_mtx);
|
||||
if(pkt->typeId == MSG_VIDEO){
|
||||
//有视频,那么启用GOP缓存
|
||||
_have_video = true;
|
||||
}
|
||||
if (pkt->isCfgFrame()) {
|
||||
_config_frame_map[pkt->typeId] = pkt;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (pkt->isCfgFrame()) {
|
||||
_config_frame_map[pkt->typeId] = pkt;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_ring) {
|
||||
weak_ptr<RtmpMediaSource> weakSelf = dynamic_pointer_cast<RtmpMediaSource>(shared_from_this());
|
||||
auto lam = [weakSelf](const EventPoller::Ptr &, int size, bool) {
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if (!strongSelf) {
|
||||
return;
|
||||
}
|
||||
strongSelf->onReaderChanged(size);
|
||||
};
|
||||
if (!_ring) {
|
||||
weak_ptr<RtmpMediaSource> weakSelf = dynamic_pointer_cast<RtmpMediaSource>(shared_from_this());
|
||||
auto lam = [weakSelf](const EventPoller::Ptr &, int size, bool) {
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if (!strongSelf) {
|
||||
return;
|
||||
}
|
||||
strongSelf->onReaderChanged(size);
|
||||
};
|
||||
|
||||
//rtmp包缓存最大允许512个,如果是纯视频(25fps)大概为20秒数据
|
||||
//但是这个是GOP缓存的上限值,真实的GOP缓存大小等于两个I帧之间的包数的两倍
|
||||
//而且每次遇到I帧,则会清空GOP缓存,所以真实的GOP缓存远小于此值
|
||||
_ring = std::make_shared<RingType>(_ring_size,std::move(lam));
|
||||
onReaderChanged(0);
|
||||
//rtmp包缓存最大允许512个,如果是纯视频(25fps)大概为20秒数据
|
||||
//但是这个是GOP缓存的上限值,真实的GOP缓存大小等于两个I帧之间的包数的两倍
|
||||
//而且每次遇到I帧,则会清空GOP缓存,所以真实的GOP缓存远小于此值
|
||||
_ring = std::make_shared<RingType>(_ring_size,std::move(lam));
|
||||
onReaderChanged(0);
|
||||
|
||||
if(_metadata){
|
||||
regist();
|
||||
}
|
||||
}
|
||||
_track_stamps_map[pkt->typeId] = pkt->timeStamp;
|
||||
//不存在视频,为了减少缓存延时,那么关闭GOP缓存
|
||||
_ring->write(pkt, _have_video ? pkt->isVideoKeyFrame() : true);
|
||||
checkNoneReader();
|
||||
}
|
||||
if(_metadata){
|
||||
regist();
|
||||
}
|
||||
}
|
||||
_track_stamps_map[pkt->typeId] = pkt->timeStamp;
|
||||
//不存在视频,为了减少缓存延时,那么关闭GOP缓存
|
||||
_ring->write(pkt, _have_video ? pkt->isVideoKeyFrame() : true);
|
||||
checkNoneReader();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前时间戳
|
||||
*/
|
||||
uint32_t getTimeStamp(TrackType trackType) override {
|
||||
lock_guard<recursive_mutex> lock(_mtx);
|
||||
switch (trackType) {
|
||||
case TrackVideo:
|
||||
return _track_stamps_map[MSG_VIDEO];
|
||||
case TrackAudio:
|
||||
return _track_stamps_map[MSG_AUDIO];
|
||||
default:
|
||||
return MAX(_track_stamps_map[MSG_VIDEO], _track_stamps_map[MSG_AUDIO]);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 获取当前时间戳
|
||||
*/
|
||||
uint32_t getTimeStamp(TrackType trackType) override {
|
||||
lock_guard<recursive_mutex> lock(_mtx);
|
||||
switch (trackType) {
|
||||
case TrackVideo:
|
||||
return _track_stamps_map[MSG_VIDEO];
|
||||
case TrackAudio:
|
||||
return _track_stamps_map[MSG_AUDIO];
|
||||
default:
|
||||
return MAX(_track_stamps_map[MSG_VIDEO], _track_stamps_map[MSG_AUDIO]);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* 每次增减消费者都会触发该函数
|
||||
*/
|
||||
void onReaderChanged(int size) {
|
||||
//我们记录最后一次活动时间
|
||||
_reader_changed_ticker.resetTime();
|
||||
if (size != 0 || totalReaderCount() != 0) {
|
||||
//还有消费者正在观看该流
|
||||
_async_emit_none_reader = false;
|
||||
return;
|
||||
}
|
||||
_async_emit_none_reader = true;
|
||||
}
|
||||
/**
|
||||
* 每次增减消费者都会触发该函数
|
||||
*/
|
||||
void onReaderChanged(int size) {
|
||||
//我们记录最后一次活动时间
|
||||
_reader_changed_ticker.resetTime();
|
||||
if (size != 0 || totalReaderCount() != 0) {
|
||||
//还有消费者正在观看该流
|
||||
_async_emit_none_reader = false;
|
||||
return;
|
||||
}
|
||||
_async_emit_none_reader = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否无人消费该流,
|
||||
* 如果无人消费且超过一定时间会触发onNoneReader事件
|
||||
*/
|
||||
void checkNoneReader() {
|
||||
GET_CONFIG(int, stream_none_reader_delay, General::kStreamNoneReaderDelayMS);
|
||||
if (_async_emit_none_reader && _reader_changed_ticker.elapsedTime() > stream_none_reader_delay) {
|
||||
_async_emit_none_reader = false;
|
||||
onNoneReader();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 检查是否无人消费该流,
|
||||
* 如果无人消费且超过一定时间会触发onNoneReader事件
|
||||
*/
|
||||
void checkNoneReader() {
|
||||
GET_CONFIG(int, stream_none_reader_delay, General::kStreamNoneReaderDelayMS);
|
||||
if (_async_emit_none_reader && _reader_changed_ticker.elapsedTime() > stream_none_reader_delay) {
|
||||
_async_emit_none_reader = false;
|
||||
onNoneReader();
|
||||
}
|
||||
}
|
||||
protected:
|
||||
int _ring_size;
|
||||
bool _async_emit_none_reader = false;
|
||||
bool _have_video = false;
|
||||
mutable recursive_mutex _mtx;
|
||||
Ticker _reader_changed_ticker;
|
||||
AMFValue _metadata;
|
||||
RingBuffer<RtmpPacket::Ptr>::Ptr _ring;
|
||||
unordered_map<int, uint32_t> _track_stamps_map;
|
||||
unordered_map<int, RtmpPacket::Ptr> _config_frame_map;
|
||||
int _ring_size;
|
||||
bool _async_emit_none_reader = false;
|
||||
bool _have_video = false;
|
||||
mutable recursive_mutex _mtx;
|
||||
Ticker _reader_changed_ticker;
|
||||
AMFValue _metadata;
|
||||
RingBuffer<RtmpPacket::Ptr>::Ptr _ring;
|
||||
unordered_map<int, uint32_t> _track_stamps_map;
|
||||
unordered_map<int, RtmpPacket::Ptr> _config_frame_map;
|
||||
};
|
||||
|
||||
} /* namespace mediakit */
|
||||
|
@ -45,46 +45,46 @@ using namespace toolkit;
|
||||
namespace mediakit {
|
||||
class RtmpMediaSourceImp: public RtmpMediaSource, public Demuxer::Listener , public MultiMediaSourceMuxer::Listener {
|
||||
public:
|
||||
typedef std::shared_ptr<RtmpMediaSourceImp> Ptr;
|
||||
typedef std::shared_ptr<RtmpMediaSourceImp> Ptr;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param vhost 虚拟主机
|
||||
* @param app 应用名
|
||||
* @param id 流id
|
||||
* @param ringSize 环形缓存大小
|
||||
*/
|
||||
RtmpMediaSourceImp(const string &vhost, const string &app, const string &id, int ringSize = RTMP_GOP_SIZE) : RtmpMediaSource(vhost, app, id, ringSize) {
|
||||
_demuxer = std::make_shared<RtmpDemuxer>();
|
||||
_demuxer->setTrackListener(this);
|
||||
}
|
||||
/**
|
||||
* 构造函数
|
||||
* @param vhost 虚拟主机
|
||||
* @param app 应用名
|
||||
* @param id 流id
|
||||
* @param ringSize 环形缓存大小
|
||||
*/
|
||||
RtmpMediaSourceImp(const string &vhost, const string &app, const string &id, int ringSize = RTMP_GOP_SIZE) : RtmpMediaSource(vhost, app, id, ringSize) {
|
||||
_demuxer = std::make_shared<RtmpDemuxer>();
|
||||
_demuxer->setTrackListener(this);
|
||||
}
|
||||
|
||||
~RtmpMediaSourceImp() = default;
|
||||
~RtmpMediaSourceImp() = default;
|
||||
|
||||
/**
|
||||
* 设置metadata
|
||||
*/
|
||||
void setMetaData(const AMFValue &metadata) override{
|
||||
_demuxer->loadMetaData(metadata);
|
||||
RtmpMediaSource::setMetaData(metadata);
|
||||
}
|
||||
/**
|
||||
* 设置metadata
|
||||
*/
|
||||
void setMetaData(const AMFValue &metadata) override{
|
||||
_demuxer->loadMetaData(metadata);
|
||||
RtmpMediaSource::setMetaData(metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* 输入rtmp并解析
|
||||
*/
|
||||
void onWrite(const RtmpPacket::Ptr &pkt,bool key_pos = true) override {
|
||||
key_pos = _demuxer->inputRtmp(pkt);
|
||||
RtmpMediaSource::onWrite(pkt,key_pos);
|
||||
}
|
||||
/**
|
||||
* 输入rtmp并解析
|
||||
*/
|
||||
void onWrite(const RtmpPacket::Ptr &pkt,bool key_pos = true) override {
|
||||
key_pos = _demuxer->inputRtmp(pkt);
|
||||
RtmpMediaSource::onWrite(pkt,key_pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置监听器
|
||||
* @param listener
|
||||
*/
|
||||
void setListener(const std::weak_ptr<MediaSourceEvent> &listener) override {
|
||||
/**
|
||||
* 设置监听器
|
||||
* @param listener
|
||||
*/
|
||||
void setListener(const std::weak_ptr<MediaSourceEvent> &listener) override {
|
||||
RtmpMediaSource::setListener(listener);
|
||||
if(_muxer){
|
||||
_muxer->setListener(listener);
|
||||
_muxer->setListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,42 +95,42 @@ public:
|
||||
return readerCount() + (_muxer ? _muxer->totalReaderCount() : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置协议转换
|
||||
* @param enableRtsp 是否转换成rtsp
|
||||
* @param enableHls 是否转换成hls
|
||||
* @param enableMP4 是否mp4录制
|
||||
*/
|
||||
void setProtocolTranslation(bool enableRtsp, bool enableHls, bool enableMP4) {
|
||||
//不重复生成rtmp
|
||||
_muxer = std::make_shared<MultiMediaSourceMuxer>(getVhost(), getApp(), getId(), _demuxer->getDuration(), enableRtsp, false, enableHls, enableMP4);
|
||||
_muxer->setListener(getListener());
|
||||
_muxer->setTrackListener(this);
|
||||
/**
|
||||
* 设置协议转换
|
||||
* @param enableRtsp 是否转换成rtsp
|
||||
* @param enableHls 是否转换成hls
|
||||
* @param enableMP4 是否mp4录制
|
||||
*/
|
||||
void setProtocolTranslation(bool enableRtsp, bool enableHls, bool enableMP4) {
|
||||
//不重复生成rtmp
|
||||
_muxer = std::make_shared<MultiMediaSourceMuxer>(getVhost(), getApp(), getId(), _demuxer->getDuration(), enableRtsp, false, enableHls, enableMP4);
|
||||
_muxer->setListener(getListener());
|
||||
_muxer->setTrackListener(this);
|
||||
for(auto &track : _demuxer->getTracks(false)){
|
||||
_muxer->addTrack(track);
|
||||
track->addDelegate(_muxer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* _demuxer触发的添加Track事件
|
||||
*/
|
||||
void onAddTrack(const Track::Ptr &track) override {
|
||||
if(_muxer){
|
||||
_muxer->addTrack(track);
|
||||
track->addDelegate(_muxer);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* _demuxer触发的添加Track事件
|
||||
*/
|
||||
void onAddTrack(const Track::Ptr &track) override {
|
||||
if(_muxer){
|
||||
_muxer->addTrack(track);
|
||||
track->addDelegate(_muxer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* _muxer触发的所有Track就绪的事件
|
||||
*/
|
||||
void onAllTrackReady() override{
|
||||
setTrackSource(_muxer);
|
||||
}
|
||||
/**
|
||||
* _muxer触发的所有Track就绪的事件
|
||||
*/
|
||||
void onAllTrackReady() override{
|
||||
setTrackSource(_muxer);
|
||||
}
|
||||
private:
|
||||
RtmpDemuxer::Ptr _demuxer;
|
||||
MultiMediaSourceMuxer::Ptr _muxer;
|
||||
RtmpDemuxer::Ptr _demuxer;
|
||||
MultiMediaSourceMuxer::Ptr _muxer;
|
||||
};
|
||||
} /* namespace mediakit */
|
||||
|
||||
|
@ -38,35 +38,35 @@ RtmpPlayer::RtmpPlayer(const EventPoller::Ptr &poller) : TcpClient(poller) {
|
||||
}
|
||||
|
||||
RtmpPlayer::~RtmpPlayer() {
|
||||
DebugL << endl;
|
||||
DebugL << endl;
|
||||
}
|
||||
|
||||
void RtmpPlayer::teardown() {
|
||||
if (alive()) {
|
||||
shutdown(SockException(Err_shutdown,"teardown"));
|
||||
}
|
||||
_strApp.clear();
|
||||
_strStream.clear();
|
||||
_strTcUrl.clear();
|
||||
_pBeatTimer.reset();
|
||||
_pPlayTimer.reset();
|
||||
_pMediaTimer.reset();
|
||||
_iSeekTo = 0;
|
||||
RtmpProtocol::reset();
|
||||
if (alive()) {
|
||||
shutdown(SockException(Err_shutdown,"teardown"));
|
||||
}
|
||||
_strApp.clear();
|
||||
_strStream.clear();
|
||||
_strTcUrl.clear();
|
||||
_pBeatTimer.reset();
|
||||
_pPlayTimer.reset();
|
||||
_pMediaTimer.reset();
|
||||
_iSeekTo = 0;
|
||||
RtmpProtocol::reset();
|
||||
|
||||
CLEAR_ARR(_aiFistStamp);
|
||||
CLEAR_ARR(_aiNowStamp);
|
||||
CLEAR_ARR(_aiFistStamp);
|
||||
CLEAR_ARR(_aiNowStamp);
|
||||
|
||||
lock_guard<recursive_mutex> lck(_mtxOnResultCB);
|
||||
_mapOnResultCB.clear();
|
||||
lock_guard<recursive_mutex> lck2(_mtxOnStatusCB);
|
||||
_dqOnStatusCB.clear();
|
||||
lock_guard<recursive_mutex> lck(_mtxOnResultCB);
|
||||
_mapOnResultCB.clear();
|
||||
lock_guard<recursive_mutex> lck2(_mtxOnStatusCB);
|
||||
_dqOnStatusCB.clear();
|
||||
}
|
||||
|
||||
void RtmpPlayer::play(const string &strUrl) {
|
||||
teardown();
|
||||
string strHost = FindField(strUrl.data(), "://", "/");
|
||||
_strApp = FindField(strUrl.data(), (strHost + "/").data(), "/");
|
||||
teardown();
|
||||
string strHost = FindField(strUrl.data(), "://", "/");
|
||||
_strApp = FindField(strUrl.data(), (strHost + "/").data(), "/");
|
||||
_strStream = FindField(strUrl.data(), (strHost + "/" + _strApp + "/").data(), NULL);
|
||||
_strTcUrl = string("rtmp://") + strHost + "/" + _strApp;
|
||||
|
||||
@ -74,48 +74,48 @@ void RtmpPlayer::play(const string &strUrl) {
|
||||
onPlayResult_l(SockException(Err_other,"rtmp url非法"),false);
|
||||
return;
|
||||
}
|
||||
DebugL << strHost << " " << _strApp << " " << _strStream;
|
||||
DebugL << strHost << " " << _strApp << " " << _strStream;
|
||||
|
||||
auto iPort = atoi(FindField(strHost.data(), ":", NULL).data());
|
||||
if (iPort <= 0) {
|
||||
auto iPort = atoi(FindField(strHost.data(), ":", NULL).data());
|
||||
if (iPort <= 0) {
|
||||
//rtmp 默认端口1935
|
||||
iPort = 1935;
|
||||
} else {
|
||||
iPort = 1935;
|
||||
} else {
|
||||
//服务器域名
|
||||
strHost = FindField(strHost.data(), NULL, ":");
|
||||
}
|
||||
if(!(*this)[kNetAdapter].empty()){
|
||||
setNetAdapter((*this)[kNetAdapter]);
|
||||
}
|
||||
strHost = FindField(strHost.data(), NULL, ":");
|
||||
}
|
||||
if(!(*this)[kNetAdapter].empty()){
|
||||
setNetAdapter((*this)[kNetAdapter]);
|
||||
}
|
||||
|
||||
weak_ptr<RtmpPlayer> weakSelf= dynamic_pointer_cast<RtmpPlayer>(shared_from_this());
|
||||
float playTimeOutSec = (*this)[kTimeoutMS].as<int>() / 1000.0;
|
||||
_pPlayTimer.reset( new Timer(playTimeOutSec, [weakSelf]() {
|
||||
auto strongSelf=weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return false;
|
||||
}
|
||||
strongSelf->onPlayResult_l(SockException(Err_timeout,"play rtmp timeout"),false);
|
||||
return false;
|
||||
},getPoller()));
|
||||
weak_ptr<RtmpPlayer> weakSelf= dynamic_pointer_cast<RtmpPlayer>(shared_from_this());
|
||||
float playTimeOutSec = (*this)[kTimeoutMS].as<int>() / 1000.0;
|
||||
_pPlayTimer.reset( new Timer(playTimeOutSec, [weakSelf]() {
|
||||
auto strongSelf=weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return false;
|
||||
}
|
||||
strongSelf->onPlayResult_l(SockException(Err_timeout,"play rtmp timeout"),false);
|
||||
return false;
|
||||
},getPoller()));
|
||||
|
||||
_metadata_got = false;
|
||||
startConnect(strHost, iPort , playTimeOutSec);
|
||||
_metadata_got = false;
|
||||
startConnect(strHost, iPort , playTimeOutSec);
|
||||
}
|
||||
void RtmpPlayer::onErr(const SockException &ex){
|
||||
//定时器_pPlayTimer为空后表明握手结束了
|
||||
onPlayResult_l(ex, !_pPlayTimer);
|
||||
onPlayResult_l(ex, !_pPlayTimer);
|
||||
}
|
||||
|
||||
void RtmpPlayer::onPlayResult_l(const SockException &ex , bool handshakeCompleted) {
|
||||
WarnL << ex.getErrCode() << " " << ex.what();
|
||||
WarnL << ex.getErrCode() << " " << ex.what();
|
||||
|
||||
if(!ex){
|
||||
//播放成功,恢复rtmp接收超时定时器
|
||||
_mediaTicker.resetTime();
|
||||
weak_ptr<RtmpPlayer> weakSelf = dynamic_pointer_cast<RtmpPlayer>(shared_from_this());
|
||||
int timeoutMS = (*this)[kMediaTimeoutMS].as<int>();
|
||||
//创建rtmp数据接收超时检测定时器
|
||||
//创建rtmp数据接收超时检测定时器
|
||||
_pMediaTimer.reset( new Timer(timeoutMS / 2000.0, [weakSelf,timeoutMS]() {
|
||||
auto strongSelf=weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
@ -130,110 +130,110 @@ void RtmpPlayer::onPlayResult_l(const SockException &ex , bool handshakeComplete
|
||||
},getPoller()));
|
||||
}
|
||||
|
||||
if (!handshakeCompleted) {
|
||||
//开始播放阶段
|
||||
_pPlayTimer.reset();
|
||||
onPlayResult(ex);
|
||||
} else if (ex) {
|
||||
//播放成功后异常断开回调
|
||||
onShutdown(ex);
|
||||
} else {
|
||||
//恢复播放
|
||||
onResume();
|
||||
}
|
||||
if (!handshakeCompleted) {
|
||||
//开始播放阶段
|
||||
_pPlayTimer.reset();
|
||||
onPlayResult(ex);
|
||||
} else if (ex) {
|
||||
//播放成功后异常断开回调
|
||||
onShutdown(ex);
|
||||
} else {
|
||||
//恢复播放
|
||||
onResume();
|
||||
}
|
||||
|
||||
if(ex){
|
||||
teardown();
|
||||
}
|
||||
if(ex){
|
||||
teardown();
|
||||
}
|
||||
}
|
||||
void RtmpPlayer::onConnect(const SockException &err){
|
||||
if(err.getErrCode() != Err_success) {
|
||||
onPlayResult_l(err, false);
|
||||
return;
|
||||
}
|
||||
weak_ptr<RtmpPlayer> weakSelf= dynamic_pointer_cast<RtmpPlayer>(shared_from_this());
|
||||
startClientSession([weakSelf](){
|
||||
if(err.getErrCode() != Err_success) {
|
||||
onPlayResult_l(err, false);
|
||||
return;
|
||||
}
|
||||
weak_ptr<RtmpPlayer> weakSelf= dynamic_pointer_cast<RtmpPlayer>(shared_from_this());
|
||||
startClientSession([weakSelf](){
|
||||
auto strongSelf=weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
if(!strongSelf) {
|
||||
return;
|
||||
}
|
||||
strongSelf->send_connect();
|
||||
});
|
||||
strongSelf->send_connect();
|
||||
});
|
||||
}
|
||||
void RtmpPlayer::onRecv(const Buffer::Ptr &pBuf){
|
||||
try {
|
||||
onParseRtmp(pBuf->data(), pBuf->size());
|
||||
} catch (exception &e) {
|
||||
SockException ex(Err_other, e.what());
|
||||
try {
|
||||
onParseRtmp(pBuf->data(), pBuf->size());
|
||||
} catch (exception &e) {
|
||||
SockException ex(Err_other, e.what());
|
||||
//定时器_pPlayTimer为空后表明握手结束了
|
||||
onPlayResult_l(ex, !_pPlayTimer);
|
||||
}
|
||||
onPlayResult_l(ex, !_pPlayTimer);
|
||||
}
|
||||
}
|
||||
|
||||
void RtmpPlayer::pause(bool bPause) {
|
||||
send_pause(bPause);
|
||||
send_pause(bPause);
|
||||
}
|
||||
|
||||
inline void RtmpPlayer::send_connect() {
|
||||
AMFValue obj(AMF_OBJECT);
|
||||
obj.set("app", _strApp);
|
||||
obj.set("tcUrl", _strTcUrl);
|
||||
//未使用代理
|
||||
obj.set("fpad", false);
|
||||
//参考librtmp,什么作用?
|
||||
obj.set("capabilities", 15);
|
||||
//SUPPORT_VID_CLIENT_SEEK 支持seek
|
||||
obj.set("videoFunction", 1);
|
||||
AMFValue obj(AMF_OBJECT);
|
||||
obj.set("app", _strApp);
|
||||
obj.set("tcUrl", _strTcUrl);
|
||||
//未使用代理
|
||||
obj.set("fpad", false);
|
||||
//参考librtmp,什么作用?
|
||||
obj.set("capabilities", 15);
|
||||
//SUPPORT_VID_CLIENT_SEEK 支持seek
|
||||
obj.set("videoFunction", 1);
|
||||
//只支持aac
|
||||
obj.set("audioCodecs", (double)(0x0400));
|
||||
//只支持H264
|
||||
obj.set("videoCodecs", (double)(0x0080));
|
||||
sendInvoke("connect", obj);
|
||||
addOnResultCB([this](AMFDecoder &dec){
|
||||
//TraceL << "connect result";
|
||||
dec.load<AMFValue>();
|
||||
auto val = dec.load<AMFValue>();
|
||||
auto level = val["level"].as_string();
|
||||
auto code = val["code"].as_string();
|
||||
if(level != "status"){
|
||||
throw std::runtime_error(StrPrinter <<"connect 失败:" << level << " " << code << endl);
|
||||
}
|
||||
send_createStream();
|
||||
});
|
||||
sendInvoke("connect", obj);
|
||||
addOnResultCB([this](AMFDecoder &dec){
|
||||
//TraceL << "connect result";
|
||||
dec.load<AMFValue>();
|
||||
auto val = dec.load<AMFValue>();
|
||||
auto level = val["level"].as_string();
|
||||
auto code = val["code"].as_string();
|
||||
if(level != "status"){
|
||||
throw std::runtime_error(StrPrinter <<"connect 失败:" << level << " " << code << endl);
|
||||
}
|
||||
send_createStream();
|
||||
});
|
||||
}
|
||||
|
||||
inline void RtmpPlayer::send_createStream() {
|
||||
AMFValue obj(AMF_NULL);
|
||||
sendInvoke("createStream", obj);
|
||||
addOnResultCB([this](AMFDecoder &dec){
|
||||
//TraceL << "createStream result";
|
||||
dec.load<AMFValue>();
|
||||
_ui32StreamId = dec.load<int>();
|
||||
send_play();
|
||||
});
|
||||
AMFValue obj(AMF_NULL);
|
||||
sendInvoke("createStream", obj);
|
||||
addOnResultCB([this](AMFDecoder &dec){
|
||||
//TraceL << "createStream result";
|
||||
dec.load<AMFValue>();
|
||||
_ui32StreamId = dec.load<int>();
|
||||
send_play();
|
||||
});
|
||||
}
|
||||
|
||||
inline void RtmpPlayer::send_play() {
|
||||
AMFEncoder enc;
|
||||
enc << "play" << ++_iReqID << nullptr << _strStream << (double)_ui32StreamId;
|
||||
sendRequest(MSG_CMD, enc.data());
|
||||
auto fun = [this](AMFValue &val){
|
||||
//TraceL << "play onStatus";
|
||||
auto level = val["level"].as_string();
|
||||
auto code = val["code"].as_string();
|
||||
if(level != "status"){
|
||||
throw std::runtime_error(StrPrinter <<"play 失败:" << level << " " << code << endl);
|
||||
}
|
||||
};
|
||||
addOnStatusCB(fun);
|
||||
addOnStatusCB(fun);
|
||||
AMFEncoder enc;
|
||||
enc << "play" << ++_iReqID << nullptr << _strStream << (double)_ui32StreamId;
|
||||
sendRequest(MSG_CMD, enc.data());
|
||||
auto fun = [this](AMFValue &val){
|
||||
//TraceL << "play onStatus";
|
||||
auto level = val["level"].as_string();
|
||||
auto code = val["code"].as_string();
|
||||
if(level != "status"){
|
||||
throw std::runtime_error(StrPrinter <<"play 失败:" << level << " " << code << endl);
|
||||
}
|
||||
};
|
||||
addOnStatusCB(fun);
|
||||
addOnStatusCB(fun);
|
||||
}
|
||||
|
||||
inline void RtmpPlayer::send_pause(bool bPause) {
|
||||
AMFEncoder enc;
|
||||
enc << "pause" << ++_iReqID << nullptr << bPause;
|
||||
sendRequest(MSG_CMD, enc.data());
|
||||
auto fun = [this,bPause](AMFValue &val){
|
||||
AMFEncoder enc;
|
||||
enc << "pause" << ++_iReqID << nullptr << bPause;
|
||||
sendRequest(MSG_CMD, enc.data());
|
||||
auto fun = [this,bPause](AMFValue &val){
|
||||
//TraceL << "pause onStatus";
|
||||
auto level = val["level"].as_string();
|
||||
auto code = val["code"].as_string();
|
||||
@ -250,147 +250,147 @@ inline void RtmpPlayer::send_pause(bool bPause) {
|
||||
_pMediaTimer.reset();
|
||||
}
|
||||
}
|
||||
};
|
||||
addOnStatusCB(fun);
|
||||
};
|
||||
addOnStatusCB(fun);
|
||||
|
||||
_pBeatTimer.reset();
|
||||
if(bPause){
|
||||
weak_ptr<RtmpPlayer> weakSelf = dynamic_pointer_cast<RtmpPlayer>(shared_from_this());
|
||||
_pBeatTimer.reset(new Timer((*this)[kBeatIntervalMS].as<int>() / 1000.0,[weakSelf](){
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if (!strongSelf){
|
||||
return false;
|
||||
}
|
||||
uint32_t timeStamp = ::time(NULL);
|
||||
strongSelf->sendUserControl(CONTROL_PING_REQUEST, timeStamp);
|
||||
return true;
|
||||
},getPoller()));
|
||||
}
|
||||
_pBeatTimer.reset();
|
||||
if(bPause){
|
||||
weak_ptr<RtmpPlayer> weakSelf = dynamic_pointer_cast<RtmpPlayer>(shared_from_this());
|
||||
_pBeatTimer.reset(new Timer((*this)[kBeatIntervalMS].as<int>() / 1000.0,[weakSelf](){
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if (!strongSelf){
|
||||
return false;
|
||||
}
|
||||
uint32_t timeStamp = ::time(NULL);
|
||||
strongSelf->sendUserControl(CONTROL_PING_REQUEST, timeStamp);
|
||||
return true;
|
||||
},getPoller()));
|
||||
}
|
||||
}
|
||||
|
||||
void RtmpPlayer::onCmd_result(AMFDecoder &dec){
|
||||
auto iReqId = dec.load<int>();
|
||||
lock_guard<recursive_mutex> lck(_mtxOnResultCB);
|
||||
auto it = _mapOnResultCB.find(iReqId);
|
||||
if(it != _mapOnResultCB.end()){
|
||||
it->second(dec);
|
||||
_mapOnResultCB.erase(it);
|
||||
}else{
|
||||
WarnL << "unhandled _result";
|
||||
}
|
||||
auto iReqId = dec.load<int>();
|
||||
lock_guard<recursive_mutex> lck(_mtxOnResultCB);
|
||||
auto it = _mapOnResultCB.find(iReqId);
|
||||
if(it != _mapOnResultCB.end()){
|
||||
it->second(dec);
|
||||
_mapOnResultCB.erase(it);
|
||||
}else{
|
||||
WarnL << "unhandled _result";
|
||||
}
|
||||
}
|
||||
void RtmpPlayer::onCmd_onStatus(AMFDecoder &dec) {
|
||||
AMFValue val;
|
||||
while(true){
|
||||
val = dec.load<AMFValue>();
|
||||
if(val.type() == AMF_OBJECT){
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(val.type() != AMF_OBJECT){
|
||||
throw std::runtime_error("onStatus:the result object was not found");
|
||||
}
|
||||
AMFValue val;
|
||||
while(true){
|
||||
val = dec.load<AMFValue>();
|
||||
if(val.type() == AMF_OBJECT){
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(val.type() != AMF_OBJECT){
|
||||
throw std::runtime_error("onStatus:the result object was not found");
|
||||
}
|
||||
|
||||
lock_guard<recursive_mutex> lck(_mtxOnStatusCB);
|
||||
if(_dqOnStatusCB.size()){
|
||||
_dqOnStatusCB.front()(val);
|
||||
_dqOnStatusCB.pop_front();
|
||||
}else{
|
||||
auto level = val["level"];
|
||||
auto code = val["code"].as_string();
|
||||
if(level.type() == AMF_STRING){
|
||||
if(level.as_string() != "status"){
|
||||
throw std::runtime_error(StrPrinter <<"onStatus 失败:" << level.as_string() << " " << code << endl);
|
||||
}
|
||||
}
|
||||
//WarnL << "unhandled onStatus:" << code;
|
||||
if(_dqOnStatusCB.size()){
|
||||
_dqOnStatusCB.front()(val);
|
||||
_dqOnStatusCB.pop_front();
|
||||
}else{
|
||||
auto level = val["level"];
|
||||
auto code = val["code"].as_string();
|
||||
if(level.type() == AMF_STRING){
|
||||
if(level.as_string() != "status"){
|
||||
throw std::runtime_error(StrPrinter <<"onStatus 失败:" << level.as_string() << " " << code << endl);
|
||||
}
|
||||
}
|
||||
//WarnL << "unhandled onStatus:" << code;
|
||||
}
|
||||
}
|
||||
|
||||
void RtmpPlayer::onCmd_onMetaData(AMFDecoder &dec) {
|
||||
//TraceL;
|
||||
auto val = dec.load<AMFValue>();
|
||||
if(!onCheckMeta(val)){
|
||||
throw std::runtime_error("onCheckMeta failed");
|
||||
}
|
||||
_metadata_got = true;
|
||||
//TraceL;
|
||||
auto val = dec.load<AMFValue>();
|
||||
if(!onCheckMeta(val)){
|
||||
throw std::runtime_error("onCheckMeta failed");
|
||||
}
|
||||
_metadata_got = true;
|
||||
}
|
||||
|
||||
void RtmpPlayer::onStreamDry(uint32_t ui32StreamId) {
|
||||
//TraceL << ui32StreamId;
|
||||
onPlayResult_l(SockException(Err_other,"rtmp stream dry"), true);
|
||||
//TraceL << ui32StreamId;
|
||||
onPlayResult_l(SockException(Err_other,"rtmp stream dry"), true);
|
||||
}
|
||||
|
||||
void RtmpPlayer::onMediaData_l(const RtmpPacket::Ptr &packet) {
|
||||
_mediaTicker.resetTime();
|
||||
if(!_pPlayTimer){
|
||||
//已经触发了onPlayResult事件,直接触发onMediaData事件
|
||||
onMediaData(packet);
|
||||
return;
|
||||
}
|
||||
_mediaTicker.resetTime();
|
||||
if(!_pPlayTimer){
|
||||
//已经触发了onPlayResult事件,直接触发onMediaData事件
|
||||
onMediaData(packet);
|
||||
return;
|
||||
}
|
||||
|
||||
if(packet->isCfgFrame()){
|
||||
//输入配置帧以便初始化完成各个track
|
||||
onMediaData(packet);
|
||||
}else{
|
||||
//先触发onPlayResult事件,这个时候解码器才能初始化完毕
|
||||
onPlayResult_l(SockException(Err_success,"play rtmp success"), false);
|
||||
//触发onPlayResult事件后,再把帧数据输入到解码器
|
||||
onMediaData(packet);
|
||||
}
|
||||
if(packet->isCfgFrame()){
|
||||
//输入配置帧以便初始化完成各个track
|
||||
onMediaData(packet);
|
||||
}else{
|
||||
//先触发onPlayResult事件,这个时候解码器才能初始化完毕
|
||||
onPlayResult_l(SockException(Err_success,"play rtmp success"), false);
|
||||
//触发onPlayResult事件后,再把帧数据输入到解码器
|
||||
onMediaData(packet);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RtmpPlayer::onRtmpChunk(RtmpPacket &chunkData) {
|
||||
typedef void (RtmpPlayer::*rtmp_func_ptr)(AMFDecoder &dec);
|
||||
static unordered_map<string, rtmp_func_ptr> s_func_map;
|
||||
static onceToken token([]() {
|
||||
s_func_map.emplace("_error",&RtmpPlayer::onCmd_result);
|
||||
s_func_map.emplace("_result",&RtmpPlayer::onCmd_result);
|
||||
s_func_map.emplace("onStatus",&RtmpPlayer::onCmd_onStatus);
|
||||
s_func_map.emplace("onMetaData",&RtmpPlayer::onCmd_onMetaData);
|
||||
}, []() {});
|
||||
typedef void (RtmpPlayer::*rtmp_func_ptr)(AMFDecoder &dec);
|
||||
static unordered_map<string, rtmp_func_ptr> s_func_map;
|
||||
static onceToken token([]() {
|
||||
s_func_map.emplace("_error",&RtmpPlayer::onCmd_result);
|
||||
s_func_map.emplace("_result",&RtmpPlayer::onCmd_result);
|
||||
s_func_map.emplace("onStatus",&RtmpPlayer::onCmd_onStatus);
|
||||
s_func_map.emplace("onMetaData",&RtmpPlayer::onCmd_onMetaData);
|
||||
}, []() {});
|
||||
|
||||
switch (chunkData.typeId) {
|
||||
case MSG_CMD:
|
||||
case MSG_CMD3:
|
||||
case MSG_DATA:
|
||||
case MSG_DATA3: {
|
||||
AMFDecoder dec(chunkData.strBuf, 0);
|
||||
std::string type = dec.load<std::string>();
|
||||
auto it = s_func_map.find(type);
|
||||
if(it != s_func_map.end()){
|
||||
auto fun = it->second;
|
||||
(this->*fun)(dec);
|
||||
}else{
|
||||
WarnL << "can not support cmd:" << type;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MSG_AUDIO:
|
||||
case MSG_VIDEO: {
|
||||
switch (chunkData.typeId) {
|
||||
case MSG_CMD:
|
||||
case MSG_CMD3:
|
||||
case MSG_DATA:
|
||||
case MSG_DATA3: {
|
||||
AMFDecoder dec(chunkData.strBuf, 0);
|
||||
std::string type = dec.load<std::string>();
|
||||
auto it = s_func_map.find(type);
|
||||
if(it != s_func_map.end()){
|
||||
auto fun = it->second;
|
||||
(this->*fun)(dec);
|
||||
}else{
|
||||
WarnL << "can not support cmd:" << type;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MSG_AUDIO:
|
||||
case MSG_VIDEO: {
|
||||
auto idx = chunkData.typeId%2;
|
||||
if (_aNowStampTicker[idx].elapsedTime() > 500) {
|
||||
//计算播放进度时间轴用
|
||||
//计算播放进度时间轴用
|
||||
_aiNowStamp[idx] = chunkData.timeStamp;
|
||||
}
|
||||
if(!_metadata_got){
|
||||
if(!onCheckMeta(TitleMeta().getMetadata())){
|
||||
throw std::runtime_error("onCheckMeta failed");
|
||||
}
|
||||
_metadata_got = true;
|
||||
}
|
||||
onMediaData_l(std::make_shared<RtmpPacket>(std::move(chunkData)));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//WarnL << "unhandled message:" << (int) chunkData.typeId << hexdump(chunkData.strBuf.data(), chunkData.strBuf.size());
|
||||
break;
|
||||
}
|
||||
if(!_metadata_got){
|
||||
if(!onCheckMeta(TitleMeta().getMetadata())){
|
||||
throw std::runtime_error("onCheckMeta failed");
|
||||
}
|
||||
_metadata_got = true;
|
||||
}
|
||||
onMediaData_l(std::make_shared<RtmpPacket>(std::move(chunkData)));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//WarnL << "unhandled message:" << (int) chunkData.typeId << hexdump(chunkData.strBuf.data(), chunkData.strBuf.size());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t RtmpPlayer::getProgressMilliSecond() const{
|
||||
uint32_t iTime[2] = {0,0};
|
||||
uint32_t iTime[2] = {0,0};
|
||||
for(auto i = 0 ;i < 2 ;i++){
|
||||
iTime[i] = _aiNowStamp[i] - _aiFistStamp[i];
|
||||
}
|
||||
@ -407,7 +407,7 @@ void RtmpPlayer::seekToMilliSecond(uint32_t seekMS){
|
||||
//TraceL << "seek result";
|
||||
_aNowStampTicker[0].resetTime();
|
||||
_aNowStampTicker[1].resetTime();
|
||||
int iTimeInc = seekMS - getProgressMilliSecond();
|
||||
int iTimeInc = seekMS - getProgressMilliSecond();
|
||||
for(auto i = 0 ;i < 2 ;i++){
|
||||
_aiFistStamp[i] = _aiNowStamp[i] + iTimeInc;
|
||||
_aiNowStamp[i] = _aiFistStamp[i];
|
||||
|
@ -47,77 +47,77 @@ namespace mediakit {
|
||||
//实现了rtmp播放器协议部分的功能,及数据接收功能
|
||||
class RtmpPlayer:public PlayerBase, public TcpClient, public RtmpProtocol{
|
||||
public:
|
||||
typedef std::shared_ptr<RtmpPlayer> Ptr;
|
||||
RtmpPlayer(const EventPoller::Ptr &poller);
|
||||
virtual ~RtmpPlayer();
|
||||
typedef std::shared_ptr<RtmpPlayer> Ptr;
|
||||
RtmpPlayer(const EventPoller::Ptr &poller);
|
||||
virtual ~RtmpPlayer();
|
||||
|
||||
void play(const string &strUrl) override;
|
||||
void pause(bool bPause) override;
|
||||
void teardown() override;
|
||||
void play(const string &strUrl) override;
|
||||
void pause(bool bPause) override;
|
||||
void teardown() override;
|
||||
protected:
|
||||
virtual bool onCheckMeta(const AMFValue &val) =0;
|
||||
virtual void onMediaData(const RtmpPacket::Ptr &chunkData) =0;
|
||||
uint32_t getProgressMilliSecond() const;
|
||||
void seekToMilliSecond(uint32_t ms);
|
||||
virtual bool onCheckMeta(const AMFValue &val) =0;
|
||||
virtual void onMediaData(const RtmpPacket::Ptr &chunkData) =0;
|
||||
uint32_t getProgressMilliSecond() const;
|
||||
void seekToMilliSecond(uint32_t ms);
|
||||
protected:
|
||||
void onMediaData_l(const RtmpPacket::Ptr &chunkData);
|
||||
//在获取config帧后才触发onPlayResult_l(而不是收到play命令回复),所以此时所有track都初始化完毕了
|
||||
void onPlayResult_l(const SockException &ex, bool handshakeCompleted);
|
||||
void onMediaData_l(const RtmpPacket::Ptr &chunkData);
|
||||
//在获取config帧后才触发onPlayResult_l(而不是收到play命令回复),所以此时所有track都初始化完毕了
|
||||
void onPlayResult_l(const SockException &ex, bool handshakeCompleted);
|
||||
|
||||
//form Tcpclient
|
||||
void onRecv(const Buffer::Ptr &pBuf) override;
|
||||
void onConnect(const SockException &err) override;
|
||||
void onErr(const SockException &ex) override;
|
||||
//from RtmpProtocol
|
||||
void onRtmpChunk(RtmpPacket &chunkData) override;
|
||||
void onStreamDry(uint32_t ui32StreamId) override;
|
||||
//form Tcpclient
|
||||
void onRecv(const Buffer::Ptr &pBuf) override;
|
||||
void onConnect(const SockException &err) override;
|
||||
void onErr(const SockException &ex) override;
|
||||
//from RtmpProtocol
|
||||
void onRtmpChunk(RtmpPacket &chunkData) override;
|
||||
void onStreamDry(uint32_t ui32StreamId) override;
|
||||
void onSendRawData(const Buffer::Ptr &buffer) override{
|
||||
send(buffer);
|
||||
}
|
||||
|
||||
template<typename FUN>
|
||||
inline void addOnResultCB(const FUN &fun) {
|
||||
lock_guard<recursive_mutex> lck(_mtxOnResultCB);
|
||||
_mapOnResultCB.emplace(_iReqID, fun);
|
||||
}
|
||||
template<typename FUN>
|
||||
inline void addOnStatusCB(const FUN &fun) {
|
||||
lock_guard<recursive_mutex> lck(_mtxOnStatusCB);
|
||||
_dqOnStatusCB.emplace_back(fun);
|
||||
}
|
||||
template<typename FUN>
|
||||
inline void addOnResultCB(const FUN &fun) {
|
||||
lock_guard<recursive_mutex> lck(_mtxOnResultCB);
|
||||
_mapOnResultCB.emplace(_iReqID, fun);
|
||||
}
|
||||
template<typename FUN>
|
||||
inline void addOnStatusCB(const FUN &fun) {
|
||||
lock_guard<recursive_mutex> lck(_mtxOnStatusCB);
|
||||
_dqOnStatusCB.emplace_back(fun);
|
||||
}
|
||||
|
||||
void onCmd_result(AMFDecoder &dec);
|
||||
void onCmd_onStatus(AMFDecoder &dec);
|
||||
void onCmd_onMetaData(AMFDecoder &dec);
|
||||
void onCmd_result(AMFDecoder &dec);
|
||||
void onCmd_onStatus(AMFDecoder &dec);
|
||||
void onCmd_onMetaData(AMFDecoder &dec);
|
||||
|
||||
inline void send_connect();
|
||||
inline void send_createStream();
|
||||
inline void send_play();
|
||||
inline void send_pause(bool bPause);
|
||||
inline void send_connect();
|
||||
inline void send_createStream();
|
||||
inline void send_play();
|
||||
inline void send_pause(bool bPause);
|
||||
private:
|
||||
string _strApp;
|
||||
string _strStream;
|
||||
string _strTcUrl;
|
||||
bool _bPaused = false;
|
||||
string _strApp;
|
||||
string _strStream;
|
||||
string _strTcUrl;
|
||||
bool _bPaused = false;
|
||||
|
||||
unordered_map<int, function<void(AMFDecoder &dec)> > _mapOnResultCB;
|
||||
recursive_mutex _mtxOnResultCB;
|
||||
deque<function<void(AMFValue &dec)> > _dqOnStatusCB;
|
||||
recursive_mutex _mtxOnStatusCB;
|
||||
unordered_map<int, function<void(AMFDecoder &dec)> > _mapOnResultCB;
|
||||
recursive_mutex _mtxOnResultCB;
|
||||
deque<function<void(AMFValue &dec)> > _dqOnStatusCB;
|
||||
recursive_mutex _mtxOnStatusCB;
|
||||
|
||||
//超时功能实现
|
||||
Ticker _mediaTicker;
|
||||
std::shared_ptr<Timer> _pMediaTimer;
|
||||
std::shared_ptr<Timer> _pPlayTimer;
|
||||
//心跳定时器
|
||||
std::shared_ptr<Timer> _pBeatTimer;
|
||||
//超时功能实现
|
||||
Ticker _mediaTicker;
|
||||
std::shared_ptr<Timer> _pMediaTimer;
|
||||
std::shared_ptr<Timer> _pPlayTimer;
|
||||
//心跳定时器
|
||||
std::shared_ptr<Timer> _pBeatTimer;
|
||||
|
||||
//播放进度控制
|
||||
uint32_t _iSeekTo = 0;
|
||||
uint32_t _aiFistStamp[2] = { 0, 0 };
|
||||
uint32_t _aiNowStamp[2] = { 0, 0 };
|
||||
Ticker _aNowStampTicker[2];
|
||||
bool _metadata_got = false;
|
||||
//播放进度控制
|
||||
uint32_t _iSeekTo = 0;
|
||||
uint32_t _aiFistStamp[2] = { 0, 0 };
|
||||
uint32_t _aiNowStamp[2] = { 0, 0 };
|
||||
Ticker _aNowStampTicker[2];
|
||||
bool _metadata_got = false;
|
||||
};
|
||||
|
||||
} /* namespace mediakit */
|
||||
|
@ -73,7 +73,7 @@ private:
|
||||
return true;
|
||||
}
|
||||
void onMediaData(const RtmpPacket::Ptr &chunkData) override {
|
||||
if(_pRtmpMediaSrc){
|
||||
if(_pRtmpMediaSrc){
|
||||
if(!_set_meta_data && !chunkData->isCfgFrame()){
|
||||
_set_meta_data = true;
|
||||
_pRtmpMediaSrc->setMetaData(TitleMeta().getMetadata());
|
||||
@ -81,7 +81,7 @@ private:
|
||||
_pRtmpMediaSrc->onWrite(chunkData);
|
||||
}
|
||||
if(!_delegate){
|
||||
//这个流没有metadata
|
||||
//这个流没有metadata
|
||||
_delegate.reset(new RtmpDemuxer());
|
||||
}
|
||||
_delegate->inputRtmp(chunkData);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -45,77 +45,77 @@ namespace mediakit {
|
||||
|
||||
class RtmpProtocol {
|
||||
public:
|
||||
RtmpProtocol();
|
||||
virtual ~RtmpProtocol();
|
||||
//作为客户端发送c0c1,等待s0s1s2并且回调
|
||||
void startClientSession(const function<void()> &cb);
|
||||
void onParseRtmp(const char *pcRawData,int iSize);
|
||||
void reset();
|
||||
RtmpProtocol();
|
||||
virtual ~RtmpProtocol();
|
||||
//作为客户端发送c0c1,等待s0s1s2并且回调
|
||||
void startClientSession(const function<void()> &cb);
|
||||
void onParseRtmp(const char *pcRawData,int iSize);
|
||||
void reset();
|
||||
protected:
|
||||
virtual void onSendRawData(const Buffer::Ptr &buffer) = 0;
|
||||
virtual void onRtmpChunk(RtmpPacket &chunkData) = 0;
|
||||
virtual void onStreamBegin(uint32_t ui32StreamId){
|
||||
_ui32StreamId = ui32StreamId;
|
||||
}
|
||||
virtual void onStreamEof(uint32_t ui32StreamId){};
|
||||
virtual void onStreamDry(uint32_t ui32StreamId){};
|
||||
virtual void onSendRawData(const Buffer::Ptr &buffer) = 0;
|
||||
virtual void onRtmpChunk(RtmpPacket &chunkData) = 0;
|
||||
virtual void onStreamBegin(uint32_t ui32StreamId){
|
||||
_ui32StreamId = ui32StreamId;
|
||||
}
|
||||
virtual void onStreamEof(uint32_t ui32StreamId){};
|
||||
virtual void onStreamDry(uint32_t ui32StreamId){};
|
||||
protected:
|
||||
void sendAcknowledgement(uint32_t ui32Size);
|
||||
void sendAcknowledgementSize(uint32_t ui32Size);
|
||||
void sendPeerBandwidth(uint32_t ui32Size);
|
||||
void sendChunkSize(uint32_t ui32Size);
|
||||
void sendPingRequest(uint32_t ui32TimeStamp = ::time(NULL));
|
||||
void sendPingResponse(uint32_t ui32TimeStamp = ::time(NULL));
|
||||
void sendSetBufferLength(uint32_t ui32StreamId, uint32_t ui32Length);
|
||||
void sendUserControl(uint16_t ui16EventType, uint32_t ui32EventData);
|
||||
void sendUserControl(uint16_t ui16EventType, const string &strEventData);
|
||||
void sendAcknowledgement(uint32_t ui32Size);
|
||||
void sendAcknowledgementSize(uint32_t ui32Size);
|
||||
void sendPeerBandwidth(uint32_t ui32Size);
|
||||
void sendChunkSize(uint32_t ui32Size);
|
||||
void sendPingRequest(uint32_t ui32TimeStamp = ::time(NULL));
|
||||
void sendPingResponse(uint32_t ui32TimeStamp = ::time(NULL));
|
||||
void sendSetBufferLength(uint32_t ui32StreamId, uint32_t ui32Length);
|
||||
void sendUserControl(uint16_t ui16EventType, uint32_t ui32EventData);
|
||||
void sendUserControl(uint16_t ui16EventType, const string &strEventData);
|
||||
|
||||
void sendInvoke(const string &strCmd, const AMFValue &val);
|
||||
void sendRequest(int iCmd, const string &str);
|
||||
void sendResponse(int iType, const string &str);
|
||||
void sendRtmp(uint8_t ui8Type, uint32_t ui32StreamId, const std::string &strBuf, uint32_t ui32TimeStamp, int iChunkID);
|
||||
void sendRtmp(uint8_t ui8Type, uint32_t ui32StreamId, const Buffer::Ptr &buffer, uint32_t ui32TimeStamp, int iChunkID);
|
||||
void sendInvoke(const string &strCmd, const AMFValue &val);
|
||||
void sendRequest(int iCmd, const string &str);
|
||||
void sendResponse(int iType, const string &str);
|
||||
void sendRtmp(uint8_t ui8Type, uint32_t ui32StreamId, const std::string &strBuf, uint32_t ui32TimeStamp, int iChunkID);
|
||||
void sendRtmp(uint8_t ui8Type, uint32_t ui32StreamId, const Buffer::Ptr &buffer, uint32_t ui32TimeStamp, int iChunkID);
|
||||
protected:
|
||||
int _iReqID = 0;
|
||||
uint32_t _ui32StreamId = STREAM_CONTROL;
|
||||
int _iNowStreamID = 0;
|
||||
int _iNowChunkID = 0;
|
||||
bool _bDataStarted = false;
|
||||
inline BufferRaw::Ptr obtainBuffer();
|
||||
inline BufferRaw::Ptr obtainBuffer(const void *data, int len);
|
||||
//ResourcePool<BufferRaw,MAX_SEND_PKT> _bufferPool;
|
||||
int _iReqID = 0;
|
||||
uint32_t _ui32StreamId = STREAM_CONTROL;
|
||||
int _iNowStreamID = 0;
|
||||
int _iNowChunkID = 0;
|
||||
bool _bDataStarted = false;
|
||||
inline BufferRaw::Ptr obtainBuffer();
|
||||
inline BufferRaw::Ptr obtainBuffer(const void *data, int len);
|
||||
//ResourcePool<BufferRaw,MAX_SEND_PKT> _bufferPool;
|
||||
private:
|
||||
void handle_S0S1S2(const function<void()> &cb);
|
||||
void handle_C0C1();
|
||||
void handle_C1_simple();
|
||||
void handle_S0S1S2(const function<void()> &cb);
|
||||
void handle_C0C1();
|
||||
void handle_C1_simple();
|
||||
#ifdef ENABLE_OPENSSL
|
||||
void handle_C1_complex();
|
||||
string get_C1_digest(const uint8_t *ptr,char **digestPos);
|
||||
string get_C1_key(const uint8_t *ptr);
|
||||
void check_C1_Digest(const string &digest,const string &data);
|
||||
void send_complex_S0S1S2(int schemeType,const string &digest);
|
||||
void handle_C1_complex();
|
||||
string get_C1_digest(const uint8_t *ptr,char **digestPos);
|
||||
string get_C1_key(const uint8_t *ptr);
|
||||
void check_C1_Digest(const string &digest,const string &data);
|
||||
void send_complex_S0S1S2(int schemeType,const string &digest);
|
||||
#endif //ENABLE_OPENSSL
|
||||
|
||||
void handle_C2();
|
||||
void handle_rtmp();
|
||||
void handle_rtmpChunk(RtmpPacket &chunkData);
|
||||
void handle_C2();
|
||||
void handle_rtmp();
|
||||
void handle_rtmpChunk(RtmpPacket &chunkData);
|
||||
|
||||
private:
|
||||
////////////ChunkSize////////////
|
||||
size_t _iChunkLenIn = DEFAULT_CHUNK_LEN;
|
||||
size_t _iChunkLenOut = DEFAULT_CHUNK_LEN;
|
||||
////////////Acknowledgement////////////
|
||||
uint32_t _ui32ByteSent = 0;
|
||||
uint32_t _ui32LastSent = 0;
|
||||
uint32_t _ui32WinSize = 0;
|
||||
///////////PeerBandwidth///////////
|
||||
uint32_t _ui32Bandwidth = 2500000;
|
||||
uint8_t _ui8LimitType = 2;
|
||||
////////////Chunk////////////
|
||||
unordered_map<int, RtmpPacket> _mapChunkData;
|
||||
//////////Rtmp parser//////////
|
||||
string _strRcvBuf;
|
||||
function<void()> _nextHandle;
|
||||
////////////ChunkSize////////////
|
||||
size_t _iChunkLenIn = DEFAULT_CHUNK_LEN;
|
||||
size_t _iChunkLenOut = DEFAULT_CHUNK_LEN;
|
||||
////////////Acknowledgement////////////
|
||||
uint32_t _ui32ByteSent = 0;
|
||||
uint32_t _ui32LastSent = 0;
|
||||
uint32_t _ui32WinSize = 0;
|
||||
///////////PeerBandwidth///////////
|
||||
uint32_t _ui32Bandwidth = 2500000;
|
||||
uint8_t _ui8LimitType = 2;
|
||||
////////////Chunk////////////
|
||||
unordered_map<int, RtmpPacket> _mapChunkData;
|
||||
//////////Rtmp parser//////////
|
||||
string _strRcvBuf;
|
||||
function<void()> _nextHandle;
|
||||
};
|
||||
|
||||
} /* namespace mediakit */
|
||||
|
@ -34,55 +34,55 @@ using namespace mediakit::Client;
|
||||
namespace mediakit {
|
||||
|
||||
RtmpPusher::RtmpPusher(const EventPoller::Ptr &poller,const RtmpMediaSource::Ptr &src) : TcpClient(poller){
|
||||
_pMediaSrc=src;
|
||||
_pMediaSrc=src;
|
||||
}
|
||||
|
||||
RtmpPusher::~RtmpPusher() {
|
||||
teardown();
|
||||
DebugL << endl;
|
||||
teardown();
|
||||
DebugL << endl;
|
||||
}
|
||||
void RtmpPusher::teardown() {
|
||||
if (alive()) {
|
||||
_strApp.clear();
|
||||
_strStream.clear();
|
||||
_strTcUrl.clear();
|
||||
{
|
||||
lock_guard<recursive_mutex> lck(_mtxOnResultCB);
|
||||
_mapOnResultCB.clear();
|
||||
}
|
||||
if (alive()) {
|
||||
_strApp.clear();
|
||||
_strStream.clear();
|
||||
_strTcUrl.clear();
|
||||
{
|
||||
lock_guard<recursive_mutex> lck(_mtxOnResultCB);
|
||||
_mapOnResultCB.clear();
|
||||
}
|
||||
{
|
||||
lock_guard<recursive_mutex> lck(_mtxOnStatusCB);
|
||||
_dqOnStatusCB.clear();
|
||||
}
|
||||
_pPublishTimer.reset();
|
||||
_pPublishTimer.reset();
|
||||
reset();
|
||||
shutdown(SockException(Err_shutdown,"teardown"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RtmpPusher::onPublishResult(const SockException &ex,bool handshakeCompleted) {
|
||||
if(!handshakeCompleted){
|
||||
//播放结果回调
|
||||
_pPublishTimer.reset();
|
||||
if(_onPublished){
|
||||
_onPublished(ex);
|
||||
}
|
||||
} else {
|
||||
//播放成功后异常断开回调
|
||||
if(_onShutdown){
|
||||
_onShutdown(ex);
|
||||
}
|
||||
}
|
||||
if(!handshakeCompleted){
|
||||
//播放结果回调
|
||||
_pPublishTimer.reset();
|
||||
if(_onPublished){
|
||||
_onPublished(ex);
|
||||
}
|
||||
} else {
|
||||
//播放成功后异常断开回调
|
||||
if(_onShutdown){
|
||||
_onShutdown(ex);
|
||||
}
|
||||
}
|
||||
|
||||
if(ex){
|
||||
teardown();
|
||||
}
|
||||
if(ex){
|
||||
teardown();
|
||||
}
|
||||
}
|
||||
|
||||
void RtmpPusher::publish(const string &strUrl) {
|
||||
teardown();
|
||||
string strHost = FindField(strUrl.data(), "://", "/");
|
||||
_strApp = FindField(strUrl.data(), (strHost + "/").data(), "/");
|
||||
teardown();
|
||||
string strHost = FindField(strUrl.data(), "://", "/");
|
||||
_strApp = FindField(strUrl.data(), (strHost + "/").data(), "/");
|
||||
_strStream = FindField(strUrl.data(), (strHost + "/" + _strApp + "/").data(), NULL);
|
||||
_strTcUrl = string("rtmp://") + strHost + "/" + _strApp;
|
||||
|
||||
@ -90,16 +90,16 @@ void RtmpPusher::publish(const string &strUrl) {
|
||||
onPublishResult(SockException(Err_other,"rtmp url非法"),false);
|
||||
return;
|
||||
}
|
||||
DebugL << strHost << " " << _strApp << " " << _strStream;
|
||||
DebugL << strHost << " " << _strApp << " " << _strStream;
|
||||
|
||||
auto iPort = atoi(FindField(strHost.data(), ":", NULL).data());
|
||||
if (iPort <= 0) {
|
||||
auto iPort = atoi(FindField(strHost.data(), ":", NULL).data());
|
||||
if (iPort <= 0) {
|
||||
//rtmp 默认端口1935
|
||||
iPort = 1935;
|
||||
} else {
|
||||
iPort = 1935;
|
||||
} else {
|
||||
//服务器域名
|
||||
strHost = FindField(strHost.data(), NULL, ":");
|
||||
}
|
||||
strHost = FindField(strHost.data(), NULL, ":");
|
||||
}
|
||||
|
||||
weak_ptr<RtmpPusher> weakSelf = dynamic_pointer_cast<RtmpPusher>(shared_from_this());
|
||||
float publishTimeOutSec = (*this)[kTimeoutMS].as<int>() / 1000.0;
|
||||
@ -116,23 +116,23 @@ void RtmpPusher::publish(const string &strUrl) {
|
||||
setNetAdapter((*this)[kNetAdapter]);
|
||||
}
|
||||
|
||||
startConnect(strHost, iPort);
|
||||
startConnect(strHost, iPort);
|
||||
}
|
||||
|
||||
void RtmpPusher::onErr(const SockException &ex){
|
||||
//定时器_pPublishTimer为空后表明握手结束了
|
||||
onPublishResult(ex,!_pPublishTimer);
|
||||
//定时器_pPublishTimer为空后表明握手结束了
|
||||
onPublishResult(ex,!_pPublishTimer);
|
||||
}
|
||||
void RtmpPusher::onConnect(const SockException &err){
|
||||
if(err) {
|
||||
onPublishResult(err,false);
|
||||
return;
|
||||
}
|
||||
//推流器不需要多大的接收缓存,节省内存占用
|
||||
_sock->setReadBuffer(std::make_shared<BufferRaw>(1 * 1024));
|
||||
if(err) {
|
||||
onPublishResult(err,false);
|
||||
return;
|
||||
}
|
||||
//推流器不需要多大的接收缓存,节省内存占用
|
||||
_sock->setReadBuffer(std::make_shared<BufferRaw>(1 * 1024));
|
||||
|
||||
weak_ptr<RtmpPusher> weakSelf = dynamic_pointer_cast<RtmpPusher>(shared_from_this());
|
||||
startClientSession([weakSelf](){
|
||||
weak_ptr<RtmpPusher> weakSelf = dynamic_pointer_cast<RtmpPusher>(shared_from_this());
|
||||
startClientSession([weakSelf](){
|
||||
auto strongSelf=weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return;
|
||||
@ -140,63 +140,63 @@ void RtmpPusher::onConnect(const SockException &err){
|
||||
|
||||
strongSelf->sendChunkSize(60000);
|
||||
strongSelf->send_connect();
|
||||
});
|
||||
});
|
||||
}
|
||||
void RtmpPusher::onRecv(const Buffer::Ptr &pBuf){
|
||||
try {
|
||||
onParseRtmp(pBuf->data(), pBuf->size());
|
||||
} catch (exception &e) {
|
||||
SockException ex(Err_other, e.what());
|
||||
//定时器_pPublishTimer为空后表明握手结束了
|
||||
onPublishResult(ex,!_pPublishTimer);
|
||||
}
|
||||
try {
|
||||
onParseRtmp(pBuf->data(), pBuf->size());
|
||||
} catch (exception &e) {
|
||||
SockException ex(Err_other, e.what());
|
||||
//定时器_pPublishTimer为空后表明握手结束了
|
||||
onPublishResult(ex,!_pPublishTimer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline void RtmpPusher::send_connect() {
|
||||
AMFValue obj(AMF_OBJECT);
|
||||
obj.set("app", _strApp);
|
||||
obj.set("type", "nonprivate");
|
||||
obj.set("tcUrl", _strTcUrl);
|
||||
obj.set("swfUrl", _strTcUrl);
|
||||
sendInvoke("connect", obj);
|
||||
addOnResultCB([this](AMFDecoder &dec){
|
||||
//TraceL << "connect result";
|
||||
dec.load<AMFValue>();
|
||||
auto val = dec.load<AMFValue>();
|
||||
auto level = val["level"].as_string();
|
||||
auto code = val["code"].as_string();
|
||||
if(level != "status"){
|
||||
throw std::runtime_error(StrPrinter <<"connect 失败:" << level << " " << code << endl);
|
||||
}
|
||||
send_createStream();
|
||||
});
|
||||
AMFValue obj(AMF_OBJECT);
|
||||
obj.set("app", _strApp);
|
||||
obj.set("type", "nonprivate");
|
||||
obj.set("tcUrl", _strTcUrl);
|
||||
obj.set("swfUrl", _strTcUrl);
|
||||
sendInvoke("connect", obj);
|
||||
addOnResultCB([this](AMFDecoder &dec){
|
||||
//TraceL << "connect result";
|
||||
dec.load<AMFValue>();
|
||||
auto val = dec.load<AMFValue>();
|
||||
auto level = val["level"].as_string();
|
||||
auto code = val["code"].as_string();
|
||||
if(level != "status"){
|
||||
throw std::runtime_error(StrPrinter <<"connect 失败:" << level << " " << code << endl);
|
||||
}
|
||||
send_createStream();
|
||||
});
|
||||
}
|
||||
|
||||
inline void RtmpPusher::send_createStream() {
|
||||
AMFValue obj(AMF_NULL);
|
||||
sendInvoke("createStream", obj);
|
||||
addOnResultCB([this](AMFDecoder &dec){
|
||||
//TraceL << "createStream result";
|
||||
dec.load<AMFValue>();
|
||||
_ui32StreamId = dec.load<int>();
|
||||
send_publish();
|
||||
});
|
||||
AMFValue obj(AMF_NULL);
|
||||
sendInvoke("createStream", obj);
|
||||
addOnResultCB([this](AMFDecoder &dec){
|
||||
//TraceL << "createStream result";
|
||||
dec.load<AMFValue>();
|
||||
_ui32StreamId = dec.load<int>();
|
||||
send_publish();
|
||||
});
|
||||
}
|
||||
inline void RtmpPusher::send_publish() {
|
||||
AMFEncoder enc;
|
||||
enc << "publish" << ++_iReqID << nullptr << _strStream << _strApp ;
|
||||
sendRequest(MSG_CMD, enc.data());
|
||||
AMFEncoder enc;
|
||||
enc << "publish" << ++_iReqID << nullptr << _strStream << _strApp ;
|
||||
sendRequest(MSG_CMD, enc.data());
|
||||
|
||||
addOnStatusCB([this](AMFValue &val) {
|
||||
auto level = val["level"].as_string();
|
||||
auto code = val["code"].as_string();
|
||||
if(level != "status") {
|
||||
throw std::runtime_error(StrPrinter <<"publish 失败:" << level << " " << code << endl);
|
||||
}
|
||||
//start send media
|
||||
send_metaData();
|
||||
});
|
||||
addOnStatusCB([this](AMFValue &val) {
|
||||
auto level = val["level"].as_string();
|
||||
auto code = val["code"].as_string();
|
||||
if(level != "status") {
|
||||
throw std::runtime_error(StrPrinter <<"publish 失败:" << level << " " << code << endl);
|
||||
}
|
||||
//start send media
|
||||
send_metaData();
|
||||
});
|
||||
}
|
||||
|
||||
inline void RtmpPusher::send_metaData(){
|
||||
@ -216,11 +216,11 @@ inline void RtmpPusher::send_metaData(){
|
||||
_pRtmpReader = src->getRing()->attach(getPoller());
|
||||
weak_ptr<RtmpPusher> weakSelf = dynamic_pointer_cast<RtmpPusher>(shared_from_this());
|
||||
_pRtmpReader->setReadCB([weakSelf](const RtmpPacket::Ptr &pkt){
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return;
|
||||
}
|
||||
strongSelf->sendRtmp(pkt->typeId, strongSelf->_ui32StreamId, pkt, pkt->timeStamp, pkt->chunkId);
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return;
|
||||
}
|
||||
strongSelf->sendRtmp(pkt->typeId, strongSelf->_ui32StreamId, pkt, pkt->timeStamp, pkt->chunkId);
|
||||
});
|
||||
_pRtmpReader->setDetachCB([weakSelf](){
|
||||
auto strongSelf = weakSelf.lock();
|
||||
@ -229,84 +229,84 @@ inline void RtmpPusher::send_metaData(){
|
||||
}
|
||||
});
|
||||
onPublishResult(SockException(Err_success,"success"), false);
|
||||
//提升发送性能
|
||||
setSocketFlags();
|
||||
//提升发送性能
|
||||
setSocketFlags();
|
||||
}
|
||||
|
||||
void RtmpPusher::setSocketFlags(){
|
||||
GET_CONFIG(bool,ultraLowDelay,General::kUltraLowDelay);
|
||||
if(!ultraLowDelay) {
|
||||
GET_CONFIG(bool,ultraLowDelay,General::kUltraLowDelay);
|
||||
if(!ultraLowDelay) {
|
||||
//提高发送性能
|
||||
(*this) << SocketFlags(SOCKET_DEFAULE_FLAGS | FLAG_MORE);
|
||||
SockUtil::setNoDelay(_sock->rawFD(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RtmpPusher::onCmd_result(AMFDecoder &dec){
|
||||
auto iReqId = dec.load<int>();
|
||||
lock_guard<recursive_mutex> lck(_mtxOnResultCB);
|
||||
auto it = _mapOnResultCB.find(iReqId);
|
||||
if(it != _mapOnResultCB.end()){
|
||||
it->second(dec);
|
||||
_mapOnResultCB.erase(it);
|
||||
}else{
|
||||
WarnL << "unhandled _result";
|
||||
}
|
||||
auto iReqId = dec.load<int>();
|
||||
lock_guard<recursive_mutex> lck(_mtxOnResultCB);
|
||||
auto it = _mapOnResultCB.find(iReqId);
|
||||
if(it != _mapOnResultCB.end()){
|
||||
it->second(dec);
|
||||
_mapOnResultCB.erase(it);
|
||||
}else{
|
||||
WarnL << "unhandled _result";
|
||||
}
|
||||
}
|
||||
void RtmpPusher::onCmd_onStatus(AMFDecoder &dec) {
|
||||
AMFValue val;
|
||||
while(true){
|
||||
val = dec.load<AMFValue>();
|
||||
if(val.type() == AMF_OBJECT){
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(val.type() != AMF_OBJECT){
|
||||
throw std::runtime_error("onStatus:the result object was not found");
|
||||
}
|
||||
AMFValue val;
|
||||
while(true){
|
||||
val = dec.load<AMFValue>();
|
||||
if(val.type() == AMF_OBJECT){
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(val.type() != AMF_OBJECT){
|
||||
throw std::runtime_error("onStatus:the result object was not found");
|
||||
}
|
||||
|
||||
lock_guard<recursive_mutex> lck(_mtxOnStatusCB);
|
||||
if(_dqOnStatusCB.size()){
|
||||
_dqOnStatusCB.front()(val);
|
||||
_dqOnStatusCB.pop_front();
|
||||
}else{
|
||||
auto level = val["level"];
|
||||
auto code = val["code"].as_string();
|
||||
if(level.type() == AMF_STRING){
|
||||
if(level.as_string() != "status"){
|
||||
throw std::runtime_error(StrPrinter <<"onStatus 失败:" << level.as_string() << " " << code << endl);
|
||||
}
|
||||
}
|
||||
if(_dqOnStatusCB.size()){
|
||||
_dqOnStatusCB.front()(val);
|
||||
_dqOnStatusCB.pop_front();
|
||||
}else{
|
||||
auto level = val["level"];
|
||||
auto code = val["code"].as_string();
|
||||
if(level.type() == AMF_STRING){
|
||||
if(level.as_string() != "status"){
|
||||
throw std::runtime_error(StrPrinter <<"onStatus 失败:" << level.as_string() << " " << code << endl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RtmpPusher::onRtmpChunk(RtmpPacket &chunkData) {
|
||||
switch (chunkData.typeId) {
|
||||
case MSG_CMD:
|
||||
case MSG_CMD3: {
|
||||
typedef void (RtmpPusher::*rtmpCMDHandle)(AMFDecoder &dec);
|
||||
static unordered_map<string, rtmpCMDHandle> g_mapCmd;
|
||||
static onceToken token([]() {
|
||||
g_mapCmd.emplace("_error",&RtmpPusher::onCmd_result);
|
||||
g_mapCmd.emplace("_result",&RtmpPusher::onCmd_result);
|
||||
g_mapCmd.emplace("onStatus",&RtmpPusher::onCmd_onStatus);
|
||||
}, []() {});
|
||||
switch (chunkData.typeId) {
|
||||
case MSG_CMD:
|
||||
case MSG_CMD3: {
|
||||
typedef void (RtmpPusher::*rtmpCMDHandle)(AMFDecoder &dec);
|
||||
static unordered_map<string, rtmpCMDHandle> g_mapCmd;
|
||||
static onceToken token([]() {
|
||||
g_mapCmd.emplace("_error",&RtmpPusher::onCmd_result);
|
||||
g_mapCmd.emplace("_result",&RtmpPusher::onCmd_result);
|
||||
g_mapCmd.emplace("onStatus",&RtmpPusher::onCmd_onStatus);
|
||||
}, []() {});
|
||||
|
||||
AMFDecoder dec(chunkData.strBuf, 0);
|
||||
std::string type = dec.load<std::string>();
|
||||
auto it = g_mapCmd.find(type);
|
||||
if(it != g_mapCmd.end()){
|
||||
auto fun = it->second;
|
||||
(this->*fun)(dec);
|
||||
}else{
|
||||
WarnL << "can not support cmd:" << type;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//WarnL << "unhandled message:" << (int) chunkData.typeId << hexdump(chunkData.strBuf.data(), chunkData.strBuf.size());
|
||||
break;
|
||||
}
|
||||
AMFDecoder dec(chunkData.strBuf, 0);
|
||||
std::string type = dec.load<std::string>();
|
||||
auto it = g_mapCmd.find(type);
|
||||
if(it != g_mapCmd.end()){
|
||||
auto fun = it->second;
|
||||
(this->*fun)(dec);
|
||||
}else{
|
||||
WarnL << "can not support cmd:" << type;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//WarnL << "unhandled message:" << (int) chunkData.typeId << hexdump(chunkData.strBuf.data(), chunkData.strBuf.size());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -36,66 +36,66 @@ namespace mediakit {
|
||||
|
||||
class RtmpPusher: public RtmpProtocol , public TcpClient , public PusherBase{
|
||||
public:
|
||||
typedef std::shared_ptr<RtmpPusher> Ptr;
|
||||
RtmpPusher(const EventPoller::Ptr &poller,const RtmpMediaSource::Ptr &src);
|
||||
virtual ~RtmpPusher();
|
||||
typedef std::shared_ptr<RtmpPusher> Ptr;
|
||||
RtmpPusher(const EventPoller::Ptr &poller,const RtmpMediaSource::Ptr &src);
|
||||
virtual ~RtmpPusher();
|
||||
|
||||
void publish(const string &strUrl) override ;
|
||||
void publish(const string &strUrl) override ;
|
||||
|
||||
void teardown() override;
|
||||
void teardown() override;
|
||||
|
||||
void setOnPublished(const Event &cb) override {
|
||||
_onPublished = cb;
|
||||
}
|
||||
void setOnPublished(const Event &cb) override {
|
||||
_onPublished = cb;
|
||||
}
|
||||
|
||||
void setOnShutdown(const Event &cb) override{
|
||||
_onShutdown = cb;
|
||||
}
|
||||
void setOnShutdown(const Event &cb) override{
|
||||
_onShutdown = cb;
|
||||
}
|
||||
protected:
|
||||
//for Tcpclient override
|
||||
void onRecv(const Buffer::Ptr &pBuf) override;
|
||||
void onConnect(const SockException &err) override;
|
||||
void onErr(const SockException &ex) override;
|
||||
//for Tcpclient override
|
||||
void onRecv(const Buffer::Ptr &pBuf) override;
|
||||
void onConnect(const SockException &err) override;
|
||||
void onErr(const SockException &ex) override;
|
||||
|
||||
//for RtmpProtocol override
|
||||
void onRtmpChunk(RtmpPacket &chunkData) override;
|
||||
void onSendRawData(const Buffer::Ptr &buffer) override{
|
||||
send(buffer);
|
||||
}
|
||||
//for RtmpProtocol override
|
||||
void onRtmpChunk(RtmpPacket &chunkData) override;
|
||||
void onSendRawData(const Buffer::Ptr &buffer) override{
|
||||
send(buffer);
|
||||
}
|
||||
private:
|
||||
void onPublishResult(const SockException &ex,bool handshakeCompleted);
|
||||
void onPublishResult(const SockException &ex,bool handshakeCompleted);
|
||||
|
||||
template<typename FUN>
|
||||
inline void addOnResultCB(const FUN &fun) {
|
||||
lock_guard<recursive_mutex> lck(_mtxOnResultCB);
|
||||
_mapOnResultCB.emplace(_iReqID, fun);
|
||||
}
|
||||
template<typename FUN>
|
||||
inline void addOnStatusCB(const FUN &fun) {
|
||||
lock_guard<recursive_mutex> lck(_mtxOnStatusCB);
|
||||
_dqOnStatusCB.emplace_back(fun);
|
||||
}
|
||||
template<typename FUN>
|
||||
inline void addOnResultCB(const FUN &fun) {
|
||||
lock_guard<recursive_mutex> lck(_mtxOnResultCB);
|
||||
_mapOnResultCB.emplace(_iReqID, fun);
|
||||
}
|
||||
template<typename FUN>
|
||||
inline void addOnStatusCB(const FUN &fun) {
|
||||
lock_guard<recursive_mutex> lck(_mtxOnStatusCB);
|
||||
_dqOnStatusCB.emplace_back(fun);
|
||||
}
|
||||
|
||||
void onCmd_result(AMFDecoder &dec);
|
||||
void onCmd_onStatus(AMFDecoder &dec);
|
||||
void onCmd_onMetaData(AMFDecoder &dec);
|
||||
void onCmd_result(AMFDecoder &dec);
|
||||
void onCmd_onStatus(AMFDecoder &dec);
|
||||
void onCmd_onMetaData(AMFDecoder &dec);
|
||||
|
||||
inline void send_connect();
|
||||
inline void send_createStream();
|
||||
inline void send_publish();
|
||||
inline void send_metaData();
|
||||
void setSocketFlags();
|
||||
inline void send_connect();
|
||||
inline void send_createStream();
|
||||
inline void send_publish();
|
||||
inline void send_metaData();
|
||||
void setSocketFlags();
|
||||
private:
|
||||
string _strApp;
|
||||
string _strStream;
|
||||
string _strTcUrl;
|
||||
string _strApp;
|
||||
string _strStream;
|
||||
string _strTcUrl;
|
||||
|
||||
unordered_map<int, function<void(AMFDecoder &dec)> > _mapOnResultCB;
|
||||
recursive_mutex _mtxOnResultCB;
|
||||
deque<function<void(AMFValue &dec)> > _dqOnStatusCB;
|
||||
recursive_mutex _mtxOnStatusCB;
|
||||
//超时功能实现
|
||||
std::shared_ptr<Timer> _pPublishTimer;
|
||||
unordered_map<int, function<void(AMFDecoder &dec)> > _mapOnResultCB;
|
||||
recursive_mutex _mtxOnResultCB;
|
||||
deque<function<void(AMFValue &dec)> > _dqOnStatusCB;
|
||||
recursive_mutex _mtxOnStatusCB;
|
||||
//超时功能实现
|
||||
std::shared_ptr<Timer> _pPublishTimer;
|
||||
//源
|
||||
std::weak_ptr<RtmpMediaSource> _pMediaSrc;
|
||||
RtmpMediaSource::RingType::RingReader::Ptr _pRtmpReader;
|
||||
|
@ -32,7 +32,7 @@
|
||||
namespace mediakit {
|
||||
|
||||
RtmpSession::RtmpSession(const Socket::Ptr &pSock) : TcpSession(pSock) {
|
||||
DebugP(this);
|
||||
DebugP(this);
|
||||
GET_CONFIG(uint32_t,keep_alive_sec,Rtmp::kKeepAliveSecond);
|
||||
pSock->setSendTimeOutSecond(keep_alive_sec);
|
||||
//起始接收buffer缓存设置为4K,节省内存
|
||||
@ -65,42 +65,42 @@ void RtmpSession::onManager() {
|
||||
GET_CONFIG(uint32_t,handshake_sec,Rtmp::kHandshakeSecond);
|
||||
GET_CONFIG(uint32_t,keep_alive_sec,Rtmp::kKeepAliveSecond);
|
||||
|
||||
if (_ticker.createdTime() > handshake_sec * 1000) {
|
||||
if (!_pRingReader && !_pPublisherSrc) {
|
||||
shutdown(SockException(Err_timeout,"illegal connection"));
|
||||
}
|
||||
}
|
||||
if (_pPublisherSrc) {
|
||||
//publisher
|
||||
if (_ticker.elapsedTime() > keep_alive_sec * 1000) {
|
||||
shutdown(SockException(Err_timeout,"recv data from rtmp pusher timeout"));
|
||||
}
|
||||
}
|
||||
if (_ticker.createdTime() > handshake_sec * 1000) {
|
||||
if (!_pRingReader && !_pPublisherSrc) {
|
||||
shutdown(SockException(Err_timeout,"illegal connection"));
|
||||
}
|
||||
}
|
||||
if (_pPublisherSrc) {
|
||||
//publisher
|
||||
if (_ticker.elapsedTime() > keep_alive_sec * 1000) {
|
||||
shutdown(SockException(Err_timeout,"recv data from rtmp pusher timeout"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RtmpSession::onRecv(const Buffer::Ptr &pBuf) {
|
||||
_ticker.resetTime();
|
||||
try {
|
||||
_ticker.resetTime();
|
||||
try {
|
||||
_ui64TotalBytes += pBuf->size();
|
||||
onParseRtmp(pBuf->data(), pBuf->size());
|
||||
} catch (exception &e) {
|
||||
shutdown(SockException(Err_shutdown, e.what()));
|
||||
}
|
||||
onParseRtmp(pBuf->data(), pBuf->size());
|
||||
} catch (exception &e) {
|
||||
shutdown(SockException(Err_shutdown, e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void RtmpSession::onCmd_connect(AMFDecoder &dec) {
|
||||
auto params = dec.load<AMFValue>();
|
||||
double amfVer = 0;
|
||||
AMFValue objectEncoding = params["objectEncoding"];
|
||||
if(objectEncoding){
|
||||
amfVer = objectEncoding.as_number();
|
||||
}
|
||||
///////////set chunk size////////////////
|
||||
sendChunkSize(60000);
|
||||
////////////window Acknowledgement size/////
|
||||
sendAcknowledgementSize(5000000);
|
||||
///////////set peerBandwidth////////////////
|
||||
sendPeerBandwidth(5000000);
|
||||
auto params = dec.load<AMFValue>();
|
||||
double amfVer = 0;
|
||||
AMFValue objectEncoding = params["objectEncoding"];
|
||||
if(objectEncoding){
|
||||
amfVer = objectEncoding.as_number();
|
||||
}
|
||||
///////////set chunk size////////////////
|
||||
sendChunkSize(60000);
|
||||
////////////window Acknowledgement size/////
|
||||
sendAcknowledgementSize(5000000);
|
||||
///////////set peerBandwidth////////////////
|
||||
sendPeerBandwidth(5000000);
|
||||
|
||||
_mediaInfo._app = params["app"].as_string();
|
||||
_strTcUrl = params["tcUrl"].as_string();
|
||||
@ -108,27 +108,27 @@ void RtmpSession::onCmd_connect(AMFDecoder &dec) {
|
||||
//defaultVhost:默认vhost
|
||||
_strTcUrl = string(RTMP_SCHEMA) + "://" + DEFAULT_VHOST + "/" + _mediaInfo._app;
|
||||
}
|
||||
bool ok = true; //(app == APP_NAME);
|
||||
AMFValue version(AMF_OBJECT);
|
||||
version.set("fmsVer", "FMS/3,0,1,123");
|
||||
version.set("capabilities", 31.0);
|
||||
AMFValue status(AMF_OBJECT);
|
||||
status.set("level", ok ? "status" : "error");
|
||||
status.set("code", ok ? "NetConnection.Connect.Success" : "NetConnection.Connect.InvalidApp");
|
||||
status.set("description", ok ? "Connection succeeded." : "InvalidApp.");
|
||||
status.set("objectEncoding", amfVer);
|
||||
sendReply(ok ? "_result" : "_error", version, status);
|
||||
if (!ok) {
|
||||
throw std::runtime_error("Unsupported application: " + _mediaInfo._app);
|
||||
}
|
||||
bool ok = true; //(app == APP_NAME);
|
||||
AMFValue version(AMF_OBJECT);
|
||||
version.set("fmsVer", "FMS/3,0,1,123");
|
||||
version.set("capabilities", 31.0);
|
||||
AMFValue status(AMF_OBJECT);
|
||||
status.set("level", ok ? "status" : "error");
|
||||
status.set("code", ok ? "NetConnection.Connect.Success" : "NetConnection.Connect.InvalidApp");
|
||||
status.set("description", ok ? "Connection succeeded." : "InvalidApp.");
|
||||
status.set("objectEncoding", amfVer);
|
||||
sendReply(ok ? "_result" : "_error", version, status);
|
||||
if (!ok) {
|
||||
throw std::runtime_error("Unsupported application: " + _mediaInfo._app);
|
||||
}
|
||||
|
||||
AMFEncoder invoke;
|
||||
invoke << "onBWDone" << 0.0 << nullptr;
|
||||
sendResponse(MSG_CMD, invoke.data());
|
||||
AMFEncoder invoke;
|
||||
invoke << "onBWDone" << 0.0 << nullptr;
|
||||
sendResponse(MSG_CMD, invoke.data());
|
||||
}
|
||||
|
||||
void RtmpSession::onCmd_createStream(AMFDecoder &dec) {
|
||||
sendReply("_result", nullptr, double(STREAM_MEDIA));
|
||||
sendReply("_result", nullptr, double(STREAM_MEDIA));
|
||||
}
|
||||
|
||||
void RtmpSession::onCmd_publish(AMFDecoder &dec) {
|
||||
@ -140,7 +140,7 @@ void RtmpSession::onCmd_publish(AMFDecoder &dec) {
|
||||
DebugP(strongSelf.get()) << "publish 回复时间:" << pTicker->elapsedTime() << "ms";
|
||||
}
|
||||
}));
|
||||
dec.load<AMFValue>();/* NULL */
|
||||
dec.load<AMFValue>();/* NULL */
|
||||
_mediaInfo.parse(_strTcUrl + "/" + getStreamId(dec.load<std::string>()));
|
||||
_mediaInfo._schema = RTMP_SCHEMA;
|
||||
|
||||
@ -203,12 +203,12 @@ void RtmpSession::onCmd_publish(AMFDecoder &dec) {
|
||||
}
|
||||
|
||||
void RtmpSession::onCmd_deleteStream(AMFDecoder &dec) {
|
||||
AMFValue status(AMF_OBJECT);
|
||||
status.set("level", "status");
|
||||
status.set("code", "NetStream.Unpublish.Success");
|
||||
status.set("description", "Stop publishing.");
|
||||
sendReply("onStatus", nullptr, status);
|
||||
throw std::runtime_error(StrPrinter << "Stop publishing" << endl);
|
||||
AMFValue status(AMF_OBJECT);
|
||||
status.set("level", "status");
|
||||
status.set("code", "NetStream.Unpublish.Success");
|
||||
status.set("description", "Stop publishing.");
|
||||
sendReply("onStatus", nullptr, status);
|
||||
throw std::runtime_error(StrPrinter << "Stop publishing" << endl);
|
||||
}
|
||||
|
||||
|
||||
@ -355,7 +355,7 @@ void RtmpSession::doPlay(AMFDecoder &dec){
|
||||
}
|
||||
}
|
||||
void RtmpSession::onCmd_play2(AMFDecoder &dec) {
|
||||
doPlay(dec);
|
||||
doPlay(dec);
|
||||
}
|
||||
|
||||
string RtmpSession::getStreamId(const string &str){
|
||||
@ -390,49 +390,49 @@ string RtmpSession::getStreamId(const string &str){
|
||||
}
|
||||
|
||||
void RtmpSession::onCmd_play(AMFDecoder &dec) {
|
||||
dec.load<AMFValue>();/* NULL */
|
||||
dec.load<AMFValue>();/* NULL */
|
||||
_mediaInfo.parse(_strTcUrl + "/" + getStreamId(dec.load<std::string>()));
|
||||
_mediaInfo._schema = RTMP_SCHEMA;
|
||||
doPlay(dec);
|
||||
doPlay(dec);
|
||||
}
|
||||
|
||||
void RtmpSession::onCmd_pause(AMFDecoder &dec) {
|
||||
dec.load<AMFValue>();/* NULL */
|
||||
bool paused = dec.load<bool>();
|
||||
TraceP(this) << paused;
|
||||
AMFValue status(AMF_OBJECT);
|
||||
status.set("level", "status");
|
||||
status.set("code", paused ? "NetStream.Pause.Notify" : "NetStream.Unpause.Notify");
|
||||
status.set("description", paused ? "Paused stream." : "Unpaused stream.");
|
||||
sendReply("onStatus", nullptr, status);
|
||||
dec.load<AMFValue>();/* NULL */
|
||||
bool paused = dec.load<bool>();
|
||||
TraceP(this) << paused;
|
||||
AMFValue status(AMF_OBJECT);
|
||||
status.set("level", "status");
|
||||
status.set("code", paused ? "NetStream.Pause.Notify" : "NetStream.Unpause.Notify");
|
||||
status.set("description", paused ? "Paused stream." : "Unpaused stream.");
|
||||
sendReply("onStatus", nullptr, status);
|
||||
//streamBegin
|
||||
sendUserControl(paused ? CONTROL_STREAM_EOF : CONTROL_STREAM_BEGIN,
|
||||
STREAM_MEDIA);
|
||||
if (!_pRingReader) {
|
||||
throw std::runtime_error("Rtmp not started yet!");
|
||||
}
|
||||
if (paused) {
|
||||
_pRingReader->setReadCB(nullptr);
|
||||
} else {
|
||||
weak_ptr<RtmpSession> weakSelf = dynamic_pointer_cast<RtmpSession>(shared_from_this());
|
||||
_pRingReader->setReadCB([weakSelf](const RtmpPacket::Ptr &pkt) {
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return;
|
||||
}
|
||||
sendUserControl(paused ? CONTROL_STREAM_EOF : CONTROL_STREAM_BEGIN,
|
||||
STREAM_MEDIA);
|
||||
if (!_pRingReader) {
|
||||
throw std::runtime_error("Rtmp not started yet!");
|
||||
}
|
||||
if (paused) {
|
||||
_pRingReader->setReadCB(nullptr);
|
||||
} else {
|
||||
weak_ptr<RtmpSession> weakSelf = dynamic_pointer_cast<RtmpSession>(shared_from_this());
|
||||
_pRingReader->setReadCB([weakSelf](const RtmpPacket::Ptr &pkt) {
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return;
|
||||
}
|
||||
strongSelf->onSendMedia(pkt);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void RtmpSession::setMetaData(AMFDecoder &dec) {
|
||||
if (!_pPublisherSrc) {
|
||||
throw std::runtime_error("not a publisher");
|
||||
}
|
||||
std::string type = dec.load<std::string>();
|
||||
if (type != "onMetaData") {
|
||||
throw std::runtime_error("can only set metadata");
|
||||
}
|
||||
if (!_pPublisherSrc) {
|
||||
throw std::runtime_error("not a publisher");
|
||||
}
|
||||
std::string type = dec.load<std::string>();
|
||||
if (type != "onMetaData") {
|
||||
throw std::runtime_error("can only set metadata");
|
||||
}
|
||||
auto metadata = dec.load<AMFValue>();
|
||||
// dumpMetadata(metadata);
|
||||
_pPublisherSrc->setMetaData(metadata);
|
||||
@ -453,42 +453,42 @@ void RtmpSession::onProcessCmd(AMFDecoder &dec) {
|
||||
s_cmd_functions.emplace("pause",&RtmpSession::onCmd_pause);}, []() {});
|
||||
|
||||
std::string method = dec.load<std::string>();
|
||||
auto it = s_cmd_functions.find(method);
|
||||
if (it == s_cmd_functions.end()) {
|
||||
auto it = s_cmd_functions.find(method);
|
||||
if (it == s_cmd_functions.end()) {
|
||||
// TraceP(this) << "can not support cmd:" << method;
|
||||
return;
|
||||
}
|
||||
_dNowReqID = dec.load<double>();
|
||||
auto fun = it->second;
|
||||
(this->*fun)(dec);
|
||||
return;
|
||||
}
|
||||
_dNowReqID = dec.load<double>();
|
||||
auto fun = it->second;
|
||||
(this->*fun)(dec);
|
||||
}
|
||||
|
||||
void RtmpSession::onRtmpChunk(RtmpPacket &chunkData) {
|
||||
switch (chunkData.typeId) {
|
||||
case MSG_CMD:
|
||||
case MSG_CMD3: {
|
||||
AMFDecoder dec(chunkData.strBuf, chunkData.typeId == MSG_CMD3 ? 1 : 0);
|
||||
onProcessCmd(dec);
|
||||
}
|
||||
break;
|
||||
switch (chunkData.typeId) {
|
||||
case MSG_CMD:
|
||||
case MSG_CMD3: {
|
||||
AMFDecoder dec(chunkData.strBuf, chunkData.typeId == MSG_CMD3 ? 1 : 0);
|
||||
onProcessCmd(dec);
|
||||
}
|
||||
break;
|
||||
|
||||
case MSG_DATA:
|
||||
case MSG_DATA3: {
|
||||
AMFDecoder dec(chunkData.strBuf, chunkData.typeId == MSG_CMD3 ? 1 : 0);
|
||||
std::string type = dec.load<std::string>();
|
||||
if (type == "@setDataFrame") {
|
||||
setMetaData(dec);
|
||||
}else{
|
||||
case MSG_DATA:
|
||||
case MSG_DATA3: {
|
||||
AMFDecoder dec(chunkData.strBuf, chunkData.typeId == MSG_CMD3 ? 1 : 0);
|
||||
std::string type = dec.load<std::string>();
|
||||
if (type == "@setDataFrame") {
|
||||
setMetaData(dec);
|
||||
}else{
|
||||
TraceP(this) << "unknown notify:" << type;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MSG_AUDIO:
|
||||
case MSG_VIDEO: {
|
||||
if (!_pPublisherSrc) {
|
||||
throw std::runtime_error("Not a rtmp publisher!");
|
||||
}
|
||||
GET_CONFIG(bool,rtmp_modify_stamp,Rtmp::kModifyStamp);
|
||||
}
|
||||
break;
|
||||
case MSG_AUDIO:
|
||||
case MSG_VIDEO: {
|
||||
if (!_pPublisherSrc) {
|
||||
throw std::runtime_error("Not a rtmp publisher!");
|
||||
}
|
||||
GET_CONFIG(bool,rtmp_modify_stamp,Rtmp::kModifyStamp);
|
||||
if(rtmp_modify_stamp){
|
||||
int64_t dts_out;
|
||||
_stamp[chunkData.typeId % 2].revise(chunkData.timeStamp, chunkData.timeStamp, dts_out, dts_out, true);
|
||||
@ -500,12 +500,12 @@ void RtmpSession::onRtmpChunk(RtmpPacket &chunkData) {
|
||||
_pPublisherSrc->setMetaData(TitleMeta().getMetadata());
|
||||
}
|
||||
_pPublisherSrc->onWrite(std::make_shared<RtmpPacket>(std::move(chunkData)));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
WarnP(this) << "unhandled message:" << (int) chunkData.typeId << hexdump(chunkData.strBuf.data(), chunkData.strBuf.size());
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
WarnP(this) << "unhandled message:" << (int) chunkData.typeId << hexdump(chunkData.strBuf.data(), chunkData.strBuf.size());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void RtmpSession::onCmd_seek(AMFDecoder &dec) {
|
||||
@ -518,12 +518,12 @@ void RtmpSession::onCmd_seek(AMFDecoder &dec) {
|
||||
_stamp[1].setPlayBack();
|
||||
stongSrc->seekTo(milliSeconds);
|
||||
}
|
||||
AMFValue status(AMF_OBJECT);
|
||||
AMFEncoder invoke;
|
||||
status.set("level", "status");
|
||||
status.set("code", "NetStream.Seek.Notify");
|
||||
status.set("description", "Seeking.");
|
||||
sendReply("onStatus", nullptr, status);
|
||||
AMFValue status(AMF_OBJECT);
|
||||
AMFEncoder invoke;
|
||||
status.set("level", "status");
|
||||
status.set("code", "NetStream.Seek.Notify");
|
||||
status.set("description", "Seeking.");
|
||||
sendReply("onStatus", nullptr, status);
|
||||
}
|
||||
|
||||
void RtmpSession::onSendMedia(const RtmpPacket::Ptr &pkt) {
|
||||
|
@ -45,65 +45,65 @@ namespace mediakit {
|
||||
|
||||
class RtmpSession: public TcpSession ,public RtmpProtocol , public MediaSourceEvent{
|
||||
public:
|
||||
typedef std::shared_ptr<RtmpSession> Ptr;
|
||||
RtmpSession(const Socket::Ptr &_sock);
|
||||
virtual ~RtmpSession();
|
||||
void onRecv(const Buffer::Ptr &pBuf) override;
|
||||
void onError(const SockException &err) override;
|
||||
void onManager() override;
|
||||
typedef std::shared_ptr<RtmpSession> Ptr;
|
||||
RtmpSession(const Socket::Ptr &_sock);
|
||||
virtual ~RtmpSession();
|
||||
void onRecv(const Buffer::Ptr &pBuf) override;
|
||||
void onError(const SockException &err) override;
|
||||
void onManager() override;
|
||||
private:
|
||||
void onProcessCmd(AMFDecoder &dec);
|
||||
void onCmd_connect(AMFDecoder &dec);
|
||||
void onCmd_createStream(AMFDecoder &dec);
|
||||
void onProcessCmd(AMFDecoder &dec);
|
||||
void onCmd_connect(AMFDecoder &dec);
|
||||
void onCmd_createStream(AMFDecoder &dec);
|
||||
|
||||
void onCmd_publish(AMFDecoder &dec);
|
||||
void onCmd_deleteStream(AMFDecoder &dec);
|
||||
void onCmd_publish(AMFDecoder &dec);
|
||||
void onCmd_deleteStream(AMFDecoder &dec);
|
||||
|
||||
void onCmd_play(AMFDecoder &dec);
|
||||
void onCmd_play2(AMFDecoder &dec);
|
||||
void doPlay(AMFDecoder &dec);
|
||||
void doPlayResponse(const string &err,const std::function<void(bool)> &cb);
|
||||
void sendPlayResponse(const string &err,const RtmpMediaSource::Ptr &src);
|
||||
void onCmd_play(AMFDecoder &dec);
|
||||
void onCmd_play2(AMFDecoder &dec);
|
||||
void doPlay(AMFDecoder &dec);
|
||||
void doPlayResponse(const string &err,const std::function<void(bool)> &cb);
|
||||
void sendPlayResponse(const string &err,const RtmpMediaSource::Ptr &src);
|
||||
|
||||
void onCmd_seek(AMFDecoder &dec);
|
||||
void onCmd_pause(AMFDecoder &dec);
|
||||
void setMetaData(AMFDecoder &dec);
|
||||
void onCmd_seek(AMFDecoder &dec);
|
||||
void onCmd_pause(AMFDecoder &dec);
|
||||
void setMetaData(AMFDecoder &dec);
|
||||
|
||||
void onSendMedia(const RtmpPacket::Ptr &pkt);
|
||||
void onSendRawData(const Buffer::Ptr &buffer) override{
|
||||
void onSendMedia(const RtmpPacket::Ptr &pkt);
|
||||
void onSendRawData(const Buffer::Ptr &buffer) override{
|
||||
_ui64TotalBytes += buffer->size();
|
||||
send(buffer);
|
||||
}
|
||||
void onRtmpChunk(RtmpPacket &chunkData) override;
|
||||
send(buffer);
|
||||
}
|
||||
void onRtmpChunk(RtmpPacket &chunkData) override;
|
||||
|
||||
template<typename first, typename second>
|
||||
inline void sendReply(const char *str, const first &reply, const second &status) {
|
||||
AMFEncoder invoke;
|
||||
invoke << str << _dNowReqID << reply << status;
|
||||
sendResponse(MSG_CMD, invoke.data());
|
||||
}
|
||||
template<typename first, typename second>
|
||||
inline void sendReply(const char *str, const first &reply, const second &status) {
|
||||
AMFEncoder invoke;
|
||||
invoke << str << _dNowReqID << reply << status;
|
||||
sendResponse(MSG_CMD, invoke.data());
|
||||
}
|
||||
|
||||
//MediaSourceEvent override
|
||||
bool close(MediaSource &sender,bool force) override ;
|
||||
//MediaSourceEvent override
|
||||
bool close(MediaSource &sender,bool force) override ;
|
||||
void onNoneReader(MediaSource &sender) override;
|
||||
int totalReaderCount(MediaSource &sender) override;
|
||||
int totalReaderCount(MediaSource &sender) override;
|
||||
|
||||
void setSocketFlags();
|
||||
string getStreamId(const string &str);
|
||||
void dumpMetadata(const AMFValue &metadata);
|
||||
void setSocketFlags();
|
||||
string getStreamId(const string &str);
|
||||
void dumpMetadata(const AMFValue &metadata);
|
||||
private:
|
||||
std::string _strTcUrl;
|
||||
MediaInfo _mediaInfo;
|
||||
double _dNowReqID = 0;
|
||||
bool _set_meta_data = false;
|
||||
Ticker _ticker;//数据接收时间
|
||||
RingBuffer<RtmpPacket::Ptr>::RingReader::Ptr _pRingReader;
|
||||
std::shared_ptr<RtmpMediaSourceImp> _pPublisherSrc;
|
||||
std::weak_ptr<RtmpMediaSource> _pPlayerSrc;
|
||||
//时间戳修整器
|
||||
Stamp _stamp[2];
|
||||
//消耗的总流量
|
||||
uint64_t _ui64TotalBytes = 0;
|
||||
std::string _strTcUrl;
|
||||
MediaInfo _mediaInfo;
|
||||
double _dNowReqID = 0;
|
||||
bool _set_meta_data = false;
|
||||
Ticker _ticker;//数据接收时间
|
||||
RingBuffer<RtmpPacket::Ptr>::RingReader::Ptr _pRingReader;
|
||||
std::shared_ptr<RtmpMediaSourceImp> _pPublisherSrc;
|
||||
std::weak_ptr<RtmpMediaSource> _pPlayerSrc;
|
||||
//时间戳修整器
|
||||
Stamp _stamp[2];
|
||||
//消耗的总流量
|
||||
uint64_t _ui64TotalBytes = 0;
|
||||
|
||||
};
|
||||
|
||||
|
794
src/Rtmp/amf.cpp
794
src/Rtmp/amf.cpp
@ -35,130 +35,130 @@ using namespace toolkit;
|
||||
|
||||
/////////////////////AMFValue/////////////////////////////
|
||||
inline void AMFValue::destroy() {
|
||||
switch (_type) {
|
||||
case AMF_STRING:
|
||||
if (_value.string) {
|
||||
delete _value.string;
|
||||
_value.string = nullptr;
|
||||
}
|
||||
break;
|
||||
case AMF_OBJECT:
|
||||
case AMF_ECMA_ARRAY:
|
||||
if (_value.object) {
|
||||
delete _value.object;
|
||||
_value.object = nullptr;
|
||||
}
|
||||
break;
|
||||
case AMF_STRICT_ARRAY:
|
||||
if (_value.array) {
|
||||
delete _value.array;
|
||||
_value.array = nullptr;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (_type) {
|
||||
case AMF_STRING:
|
||||
if (_value.string) {
|
||||
delete _value.string;
|
||||
_value.string = nullptr;
|
||||
}
|
||||
break;
|
||||
case AMF_OBJECT:
|
||||
case AMF_ECMA_ARRAY:
|
||||
if (_value.object) {
|
||||
delete _value.object;
|
||||
_value.object = nullptr;
|
||||
}
|
||||
break;
|
||||
case AMF_STRICT_ARRAY:
|
||||
if (_value.array) {
|
||||
delete _value.array;
|
||||
_value.array = nullptr;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
inline void AMFValue::init() {
|
||||
switch (_type) {
|
||||
case AMF_OBJECT:
|
||||
case AMF_ECMA_ARRAY:
|
||||
_value.object = new mapType;
|
||||
break;
|
||||
case AMF_STRING:
|
||||
_value.string = new std::string;
|
||||
break;
|
||||
case AMF_STRICT_ARRAY:
|
||||
_value.array = new arrayType;
|
||||
break;
|
||||
switch (_type) {
|
||||
case AMF_OBJECT:
|
||||
case AMF_ECMA_ARRAY:
|
||||
_value.object = new mapType;
|
||||
break;
|
||||
case AMF_STRING:
|
||||
_value.string = new std::string;
|
||||
break;
|
||||
case AMF_STRICT_ARRAY:
|
||||
_value.array = new arrayType;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
AMFValue::AMFValue(AMFType type) :
|
||||
_type(type) {
|
||||
init();
|
||||
_type(type) {
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
AMFValue::~AMFValue() {
|
||||
destroy();
|
||||
destroy();
|
||||
}
|
||||
|
||||
AMFValue::AMFValue(const char *s) :
|
||||
_type(AMF_STRING) {
|
||||
init();
|
||||
*_value.string = s;
|
||||
_type(AMF_STRING) {
|
||||
init();
|
||||
*_value.string = s;
|
||||
}
|
||||
|
||||
|
||||
AMFValue::AMFValue(const std::string &s) :
|
||||
_type(AMF_STRING) {
|
||||
init();
|
||||
*_value.string = s;
|
||||
_type(AMF_STRING) {
|
||||
init();
|
||||
*_value.string = s;
|
||||
}
|
||||
|
||||
AMFValue::AMFValue(double n) :
|
||||
_type(AMF_NUMBER) {
|
||||
init();
|
||||
_value.number = n;
|
||||
_type(AMF_NUMBER) {
|
||||
init();
|
||||
_value.number = n;
|
||||
}
|
||||
|
||||
AMFValue::AMFValue(int i) :
|
||||
_type(AMF_INTEGER) {
|
||||
init();
|
||||
_value.integer = i;
|
||||
_type(AMF_INTEGER) {
|
||||
init();
|
||||
_value.integer = i;
|
||||
}
|
||||
|
||||
AMFValue::AMFValue(bool b) :
|
||||
_type(AMF_BOOLEAN) {
|
||||
init();
|
||||
_value.boolean = b;
|
||||
_type(AMF_BOOLEAN) {
|
||||
init();
|
||||
_value.boolean = b;
|
||||
}
|
||||
|
||||
AMFValue::AMFValue(const AMFValue &from) :
|
||||
_type(AMF_NULL) {
|
||||
*this = from;
|
||||
_type(AMF_NULL) {
|
||||
*this = from;
|
||||
}
|
||||
|
||||
AMFValue::AMFValue(AMFValue &&from) {
|
||||
*this = std::forward<AMFValue>(from);
|
||||
*this = std::forward<AMFValue>(from);
|
||||
}
|
||||
|
||||
AMFValue& AMFValue::operator =(const AMFValue &from) {
|
||||
return *this = const_cast<AMFValue &&>(from);
|
||||
return *this = const_cast<AMFValue &&>(from);
|
||||
|
||||
}
|
||||
AMFValue& AMFValue::operator =(AMFValue &&from) {
|
||||
destroy();
|
||||
_type = from._type;
|
||||
init();
|
||||
switch (_type) {
|
||||
case AMF_STRING:
|
||||
*_value.string = (*from._value.string);
|
||||
break;
|
||||
case AMF_OBJECT:
|
||||
case AMF_ECMA_ARRAY:
|
||||
*_value.object = (*from._value.object);
|
||||
break;
|
||||
case AMF_STRICT_ARRAY:
|
||||
*_value.array = (*from._value.array);
|
||||
break;
|
||||
case AMF_NUMBER:
|
||||
_value.number = from._value.number;
|
||||
break;
|
||||
case AMF_INTEGER:
|
||||
_value.integer = from._value.integer;
|
||||
break;
|
||||
case AMF_BOOLEAN:
|
||||
_value.boolean = from._value.boolean;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
destroy();
|
||||
_type = from._type;
|
||||
init();
|
||||
switch (_type) {
|
||||
case AMF_STRING:
|
||||
*_value.string = (*from._value.string);
|
||||
break;
|
||||
case AMF_OBJECT:
|
||||
case AMF_ECMA_ARRAY:
|
||||
*_value.object = (*from._value.object);
|
||||
break;
|
||||
case AMF_STRICT_ARRAY:
|
||||
*_value.array = (*from._value.array);
|
||||
break;
|
||||
case AMF_NUMBER:
|
||||
_value.number = from._value.number;
|
||||
break;
|
||||
case AMF_INTEGER:
|
||||
_value.integer = from._value.integer;
|
||||
break;
|
||||
case AMF_BOOLEAN:
|
||||
_value.boolean = from._value.boolean;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
|
||||
}
|
||||
|
||||
@ -305,156 +305,156 @@ const AMFValue::arrayType &AMFValue::getArr() const {
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
enum {
|
||||
AMF0_NUMBER,
|
||||
AMF0_BOOLEAN,
|
||||
AMF0_STRING,
|
||||
AMF0_OBJECT,
|
||||
AMF0_MOVIECLIP,
|
||||
AMF0_NULL,
|
||||
AMF0_UNDEFINED,
|
||||
AMF0_REFERENCE,
|
||||
AMF0_ECMA_ARRAY,
|
||||
AMF0_OBJECT_END,
|
||||
AMF0_STRICT_ARRAY,
|
||||
AMF0_DATE,
|
||||
AMF0_LONG_STRING,
|
||||
AMF0_UNSUPPORTED,
|
||||
AMF0_RECORD_SET,
|
||||
AMF0_XML_OBJECT,
|
||||
AMF0_TYPED_OBJECT,
|
||||
AMF0_SWITCH_AMF3,
|
||||
AMF0_NUMBER,
|
||||
AMF0_BOOLEAN,
|
||||
AMF0_STRING,
|
||||
AMF0_OBJECT,
|
||||
AMF0_MOVIECLIP,
|
||||
AMF0_NULL,
|
||||
AMF0_UNDEFINED,
|
||||
AMF0_REFERENCE,
|
||||
AMF0_ECMA_ARRAY,
|
||||
AMF0_OBJECT_END,
|
||||
AMF0_STRICT_ARRAY,
|
||||
AMF0_DATE,
|
||||
AMF0_LONG_STRING,
|
||||
AMF0_UNSUPPORTED,
|
||||
AMF0_RECORD_SET,
|
||||
AMF0_XML_OBJECT,
|
||||
AMF0_TYPED_OBJECT,
|
||||
AMF0_SWITCH_AMF3,
|
||||
};
|
||||
|
||||
enum {
|
||||
AMF3_UNDEFINED,
|
||||
AMF3_NULL,
|
||||
AMF3_FALSE,
|
||||
AMF3_TRUE,
|
||||
AMF3_INTEGER,
|
||||
AMF3_NUMBER,
|
||||
AMF3_STRING,
|
||||
AMF3_LEGACY_XML,
|
||||
AMF3_DATE,
|
||||
AMF3_ARRAY,
|
||||
AMF3_OBJECT,
|
||||
AMF3_XML,
|
||||
AMF3_BYTE_ARRAY,
|
||||
AMF3_UNDEFINED,
|
||||
AMF3_NULL,
|
||||
AMF3_FALSE,
|
||||
AMF3_TRUE,
|
||||
AMF3_INTEGER,
|
||||
AMF3_NUMBER,
|
||||
AMF3_STRING,
|
||||
AMF3_LEGACY_XML,
|
||||
AMF3_DATE,
|
||||
AMF3_ARRAY,
|
||||
AMF3_OBJECT,
|
||||
AMF3_XML,
|
||||
AMF3_BYTE_ARRAY,
|
||||
};
|
||||
|
||||
////////////////////////////////Encoder//////////////////////////////////////////
|
||||
AMFEncoder & AMFEncoder::operator <<(const char *s) {
|
||||
if (s) {
|
||||
buf += char(AMF0_STRING);
|
||||
uint16_t str_len = htons(strlen(s));
|
||||
buf.append((char *) &str_len, 2);
|
||||
buf += s;
|
||||
} else {
|
||||
buf += char(AMF0_NULL);
|
||||
}
|
||||
return *this;
|
||||
if (s) {
|
||||
buf += char(AMF0_STRING);
|
||||
uint16_t str_len = htons(strlen(s));
|
||||
buf.append((char *) &str_len, 2);
|
||||
buf += s;
|
||||
} else {
|
||||
buf += char(AMF0_NULL);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
AMFEncoder & AMFEncoder::operator <<(const std::string &s) {
|
||||
if (!s.empty()) {
|
||||
buf += char(AMF0_STRING);
|
||||
uint16_t str_len = htons(s.size());
|
||||
buf.append((char *) &str_len, 2);
|
||||
buf += s;
|
||||
} else {
|
||||
buf += char(AMF0_NULL);
|
||||
}
|
||||
return *this;
|
||||
if (!s.empty()) {
|
||||
buf += char(AMF0_STRING);
|
||||
uint16_t str_len = htons(s.size());
|
||||
buf.append((char *) &str_len, 2);
|
||||
buf += s;
|
||||
} else {
|
||||
buf += char(AMF0_NULL);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
AMFEncoder & AMFEncoder::operator <<(std::nullptr_t) {
|
||||
buf += char(AMF0_NULL);
|
||||
return *this;
|
||||
buf += char(AMF0_NULL);
|
||||
return *this;
|
||||
}
|
||||
AMFEncoder & AMFEncoder::write_undefined() {
|
||||
buf += char(AMF0_UNDEFINED);
|
||||
return *this;
|
||||
buf += char(AMF0_UNDEFINED);
|
||||
return *this;
|
||||
|
||||
}
|
||||
AMFEncoder & AMFEncoder::operator <<(const int n){
|
||||
return (*this) << (double)n;
|
||||
return (*this) << (double)n;
|
||||
}
|
||||
AMFEncoder & AMFEncoder::operator <<(const double n) {
|
||||
buf += char(AMF0_NUMBER);
|
||||
uint64_t encoded = 0;
|
||||
memcpy(&encoded, &n, 8);
|
||||
uint32_t val = htonl(encoded >> 32);
|
||||
buf.append((char *) &val, 4);
|
||||
val = htonl(encoded);
|
||||
buf.append((char *) &val, 4);
|
||||
return *this;
|
||||
buf += char(AMF0_NUMBER);
|
||||
uint64_t encoded = 0;
|
||||
memcpy(&encoded, &n, 8);
|
||||
uint32_t val = htonl(encoded >> 32);
|
||||
buf.append((char *) &val, 4);
|
||||
val = htonl(encoded);
|
||||
buf.append((char *) &val, 4);
|
||||
return *this;
|
||||
}
|
||||
|
||||
AMFEncoder & AMFEncoder::operator <<(const bool b) {
|
||||
buf += char(AMF0_BOOLEAN);
|
||||
buf += char(b);
|
||||
return *this;
|
||||
buf += char(AMF0_BOOLEAN);
|
||||
buf += char(b);
|
||||
return *this;
|
||||
}
|
||||
|
||||
AMFEncoder & AMFEncoder::operator <<(const AMFValue& value) {
|
||||
switch ((int) value.type()) {
|
||||
case AMF_STRING:
|
||||
*this << value.as_string();
|
||||
break;
|
||||
case AMF_NUMBER:
|
||||
*this << value.as_number();
|
||||
break;
|
||||
case AMF_INTEGER:
|
||||
*this << value.as_integer();
|
||||
break;
|
||||
case AMF_BOOLEAN:
|
||||
*this << value.as_boolean();
|
||||
break;
|
||||
case AMF_OBJECT: {
|
||||
buf += char(AMF0_OBJECT);
|
||||
for (auto &pr : value.getMap()) {
|
||||
write_key(pr.first);
|
||||
*this << pr.second;
|
||||
}
|
||||
write_key("");
|
||||
buf += char(AMF0_OBJECT_END);
|
||||
}
|
||||
break;
|
||||
case AMF_ECMA_ARRAY: {
|
||||
buf += char(AMF0_ECMA_ARRAY);
|
||||
uint32_t sz = htonl(value.getMap().size());
|
||||
buf.append((char *) &sz, 4);
|
||||
for (auto &pr : value.getMap()) {
|
||||
write_key(pr.first);
|
||||
*this << pr.second;
|
||||
}
|
||||
write_key("");
|
||||
buf += char(AMF0_OBJECT_END);
|
||||
}
|
||||
break;
|
||||
case AMF_NULL:
|
||||
*this << nullptr;
|
||||
break;
|
||||
case AMF_UNDEFINED:
|
||||
this->write_undefined();
|
||||
break;
|
||||
case AMF_STRICT_ARRAY: {
|
||||
buf += char(AMF0_STRICT_ARRAY);
|
||||
uint32_t sz = htonl(value.getArr().size());
|
||||
buf.append((char *) &sz, 4);
|
||||
for (auto &val : value.getArr()) {
|
||||
*this << val;
|
||||
}
|
||||
//write_key("");
|
||||
//buf += char(AMF0_OBJECT_END);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
switch ((int) value.type()) {
|
||||
case AMF_STRING:
|
||||
*this << value.as_string();
|
||||
break;
|
||||
case AMF_NUMBER:
|
||||
*this << value.as_number();
|
||||
break;
|
||||
case AMF_INTEGER:
|
||||
*this << value.as_integer();
|
||||
break;
|
||||
case AMF_BOOLEAN:
|
||||
*this << value.as_boolean();
|
||||
break;
|
||||
case AMF_OBJECT: {
|
||||
buf += char(AMF0_OBJECT);
|
||||
for (auto &pr : value.getMap()) {
|
||||
write_key(pr.first);
|
||||
*this << pr.second;
|
||||
}
|
||||
write_key("");
|
||||
buf += char(AMF0_OBJECT_END);
|
||||
}
|
||||
break;
|
||||
case AMF_ECMA_ARRAY: {
|
||||
buf += char(AMF0_ECMA_ARRAY);
|
||||
uint32_t sz = htonl(value.getMap().size());
|
||||
buf.append((char *) &sz, 4);
|
||||
for (auto &pr : value.getMap()) {
|
||||
write_key(pr.first);
|
||||
*this << pr.second;
|
||||
}
|
||||
write_key("");
|
||||
buf += char(AMF0_OBJECT_END);
|
||||
}
|
||||
break;
|
||||
case AMF_NULL:
|
||||
*this << nullptr;
|
||||
break;
|
||||
case AMF_UNDEFINED:
|
||||
this->write_undefined();
|
||||
break;
|
||||
case AMF_STRICT_ARRAY: {
|
||||
buf += char(AMF0_STRICT_ARRAY);
|
||||
uint32_t sz = htonl(value.getArr().size());
|
||||
buf.append((char *) &sz, 4);
|
||||
for (auto &val : value.getArr()) {
|
||||
*this << val;
|
||||
}
|
||||
//write_key("");
|
||||
//buf += char(AMF0_OBJECT_END);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
|
||||
}
|
||||
|
||||
void AMFEncoder::write_key(const std::string& s) {
|
||||
uint16_t str_len = htons(s.size());
|
||||
buf.append((char *) &str_len, 2);
|
||||
buf += s;
|
||||
uint16_t str_len = htons(s.size());
|
||||
buf.append((char *) &str_len, 2);
|
||||
buf += s;
|
||||
}
|
||||
|
||||
void AMFEncoder::clear() {
|
||||
@ -468,237 +468,237 @@ const std::string& AMFEncoder::data() const {
|
||||
//////////////////Decoder//////////////////
|
||||
|
||||
uint8_t AMFDecoder::front() {
|
||||
if (pos >= buf.size()) {
|
||||
throw std::runtime_error("Not enough data");
|
||||
}
|
||||
return uint8_t(buf[pos]);
|
||||
if (pos >= buf.size()) {
|
||||
throw std::runtime_error("Not enough data");
|
||||
}
|
||||
return uint8_t(buf[pos]);
|
||||
}
|
||||
|
||||
uint8_t AMFDecoder::pop_front() {
|
||||
if (version == 0 && front() == AMF0_SWITCH_AMF3) {
|
||||
InfoL << "entering AMF3 mode";
|
||||
pos++;
|
||||
version = 3;
|
||||
}
|
||||
if (version == 0 && front() == AMF0_SWITCH_AMF3) {
|
||||
InfoL << "entering AMF3 mode";
|
||||
pos++;
|
||||
version = 3;
|
||||
}
|
||||
|
||||
if (pos >= buf.size()) {
|
||||
throw std::runtime_error("Not enough data");
|
||||
}
|
||||
return uint8_t(buf[pos++]);
|
||||
if (pos >= buf.size()) {
|
||||
throw std::runtime_error("Not enough data");
|
||||
}
|
||||
return uint8_t(buf[pos++]);
|
||||
}
|
||||
|
||||
template<>
|
||||
double AMFDecoder::load<double>() {
|
||||
if (pop_front() != AMF0_NUMBER) {
|
||||
throw std::runtime_error("Expected a number");
|
||||
}
|
||||
if (pos + 8 > buf.size()) {
|
||||
throw std::runtime_error("Not enough data");
|
||||
}
|
||||
uint64_t val = ((uint64_t) load_be32(&buf[pos]) << 32)
|
||||
| load_be32(&buf[pos + 4]);
|
||||
double n = 0;
|
||||
memcpy(&n, &val, 8);
|
||||
pos += 8;
|
||||
return n;
|
||||
if (pop_front() != AMF0_NUMBER) {
|
||||
throw std::runtime_error("Expected a number");
|
||||
}
|
||||
if (pos + 8 > buf.size()) {
|
||||
throw std::runtime_error("Not enough data");
|
||||
}
|
||||
uint64_t val = ((uint64_t) load_be32(&buf[pos]) << 32)
|
||||
| load_be32(&buf[pos + 4]);
|
||||
double n = 0;
|
||||
memcpy(&n, &val, 8);
|
||||
pos += 8;
|
||||
return n;
|
||||
|
||||
}
|
||||
|
||||
template<>
|
||||
bool AMFDecoder::load<bool>() {
|
||||
if (pop_front() != AMF0_BOOLEAN) {
|
||||
throw std::runtime_error("Expected a boolean");
|
||||
}
|
||||
return pop_front() != 0;
|
||||
if (pop_front() != AMF0_BOOLEAN) {
|
||||
throw std::runtime_error("Expected a boolean");
|
||||
}
|
||||
return pop_front() != 0;
|
||||
}
|
||||
template<>
|
||||
unsigned int AMFDecoder::load<unsigned int>() {
|
||||
unsigned int value = 0;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
uint8_t b = pop_front();
|
||||
if (i == 3) {
|
||||
/* use all bits from 4th byte */
|
||||
value = (value << 8) | b;
|
||||
break;
|
||||
}
|
||||
value = (value << 7) | (b & 0x7f);
|
||||
if ((b & 0x80) == 0)
|
||||
break;
|
||||
}
|
||||
return value;
|
||||
unsigned int value = 0;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
uint8_t b = pop_front();
|
||||
if (i == 3) {
|
||||
/* use all bits from 4th byte */
|
||||
value = (value << 8) | b;
|
||||
break;
|
||||
}
|
||||
value = (value << 7) | (b & 0x7f);
|
||||
if ((b & 0x80) == 0)
|
||||
break;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
template<>
|
||||
int AMFDecoder::load<int>() {
|
||||
if (version == 3) {
|
||||
return load<unsigned int>();
|
||||
} else {
|
||||
return load<double>();
|
||||
}
|
||||
if (version == 3) {
|
||||
return load<unsigned int>();
|
||||
} else {
|
||||
return load<double>();
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
std::string AMFDecoder::load<std::string>() {
|
||||
size_t str_len = 0;
|
||||
uint8_t type = pop_front();
|
||||
if (version == 3) {
|
||||
if (type != AMF3_STRING) {
|
||||
throw std::runtime_error("Expected a string");
|
||||
}
|
||||
str_len = load<unsigned int>() / 2;
|
||||
size_t str_len = 0;
|
||||
uint8_t type = pop_front();
|
||||
if (version == 3) {
|
||||
if (type != AMF3_STRING) {
|
||||
throw std::runtime_error("Expected a string");
|
||||
}
|
||||
str_len = load<unsigned int>() / 2;
|
||||
|
||||
} else {
|
||||
if (type != AMF0_STRING) {
|
||||
throw std::runtime_error("Expected a string");
|
||||
}
|
||||
if (pos + 2 > buf.size()) {
|
||||
throw std::runtime_error("Not enough data");
|
||||
}
|
||||
str_len = load_be16(&buf[pos]);
|
||||
pos += 2;
|
||||
}
|
||||
if (pos + str_len > buf.size()) {
|
||||
throw std::runtime_error("Not enough data");
|
||||
}
|
||||
std::string s(buf, pos, str_len);
|
||||
pos += str_len;
|
||||
return s;
|
||||
} else {
|
||||
if (type != AMF0_STRING) {
|
||||
throw std::runtime_error("Expected a string");
|
||||
}
|
||||
if (pos + 2 > buf.size()) {
|
||||
throw std::runtime_error("Not enough data");
|
||||
}
|
||||
str_len = load_be16(&buf[pos]);
|
||||
pos += 2;
|
||||
}
|
||||
if (pos + str_len > buf.size()) {
|
||||
throw std::runtime_error("Not enough data");
|
||||
}
|
||||
std::string s(buf, pos, str_len);
|
||||
pos += str_len;
|
||||
return s;
|
||||
}
|
||||
|
||||
template<>
|
||||
AMFValue AMFDecoder::load<AMFValue>() {
|
||||
uint8_t type = front();
|
||||
if (version == 3) {
|
||||
switch (type) {
|
||||
case AMF3_STRING:
|
||||
return load<std::string>();
|
||||
case AMF3_NUMBER:
|
||||
return load<double>();
|
||||
case AMF3_INTEGER:
|
||||
return load<int>();
|
||||
case AMF3_FALSE:
|
||||
pos++;
|
||||
return false;
|
||||
case AMF3_TRUE:
|
||||
pos++;
|
||||
return true;
|
||||
case AMF3_OBJECT:
|
||||
return load_object();
|
||||
case AMF3_ARRAY:
|
||||
return load_ecma();
|
||||
case AMF3_NULL:
|
||||
pos++;
|
||||
return AMF_NULL;
|
||||
case AMF3_UNDEFINED:
|
||||
pos++;
|
||||
return AMF_UNDEFINED;
|
||||
default:
|
||||
throw std::runtime_error(
|
||||
StrPrinter << "Unsupported AMF3 type:" << (int) type << endl);
|
||||
}
|
||||
} else {
|
||||
switch (type) {
|
||||
case AMF0_STRING:
|
||||
return load<std::string>();
|
||||
case AMF0_NUMBER:
|
||||
return load<double>();
|
||||
case AMF0_BOOLEAN:
|
||||
return load<bool>();
|
||||
case AMF0_OBJECT:
|
||||
return load_object();
|
||||
case AMF0_ECMA_ARRAY:
|
||||
return load_ecma();
|
||||
case AMF0_NULL:
|
||||
pos++;
|
||||
return AMF_NULL;
|
||||
case AMF0_UNDEFINED:
|
||||
pos++;
|
||||
return AMF_UNDEFINED;
|
||||
case AMF0_STRICT_ARRAY:
|
||||
return load_arr();
|
||||
default:
|
||||
throw std::runtime_error(
|
||||
StrPrinter << "Unsupported AMF type:" << (int) type << endl);
|
||||
}
|
||||
}
|
||||
uint8_t type = front();
|
||||
if (version == 3) {
|
||||
switch (type) {
|
||||
case AMF3_STRING:
|
||||
return load<std::string>();
|
||||
case AMF3_NUMBER:
|
||||
return load<double>();
|
||||
case AMF3_INTEGER:
|
||||
return load<int>();
|
||||
case AMF3_FALSE:
|
||||
pos++;
|
||||
return false;
|
||||
case AMF3_TRUE:
|
||||
pos++;
|
||||
return true;
|
||||
case AMF3_OBJECT:
|
||||
return load_object();
|
||||
case AMF3_ARRAY:
|
||||
return load_ecma();
|
||||
case AMF3_NULL:
|
||||
pos++;
|
||||
return AMF_NULL;
|
||||
case AMF3_UNDEFINED:
|
||||
pos++;
|
||||
return AMF_UNDEFINED;
|
||||
default:
|
||||
throw std::runtime_error(
|
||||
StrPrinter << "Unsupported AMF3 type:" << (int) type << endl);
|
||||
}
|
||||
} else {
|
||||
switch (type) {
|
||||
case AMF0_STRING:
|
||||
return load<std::string>();
|
||||
case AMF0_NUMBER:
|
||||
return load<double>();
|
||||
case AMF0_BOOLEAN:
|
||||
return load<bool>();
|
||||
case AMF0_OBJECT:
|
||||
return load_object();
|
||||
case AMF0_ECMA_ARRAY:
|
||||
return load_ecma();
|
||||
case AMF0_NULL:
|
||||
pos++;
|
||||
return AMF_NULL;
|
||||
case AMF0_UNDEFINED:
|
||||
pos++;
|
||||
return AMF_UNDEFINED;
|
||||
case AMF0_STRICT_ARRAY:
|
||||
return load_arr();
|
||||
default:
|
||||
throw std::runtime_error(
|
||||
StrPrinter << "Unsupported AMF type:" << (int) type << endl);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::string AMFDecoder::load_key() {
|
||||
if (pos + 2 > buf.size()) {
|
||||
throw std::runtime_error("Not enough data");
|
||||
}
|
||||
size_t str_len = load_be16(&buf[pos]);
|
||||
pos += 2;
|
||||
if (pos + str_len > buf.size()) {
|
||||
throw std::runtime_error("Not enough data");
|
||||
}
|
||||
std::string s(buf, pos, str_len);
|
||||
pos += str_len;
|
||||
return s;
|
||||
if (pos + 2 > buf.size()) {
|
||||
throw std::runtime_error("Not enough data");
|
||||
}
|
||||
size_t str_len = load_be16(&buf[pos]);
|
||||
pos += 2;
|
||||
if (pos + str_len > buf.size()) {
|
||||
throw std::runtime_error("Not enough data");
|
||||
}
|
||||
std::string s(buf, pos, str_len);
|
||||
pos += str_len;
|
||||
return s;
|
||||
|
||||
}
|
||||
|
||||
AMFValue AMFDecoder::load_object() {
|
||||
AMFValue object(AMF_OBJECT);
|
||||
if (pop_front() != AMF0_OBJECT) {
|
||||
throw std::runtime_error("Expected an object");
|
||||
}
|
||||
while (1) {
|
||||
std::string key = load_key();
|
||||
if (key.empty())
|
||||
break;
|
||||
AMFValue value = load<AMFValue>();
|
||||
object.set(key, value);
|
||||
}
|
||||
if (pop_front() != AMF0_OBJECT_END) {
|
||||
throw std::runtime_error("expected object end");
|
||||
}
|
||||
return object;
|
||||
AMFValue object(AMF_OBJECT);
|
||||
if (pop_front() != AMF0_OBJECT) {
|
||||
throw std::runtime_error("Expected an object");
|
||||
}
|
||||
while (1) {
|
||||
std::string key = load_key();
|
||||
if (key.empty())
|
||||
break;
|
||||
AMFValue value = load<AMFValue>();
|
||||
object.set(key, value);
|
||||
}
|
||||
if (pop_front() != AMF0_OBJECT_END) {
|
||||
throw std::runtime_error("expected object end");
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
AMFValue AMFDecoder::load_ecma() {
|
||||
/* ECMA array is the same as object, with 4 extra zero bytes */
|
||||
AMFValue object(AMF_ECMA_ARRAY);
|
||||
if (pop_front() != AMF0_ECMA_ARRAY) {
|
||||
throw std::runtime_error("Expected an ECMA array");
|
||||
}
|
||||
if (pos + 4 > buf.size()) {
|
||||
throw std::runtime_error("Not enough data");
|
||||
}
|
||||
pos += 4;
|
||||
while (1) {
|
||||
std::string key = load_key();
|
||||
if (key.empty())
|
||||
break;
|
||||
AMFValue value = load<AMFValue>();
|
||||
object.set(key, value);
|
||||
}
|
||||
if (pop_front() != AMF0_OBJECT_END) {
|
||||
throw std::runtime_error("expected object end");
|
||||
}
|
||||
return object;
|
||||
/* ECMA array is the same as object, with 4 extra zero bytes */
|
||||
AMFValue object(AMF_ECMA_ARRAY);
|
||||
if (pop_front() != AMF0_ECMA_ARRAY) {
|
||||
throw std::runtime_error("Expected an ECMA array");
|
||||
}
|
||||
if (pos + 4 > buf.size()) {
|
||||
throw std::runtime_error("Not enough data");
|
||||
}
|
||||
pos += 4;
|
||||
while (1) {
|
||||
std::string key = load_key();
|
||||
if (key.empty())
|
||||
break;
|
||||
AMFValue value = load<AMFValue>();
|
||||
object.set(key, value);
|
||||
}
|
||||
if (pop_front() != AMF0_OBJECT_END) {
|
||||
throw std::runtime_error("expected object end");
|
||||
}
|
||||
return object;
|
||||
}
|
||||
AMFValue AMFDecoder::load_arr() {
|
||||
/* ECMA array is the same as object, with 4 extra zero bytes */
|
||||
AMFValue object(AMF_STRICT_ARRAY);
|
||||
if (pop_front() != AMF0_STRICT_ARRAY) {
|
||||
throw std::runtime_error("Expected an STRICT array");
|
||||
}
|
||||
if (pos + 4 > buf.size()) {
|
||||
throw std::runtime_error("Not enough data");
|
||||
}
|
||||
int arrSize = load_be32(&buf[pos]);
|
||||
pos += 4;
|
||||
while (arrSize--) {
|
||||
AMFValue value = load<AMFValue>();
|
||||
object.add(value);
|
||||
}
|
||||
/*pos += 2;
|
||||
if (pop_front() != AMF0_OBJECT_END) {
|
||||
throw std::runtime_error("expected object end");
|
||||
}*/
|
||||
return object;
|
||||
/* ECMA array is the same as object, with 4 extra zero bytes */
|
||||
AMFValue object(AMF_STRICT_ARRAY);
|
||||
if (pop_front() != AMF0_STRICT_ARRAY) {
|
||||
throw std::runtime_error("Expected an STRICT array");
|
||||
}
|
||||
if (pos + 4 > buf.size()) {
|
||||
throw std::runtime_error("Not enough data");
|
||||
}
|
||||
int arrSize = load_be32(&buf[pos]);
|
||||
pos += 4;
|
||||
while (arrSize--) {
|
||||
AMFValue value = load<AMFValue>();
|
||||
object.add(value);
|
||||
}
|
||||
/*pos += 2;
|
||||
if (pop_front() != AMF0_OBJECT_END) {
|
||||
throw std::runtime_error("expected object end");
|
||||
}*/
|
||||
return object;
|
||||
}
|
||||
|
||||
AMFDecoder::AMFDecoder(const std::string &buf_in, size_t pos_in, int version_in) :
|
||||
|
126
src/Rtmp/amf.h
126
src/Rtmp/amf.h
@ -36,15 +36,15 @@
|
||||
using namespace std;
|
||||
|
||||
enum AMFType {
|
||||
AMF_NUMBER,
|
||||
AMF_INTEGER,
|
||||
AMF_BOOLEAN,
|
||||
AMF_STRING,
|
||||
AMF_OBJECT,
|
||||
AMF_NULL,
|
||||
AMF_UNDEFINED,
|
||||
AMF_ECMA_ARRAY,
|
||||
AMF_STRICT_ARRAY,
|
||||
AMF_NUMBER,
|
||||
AMF_INTEGER,
|
||||
AMF_BOOLEAN,
|
||||
AMF_STRING,
|
||||
AMF_OBJECT,
|
||||
AMF_NULL,
|
||||
AMF_UNDEFINED,
|
||||
AMF_ECMA_ARRAY,
|
||||
AMF_STRICT_ARRAY,
|
||||
};
|
||||
|
||||
class AMFValue;
|
||||
@ -55,79 +55,79 @@ public:
|
||||
typedef std::map<std::string, AMFValue> mapType;
|
||||
typedef std::vector<AMFValue> arrayType;
|
||||
|
||||
AMFValue(AMFType type = AMF_NULL);
|
||||
AMFValue(const char *s);
|
||||
AMFValue(const std::string &s);
|
||||
AMFValue(double n);
|
||||
AMFValue(int i);
|
||||
AMFValue(bool b);
|
||||
AMFValue(const AMFValue &from);
|
||||
AMFValue(AMFValue &&from);
|
||||
AMFValue &operator =(const AMFValue &from);
|
||||
AMFValue &operator =(AMFValue &&from);
|
||||
~AMFValue();
|
||||
AMFValue(AMFType type = AMF_NULL);
|
||||
AMFValue(const char *s);
|
||||
AMFValue(const std::string &s);
|
||||
AMFValue(double n);
|
||||
AMFValue(int i);
|
||||
AMFValue(bool b);
|
||||
AMFValue(const AMFValue &from);
|
||||
AMFValue(AMFValue &&from);
|
||||
AMFValue &operator =(const AMFValue &from);
|
||||
AMFValue &operator =(AMFValue &&from);
|
||||
~AMFValue();
|
||||
|
||||
void clear();
|
||||
AMFType type() const ;
|
||||
const std::string &as_string() const;
|
||||
double as_number() const;
|
||||
int as_integer() const;
|
||||
void clear();
|
||||
AMFType type() const ;
|
||||
const std::string &as_string() const;
|
||||
double as_number() const;
|
||||
int as_integer() const;
|
||||
bool as_boolean() const;
|
||||
string to_string() const;
|
||||
const AMFValue &operator[](const char *str) const;
|
||||
void object_for_each(const function<void(const string &key, const AMFValue &val)> &fun) const ;
|
||||
operator bool() const;
|
||||
void set(const std::string &s, const AMFValue &val);
|
||||
void add(const AMFValue &val);
|
||||
string to_string() const;
|
||||
const AMFValue &operator[](const char *str) const;
|
||||
void object_for_each(const function<void(const string &key, const AMFValue &val)> &fun) const ;
|
||||
operator bool() const;
|
||||
void set(const std::string &s, const AMFValue &val);
|
||||
void add(const AMFValue &val);
|
||||
private:
|
||||
const mapType &getMap() const;
|
||||
const arrayType &getArr() const;
|
||||
void destroy();
|
||||
void init();
|
||||
private:
|
||||
AMFType _type;
|
||||
union {
|
||||
std::string *string;
|
||||
double number;
|
||||
int integer;
|
||||
bool boolean;
|
||||
mapType *object;
|
||||
arrayType *array;
|
||||
} _value;
|
||||
AMFType _type;
|
||||
union {
|
||||
std::string *string;
|
||||
double number;
|
||||
int integer;
|
||||
bool boolean;
|
||||
mapType *object;
|
||||
arrayType *array;
|
||||
} _value;
|
||||
};
|
||||
|
||||
class AMFDecoder {
|
||||
public:
|
||||
AMFDecoder(const std::string &buf, size_t pos, int version = 0);
|
||||
template<typename TP>
|
||||
TP load();
|
||||
AMFDecoder(const std::string &buf, size_t pos, int version = 0);
|
||||
template<typename TP>
|
||||
TP load();
|
||||
private:
|
||||
std::string load_key();
|
||||
AMFValue load_object();
|
||||
AMFValue load_ecma();
|
||||
AMFValue load_arr();
|
||||
uint8_t front();
|
||||
uint8_t pop_front();
|
||||
std::string load_key();
|
||||
AMFValue load_object();
|
||||
AMFValue load_ecma();
|
||||
AMFValue load_arr();
|
||||
uint8_t front();
|
||||
uint8_t pop_front();
|
||||
private:
|
||||
const std::string &buf;
|
||||
size_t pos;
|
||||
int version;
|
||||
const std::string &buf;
|
||||
size_t pos;
|
||||
int version;
|
||||
};
|
||||
|
||||
class AMFEncoder {
|
||||
public:
|
||||
AMFEncoder & operator <<(const char *s);
|
||||
AMFEncoder & operator <<(const std::string &s);
|
||||
AMFEncoder & operator <<(std::nullptr_t);
|
||||
AMFEncoder & operator <<(const int n);
|
||||
AMFEncoder & operator <<(const double n);
|
||||
AMFEncoder & operator <<(const bool b);
|
||||
AMFEncoder & operator <<(const AMFValue &value);
|
||||
const std::string& data() const ;
|
||||
void clear() ;
|
||||
AMFEncoder & operator <<(const char *s);
|
||||
AMFEncoder & operator <<(const std::string &s);
|
||||
AMFEncoder & operator <<(std::nullptr_t);
|
||||
AMFEncoder & operator <<(const int n);
|
||||
AMFEncoder & operator <<(const double n);
|
||||
AMFEncoder & operator <<(const bool b);
|
||||
AMFEncoder & operator <<(const AMFValue &value);
|
||||
const std::string& data() const ;
|
||||
void clear() ;
|
||||
private:
|
||||
void write_key(const std::string &s);
|
||||
AMFEncoder &write_undefined();
|
||||
void write_key(const std::string &s);
|
||||
AMFEncoder &write_undefined();
|
||||
private:
|
||||
std::string buf;
|
||||
};
|
||||
|
@ -38,54 +38,54 @@ using namespace toolkit;
|
||||
*/
|
||||
uint32_t load_be32(const void *p)
|
||||
{
|
||||
uint32_t val;
|
||||
memcpy(&val, p, sizeof val);
|
||||
return ntohl(val);
|
||||
uint32_t val;
|
||||
memcpy(&val, p, sizeof val);
|
||||
return ntohl(val);
|
||||
}
|
||||
|
||||
uint16_t load_be16(const void *p)
|
||||
{
|
||||
uint16_t val;
|
||||
memcpy(&val, p, sizeof val);
|
||||
return ntohs(val);
|
||||
uint16_t val;
|
||||
memcpy(&val, p, sizeof val);
|
||||
return ntohs(val);
|
||||
}
|
||||
|
||||
uint32_t load_le32(const void *p)
|
||||
{
|
||||
const uint8_t *data = (const uint8_t *) p;
|
||||
return data[0] | ((uint32_t) data[1] << 8) |
|
||||
((uint32_t) data[2] << 16) | ((uint32_t) data[3] << 24);
|
||||
const uint8_t *data = (const uint8_t *) p;
|
||||
return data[0] | ((uint32_t) data[1] << 8) |
|
||||
((uint32_t) data[2] << 16) | ((uint32_t) data[3] << 24);
|
||||
}
|
||||
|
||||
uint32_t load_be24(const void *p)
|
||||
{
|
||||
const uint8_t *data = (const uint8_t *) p;
|
||||
return data[2] | ((uint32_t) data[1] << 8) | ((uint32_t) data[0] << 16);
|
||||
const uint8_t *data = (const uint8_t *) p;
|
||||
return data[2] | ((uint32_t) data[1] << 8) | ((uint32_t) data[0] << 16);
|
||||
}
|
||||
|
||||
void set_be24(void *p, uint32_t val)
|
||||
{
|
||||
uint8_t *data = (uint8_t *) p;
|
||||
data[0] = val >> 16;
|
||||
data[1] = val >> 8;
|
||||
data[2] = val;
|
||||
uint8_t *data = (uint8_t *) p;
|
||||
data[0] = val >> 16;
|
||||
data[1] = val >> 8;
|
||||
data[2] = val;
|
||||
}
|
||||
|
||||
void set_le32(void *p, uint32_t val)
|
||||
{
|
||||
uint8_t *data = (uint8_t *) p;
|
||||
data[0] = val;
|
||||
data[1] = val >> 8;
|
||||
data[2] = val >> 16;
|
||||
data[3] = val >> 24;
|
||||
uint8_t *data = (uint8_t *) p;
|
||||
data[0] = val;
|
||||
data[1] = val >> 8;
|
||||
data[2] = val >> 16;
|
||||
data[3] = val >> 24;
|
||||
}
|
||||
|
||||
void set_be32(void *p, uint32_t val)
|
||||
{
|
||||
uint8_t *data = (uint8_t *) p;
|
||||
data[3] = val;
|
||||
data[2] = val >> 8;
|
||||
data[1] = val >> 16;
|
||||
data[0] = val >> 24;
|
||||
uint8_t *data = (uint8_t *) p;
|
||||
data[3] = val;
|
||||
data[2] = val >> 8;
|
||||
data[1] = val >> 16;
|
||||
data[0] = val >> 24;
|
||||
}
|
||||
|
||||
|
@ -37,47 +37,47 @@ using namespace toolkit;
|
||||
namespace mediakit{
|
||||
|
||||
MultiCastAddressMaker &MultiCastAddressMaker::Instance() {
|
||||
static MultiCastAddressMaker instance;
|
||||
return instance;
|
||||
static MultiCastAddressMaker instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
static uint32_t addressToInt(const string &ip){
|
||||
struct in_addr addr;
|
||||
bzero(&addr,sizeof(addr));
|
||||
addr.s_addr = inet_addr(ip.data());
|
||||
addr.s_addr = inet_addr(ip.data());
|
||||
return (uint32_t)ntohl((uint32_t &)addr.s_addr);
|
||||
}
|
||||
|
||||
std::shared_ptr<uint32_t> MultiCastAddressMaker::obtain(uint32_t iTry) {
|
||||
lock_guard<recursive_mutex> lck(_mtx);
|
||||
lock_guard<recursive_mutex> lck(_mtx);
|
||||
GET_CONFIG(string,addrMinStr,MultiCast::kAddrMin);
|
||||
GET_CONFIG(string,addrMaxStr,MultiCast::kAddrMax);
|
||||
uint32_t addrMin = addressToInt(addrMinStr);
|
||||
uint32_t addrMax = addressToInt(addrMaxStr);
|
||||
uint32_t addrMax = addressToInt(addrMaxStr);
|
||||
|
||||
if(_iAddr > addrMax || _iAddr == 0){
|
||||
_iAddr = addrMin;
|
||||
}
|
||||
auto iGotAddr = _iAddr++;
|
||||
if(_setBadAddr.find(iGotAddr) != _setBadAddr.end()){
|
||||
//已经分配过了
|
||||
if(iTry){
|
||||
return obtain(--iTry);
|
||||
}
|
||||
//分配完了,应该不可能到这里
|
||||
ErrorL;
|
||||
return nullptr;
|
||||
}
|
||||
_setBadAddr.emplace(iGotAddr);
|
||||
std::shared_ptr<uint32_t> ret(new uint32_t(iGotAddr),[](uint32_t *ptr){
|
||||
MultiCastAddressMaker::Instance().release(*ptr);
|
||||
delete ptr;
|
||||
});
|
||||
return ret;
|
||||
if(_iAddr > addrMax || _iAddr == 0){
|
||||
_iAddr = addrMin;
|
||||
}
|
||||
auto iGotAddr = _iAddr++;
|
||||
if(_setBadAddr.find(iGotAddr) != _setBadAddr.end()){
|
||||
//已经分配过了
|
||||
if(iTry){
|
||||
return obtain(--iTry);
|
||||
}
|
||||
//分配完了,应该不可能到这里
|
||||
ErrorL;
|
||||
return nullptr;
|
||||
}
|
||||
_setBadAddr.emplace(iGotAddr);
|
||||
std::shared_ptr<uint32_t> ret(new uint32_t(iGotAddr),[](uint32_t *ptr){
|
||||
MultiCastAddressMaker::Instance().release(*ptr);
|
||||
delete ptr;
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
void MultiCastAddressMaker::release(uint32_t iAddr){
|
||||
lock_guard<recursive_mutex> lck(_mtx);
|
||||
_setBadAddr.erase(iAddr);
|
||||
lock_guard<recursive_mutex> lck(_mtx);
|
||||
_setBadAddr.erase(iAddr);
|
||||
}
|
||||
|
||||
|
||||
@ -85,106 +85,106 @@ recursive_mutex RtpMultiCaster::g_mtx;
|
||||
unordered_map<string, weak_ptr<RtpMultiCaster> > RtpMultiCaster::g_mapBroadCaster;
|
||||
|
||||
void RtpMultiCaster::setDetachCB(void* listener, const onDetach& cb) {
|
||||
lock_guard<recursive_mutex> lck(_mtx);
|
||||
if(cb){
|
||||
_mapDetach.emplace(listener,cb);
|
||||
}else{
|
||||
_mapDetach.erase(listener);
|
||||
}
|
||||
lock_guard<recursive_mutex> lck(_mtx);
|
||||
if(cb){
|
||||
_mapDetach.emplace(listener,cb);
|
||||
}else{
|
||||
_mapDetach.erase(listener);
|
||||
}
|
||||
}
|
||||
RtpMultiCaster::~RtpMultiCaster() {
|
||||
_pReader->setReadCB(nullptr);
|
||||
_pReader->setDetachCB(nullptr);
|
||||
DebugL;
|
||||
_pReader->setReadCB(nullptr);
|
||||
_pReader->setDetachCB(nullptr);
|
||||
DebugL;
|
||||
}
|
||||
RtpMultiCaster::RtpMultiCaster(const EventPoller::Ptr &poller,const string &strLocalIp,const string &strVhost,const string &strApp,const string &strStream) {
|
||||
auto src = dynamic_pointer_cast<RtspMediaSource>(MediaSource::find(RTSP_SCHEMA,strVhost,strApp, strStream));
|
||||
if(!src){
|
||||
auto strErr = StrPrinter << "未找到媒体源:" << strVhost << " " << strApp << " " << strStream << endl;
|
||||
throw std::runtime_error(strErr);
|
||||
}
|
||||
_multiAddr = MultiCastAddressMaker::Instance().obtain();
|
||||
for(auto i = 0; i < 2; i++){
|
||||
_apUdpSock[i].reset(new Socket(poller));
|
||||
if(!_apUdpSock[i]->bindUdpSock(0, strLocalIp.data())){
|
||||
auto strErr = StrPrinter << "绑定UDP端口失败:" << strLocalIp << endl;
|
||||
throw std::runtime_error(strErr);
|
||||
}
|
||||
auto fd = _apUdpSock[i]->rawFD();
|
||||
auto src = dynamic_pointer_cast<RtspMediaSource>(MediaSource::find(RTSP_SCHEMA,strVhost,strApp, strStream));
|
||||
if(!src){
|
||||
auto strErr = StrPrinter << "未找到媒体源:" << strVhost << " " << strApp << " " << strStream << endl;
|
||||
throw std::runtime_error(strErr);
|
||||
}
|
||||
_multiAddr = MultiCastAddressMaker::Instance().obtain();
|
||||
for(auto i = 0; i < 2; i++){
|
||||
_apUdpSock[i].reset(new Socket(poller));
|
||||
if(!_apUdpSock[i]->bindUdpSock(0, strLocalIp.data())){
|
||||
auto strErr = StrPrinter << "绑定UDP端口失败:" << strLocalIp << endl;
|
||||
throw std::runtime_error(strErr);
|
||||
}
|
||||
auto fd = _apUdpSock[i]->rawFD();
|
||||
GET_CONFIG(uint32_t,udpTTL,MultiCast::kUdpTTL);
|
||||
|
||||
SockUtil::setMultiTTL(fd, udpTTL);
|
||||
SockUtil::setMultiLOOP(fd, false);
|
||||
SockUtil::setMultiIF(fd, strLocalIp.data());
|
||||
SockUtil::setMultiLOOP(fd, false);
|
||||
SockUtil::setMultiIF(fd, strLocalIp.data());
|
||||
|
||||
struct sockaddr_in &peerAddr = _aPeerUdpAddr[i];
|
||||
peerAddr.sin_family = AF_INET;
|
||||
peerAddr.sin_port = htons(_apUdpSock[i]->get_local_port());
|
||||
peerAddr.sin_addr.s_addr = htonl(*_multiAddr);
|
||||
bzero(&(peerAddr.sin_zero), sizeof peerAddr.sin_zero);
|
||||
_apUdpSock[i]->setSendPeerAddr((struct sockaddr *)&peerAddr);
|
||||
}
|
||||
_pReader = src->getRing()->attach(poller);
|
||||
_pReader->setReadCB([this](const RtpPacket::Ptr &pkt){
|
||||
int i = (int)(pkt->type);
|
||||
auto &pSock = _apUdpSock[i];
|
||||
auto &peerAddr = _aPeerUdpAddr[i];
|
||||
struct sockaddr_in &peerAddr = _aPeerUdpAddr[i];
|
||||
peerAddr.sin_family = AF_INET;
|
||||
peerAddr.sin_port = htons(_apUdpSock[i]->get_local_port());
|
||||
peerAddr.sin_addr.s_addr = htonl(*_multiAddr);
|
||||
bzero(&(peerAddr.sin_zero), sizeof peerAddr.sin_zero);
|
||||
_apUdpSock[i]->setSendPeerAddr((struct sockaddr *)&peerAddr);
|
||||
}
|
||||
_pReader = src->getRing()->attach(poller);
|
||||
_pReader->setReadCB([this](const RtpPacket::Ptr &pkt){
|
||||
int i = (int)(pkt->type);
|
||||
auto &pSock = _apUdpSock[i];
|
||||
auto &peerAddr = _aPeerUdpAddr[i];
|
||||
BufferRtp::Ptr buffer(new BufferRtp(pkt,4));
|
||||
pSock->send(buffer);
|
||||
});
|
||||
_pReader->setDetachCB([this](){
|
||||
unordered_map<void * , onDetach > _mapDetach_copy;
|
||||
{
|
||||
lock_guard<recursive_mutex> lck(_mtx);
|
||||
_mapDetach_copy = std::move(_mapDetach);
|
||||
}
|
||||
for(auto &pr : _mapDetach_copy){
|
||||
pr.second();
|
||||
}
|
||||
});
|
||||
DebugL << MultiCastAddressMaker::toString(*_multiAddr) << " "
|
||||
<< _apUdpSock[0]->get_local_port() << " "
|
||||
<< _apUdpSock[1]->get_local_port() << " "
|
||||
pSock->send(buffer);
|
||||
});
|
||||
_pReader->setDetachCB([this](){
|
||||
unordered_map<void * , onDetach > _mapDetach_copy;
|
||||
{
|
||||
lock_guard<recursive_mutex> lck(_mtx);
|
||||
_mapDetach_copy = std::move(_mapDetach);
|
||||
}
|
||||
for(auto &pr : _mapDetach_copy){
|
||||
pr.second();
|
||||
}
|
||||
});
|
||||
DebugL << MultiCastAddressMaker::toString(*_multiAddr) << " "
|
||||
<< _apUdpSock[0]->get_local_port() << " "
|
||||
<< _apUdpSock[1]->get_local_port() << " "
|
||||
<< strVhost << " "
|
||||
<< strApp << " " << strStream;
|
||||
<< strApp << " " << strStream;
|
||||
}
|
||||
uint16_t RtpMultiCaster::getPort(TrackType trackType){
|
||||
return _apUdpSock[trackType]->get_local_port();
|
||||
return _apUdpSock[trackType]->get_local_port();
|
||||
}
|
||||
string RtpMultiCaster::getIP(){
|
||||
return inet_ntoa(_aPeerUdpAddr[0].sin_addr);
|
||||
return inet_ntoa(_aPeerUdpAddr[0].sin_addr);
|
||||
}
|
||||
RtpMultiCaster::Ptr RtpMultiCaster::make(const EventPoller::Ptr &poller,const string &strLocalIp,const string &strVhost,const string &strApp,const string &strStream){
|
||||
try{
|
||||
auto ret = Ptr(new RtpMultiCaster(poller,strLocalIp,strVhost,strApp,strStream),[poller](RtpMultiCaster *ptr){
|
||||
try{
|
||||
auto ret = Ptr(new RtpMultiCaster(poller,strLocalIp,strVhost,strApp,strStream),[poller](RtpMultiCaster *ptr){
|
||||
poller->async([ptr]() {
|
||||
delete ptr;
|
||||
});
|
||||
});
|
||||
lock_guard<recursive_mutex> lck(g_mtx);
|
||||
string strKey = StrPrinter << strLocalIp << " " << strVhost << " " << strApp << " " << strStream << endl;
|
||||
weak_ptr<RtpMultiCaster> weakPtr = ret;
|
||||
g_mapBroadCaster.emplace(strKey,weakPtr);
|
||||
return ret;
|
||||
}catch (std::exception &ex) {
|
||||
WarnL << ex.what();
|
||||
return nullptr;
|
||||
}
|
||||
});
|
||||
lock_guard<recursive_mutex> lck(g_mtx);
|
||||
string strKey = StrPrinter << strLocalIp << " " << strVhost << " " << strApp << " " << strStream << endl;
|
||||
weak_ptr<RtpMultiCaster> weakPtr = ret;
|
||||
g_mapBroadCaster.emplace(strKey,weakPtr);
|
||||
return ret;
|
||||
}catch (std::exception &ex) {
|
||||
WarnL << ex.what();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
RtpMultiCaster::Ptr RtpMultiCaster::get(const EventPoller::Ptr &poller,const string &strLocalIp,const string &strVhost,const string &strApp,const string &strStream) {
|
||||
string strKey = StrPrinter << strLocalIp << " " << strVhost << " " << strApp << " " << strStream << endl;
|
||||
lock_guard<recursive_mutex> lck(g_mtx);
|
||||
auto it = g_mapBroadCaster.find(strKey);
|
||||
if (it == g_mapBroadCaster.end()) {
|
||||
return make(poller,strLocalIp,strVhost,strApp, strStream);
|
||||
}
|
||||
auto ret = it->second.lock();
|
||||
if (!ret) {
|
||||
g_mapBroadCaster.erase(it);
|
||||
return make(poller,strLocalIp,strVhost,strApp, strStream);
|
||||
}
|
||||
return ret;
|
||||
string strKey = StrPrinter << strLocalIp << " " << strVhost << " " << strApp << " " << strStream << endl;
|
||||
lock_guard<recursive_mutex> lck(g_mtx);
|
||||
auto it = g_mapBroadCaster.find(strKey);
|
||||
if (it == g_mapBroadCaster.end()) {
|
||||
return make(poller,strLocalIp,strVhost,strApp, strStream);
|
||||
}
|
||||
auto ret = it->second.lock();
|
||||
if (!ret) {
|
||||
g_mapBroadCaster.erase(it);
|
||||
return make(poller,strLocalIp,strVhost,strApp, strStream);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
@ -45,48 +45,48 @@ namespace mediakit{
|
||||
class MultiCastAddressMaker
|
||||
{
|
||||
public:
|
||||
static MultiCastAddressMaker &Instance();
|
||||
static MultiCastAddressMaker &Instance();
|
||||
|
||||
static bool isMultiCastAddress(uint32_t iAddr){
|
||||
static uint32_t addrMin = mINI::Instance()[MultiCast::kAddrMin].as<uint32_t>();
|
||||
static uint32_t addrMax = mINI::Instance()[MultiCast::kAddrMax].as<uint32_t>();
|
||||
return iAddr >= addrMin && iAddr <= addrMax;
|
||||
}
|
||||
static string toString(uint32_t iAddr){
|
||||
iAddr = htonl(iAddr);
|
||||
return ::inet_ntoa((struct in_addr &)(iAddr));
|
||||
}
|
||||
virtual ~MultiCastAddressMaker(){}
|
||||
std::shared_ptr<uint32_t> obtain(uint32_t iTry = 10);
|
||||
static bool isMultiCastAddress(uint32_t iAddr){
|
||||
static uint32_t addrMin = mINI::Instance()[MultiCast::kAddrMin].as<uint32_t>();
|
||||
static uint32_t addrMax = mINI::Instance()[MultiCast::kAddrMax].as<uint32_t>();
|
||||
return iAddr >= addrMin && iAddr <= addrMax;
|
||||
}
|
||||
static string toString(uint32_t iAddr){
|
||||
iAddr = htonl(iAddr);
|
||||
return ::inet_ntoa((struct in_addr &)(iAddr));
|
||||
}
|
||||
virtual ~MultiCastAddressMaker(){}
|
||||
std::shared_ptr<uint32_t> obtain(uint32_t iTry = 10);
|
||||
private:
|
||||
MultiCastAddressMaker(){};
|
||||
void release(uint32_t iAddr);
|
||||
uint32_t _iAddr = 0;
|
||||
recursive_mutex _mtx;
|
||||
unordered_set<uint32_t> _setBadAddr;
|
||||
MultiCastAddressMaker(){};
|
||||
void release(uint32_t iAddr);
|
||||
uint32_t _iAddr = 0;
|
||||
recursive_mutex _mtx;
|
||||
unordered_set<uint32_t> _setBadAddr;
|
||||
};
|
||||
class RtpMultiCaster {
|
||||
public:
|
||||
typedef std::shared_ptr<RtpMultiCaster> Ptr;
|
||||
typedef function<void()> onDetach;
|
||||
virtual ~RtpMultiCaster();
|
||||
static Ptr get(const EventPoller::Ptr &poller,const string &strLocalIp,const string &strVhost,const string &strApp,const string &strStream);
|
||||
void setDetachCB(void *listener,const onDetach &cb);
|
||||
uint16_t getPort(TrackType trackType);
|
||||
string getIP();
|
||||
typedef std::shared_ptr<RtpMultiCaster> Ptr;
|
||||
typedef function<void()> onDetach;
|
||||
virtual ~RtpMultiCaster();
|
||||
static Ptr get(const EventPoller::Ptr &poller,const string &strLocalIp,const string &strVhost,const string &strApp,const string &strStream);
|
||||
void setDetachCB(void *listener,const onDetach &cb);
|
||||
uint16_t getPort(TrackType trackType);
|
||||
string getIP();
|
||||
private:
|
||||
static recursive_mutex g_mtx;
|
||||
static unordered_map<string , weak_ptr<RtpMultiCaster> > g_mapBroadCaster;
|
||||
static Ptr make(const EventPoller::Ptr &poller,const string &strLocalIp,const string &strVhost,const string &strApp,const string &strStream);
|
||||
static recursive_mutex g_mtx;
|
||||
static unordered_map<string , weak_ptr<RtpMultiCaster> > g_mapBroadCaster;
|
||||
static Ptr make(const EventPoller::Ptr &poller,const string &strLocalIp,const string &strVhost,const string &strApp,const string &strStream);
|
||||
|
||||
std::shared_ptr<uint32_t> _multiAddr;
|
||||
recursive_mutex _mtx;
|
||||
unordered_map<void * , onDetach > _mapDetach;
|
||||
RtspMediaSource::RingType::RingReader::Ptr _pReader;
|
||||
Socket::Ptr _apUdpSock[2];
|
||||
struct sockaddr_in _aPeerUdpAddr[2];
|
||||
std::shared_ptr<uint32_t> _multiAddr;
|
||||
recursive_mutex _mtx;
|
||||
unordered_map<void * , onDetach > _mapDetach;
|
||||
RtspMediaSource::RingType::RingReader::Ptr _pReader;
|
||||
Socket::Ptr _apUdpSock[2];
|
||||
struct sockaddr_in _aPeerUdpAddr[2];
|
||||
|
||||
RtpMultiCaster(const EventPoller::Ptr &poller,const string &strLocalIp,const string &strVhost,const string &strApp,const string &strStream);
|
||||
RtpMultiCaster(const EventPoller::Ptr &poller,const string &strLocalIp,const string &strVhost,const string &strApp,const string &strStream);
|
||||
|
||||
};
|
||||
|
||||
|
@ -28,9 +28,9 @@
|
||||
#include "RtpReceiver.h"
|
||||
|
||||
#define POP_HEAD(trackidx) \
|
||||
auto it = _rtp_sort_cache_map[trackidx].begin(); \
|
||||
onRtpSorted(it->second, trackidx); \
|
||||
_rtp_sort_cache_map[trackidx].erase(it);
|
||||
auto it = _rtp_sort_cache_map[trackidx].begin(); \
|
||||
onRtpSorted(it->second, trackidx); \
|
||||
_rtp_sort_cache_map[trackidx].erase(it);
|
||||
|
||||
#define AV_RB16(x) \
|
||||
((((const uint8_t*)(x))[0] << 8) | \
|
||||
|
@ -31,176 +31,176 @@
|
||||
namespace mediakit{
|
||||
|
||||
static void getAttrSdp(const map<string, string> &attr, _StrPrinter &printer){
|
||||
const map<string, string>::value_type *ptr = nullptr;
|
||||
for(auto &pr : attr){
|
||||
if(pr.first == "control"){
|
||||
ptr = ≺
|
||||
continue;
|
||||
}
|
||||
if(pr.second.empty()){
|
||||
printer << "a=" << pr.first << "\r\n";
|
||||
}else{
|
||||
printer << "a=" << pr.first << ":" << pr.second << "\r\n";
|
||||
}
|
||||
}
|
||||
if(ptr){
|
||||
printer << "a=" << ptr->first << ":" << ptr->second << "\r\n";
|
||||
}
|
||||
const map<string, string>::value_type *ptr = nullptr;
|
||||
for(auto &pr : attr){
|
||||
if(pr.first == "control"){
|
||||
ptr = ≺
|
||||
continue;
|
||||
}
|
||||
if(pr.second.empty()){
|
||||
printer << "a=" << pr.first << "\r\n";
|
||||
}else{
|
||||
printer << "a=" << pr.first << ":" << pr.second << "\r\n";
|
||||
}
|
||||
}
|
||||
if(ptr){
|
||||
printer << "a=" << ptr->first << ":" << ptr->second << "\r\n";
|
||||
}
|
||||
}
|
||||
string SdpTrack::toString() const {
|
||||
_StrPrinter _printer;
|
||||
switch (_type){
|
||||
case TrackTitle:{
|
||||
_printer << "v=" << 0 << "\r\n";
|
||||
if(!_o.empty()){
|
||||
_printer << "o="<< _o << "\r\n";
|
||||
}
|
||||
if(!_c.empty()){
|
||||
_printer << "c=" << _c << "\r\n";
|
||||
}
|
||||
if(!_t.empty()){
|
||||
_printer << "t=" << _t << "\r\n";
|
||||
}
|
||||
_StrPrinter _printer;
|
||||
switch (_type){
|
||||
case TrackTitle:{
|
||||
_printer << "v=" << 0 << "\r\n";
|
||||
if(!_o.empty()){
|
||||
_printer << "o="<< _o << "\r\n";
|
||||
}
|
||||
if(!_c.empty()){
|
||||
_printer << "c=" << _c << "\r\n";
|
||||
}
|
||||
if(!_t.empty()){
|
||||
_printer << "t=" << _t << "\r\n";
|
||||
}
|
||||
|
||||
_printer << "s=RTSP Session, streamed by the ZLMediaKit\r\n";
|
||||
_printer << "i=ZLMediaKit Live Stream\r\n";
|
||||
getAttrSdp(_attr,_printer);
|
||||
}
|
||||
break;
|
||||
case TrackAudio:
|
||||
case TrackVideo:{
|
||||
if(_type == TrackAudio){
|
||||
_printer << "m=audio 0 RTP/AVP " << _pt << "\r\n";
|
||||
}else{
|
||||
_printer << "m=video 0 RTP/AVP " << _pt << "\r\n";
|
||||
}
|
||||
if(!_b.empty()){
|
||||
_printer << "b=" <<_b << "\r\n";
|
||||
}
|
||||
getAttrSdp(_attr,_printer);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return _printer;
|
||||
_printer << "s=RTSP Session, streamed by the ZLMediaKit\r\n";
|
||||
_printer << "i=ZLMediaKit Live Stream\r\n";
|
||||
getAttrSdp(_attr,_printer);
|
||||
}
|
||||
break;
|
||||
case TrackAudio:
|
||||
case TrackVideo:{
|
||||
if(_type == TrackAudio){
|
||||
_printer << "m=audio 0 RTP/AVP " << _pt << "\r\n";
|
||||
}else{
|
||||
_printer << "m=video 0 RTP/AVP " << _pt << "\r\n";
|
||||
}
|
||||
if(!_b.empty()){
|
||||
_printer << "b=" <<_b << "\r\n";
|
||||
}
|
||||
getAttrSdp(_attr,_printer);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return _printer;
|
||||
}
|
||||
|
||||
static TrackType toTrackType(const string &str) {
|
||||
if (str == "") {
|
||||
return TrackTitle;
|
||||
}
|
||||
if (str == "") {
|
||||
return TrackTitle;
|
||||
}
|
||||
|
||||
if (str == "video") {
|
||||
return TrackVideo;
|
||||
}
|
||||
if (str == "video") {
|
||||
return TrackVideo;
|
||||
}
|
||||
|
||||
if (str == "audio") {
|
||||
return TrackAudio;
|
||||
}
|
||||
if (str == "audio") {
|
||||
return TrackAudio;
|
||||
}
|
||||
|
||||
return TrackInvalid;
|
||||
return TrackInvalid;
|
||||
}
|
||||
|
||||
void SdpParser::load(const string &sdp) {
|
||||
{
|
||||
_track_vec.clear();
|
||||
string key;
|
||||
SdpTrack::Ptr track = std::make_shared<SdpTrack>();
|
||||
{
|
||||
_track_vec.clear();
|
||||
string key;
|
||||
SdpTrack::Ptr track = std::make_shared<SdpTrack>();
|
||||
|
||||
auto lines = split(sdp, "\n");
|
||||
for (auto &line : lines) {
|
||||
trim(line);
|
||||
if (line.size() < 2 || line[1] != '=') {
|
||||
continue;
|
||||
}
|
||||
char opt = line[0];
|
||||
string opt_val = line.substr(2);
|
||||
switch (opt) {
|
||||
case 'o':
|
||||
track->_o = opt_val;
|
||||
break;
|
||||
case 's':
|
||||
track->_s = opt_val;
|
||||
break;
|
||||
case 'i':
|
||||
track->_i = opt_val;
|
||||
break;
|
||||
case 'c':
|
||||
track->_c = opt_val;
|
||||
break;
|
||||
case 't':
|
||||
track->_t = opt_val;
|
||||
break;
|
||||
case 'b':
|
||||
track->_b = opt_val;
|
||||
break;
|
||||
case 'm': {
|
||||
track->_type = toTrackType(key);
|
||||
_track_vec.emplace_back(track);
|
||||
track = std::make_shared<SdpTrack>();
|
||||
key = FindField(opt_val.data(), nullptr, " ");
|
||||
track->_m = opt_val;
|
||||
}
|
||||
break;
|
||||
case 'a': {
|
||||
string attr = FindField(opt_val.data(), nullptr, ":");
|
||||
if (attr.empty()) {
|
||||
track->_attr[opt_val] = "";
|
||||
} else {
|
||||
track->_attr[attr] = FindField(opt_val.data(), ":", nullptr);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
track->_other[opt] = opt_val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
track->_type = toTrackType(key);
|
||||
_track_vec.emplace_back(track);
|
||||
}
|
||||
auto lines = split(sdp, "\n");
|
||||
for (auto &line : lines) {
|
||||
trim(line);
|
||||
if (line.size() < 2 || line[1] != '=') {
|
||||
continue;
|
||||
}
|
||||
char opt = line[0];
|
||||
string opt_val = line.substr(2);
|
||||
switch (opt) {
|
||||
case 'o':
|
||||
track->_o = opt_val;
|
||||
break;
|
||||
case 's':
|
||||
track->_s = opt_val;
|
||||
break;
|
||||
case 'i':
|
||||
track->_i = opt_val;
|
||||
break;
|
||||
case 'c':
|
||||
track->_c = opt_val;
|
||||
break;
|
||||
case 't':
|
||||
track->_t = opt_val;
|
||||
break;
|
||||
case 'b':
|
||||
track->_b = opt_val;
|
||||
break;
|
||||
case 'm': {
|
||||
track->_type = toTrackType(key);
|
||||
_track_vec.emplace_back(track);
|
||||
track = std::make_shared<SdpTrack>();
|
||||
key = FindField(opt_val.data(), nullptr, " ");
|
||||
track->_m = opt_val;
|
||||
}
|
||||
break;
|
||||
case 'a': {
|
||||
string attr = FindField(opt_val.data(), nullptr, ":");
|
||||
if (attr.empty()) {
|
||||
track->_attr[opt_val] = "";
|
||||
} else {
|
||||
track->_attr[attr] = FindField(opt_val.data(), ":", nullptr);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
track->_other[opt] = opt_val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
track->_type = toTrackType(key);
|
||||
_track_vec.emplace_back(track);
|
||||
}
|
||||
|
||||
for (auto &track_ptr : _track_vec) {
|
||||
auto &track = *track_ptr;
|
||||
auto it = track._attr.find("range");
|
||||
if (it != track._attr.end()) {
|
||||
char name[16] = {0}, start[16] = {0}, end[16] = {0};
|
||||
int ret = sscanf(it->second.data(), "%15[^=]=%15[^-]-%15s", name, start, end);
|
||||
if (3 == ret || 2 == ret) {
|
||||
if (strcmp(start, "now") == 0) {
|
||||
strcpy(start, "0");
|
||||
}
|
||||
track._start = atof(start);
|
||||
track._end = atof(end);
|
||||
track._duration = track._end - track._start;
|
||||
}
|
||||
}
|
||||
for (auto &track_ptr : _track_vec) {
|
||||
auto &track = *track_ptr;
|
||||
auto it = track._attr.find("range");
|
||||
if (it != track._attr.end()) {
|
||||
char name[16] = {0}, start[16] = {0}, end[16] = {0};
|
||||
int ret = sscanf(it->second.data(), "%15[^=]=%15[^-]-%15s", name, start, end);
|
||||
if (3 == ret || 2 == ret) {
|
||||
if (strcmp(start, "now") == 0) {
|
||||
strcpy(start, "0");
|
||||
}
|
||||
track._start = atof(start);
|
||||
track._end = atof(end);
|
||||
track._duration = track._end - track._start;
|
||||
}
|
||||
}
|
||||
|
||||
it = track._attr.find("rtpmap");
|
||||
if(it != track._attr.end()){
|
||||
auto rtpmap = it->second;
|
||||
int pt, samplerate;
|
||||
char codec[16] = {0};
|
||||
if (3 == sscanf(rtpmap.data(), "%d %15[^/]/%d", &pt, codec, &samplerate)) {
|
||||
track._pt = pt;
|
||||
track._codec = codec;
|
||||
track._samplerate = samplerate;
|
||||
}
|
||||
}
|
||||
it = track._attr.find("rtpmap");
|
||||
if(it != track._attr.end()){
|
||||
auto rtpmap = it->second;
|
||||
int pt, samplerate;
|
||||
char codec[16] = {0};
|
||||
if (3 == sscanf(rtpmap.data(), "%d %15[^/]/%d", &pt, codec, &samplerate)) {
|
||||
track._pt = pt;
|
||||
track._codec = codec;
|
||||
track._samplerate = samplerate;
|
||||
}
|
||||
}
|
||||
|
||||
it = track._attr.find("fmtp");
|
||||
if(it != track._attr.end()) {
|
||||
track._fmtp = it->second;
|
||||
}
|
||||
it = track._attr.find("fmtp");
|
||||
if(it != track._attr.end()) {
|
||||
track._fmtp = it->second;
|
||||
}
|
||||
|
||||
it = track._attr.find("control");
|
||||
if(it != track._attr.end()) {
|
||||
track._control = it->second;
|
||||
auto surffix = string("/") + track._control;
|
||||
track._control_surffix = surffix.substr(1 + surffix.rfind('/'));
|
||||
}
|
||||
}
|
||||
it = track._attr.find("control");
|
||||
if(it != track._attr.end()) {
|
||||
track._control = it->second;
|
||||
auto surffix = string("/") + track._control;
|
||||
track._control_surffix = surffix.substr(1 + surffix.rfind('/'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SdpParser::available() const {
|
||||
@ -208,112 +208,112 @@ bool SdpParser::available() const {
|
||||
}
|
||||
|
||||
SdpTrack::Ptr SdpParser::getTrack(TrackType type) const {
|
||||
for (auto &track : _track_vec){
|
||||
if(track->_type == type){
|
||||
return track;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
for (auto &track : _track_vec){
|
||||
if(track->_type == type){
|
||||
return track;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
vector<SdpTrack::Ptr> SdpParser::getAvailableTrack() const {
|
||||
vector<SdpTrack::Ptr> ret;
|
||||
bool audio_added = false;
|
||||
bool video_added = false;
|
||||
for (auto &track : _track_vec){
|
||||
if(track->_type == TrackAudio ){
|
||||
if(!audio_added){
|
||||
ret.emplace_back(track);
|
||||
audio_added = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
vector<SdpTrack::Ptr> ret;
|
||||
bool audio_added = false;
|
||||
bool video_added = false;
|
||||
for (auto &track : _track_vec){
|
||||
if(track->_type == TrackAudio ){
|
||||
if(!audio_added){
|
||||
ret.emplace_back(track);
|
||||
audio_added = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if(track->_type == TrackVideo ){
|
||||
if(!video_added){
|
||||
ret.emplace_back(track);
|
||||
video_added = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return std::move(ret);
|
||||
if(track->_type == TrackVideo ){
|
||||
if(!video_added){
|
||||
ret.emplace_back(track);
|
||||
video_added = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return std::move(ret);
|
||||
}
|
||||
|
||||
string SdpParser::toString() const {
|
||||
string title,audio,video;
|
||||
for(auto &track : _track_vec){
|
||||
switch (track->_type){
|
||||
case TrackTitle:{
|
||||
title = track->toString();
|
||||
}
|
||||
break;
|
||||
case TrackVideo:{
|
||||
video = track->toString();
|
||||
}
|
||||
break;
|
||||
case TrackAudio:{
|
||||
audio = track->toString();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return title + video + audio;
|
||||
string title,audio,video;
|
||||
for(auto &track : _track_vec){
|
||||
switch (track->_type){
|
||||
case TrackTitle:{
|
||||
title = track->toString();
|
||||
}
|
||||
break;
|
||||
case TrackVideo:{
|
||||
video = track->toString();
|
||||
}
|
||||
break;
|
||||
case TrackAudio:{
|
||||
audio = track->toString();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return title + video + audio;
|
||||
}
|
||||
|
||||
bool RtspUrl::parse(const string &strUrl) {
|
||||
auto schema = FindField(strUrl.data(), nullptr, "://");
|
||||
bool isSSL = strcasecmp(schema.data(), "rtsps") == 0;
|
||||
//查找"://"与"/"之间的字符串,用于提取用户名密码
|
||||
auto middle_url = FindField(strUrl.data(), "://", "/");
|
||||
if (middle_url.empty()) {
|
||||
middle_url = FindField(strUrl.data(), "://", nullptr);
|
||||
}
|
||||
auto pos = middle_url.rfind('@');
|
||||
if (pos == string::npos) {
|
||||
//并没有用户名密码
|
||||
return setup(isSSL, strUrl, "", "");
|
||||
}
|
||||
auto schema = FindField(strUrl.data(), nullptr, "://");
|
||||
bool isSSL = strcasecmp(schema.data(), "rtsps") == 0;
|
||||
//查找"://"与"/"之间的字符串,用于提取用户名密码
|
||||
auto middle_url = FindField(strUrl.data(), "://", "/");
|
||||
if (middle_url.empty()) {
|
||||
middle_url = FindField(strUrl.data(), "://", nullptr);
|
||||
}
|
||||
auto pos = middle_url.rfind('@');
|
||||
if (pos == string::npos) {
|
||||
//并没有用户名密码
|
||||
return setup(isSSL, strUrl, "", "");
|
||||
}
|
||||
|
||||
//包含用户名密码
|
||||
auto user_pwd = middle_url.substr(0, pos);
|
||||
auto suffix = strUrl.substr(schema.size() + 3 + pos + 1);
|
||||
auto url = StrPrinter << "rtsp://" << suffix << endl;
|
||||
if (user_pwd.find(":") == string::npos) {
|
||||
return setup(isSSL, url, user_pwd, "");
|
||||
}
|
||||
auto user = FindField(user_pwd.data(), nullptr, ":");
|
||||
auto pwd = FindField(user_pwd.data(), ":", nullptr);
|
||||
return setup(isSSL, url, user, pwd);
|
||||
//包含用户名密码
|
||||
auto user_pwd = middle_url.substr(0, pos);
|
||||
auto suffix = strUrl.substr(schema.size() + 3 + pos + 1);
|
||||
auto url = StrPrinter << "rtsp://" << suffix << endl;
|
||||
if (user_pwd.find(":") == string::npos) {
|
||||
return setup(isSSL, url, user_pwd, "");
|
||||
}
|
||||
auto user = FindField(user_pwd.data(), nullptr, ":");
|
||||
auto pwd = FindField(user_pwd.data(), ":", nullptr);
|
||||
return setup(isSSL, url, user, pwd);
|
||||
}
|
||||
|
||||
bool RtspUrl::setup(bool isSSL, const string &strUrl, const string &strUser, const string &strPwd) {
|
||||
auto ip = FindField(strUrl.data(), "://", "/");
|
||||
if (ip.empty()) {
|
||||
ip = split(FindField(strUrl.data(), "://", NULL), "?")[0];
|
||||
}
|
||||
auto port = atoi(FindField(ip.data(), ":", NULL).data());
|
||||
if (port <= 0 || port > UINT16_MAX) {
|
||||
//rtsp 默认端口554
|
||||
port = isSSL ? 322 : 554;
|
||||
} else {
|
||||
//服务器域名
|
||||
ip = FindField(ip.data(), NULL, ":");
|
||||
}
|
||||
auto ip = FindField(strUrl.data(), "://", "/");
|
||||
if (ip.empty()) {
|
||||
ip = split(FindField(strUrl.data(), "://", NULL), "?")[0];
|
||||
}
|
||||
auto port = atoi(FindField(ip.data(), ":", NULL).data());
|
||||
if (port <= 0 || port > UINT16_MAX) {
|
||||
//rtsp 默认端口554
|
||||
port = isSSL ? 322 : 554;
|
||||
} else {
|
||||
//服务器域名
|
||||
ip = FindField(ip.data(), NULL, ":");
|
||||
}
|
||||
|
||||
if (ip.empty()) {
|
||||
return false;
|
||||
}
|
||||
if (ip.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_url = std::move(strUrl);
|
||||
_user = std::move(strUser);
|
||||
_passwd = std::move(strPwd);
|
||||
_host = std::move(ip);
|
||||
_port = port;
|
||||
_is_ssl = isSSL;
|
||||
return true;
|
||||
_url = std::move(strUrl);
|
||||
_user = std::move(strUser);
|
||||
_passwd = std::move(strPwd);
|
||||
_host = std::move(ip);
|
||||
_port = port;
|
||||
_is_ssl = isSSL;
|
||||
return true;
|
||||
}
|
||||
|
||||
}//namespace mediakit
|
||||
|
288
src/Rtsp/Rtsp.h
288
src/Rtsp/Rtsp.h
@ -42,87 +42,87 @@ namespace mediakit {
|
||||
|
||||
namespace Rtsp {
|
||||
typedef enum {
|
||||
RTP_Invalid = -1,
|
||||
RTP_TCP = 0,
|
||||
RTP_UDP = 1,
|
||||
RTP_MULTICAST = 2,
|
||||
RTP_Invalid = -1,
|
||||
RTP_TCP = 0,
|
||||
RTP_UDP = 1,
|
||||
RTP_MULTICAST = 2,
|
||||
} eRtpType;
|
||||
};
|
||||
|
||||
class RtpPacket : public BufferRaw{
|
||||
public:
|
||||
typedef std::shared_ptr<RtpPacket> Ptr;
|
||||
uint8_t interleaved;
|
||||
uint8_t PT;
|
||||
bool mark;
|
||||
//时间戳,单位毫秒
|
||||
uint32_t timeStamp;
|
||||
uint16_t sequence;
|
||||
uint32_t ssrc;
|
||||
uint32_t offset;
|
||||
TrackType type;
|
||||
typedef std::shared_ptr<RtpPacket> Ptr;
|
||||
uint8_t interleaved;
|
||||
uint8_t PT;
|
||||
bool mark;
|
||||
//时间戳,单位毫秒
|
||||
uint32_t timeStamp;
|
||||
uint16_t sequence;
|
||||
uint32_t ssrc;
|
||||
uint32_t offset;
|
||||
TrackType type;
|
||||
};
|
||||
|
||||
class RtcpCounter {
|
||||
public:
|
||||
uint32_t pktCnt = 0;
|
||||
uint32_t octCount = 0;
|
||||
//网络字节序
|
||||
uint32_t timeStamp = 0;
|
||||
uint32_t lastTimeStamp = 0;
|
||||
uint32_t pktCnt = 0;
|
||||
uint32_t octCount = 0;
|
||||
//网络字节序
|
||||
uint32_t timeStamp = 0;
|
||||
uint32_t lastTimeStamp = 0;
|
||||
};
|
||||
|
||||
class SdpTrack {
|
||||
public:
|
||||
typedef std::shared_ptr<SdpTrack> Ptr;
|
||||
typedef std::shared_ptr<SdpTrack> Ptr;
|
||||
|
||||
string _m;
|
||||
string _o;
|
||||
string _s;
|
||||
string _i;
|
||||
string _c;
|
||||
string _t;
|
||||
string _b;
|
||||
string _m;
|
||||
string _o;
|
||||
string _s;
|
||||
string _i;
|
||||
string _c;
|
||||
string _t;
|
||||
string _b;
|
||||
|
||||
float _duration = 0;
|
||||
float _start = 0;
|
||||
float _end = 0;
|
||||
float _duration = 0;
|
||||
float _start = 0;
|
||||
float _end = 0;
|
||||
|
||||
map<char, string> _other;
|
||||
map<string, string> _attr;
|
||||
map<char, string> _other;
|
||||
map<string, string> _attr;
|
||||
|
||||
string toString() const;
|
||||
string toString() const;
|
||||
public:
|
||||
int _pt;
|
||||
string _codec;
|
||||
int _samplerate;
|
||||
string _fmtp;
|
||||
string _control;
|
||||
string _control_surffix;
|
||||
TrackType _type;
|
||||
int _pt;
|
||||
string _codec;
|
||||
int _samplerate;
|
||||
string _fmtp;
|
||||
string _control;
|
||||
string _control_surffix;
|
||||
TrackType _type;
|
||||
public:
|
||||
uint8_t _interleaved = 0;
|
||||
bool _inited = false;
|
||||
uint32_t _ssrc = 0;
|
||||
uint16_t _seq = 0;
|
||||
//时间戳,单位毫秒
|
||||
uint32_t _time_stamp = 0;
|
||||
uint8_t _interleaved = 0;
|
||||
bool _inited = false;
|
||||
uint32_t _ssrc = 0;
|
||||
uint16_t _seq = 0;
|
||||
//时间戳,单位毫秒
|
||||
uint32_t _time_stamp = 0;
|
||||
};
|
||||
|
||||
class SdpParser {
|
||||
public:
|
||||
typedef std::shared_ptr<SdpParser> Ptr;
|
||||
typedef std::shared_ptr<SdpParser> Ptr;
|
||||
|
||||
SdpParser() {}
|
||||
SdpParser(const string &sdp) { load(sdp); }
|
||||
~SdpParser() {}
|
||||
void load(const string &sdp);
|
||||
bool available() const;
|
||||
SdpTrack::Ptr getTrack(TrackType type) const;
|
||||
vector<SdpTrack::Ptr> getAvailableTrack() const;
|
||||
string toString() const ;
|
||||
SdpParser() {}
|
||||
SdpParser(const string &sdp) { load(sdp); }
|
||||
~SdpParser() {}
|
||||
void load(const string &sdp);
|
||||
bool available() const;
|
||||
SdpTrack::Ptr getTrack(TrackType type) const;
|
||||
vector<SdpTrack::Ptr> getAvailableTrack() const;
|
||||
string toString() const ;
|
||||
private:
|
||||
vector<SdpTrack::Ptr> _track_vec;
|
||||
vector<SdpTrack::Ptr> _track_vec;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -130,18 +130,18 @@ private:
|
||||
*/
|
||||
class RtspUrl{
|
||||
public:
|
||||
string _url;
|
||||
string _user;
|
||||
string _passwd;
|
||||
string _host;
|
||||
uint16_t _port;
|
||||
bool _is_ssl;
|
||||
string _url;
|
||||
string _user;
|
||||
string _passwd;
|
||||
string _host;
|
||||
uint16_t _port;
|
||||
bool _is_ssl;
|
||||
public:
|
||||
RtspUrl() = default;
|
||||
~RtspUrl() = default;
|
||||
bool parse(const string &url);
|
||||
RtspUrl() = default;
|
||||
~RtspUrl() = default;
|
||||
bool parse(const string &url);
|
||||
private:
|
||||
bool setup(bool,const string &, const string &, const string &);
|
||||
bool setup(bool,const string &, const string &, const string &);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -149,44 +149,44 @@ private:
|
||||
*/
|
||||
class Sdp : public CodecInfo{
|
||||
public:
|
||||
typedef std::shared_ptr<Sdp> Ptr;
|
||||
typedef std::shared_ptr<Sdp> Ptr;
|
||||
|
||||
/**
|
||||
* 构造sdp
|
||||
* @param sample_rate 采样率
|
||||
* @param playload_type pt类型
|
||||
*/
|
||||
Sdp(uint32_t sample_rate, uint8_t playload_type){
|
||||
_sample_rate = sample_rate;
|
||||
_playload_type = playload_type;
|
||||
}
|
||||
/**
|
||||
* 构造sdp
|
||||
* @param sample_rate 采样率
|
||||
* @param playload_type pt类型
|
||||
*/
|
||||
Sdp(uint32_t sample_rate, uint8_t playload_type){
|
||||
_sample_rate = sample_rate;
|
||||
_playload_type = playload_type;
|
||||
}
|
||||
|
||||
virtual ~Sdp(){}
|
||||
virtual ~Sdp(){}
|
||||
|
||||
/**
|
||||
* 获取sdp字符串
|
||||
* @return
|
||||
*/
|
||||
virtual string getSdp() const = 0;
|
||||
/**
|
||||
* 获取sdp字符串
|
||||
* @return
|
||||
*/
|
||||
virtual string getSdp() const = 0;
|
||||
|
||||
/**
|
||||
* 获取pt
|
||||
* @return
|
||||
*/
|
||||
uint8_t getPlayloadType() const{
|
||||
return _playload_type;
|
||||
}
|
||||
/**
|
||||
* 获取pt
|
||||
* @return
|
||||
*/
|
||||
uint8_t getPlayloadType() const{
|
||||
return _playload_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取采样率
|
||||
* @return
|
||||
*/
|
||||
uint32_t getSampleRate() const{
|
||||
return _sample_rate;
|
||||
}
|
||||
/**
|
||||
* 获取采样率
|
||||
* @return
|
||||
*/
|
||||
uint32_t getSampleRate() const{
|
||||
return _sample_rate;
|
||||
}
|
||||
private:
|
||||
uint8_t _playload_type;
|
||||
uint32_t _sample_rate;
|
||||
uint8_t _playload_type;
|
||||
uint32_t _sample_rate;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -195,56 +195,56 @@ private:
|
||||
class TitleSdp : public Sdp{
|
||||
public:
|
||||
|
||||
/**
|
||||
* 构造title类型sdp
|
||||
* @param dur_sec rtsp点播时长,0代表直播,单位秒
|
||||
* @param header 自定义sdp描述
|
||||
* @param version sdp版本
|
||||
*/
|
||||
TitleSdp(float dur_sec = 0,
|
||||
const map<string,string> &header = map<string,string>(),
|
||||
int version = 0) : Sdp(0,0){
|
||||
_printer << "v=" << version << "\r\n";
|
||||
/**
|
||||
* 构造title类型sdp
|
||||
* @param dur_sec rtsp点播时长,0代表直播,单位秒
|
||||
* @param header 自定义sdp描述
|
||||
* @param version sdp版本
|
||||
*/
|
||||
TitleSdp(float dur_sec = 0,
|
||||
const map<string,string> &header = map<string,string>(),
|
||||
int version = 0) : Sdp(0,0){
|
||||
_printer << "v=" << version << "\r\n";
|
||||
|
||||
if(!header.empty()){
|
||||
for (auto &pr : header){
|
||||
_printer << pr.first << "=" << pr.second << "\r\n";
|
||||
}
|
||||
} else {
|
||||
_printer << "o=- 1383190487994921 1 IN IP4 0.0.0.0\r\n";
|
||||
_printer << "s=RTSP Session, streamed by the ZLMediaKit\r\n";
|
||||
_printer << "i=ZLMediaKit Live Stream\r\n";
|
||||
_printer << "c=IN IP4 0.0.0.0\r\n";
|
||||
_printer << "t=0 0\r\n";
|
||||
}
|
||||
if(!header.empty()){
|
||||
for (auto &pr : header){
|
||||
_printer << pr.first << "=" << pr.second << "\r\n";
|
||||
}
|
||||
} else {
|
||||
_printer << "o=- 1383190487994921 1 IN IP4 0.0.0.0\r\n";
|
||||
_printer << "s=RTSP Session, streamed by the ZLMediaKit\r\n";
|
||||
_printer << "i=ZLMediaKit Live Stream\r\n";
|
||||
_printer << "c=IN IP4 0.0.0.0\r\n";
|
||||
_printer << "t=0 0\r\n";
|
||||
}
|
||||
|
||||
if(dur_sec <= 0){
|
||||
_printer << "a=range:npt=0-\r\n";
|
||||
}else{
|
||||
_printer << "a=range:npt=0-" << dur_sec << "\r\n";
|
||||
}
|
||||
_printer << "a=control:*\r\n";
|
||||
}
|
||||
string getSdp() const override {
|
||||
return _printer;
|
||||
}
|
||||
/**
|
||||
* 返回音频或视频类型
|
||||
* @return
|
||||
*/
|
||||
TrackType getTrackType() const override {
|
||||
return TrackTitle;
|
||||
}
|
||||
if(dur_sec <= 0){
|
||||
_printer << "a=range:npt=0-\r\n";
|
||||
}else{
|
||||
_printer << "a=range:npt=0-" << dur_sec << "\r\n";
|
||||
}
|
||||
_printer << "a=control:*\r\n";
|
||||
}
|
||||
string getSdp() const override {
|
||||
return _printer;
|
||||
}
|
||||
/**
|
||||
* 返回音频或视频类型
|
||||
* @return
|
||||
*/
|
||||
TrackType getTrackType() const override {
|
||||
return TrackTitle;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回编码器id
|
||||
* @return
|
||||
*/
|
||||
CodecId getCodecId() const override{
|
||||
return CodecInvalid;
|
||||
}
|
||||
/**
|
||||
* 返回编码器id
|
||||
* @return
|
||||
*/
|
||||
CodecId getCodecId() const override{
|
||||
return CodecInvalid;
|
||||
}
|
||||
private:
|
||||
_StrPrinter _printer;
|
||||
_StrPrinter _printer;
|
||||
};
|
||||
|
||||
} //namespace mediakit
|
||||
|
@ -35,83 +35,83 @@ using namespace std;
|
||||
namespace mediakit {
|
||||
|
||||
void RtspDemuxer::loadSdp(const string &sdp){
|
||||
loadSdp(SdpParser(sdp));
|
||||
loadSdp(SdpParser(sdp));
|
||||
}
|
||||
|
||||
void RtspDemuxer::loadSdp(const SdpParser &attr) {
|
||||
auto tracks = attr.getAvailableTrack();
|
||||
for (auto &track : tracks){
|
||||
switch (track->_type) {
|
||||
case TrackVideo: {
|
||||
makeVideoTrack(track);
|
||||
}
|
||||
break;
|
||||
case TrackAudio: {
|
||||
makeAudioTrack(track);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto titleTrack = attr.getTrack(TrackTitle);
|
||||
if(titleTrack){
|
||||
_fDuration = titleTrack->_duration;
|
||||
}
|
||||
auto tracks = attr.getAvailableTrack();
|
||||
for (auto &track : tracks){
|
||||
switch (track->_type) {
|
||||
case TrackVideo: {
|
||||
makeVideoTrack(track);
|
||||
}
|
||||
break;
|
||||
case TrackAudio: {
|
||||
makeAudioTrack(track);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto titleTrack = attr.getTrack(TrackTitle);
|
||||
if(titleTrack){
|
||||
_fDuration = titleTrack->_duration;
|
||||
}
|
||||
}
|
||||
bool RtspDemuxer::inputRtp(const RtpPacket::Ptr & rtp) {
|
||||
switch (rtp->type) {
|
||||
case TrackVideo:{
|
||||
if(_videoRtpDecoder){
|
||||
return _videoRtpDecoder->inputRtp(rtp, true);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case TrackAudio:{
|
||||
if(_audioRtpDecoder){
|
||||
_audioRtpDecoder->inputRtp(rtp, false);
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
switch (rtp->type) {
|
||||
case TrackVideo:{
|
||||
if(_videoRtpDecoder){
|
||||
return _videoRtpDecoder->inputRtp(rtp, true);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case TrackAudio:{
|
||||
if(_audioRtpDecoder){
|
||||
_audioRtpDecoder->inputRtp(rtp, false);
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RtspDemuxer::makeAudioTrack(const SdpTrack::Ptr &audio) {
|
||||
//生成Track对象
|
||||
//生成Track对象
|
||||
_audioTrack = dynamic_pointer_cast<AudioTrack>(Factory::getTrackBySdp(audio));
|
||||
if(_audioTrack){
|
||||
//生成RtpCodec对象以便解码rtp
|
||||
_audioRtpDecoder = Factory::getRtpDecoderByTrack(_audioTrack);
|
||||
if(_audioRtpDecoder){
|
||||
//设置rtp解码器代理,生成的frame写入该Track
|
||||
_audioRtpDecoder->addDelegate(_audioTrack);
|
||||
onAddTrack(_audioTrack);
|
||||
} else{
|
||||
//找不到相应的rtp解码器,该track无效
|
||||
_audioTrack.reset();
|
||||
}
|
||||
//生成RtpCodec对象以便解码rtp
|
||||
_audioRtpDecoder = Factory::getRtpDecoderByTrack(_audioTrack);
|
||||
if(_audioRtpDecoder){
|
||||
//设置rtp解码器代理,生成的frame写入该Track
|
||||
_audioRtpDecoder->addDelegate(_audioTrack);
|
||||
onAddTrack(_audioTrack);
|
||||
} else{
|
||||
//找不到相应的rtp解码器,该track无效
|
||||
_audioTrack.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RtspDemuxer::makeVideoTrack(const SdpTrack::Ptr &video) {
|
||||
//生成Track对象
|
||||
_videoTrack = dynamic_pointer_cast<VideoTrack>(Factory::getTrackBySdp(video));
|
||||
if(_videoTrack){
|
||||
//生成RtpCodec对象以便解码rtp
|
||||
_videoRtpDecoder = Factory::getRtpDecoderByTrack(_videoTrack);
|
||||
if(_videoRtpDecoder){
|
||||
//设置rtp解码器代理,生成的frame写入该Track
|
||||
_videoRtpDecoder->addDelegate(_videoTrack);
|
||||
onAddTrack(_videoTrack);
|
||||
}else{
|
||||
//找不到相应的rtp解码器,该track无效
|
||||
_videoTrack.reset();
|
||||
}
|
||||
}
|
||||
//生成Track对象
|
||||
_videoTrack = dynamic_pointer_cast<VideoTrack>(Factory::getTrackBySdp(video));
|
||||
if(_videoTrack){
|
||||
//生成RtpCodec对象以便解码rtp
|
||||
_videoRtpDecoder = Factory::getRtpDecoderByTrack(_videoTrack);
|
||||
if(_videoRtpDecoder){
|
||||
//设置rtp解码器代理,生成的frame写入该Track
|
||||
_videoRtpDecoder->addDelegate(_videoTrack);
|
||||
onAddTrack(_videoTrack);
|
||||
}else{
|
||||
//找不到相应的rtp解码器,该track无效
|
||||
_videoTrack.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace mediakit */
|
||||
|
@ -39,8 +39,8 @@ namespace mediakit {
|
||||
|
||||
class RtspDemuxer : public Demuxer{
|
||||
public:
|
||||
typedef std::shared_ptr<RtspDemuxer> Ptr;
|
||||
RtspDemuxer() = default;
|
||||
typedef std::shared_ptr<RtspDemuxer> Ptr;
|
||||
RtspDemuxer() = default;
|
||||
virtual ~RtspDemuxer() = default;
|
||||
|
||||
/**
|
||||
@ -48,19 +48,19 @@ public:
|
||||
*/
|
||||
void loadSdp(const string &sdp);
|
||||
|
||||
/**
|
||||
* 开始解复用
|
||||
* @param rtp rtp包
|
||||
* @return true 代表是i帧第一个rtp包
|
||||
*/
|
||||
bool inputRtp(const RtpPacket::Ptr &rtp);
|
||||
/**
|
||||
* 开始解复用
|
||||
* @param rtp rtp包
|
||||
* @return true 代表是i帧第一个rtp包
|
||||
*/
|
||||
bool inputRtp(const RtpPacket::Ptr &rtp);
|
||||
private:
|
||||
void makeAudioTrack(const SdpTrack::Ptr &audio);
|
||||
void makeVideoTrack(const SdpTrack::Ptr &video);
|
||||
void loadSdp(const SdpParser &parser);
|
||||
void makeAudioTrack(const SdpTrack::Ptr &audio);
|
||||
void makeVideoTrack(const SdpTrack::Ptr &video);
|
||||
void loadSdp(const SdpParser &parser);
|
||||
private:
|
||||
RtpCodec::Ptr _audioRtpDecoder;
|
||||
RtpCodec::Ptr _videoRtpDecoder;
|
||||
RtpCodec::Ptr _audioRtpDecoder;
|
||||
RtpCodec::Ptr _videoRtpDecoder;
|
||||
};
|
||||
|
||||
} /* namespace mediakit */
|
||||
|
@ -56,177 +56,177 @@ namespace mediakit {
|
||||
*/
|
||||
class RtspMediaSource : public MediaSource, public RingDelegate<RtpPacket::Ptr> {
|
||||
public:
|
||||
typedef ResourcePool<RtpPacket> PoolType;
|
||||
typedef std::shared_ptr<RtspMediaSource> Ptr;
|
||||
typedef RingBuffer<RtpPacket::Ptr> RingType;
|
||||
typedef ResourcePool<RtpPacket> PoolType;
|
||||
typedef std::shared_ptr<RtspMediaSource> Ptr;
|
||||
typedef RingBuffer<RtpPacket::Ptr> RingType;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param vhost 虚拟主机名
|
||||
* @param app 应用名
|
||||
* @param stream_id 流id
|
||||
* @param ring_size 可以设置固定的环形缓冲大小,0则自适应
|
||||
*/
|
||||
RtspMediaSource(const string &vhost,
|
||||
const string &app,
|
||||
const string &stream_id,
|
||||
int ring_size = RTP_GOP_SIZE) :
|
||||
MediaSource(RTSP_SCHEMA, vhost, app, stream_id), _ring_size(ring_size) {}
|
||||
/**
|
||||
* 构造函数
|
||||
* @param vhost 虚拟主机名
|
||||
* @param app 应用名
|
||||
* @param stream_id 流id
|
||||
* @param ring_size 可以设置固定的环形缓冲大小,0则自适应
|
||||
*/
|
||||
RtspMediaSource(const string &vhost,
|
||||
const string &app,
|
||||
const string &stream_id,
|
||||
int ring_size = RTP_GOP_SIZE) :
|
||||
MediaSource(RTSP_SCHEMA, vhost, app, stream_id), _ring_size(ring_size) {}
|
||||
|
||||
virtual ~RtspMediaSource() {}
|
||||
virtual ~RtspMediaSource() {}
|
||||
|
||||
/**
|
||||
* 获取媒体源的环形缓冲
|
||||
*/
|
||||
const RingType::Ptr &getRing() const {
|
||||
return _ring;
|
||||
}
|
||||
/**
|
||||
* 获取媒体源的环形缓冲
|
||||
*/
|
||||
const RingType::Ptr &getRing() const {
|
||||
return _ring;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取播放器个数
|
||||
*/
|
||||
int readerCount() override {
|
||||
return _ring ? _ring->readerCount() : 0;
|
||||
}
|
||||
/**
|
||||
* 获取播放器个数
|
||||
*/
|
||||
int readerCount() override {
|
||||
return _ring ? _ring->readerCount() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取该源的sdp
|
||||
*/
|
||||
const string &getSdp() const {
|
||||
return _sdp;
|
||||
}
|
||||
/**
|
||||
* 获取该源的sdp
|
||||
*/
|
||||
const string &getSdp() const {
|
||||
return _sdp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取相应轨道的ssrc
|
||||
*/
|
||||
virtual uint32_t getSsrc(TrackType trackType) {
|
||||
auto track = _sdp_parser.getTrack(trackType);
|
||||
if (!track) {
|
||||
return 0;
|
||||
}
|
||||
return track->_ssrc;
|
||||
}
|
||||
/**
|
||||
* 获取相应轨道的ssrc
|
||||
*/
|
||||
virtual uint32_t getSsrc(TrackType trackType) {
|
||||
auto track = _sdp_parser.getTrack(trackType);
|
||||
if (!track) {
|
||||
return 0;
|
||||
}
|
||||
return track->_ssrc;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取相应轨道的seqence
|
||||
*/
|
||||
virtual uint16_t getSeqence(TrackType trackType) {
|
||||
auto track = _sdp_parser.getTrack(trackType);
|
||||
if (!track) {
|
||||
return 0;
|
||||
}
|
||||
return track->_seq;
|
||||
}
|
||||
/**
|
||||
* 获取相应轨道的seqence
|
||||
*/
|
||||
virtual uint16_t getSeqence(TrackType trackType) {
|
||||
auto track = _sdp_parser.getTrack(trackType);
|
||||
if (!track) {
|
||||
return 0;
|
||||
}
|
||||
return track->_seq;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取相应轨道的时间戳,单位毫秒
|
||||
*/
|
||||
uint32_t getTimeStamp(TrackType trackType) override {
|
||||
auto track = _sdp_parser.getTrack(trackType);
|
||||
if (track) {
|
||||
return track->_time_stamp;
|
||||
}
|
||||
auto tracks = _sdp_parser.getAvailableTrack();
|
||||
switch (tracks.size()) {
|
||||
case 0:
|
||||
return 0;
|
||||
case 1:
|
||||
return tracks[0]->_time_stamp;
|
||||
default:
|
||||
return MAX(tracks[0]->_time_stamp, tracks[1]->_time_stamp);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 获取相应轨道的时间戳,单位毫秒
|
||||
*/
|
||||
uint32_t getTimeStamp(TrackType trackType) override {
|
||||
auto track = _sdp_parser.getTrack(trackType);
|
||||
if (track) {
|
||||
return track->_time_stamp;
|
||||
}
|
||||
auto tracks = _sdp_parser.getAvailableTrack();
|
||||
switch (tracks.size()) {
|
||||
case 0:
|
||||
return 0;
|
||||
case 1:
|
||||
return tracks[0]->_time_stamp;
|
||||
default:
|
||||
return MAX(tracks[0]->_time_stamp, tracks[1]->_time_stamp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新时间戳
|
||||
*/
|
||||
void setTimeStamp(uint32_t uiStamp) override {
|
||||
auto tracks = _sdp_parser.getAvailableTrack();
|
||||
for (auto &track : tracks) {
|
||||
track->_time_stamp = uiStamp;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 更新时间戳
|
||||
*/
|
||||
void setTimeStamp(uint32_t uiStamp) override {
|
||||
auto tracks = _sdp_parser.getAvailableTrack();
|
||||
for (auto &track : tracks) {
|
||||
track->_time_stamp = uiStamp;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置sdp
|
||||
*/
|
||||
virtual void setSdp(const string &sdp) {
|
||||
_sdp = sdp;
|
||||
_sdp_parser.load(sdp);
|
||||
_have_video = (bool)_sdp_parser.getTrack(TrackVideo);
|
||||
if (_ring) {
|
||||
regist();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 设置sdp
|
||||
*/
|
||||
virtual void setSdp(const string &sdp) {
|
||||
_sdp = sdp;
|
||||
_sdp_parser.load(sdp);
|
||||
_have_video = (bool)_sdp_parser.getTrack(TrackVideo);
|
||||
if (_ring) {
|
||||
regist();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 输入rtp
|
||||
* @param rtp rtp包
|
||||
* @param keyPos 该包是否为关键帧的第一个包
|
||||
*/
|
||||
void onWrite(const RtpPacket::Ptr &rtp, bool keyPos) override {
|
||||
auto track = _sdp_parser.getTrack(rtp->type);
|
||||
if (track) {
|
||||
track->_seq = rtp->sequence;
|
||||
track->_time_stamp = rtp->timeStamp;
|
||||
track->_ssrc = rtp->ssrc;
|
||||
}
|
||||
if (!_ring) {
|
||||
weak_ptr<RtspMediaSource> weakSelf = dynamic_pointer_cast<RtspMediaSource>(shared_from_this());
|
||||
auto lam = [weakSelf](const EventPoller::Ptr &, int size, bool) {
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if (!strongSelf) {
|
||||
return;
|
||||
}
|
||||
strongSelf->onReaderChanged(size);
|
||||
};
|
||||
/**
|
||||
* 输入rtp
|
||||
* @param rtp rtp包
|
||||
* @param keyPos 该包是否为关键帧的第一个包
|
||||
*/
|
||||
void onWrite(const RtpPacket::Ptr &rtp, bool keyPos) override {
|
||||
auto track = _sdp_parser.getTrack(rtp->type);
|
||||
if (track) {
|
||||
track->_seq = rtp->sequence;
|
||||
track->_time_stamp = rtp->timeStamp;
|
||||
track->_ssrc = rtp->ssrc;
|
||||
}
|
||||
if (!_ring) {
|
||||
weak_ptr<RtspMediaSource> weakSelf = dynamic_pointer_cast<RtspMediaSource>(shared_from_this());
|
||||
auto lam = [weakSelf](const EventPoller::Ptr &, int size, bool) {
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if (!strongSelf) {
|
||||
return;
|
||||
}
|
||||
strongSelf->onReaderChanged(size);
|
||||
};
|
||||
//rtp包缓存最大允许2048个,大概最多3MB数据
|
||||
//但是这个是GOP缓存的上限值,真实的GOP缓存大小等于两个I帧之间的包数的两倍
|
||||
//而且每次遇到I帧,则会清空GOP缓存,所以真实的GOP缓存远小于此值
|
||||
_ring = std::make_shared<RingType>(_ring_size, std::move(lam));
|
||||
onReaderChanged(0);
|
||||
if (!_sdp.empty()) {
|
||||
regist();
|
||||
}
|
||||
}
|
||||
//不存在视频,为了减少缓存延时,那么关闭GOP缓存
|
||||
_ring->write(rtp, _have_video ? keyPos : true);
|
||||
checkNoneReader();
|
||||
}
|
||||
_ring = std::make_shared<RingType>(_ring_size, std::move(lam));
|
||||
onReaderChanged(0);
|
||||
if (!_sdp.empty()) {
|
||||
regist();
|
||||
}
|
||||
}
|
||||
//不存在视频,为了减少缓存延时,那么关闭GOP缓存
|
||||
_ring->write(rtp, _have_video ? keyPos : true);
|
||||
checkNoneReader();
|
||||
}
|
||||
private:
|
||||
/**
|
||||
* 每次增减消费者都会触发该函数
|
||||
*/
|
||||
void onReaderChanged(int size) {
|
||||
//我们记录最后一次活动时间
|
||||
_reader_changed_ticker.resetTime();
|
||||
if (size != 0 || totalReaderCount() != 0) {
|
||||
//还有消费者正在观看该流
|
||||
_async_emit_none_reader = false;
|
||||
return;
|
||||
}
|
||||
_async_emit_none_reader = true;
|
||||
}
|
||||
/**
|
||||
* 每次增减消费者都会触发该函数
|
||||
*/
|
||||
void onReaderChanged(int size) {
|
||||
//我们记录最后一次活动时间
|
||||
_reader_changed_ticker.resetTime();
|
||||
if (size != 0 || totalReaderCount() != 0) {
|
||||
//还有消费者正在观看该流
|
||||
_async_emit_none_reader = false;
|
||||
return;
|
||||
}
|
||||
_async_emit_none_reader = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否无人消费该流,
|
||||
* 如果无人消费且超过一定时间会触发onNoneReader事件
|
||||
*/
|
||||
void checkNoneReader() {
|
||||
GET_CONFIG(int, stream_none_reader_delay, General::kStreamNoneReaderDelayMS);
|
||||
if (_async_emit_none_reader && _reader_changed_ticker.elapsedTime() > stream_none_reader_delay) {
|
||||
_async_emit_none_reader = false;
|
||||
onNoneReader();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 检查是否无人消费该流,
|
||||
* 如果无人消费且超过一定时间会触发onNoneReader事件
|
||||
*/
|
||||
void checkNoneReader() {
|
||||
GET_CONFIG(int, stream_none_reader_delay, General::kStreamNoneReaderDelayMS);
|
||||
if (_async_emit_none_reader && _reader_changed_ticker.elapsedTime() > stream_none_reader_delay) {
|
||||
_async_emit_none_reader = false;
|
||||
onNoneReader();
|
||||
}
|
||||
}
|
||||
protected:
|
||||
int _ring_size;
|
||||
bool _async_emit_none_reader = false;
|
||||
bool _have_video = false;
|
||||
Ticker _reader_changed_ticker;
|
||||
SdpParser _sdp_parser;
|
||||
string _sdp;
|
||||
RingType::Ptr _ring;
|
||||
int _ring_size;
|
||||
bool _async_emit_none_reader = false;
|
||||
bool _have_video = false;
|
||||
Ticker _reader_changed_ticker;
|
||||
SdpParser _sdp_parser;
|
||||
string _sdp;
|
||||
RingType::Ptr _ring;
|
||||
};
|
||||
|
||||
} /* namespace mediakit */
|
||||
|
@ -39,12 +39,12 @@ public:
|
||||
typedef std::shared_ptr<RtspMediaSourceImp> Ptr;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param vhost 虚拟主机
|
||||
* @param app 应用名
|
||||
* @param id 流id
|
||||
* @param ringSize 环形缓存大小
|
||||
*/
|
||||
* 构造函数
|
||||
* @param vhost 虚拟主机
|
||||
* @param app 应用名
|
||||
* @param id 流id
|
||||
* @param ringSize 环形缓存大小
|
||||
*/
|
||||
RtspMediaSourceImp(const string &vhost, const string &app, const string &id, int ringSize = RTP_GOP_SIZE) : RtspMediaSource(vhost, app, id,ringSize) {
|
||||
_demuxer = std::make_shared<RtspDemuxer>();
|
||||
_demuxer->setTrackListener(this);
|
||||
@ -69,9 +69,9 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置监听器
|
||||
* @param listener
|
||||
*/
|
||||
* 设置监听器
|
||||
* @param listener
|
||||
*/
|
||||
void setListener(const std::weak_ptr<MediaSourceEvent> &listener) override {
|
||||
RtspMediaSource::setListener(listener);
|
||||
if(_muxer){
|
||||
@ -87,11 +87,11 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置协议转换
|
||||
* @param enableRtmp 是否转换成rtmp
|
||||
* @param enableHls 是否转换成hls
|
||||
* @param enableMP4 是否mp4录制
|
||||
*/
|
||||
* 设置协议转换
|
||||
* @param enableRtmp 是否转换成rtmp
|
||||
* @param enableHls 是否转换成hls
|
||||
* @param enableMP4 是否mp4录制
|
||||
*/
|
||||
void setProtocolTranslation(bool enableRtmp,bool enableHls,bool enableMP4){
|
||||
//不重复生成rtsp
|
||||
_muxer = std::make_shared<MultiMediaSourceMuxer>(getVhost(), getApp(), getId(), _demuxer->getDuration(), false, enableRtmp, enableHls, enableMP4);
|
||||
@ -104,8 +104,8 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* _demuxer触发的添加Track事件
|
||||
*/
|
||||
* _demuxer触发的添加Track事件
|
||||
*/
|
||||
void onAddTrack(const Track::Ptr &track) override {
|
||||
if(_muxer){
|
||||
_muxer->addTrack(track);
|
||||
|
@ -43,29 +43,29 @@ using namespace mediakit::Client;
|
||||
namespace mediakit {
|
||||
|
||||
enum PlayType {
|
||||
type_play = 0,
|
||||
type_pause,
|
||||
type_seek
|
||||
type_play = 0,
|
||||
type_pause,
|
||||
type_seek
|
||||
};
|
||||
|
||||
RtspPlayer::RtspPlayer(const EventPoller::Ptr &poller) : TcpClient(poller){
|
||||
RtpReceiver::setPoolSize(64);
|
||||
RtpReceiver::setPoolSize(64);
|
||||
}
|
||||
RtspPlayer::~RtspPlayer(void) {
|
||||
DebugL << endl;
|
||||
}
|
||||
void RtspPlayer::teardown(){
|
||||
if (alive()) {
|
||||
sendRtspRequest("TEARDOWN" ,_strContentBase);
|
||||
shutdown(SockException(Err_shutdown,"teardown"));
|
||||
}
|
||||
if (alive()) {
|
||||
sendRtspRequest("TEARDOWN" ,_strContentBase);
|
||||
shutdown(SockException(Err_shutdown,"teardown"));
|
||||
}
|
||||
|
||||
_rtspMd5Nonce.clear();
|
||||
_rtspRealm.clear();
|
||||
_aTrackInfo.clear();
|
||||
_rtspMd5Nonce.clear();
|
||||
_rtspRealm.clear();
|
||||
_aTrackInfo.clear();
|
||||
_strSession.clear();
|
||||
_strContentBase.clear();
|
||||
RtpReceiver::clear();
|
||||
RtpReceiver::clear();
|
||||
|
||||
CLEAR_ARR(_apRtpSock);
|
||||
CLEAR_ARR(_apRtcpSock);
|
||||
@ -74,65 +74,65 @@ void RtspPlayer::teardown(){
|
||||
CLEAR_ARR(_aui64RtpRecv)
|
||||
CLEAR_ARR(_aui16NowSeq)
|
||||
|
||||
_pPlayTimer.reset();
|
||||
_pPlayTimer.reset();
|
||||
_pRtpTimer.reset();
|
||||
_uiCseq = 1;
|
||||
_onHandshake = nullptr;
|
||||
_uiCseq = 1;
|
||||
_onHandshake = nullptr;
|
||||
}
|
||||
|
||||
void RtspPlayer::play(const string &strUrl){
|
||||
RtspUrl url;
|
||||
if(!url.parse(strUrl)){
|
||||
onPlayResult_l(SockException(Err_other,StrPrinter << "illegal rtsp url:" << strUrl),false);
|
||||
return;
|
||||
}
|
||||
RtspUrl url;
|
||||
if(!url.parse(strUrl)){
|
||||
onPlayResult_l(SockException(Err_other,StrPrinter << "illegal rtsp url:" << strUrl),false);
|
||||
return;
|
||||
}
|
||||
|
||||
teardown();
|
||||
teardown();
|
||||
|
||||
if (url._user.size()) {
|
||||
(*this)[kRtspUser] = url._user;
|
||||
}
|
||||
if (url._passwd.size()) {
|
||||
(*this)[kRtspPwd] = url._passwd;
|
||||
(*this)[kRtspPwdIsMD5] = false;
|
||||
}
|
||||
if (url._user.size()) {
|
||||
(*this)[kRtspUser] = url._user;
|
||||
}
|
||||
if (url._passwd.size()) {
|
||||
(*this)[kRtspPwd] = url._passwd;
|
||||
(*this)[kRtspPwdIsMD5] = false;
|
||||
}
|
||||
|
||||
_strUrl = url._url;
|
||||
_eType = (Rtsp::eRtpType)(int)(*this)[kRtpType];
|
||||
DebugL << url._url << " " << (url._user.size() ? url._user : "null") << " " << (url._passwd.size() ? url._passwd : "null") << " " << _eType;
|
||||
_strUrl = url._url;
|
||||
_eType = (Rtsp::eRtpType)(int)(*this)[kRtpType];
|
||||
DebugL << url._url << " " << (url._user.size() ? url._user : "null") << " " << (url._passwd.size() ? url._passwd : "null") << " " << _eType;
|
||||
|
||||
weak_ptr<RtspPlayer> weakSelf = dynamic_pointer_cast<RtspPlayer>(shared_from_this());
|
||||
float playTimeOutSec = (*this)[kTimeoutMS].as<int>() / 1000.0;
|
||||
_pPlayTimer.reset( new Timer(playTimeOutSec, [weakSelf]() {
|
||||
auto strongSelf=weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return false;
|
||||
}
|
||||
strongSelf->onPlayResult_l(SockException(Err_timeout,"play rtsp timeout"),false);
|
||||
return false;
|
||||
},getPoller()));
|
||||
weak_ptr<RtspPlayer> weakSelf = dynamic_pointer_cast<RtspPlayer>(shared_from_this());
|
||||
float playTimeOutSec = (*this)[kTimeoutMS].as<int>() / 1000.0;
|
||||
_pPlayTimer.reset( new Timer(playTimeOutSec, [weakSelf]() {
|
||||
auto strongSelf=weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return false;
|
||||
}
|
||||
strongSelf->onPlayResult_l(SockException(Err_timeout,"play rtsp timeout"),false);
|
||||
return false;
|
||||
},getPoller()));
|
||||
|
||||
if(!(*this)[kNetAdapter].empty()){
|
||||
setNetAdapter((*this)[kNetAdapter]);
|
||||
}
|
||||
startConnect(url._host, url._port, playTimeOutSec);
|
||||
if(!(*this)[kNetAdapter].empty()){
|
||||
setNetAdapter((*this)[kNetAdapter]);
|
||||
}
|
||||
startConnect(url._host, url._port, playTimeOutSec);
|
||||
}
|
||||
|
||||
void RtspPlayer::onConnect(const SockException &err){
|
||||
if(err.getErrCode() != Err_success) {
|
||||
onPlayResult_l(err,false);
|
||||
return;
|
||||
}
|
||||
if(err.getErrCode() != Err_success) {
|
||||
onPlayResult_l(err,false);
|
||||
return;
|
||||
}
|
||||
|
||||
sendDescribe();
|
||||
sendDescribe();
|
||||
}
|
||||
|
||||
void RtspPlayer::onRecv(const Buffer::Ptr& pBuf) {
|
||||
input(pBuf->data(),pBuf->size());
|
||||
}
|
||||
void RtspPlayer::onErr(const SockException &ex) {
|
||||
//定时器_pPlayTimer为空后表明握手结束了
|
||||
onPlayResult_l(ex,!_pPlayTimer);
|
||||
//定时器_pPlayTimer为空后表明握手结束了
|
||||
onPlayResult_l(ex,!_pPlayTimer);
|
||||
}
|
||||
// from live555
|
||||
bool RtspPlayer::handleAuthenticationFailure(const string ¶msStr) {
|
||||
@ -167,25 +167,25 @@ bool RtspPlayer::handleAuthenticationFailure(const string ¶msStr) {
|
||||
return false;
|
||||
}
|
||||
void RtspPlayer::handleResDESCRIBE(const Parser& parser) {
|
||||
string authInfo = parser["WWW-Authenticate"];
|
||||
//发送DESCRIBE命令后的回复
|
||||
if ((parser.Url() == "401") && handleAuthenticationFailure(authInfo)) {
|
||||
sendDescribe();
|
||||
return;
|
||||
}
|
||||
if(parser.Url() == "302" || parser.Url() == "301"){
|
||||
auto newUrl = parser["Location"];
|
||||
if(newUrl.empty()){
|
||||
throw std::runtime_error("未找到Location字段(跳转url)");
|
||||
}
|
||||
play(newUrl);
|
||||
return;
|
||||
}
|
||||
if (parser.Url() != "200") {
|
||||
throw std::runtime_error(
|
||||
StrPrinter << "DESCRIBE:" << parser.Url() << " " << parser.Tail() << endl);
|
||||
}
|
||||
_strContentBase = parser["Content-Base"];
|
||||
string authInfo = parser["WWW-Authenticate"];
|
||||
//发送DESCRIBE命令后的回复
|
||||
if ((parser.Url() == "401") && handleAuthenticationFailure(authInfo)) {
|
||||
sendDescribe();
|
||||
return;
|
||||
}
|
||||
if(parser.Url() == "302" || parser.Url() == "301"){
|
||||
auto newUrl = parser["Location"];
|
||||
if(newUrl.empty()){
|
||||
throw std::runtime_error("未找到Location字段(跳转url)");
|
||||
}
|
||||
play(newUrl);
|
||||
return;
|
||||
}
|
||||
if (parser.Url() != "200") {
|
||||
throw std::runtime_error(
|
||||
StrPrinter << "DESCRIBE:" << parser.Url() << " " << parser.Tail() << endl);
|
||||
}
|
||||
_strContentBase = parser["Content-Base"];
|
||||
|
||||
if(_strContentBase.empty()){
|
||||
_strContentBase = _strUrl;
|
||||
@ -194,58 +194,58 @@ void RtspPlayer::handleResDESCRIBE(const Parser& parser) {
|
||||
_strContentBase.pop_back();
|
||||
}
|
||||
|
||||
SdpParser sdpParser(parser.Content());
|
||||
//解析sdp
|
||||
_aTrackInfo = sdpParser.getAvailableTrack();
|
||||
auto title = sdpParser.getTrack(TrackTitle);
|
||||
bool isPlayback = false;
|
||||
if(title && title->_duration ){
|
||||
SdpParser sdpParser(parser.Content());
|
||||
//解析sdp
|
||||
_aTrackInfo = sdpParser.getAvailableTrack();
|
||||
auto title = sdpParser.getTrack(TrackTitle);
|
||||
bool isPlayback = false;
|
||||
if(title && title->_duration ){
|
||||
isPlayback = true;
|
||||
}
|
||||
}
|
||||
|
||||
for(auto &stamp : _stamp){
|
||||
stamp.setPlayBack(isPlayback);
|
||||
stamp.setRelativeStamp(0);
|
||||
}
|
||||
|
||||
if (_aTrackInfo.empty()) {
|
||||
throw std::runtime_error("无有效的Sdp Track");
|
||||
}
|
||||
if (!onCheckSDP(sdpParser.toString())) {
|
||||
throw std::runtime_error("onCheckSDP faied");
|
||||
}
|
||||
if (_aTrackInfo.empty()) {
|
||||
throw std::runtime_error("无有效的Sdp Track");
|
||||
}
|
||||
if (!onCheckSDP(sdpParser.toString())) {
|
||||
throw std::runtime_error("onCheckSDP faied");
|
||||
}
|
||||
|
||||
sendSetup(0);
|
||||
sendSetup(0);
|
||||
}
|
||||
|
||||
//有必要的情况下创建udp端口
|
||||
void RtspPlayer::createUdpSockIfNecessary(int track_idx){
|
||||
auto &rtpSockRef = _apRtpSock[track_idx];
|
||||
auto &rtcpSockRef = _apRtcpSock[track_idx];
|
||||
if(!rtpSockRef){
|
||||
rtpSockRef.reset(new Socket(getPoller()));
|
||||
//rtp随机端口
|
||||
if (!rtpSockRef->bindUdpSock(0, get_local_ip().data())) {
|
||||
rtpSockRef.reset();
|
||||
throw std::runtime_error("open rtp sock failed");
|
||||
}
|
||||
}
|
||||
auto &rtpSockRef = _apRtpSock[track_idx];
|
||||
auto &rtcpSockRef = _apRtcpSock[track_idx];
|
||||
if(!rtpSockRef){
|
||||
rtpSockRef.reset(new Socket(getPoller()));
|
||||
//rtp随机端口
|
||||
if (!rtpSockRef->bindUdpSock(0, get_local_ip().data())) {
|
||||
rtpSockRef.reset();
|
||||
throw std::runtime_error("open rtp sock failed");
|
||||
}
|
||||
}
|
||||
|
||||
if(!rtcpSockRef){
|
||||
rtcpSockRef.reset(new Socket(getPoller()));
|
||||
//rtcp端口为rtp端口+1,目的是为了兼容某些服务器,其实更推荐随机端口
|
||||
if (!rtcpSockRef->bindUdpSock(rtpSockRef->get_local_port() + 1, get_local_ip().data())) {
|
||||
rtcpSockRef.reset();
|
||||
throw std::runtime_error("open rtcp sock failed");
|
||||
}
|
||||
}
|
||||
if(!rtcpSockRef){
|
||||
rtcpSockRef.reset(new Socket(getPoller()));
|
||||
//rtcp端口为rtp端口+1,目的是为了兼容某些服务器,其实更推荐随机端口
|
||||
if (!rtcpSockRef->bindUdpSock(rtpSockRef->get_local_port() + 1, get_local_ip().data())) {
|
||||
rtcpSockRef.reset();
|
||||
throw std::runtime_error("open rtcp sock failed");
|
||||
}
|
||||
}
|
||||
|
||||
if(rtpSockRef->get_local_port() % 2 != 0){
|
||||
//如果rtp端口不是偶数,那么与rtcp端口互换,目的是兼容一些要求严格的服务器
|
||||
Socket::Ptr tmp = rtpSockRef;
|
||||
rtpSockRef = rtcpSockRef;
|
||||
rtcpSockRef = tmp;
|
||||
}
|
||||
if(rtpSockRef->get_local_port() % 2 != 0){
|
||||
//如果rtp端口不是偶数,那么与rtcp端口互换,目的是兼容一些要求严格的服务器
|
||||
Socket::Ptr tmp = rtpSockRef;
|
||||
rtpSockRef = rtcpSockRef;
|
||||
rtcpSockRef = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -253,91 +253,91 @@ void RtspPlayer::createUdpSockIfNecessary(int track_idx){
|
||||
void RtspPlayer::sendSetup(unsigned int trackIndex) {
|
||||
_onHandshake = std::bind(&RtspPlayer::handleResSETUP,this, placeholders::_1,trackIndex);
|
||||
auto &track = _aTrackInfo[trackIndex];
|
||||
auto baseUrl = _strContentBase + "/" + track->_control_surffix;
|
||||
switch (_eType) {
|
||||
case Rtsp::RTP_TCP: {
|
||||
sendRtspRequest("SETUP",baseUrl,{"Transport",StrPrinter << "RTP/AVP/TCP;unicast;interleaved=" << track->_type * 2 << "-" << track->_type * 2 + 1});
|
||||
}
|
||||
break;
|
||||
case Rtsp::RTP_MULTICAST: {
|
||||
sendRtspRequest("SETUP",baseUrl,{"Transport","Transport: RTP/AVP;multicast"});
|
||||
}
|
||||
break;
|
||||
case Rtsp::RTP_UDP: {
|
||||
createUdpSockIfNecessary(trackIndex);
|
||||
sendRtspRequest("SETUP",baseUrl,{"Transport",
|
||||
auto baseUrl = _strContentBase + "/" + track->_control_surffix;
|
||||
switch (_eType) {
|
||||
case Rtsp::RTP_TCP: {
|
||||
sendRtspRequest("SETUP",baseUrl,{"Transport",StrPrinter << "RTP/AVP/TCP;unicast;interleaved=" << track->_type * 2 << "-" << track->_type * 2 + 1});
|
||||
}
|
||||
break;
|
||||
case Rtsp::RTP_MULTICAST: {
|
||||
sendRtspRequest("SETUP",baseUrl,{"Transport","Transport: RTP/AVP;multicast"});
|
||||
}
|
||||
break;
|
||||
case Rtsp::RTP_UDP: {
|
||||
createUdpSockIfNecessary(trackIndex);
|
||||
sendRtspRequest("SETUP",baseUrl,{"Transport",
|
||||
StrPrinter << "RTP/AVP;unicast;client_port="
|
||||
<< _apRtpSock[trackIndex]->get_local_port() << "-"
|
||||
<< _apRtcpSock[trackIndex]->get_local_port()});
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int uiTrackIndex) {
|
||||
if (parser.Url() != "200") {
|
||||
throw std::runtime_error(
|
||||
StrPrinter << "SETUP:" << parser.Url() << " " << parser.Tail() << endl);
|
||||
}
|
||||
if (uiTrackIndex == 0) {
|
||||
_strSession = parser["Session"];
|
||||
if (parser.Url() != "200") {
|
||||
throw std::runtime_error(
|
||||
StrPrinter << "SETUP:" << parser.Url() << " " << parser.Tail() << endl);
|
||||
}
|
||||
if (uiTrackIndex == 0) {
|
||||
_strSession = parser["Session"];
|
||||
_strSession.append(";");
|
||||
_strSession = FindField(_strSession.data(), nullptr, ";");
|
||||
}
|
||||
}
|
||||
|
||||
auto strTransport = parser["Transport"];
|
||||
if(strTransport.find("TCP") != string::npos || strTransport.find("interleaved") != string::npos){
|
||||
_eType = Rtsp::RTP_TCP;
|
||||
}else if(strTransport.find("multicast") != string::npos){
|
||||
_eType = Rtsp::RTP_MULTICAST;
|
||||
}else{
|
||||
_eType = Rtsp::RTP_UDP;
|
||||
}
|
||||
auto strTransport = parser["Transport"];
|
||||
if(strTransport.find("TCP") != string::npos || strTransport.find("interleaved") != string::npos){
|
||||
_eType = Rtsp::RTP_TCP;
|
||||
}else if(strTransport.find("multicast") != string::npos){
|
||||
_eType = Rtsp::RTP_MULTICAST;
|
||||
}else{
|
||||
_eType = Rtsp::RTP_UDP;
|
||||
}
|
||||
|
||||
RtspSplitter::enableRecvRtp(_eType == Rtsp::RTP_TCP);
|
||||
RtspSplitter::enableRecvRtp(_eType == Rtsp::RTP_TCP);
|
||||
|
||||
if(_eType == Rtsp::RTP_TCP) {
|
||||
string interleaved = FindField( FindField((strTransport + ";").data(), "interleaved=", ";").data(), NULL, "-");
|
||||
_aTrackInfo[uiTrackIndex]->_interleaved = atoi(interleaved.data());
|
||||
}else{
|
||||
const char *strPos = (_eType == Rtsp::RTP_MULTICAST ? "port=" : "server_port=") ;
|
||||
auto port_str = FindField((strTransport + ";").data(), strPos, ";");
|
||||
uint16_t rtp_port = atoi(FindField(port_str.data(), NULL, "-").data());
|
||||
if(_eType == Rtsp::RTP_TCP) {
|
||||
string interleaved = FindField( FindField((strTransport + ";").data(), "interleaved=", ";").data(), NULL, "-");
|
||||
_aTrackInfo[uiTrackIndex]->_interleaved = atoi(interleaved.data());
|
||||
}else{
|
||||
const char *strPos = (_eType == Rtsp::RTP_MULTICAST ? "port=" : "server_port=") ;
|
||||
auto port_str = FindField((strTransport + ";").data(), strPos, ";");
|
||||
uint16_t rtp_port = atoi(FindField(port_str.data(), NULL, "-").data());
|
||||
uint16_t rtcp_port = atoi(FindField(port_str.data(), "-",NULL).data());
|
||||
auto &pRtpSockRef = _apRtpSock[uiTrackIndex];
|
||||
auto &pRtcpSockRef = _apRtcpSock[uiTrackIndex];
|
||||
|
||||
if (_eType == Rtsp::RTP_MULTICAST) {
|
||||
//udp组播
|
||||
auto multiAddr = FindField((strTransport + ";").data(), "destination=", ";");
|
||||
if (_eType == Rtsp::RTP_MULTICAST) {
|
||||
//udp组播
|
||||
auto multiAddr = FindField((strTransport + ";").data(), "destination=", ";");
|
||||
pRtpSockRef.reset(new Socket(getPoller()));
|
||||
if (!pRtpSockRef->bindUdpSock(rtp_port, multiAddr.data())) {
|
||||
pRtpSockRef.reset();
|
||||
throw std::runtime_error("open udp sock err");
|
||||
}
|
||||
auto fd = pRtpSockRef->rawFD();
|
||||
if (-1 == SockUtil::joinMultiAddrFilter(fd, multiAddr.data(), get_peer_ip().data(),get_local_ip().data())) {
|
||||
SockUtil::joinMultiAddr(fd, multiAddr.data(),get_local_ip().data());
|
||||
}
|
||||
} else {
|
||||
createUdpSockIfNecessary(uiTrackIndex);
|
||||
//udp单播
|
||||
struct sockaddr_in rtpto;
|
||||
rtpto.sin_port = ntohs(rtp_port);
|
||||
rtpto.sin_family = AF_INET;
|
||||
rtpto.sin_addr.s_addr = inet_addr(get_peer_ip().data());
|
||||
pRtpSockRef->setSendPeerAddr((struct sockaddr *)&(rtpto));
|
||||
//发送rtp打洞包
|
||||
pRtpSockRef->send("\xce\xfa\xed\xfe", 4);
|
||||
if (!pRtpSockRef->bindUdpSock(rtp_port, multiAddr.data())) {
|
||||
pRtpSockRef.reset();
|
||||
throw std::runtime_error("open udp sock err");
|
||||
}
|
||||
auto fd = pRtpSockRef->rawFD();
|
||||
if (-1 == SockUtil::joinMultiAddrFilter(fd, multiAddr.data(), get_peer_ip().data(),get_local_ip().data())) {
|
||||
SockUtil::joinMultiAddr(fd, multiAddr.data(),get_local_ip().data());
|
||||
}
|
||||
} else {
|
||||
createUdpSockIfNecessary(uiTrackIndex);
|
||||
//udp单播
|
||||
struct sockaddr_in rtpto;
|
||||
rtpto.sin_port = ntohs(rtp_port);
|
||||
rtpto.sin_family = AF_INET;
|
||||
rtpto.sin_addr.s_addr = inet_addr(get_peer_ip().data());
|
||||
pRtpSockRef->setSendPeerAddr((struct sockaddr *)&(rtpto));
|
||||
//发送rtp打洞包
|
||||
pRtpSockRef->send("\xce\xfa\xed\xfe", 4);
|
||||
|
||||
//设置rtcp发送目标,为后续发送rtcp做准备
|
||||
//设置rtcp发送目标,为后续发送rtcp做准备
|
||||
rtpto.sin_port = ntohs(rtcp_port);
|
||||
rtpto.sin_family = AF_INET;
|
||||
rtpto.sin_addr.s_addr = inet_addr(get_peer_ip().data());
|
||||
pRtcpSockRef->setSendPeerAddr((struct sockaddr *)&(rtpto));
|
||||
}
|
||||
}
|
||||
|
||||
auto srcIP = inet_addr(get_peer_ip().data());
|
||||
weak_ptr<RtspPlayer> weakSelf = dynamic_pointer_cast<RtspPlayer>(shared_from_this());
|
||||
@ -368,98 +368,98 @@ void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int uiTrackIndex)
|
||||
strongSelf->onRtcpPacket(uiTrackIndex, strongSelf->_aTrackInfo[uiTrackIndex], (unsigned char *) buf->data(), buf->size());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (uiTrackIndex < _aTrackInfo.size() - 1) {
|
||||
//需要继续发送SETUP命令
|
||||
sendSetup(uiTrackIndex + 1);
|
||||
return;
|
||||
}
|
||||
//所有setup命令发送完毕
|
||||
//发送play命令
|
||||
if (uiTrackIndex < _aTrackInfo.size() - 1) {
|
||||
//需要继续发送SETUP命令
|
||||
sendSetup(uiTrackIndex + 1);
|
||||
return;
|
||||
}
|
||||
//所有setup命令发送完毕
|
||||
//发送play命令
|
||||
sendPause(type_play, 0);
|
||||
}
|
||||
|
||||
void RtspPlayer::sendDescribe() {
|
||||
//发送DESCRIBE命令后处理函数:handleResDESCRIBE
|
||||
_onHandshake = std::bind(&RtspPlayer::handleResDESCRIBE,this, placeholders::_1);
|
||||
sendRtspRequest("DESCRIBE",_strUrl,{"Accept","application/sdp"});
|
||||
//发送DESCRIBE命令后处理函数:handleResDESCRIBE
|
||||
_onHandshake = std::bind(&RtspPlayer::handleResDESCRIBE,this, placeholders::_1);
|
||||
sendRtspRequest("DESCRIBE",_strUrl,{"Accept","application/sdp"});
|
||||
}
|
||||
|
||||
void RtspPlayer::sendPause(int type , uint32_t seekMS){
|
||||
_onHandshake = std::bind(&RtspPlayer::handleResPAUSE,this, placeholders::_1,type);
|
||||
//开启或暂停rtsp
|
||||
switch (type){
|
||||
case type_pause:
|
||||
sendRtspRequest("PAUSE", _strContentBase);
|
||||
break;
|
||||
case type_play:
|
||||
sendRtspRequest("PLAY", _strContentBase);
|
||||
break;
|
||||
case type_seek:
|
||||
sendRtspRequest("PLAY", _strContentBase, {"Range",StrPrinter << "npt=" << setiosflags(ios::fixed) << setprecision(2) << seekMS / 1000.0 << "-"});
|
||||
break;
|
||||
default:
|
||||
WarnL << "unknown type : " << type;
|
||||
_onHandshake = nullptr;
|
||||
break;
|
||||
}
|
||||
_onHandshake = std::bind(&RtspPlayer::handleResPAUSE,this, placeholders::_1,type);
|
||||
//开启或暂停rtsp
|
||||
switch (type){
|
||||
case type_pause:
|
||||
sendRtspRequest("PAUSE", _strContentBase);
|
||||
break;
|
||||
case type_play:
|
||||
sendRtspRequest("PLAY", _strContentBase);
|
||||
break;
|
||||
case type_seek:
|
||||
sendRtspRequest("PLAY", _strContentBase, {"Range",StrPrinter << "npt=" << setiosflags(ios::fixed) << setprecision(2) << seekMS / 1000.0 << "-"});
|
||||
break;
|
||||
default:
|
||||
WarnL << "unknown type : " << type;
|
||||
_onHandshake = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
void RtspPlayer::pause(bool bPause) {
|
||||
sendPause(bPause ? type_pause : type_seek, getProgressMilliSecond());
|
||||
}
|
||||
|
||||
void RtspPlayer::handleResPAUSE(const Parser& parser,int type) {
|
||||
if (parser.Url() != "200") {
|
||||
switch (type) {
|
||||
case type_pause:
|
||||
WarnL << "Pause failed:" << parser.Url() << " " << parser.Tail() << endl;
|
||||
break;
|
||||
case type_play:
|
||||
WarnL << "Play failed:" << parser.Url() << " " << parser.Tail() << endl;
|
||||
break;
|
||||
case type_seek:
|
||||
WarnL << "Seek failed:" << parser.Url() << " " << parser.Tail() << endl;
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (parser.Url() != "200") {
|
||||
switch (type) {
|
||||
case type_pause:
|
||||
WarnL << "Pause failed:" << parser.Url() << " " << parser.Tail() << endl;
|
||||
break;
|
||||
case type_play:
|
||||
WarnL << "Play failed:" << parser.Url() << " " << parser.Tail() << endl;
|
||||
break;
|
||||
case type_seek:
|
||||
WarnL << "Seek failed:" << parser.Url() << " " << parser.Tail() << endl;
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == type_pause) {
|
||||
//暂停成功!
|
||||
_pRtpTimer.reset();
|
||||
return;
|
||||
}
|
||||
if (type == type_pause) {
|
||||
//暂停成功!
|
||||
_pRtpTimer.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
//play或seek成功
|
||||
uint32_t iSeekTo = 0;
|
||||
//修正时间轴
|
||||
auto strRange = parser["Range"];
|
||||
if (strRange.size()) {
|
||||
auto strStart = FindField(strRange.data(), "npt=", "-");
|
||||
if (strStart == "now") {
|
||||
strStart = "0";
|
||||
}
|
||||
iSeekTo = 1000 * atof(strStart.data());
|
||||
DebugL << "seekTo(ms):" << iSeekTo;
|
||||
}
|
||||
//设置相对时间戳
|
||||
_stamp[0].setRelativeStamp(iSeekTo);
|
||||
_stamp[1].setRelativeStamp(iSeekTo);
|
||||
onPlayResult_l(SockException(Err_success, type == type_seek ? "resum rtsp success" : "rtsp play success"), type == type_seek);
|
||||
//play或seek成功
|
||||
uint32_t iSeekTo = 0;
|
||||
//修正时间轴
|
||||
auto strRange = parser["Range"];
|
||||
if (strRange.size()) {
|
||||
auto strStart = FindField(strRange.data(), "npt=", "-");
|
||||
if (strStart == "now") {
|
||||
strStart = "0";
|
||||
}
|
||||
iSeekTo = 1000 * atof(strStart.data());
|
||||
DebugL << "seekTo(ms):" << iSeekTo;
|
||||
}
|
||||
//设置相对时间戳
|
||||
_stamp[0].setRelativeStamp(iSeekTo);
|
||||
_stamp[1].setRelativeStamp(iSeekTo);
|
||||
onPlayResult_l(SockException(Err_success, type == type_seek ? "resum rtsp success" : "rtsp play success"), type == type_seek);
|
||||
}
|
||||
|
||||
void RtspPlayer::onWholeRtspPacket(Parser &parser) {
|
||||
try {
|
||||
decltype(_onHandshake) fun;
|
||||
_onHandshake.swap(fun);
|
||||
decltype(_onHandshake) fun;
|
||||
_onHandshake.swap(fun);
|
||||
if(fun){
|
||||
fun(parser);
|
||||
}
|
||||
parser.Clear();
|
||||
} catch (std::exception &err) {
|
||||
//定时器_pPlayTimer为空后表明握手结束了
|
||||
onPlayResult_l(SockException(Err_other, err.what()),!_pPlayTimer);
|
||||
//定时器_pPlayTimer为空后表明握手结束了
|
||||
onPlayResult_l(SockException(Err_other, err.what()),!_pPlayTimer);
|
||||
}
|
||||
}
|
||||
|
||||
@ -610,39 +610,39 @@ void RtspPlayer::sendReceiverReport(bool overTcp,int iTrackIndex){
|
||||
|
||||
|
||||
void RtspPlayer::onRtpSorted(const RtpPacket::Ptr &rtppt, int trackidx){
|
||||
//统计丢包率
|
||||
if (_aui16FirstSeq[trackidx] == 0 || rtppt->sequence < _aui16FirstSeq[trackidx]) {
|
||||
_aui16FirstSeq[trackidx] = rtppt->sequence;
|
||||
_aui64RtpRecv[trackidx] = 0;
|
||||
}
|
||||
_aui64RtpRecv[trackidx] ++;
|
||||
_aui16NowSeq[trackidx] = rtppt->sequence;
|
||||
//统计丢包率
|
||||
if (_aui16FirstSeq[trackidx] == 0 || rtppt->sequence < _aui16FirstSeq[trackidx]) {
|
||||
_aui16FirstSeq[trackidx] = rtppt->sequence;
|
||||
_aui64RtpRecv[trackidx] = 0;
|
||||
}
|
||||
_aui64RtpRecv[trackidx] ++;
|
||||
_aui16NowSeq[trackidx] = rtppt->sequence;
|
||||
|
||||
//计算相对时间戳
|
||||
int64_t dts_out;
|
||||
_stamp[trackidx].revise(rtppt->timeStamp,rtppt->timeStamp,dts_out,dts_out);
|
||||
//计算相对时间戳
|
||||
int64_t dts_out;
|
||||
_stamp[trackidx].revise(rtppt->timeStamp,rtppt->timeStamp,dts_out,dts_out);
|
||||
rtppt->timeStamp = dts_out;
|
||||
onRecvRTP_l(rtppt,_aTrackInfo[trackidx]);
|
||||
onRecvRTP_l(rtppt,_aTrackInfo[trackidx]);
|
||||
}
|
||||
float RtspPlayer::getPacketLossRate(TrackType type) const{
|
||||
int iTrackIdx = getTrackIndexByTrackType(type);
|
||||
if(iTrackIdx == -1){
|
||||
uint64_t totalRecv = 0;
|
||||
uint64_t totalSend = 0;
|
||||
for (unsigned int i = 0; i < _aTrackInfo.size(); i++) {
|
||||
totalRecv += _aui64RtpRecv[i];
|
||||
totalSend += (_aui16NowSeq[i] - _aui16FirstSeq[i] + 1);
|
||||
}
|
||||
if(totalSend == 0){
|
||||
return 0;
|
||||
}
|
||||
return 1.0 - (double)totalRecv / totalSend;
|
||||
}
|
||||
int iTrackIdx = getTrackIndexByTrackType(type);
|
||||
if(iTrackIdx == -1){
|
||||
uint64_t totalRecv = 0;
|
||||
uint64_t totalSend = 0;
|
||||
for (unsigned int i = 0; i < _aTrackInfo.size(); i++) {
|
||||
totalRecv += _aui64RtpRecv[i];
|
||||
totalSend += (_aui16NowSeq[i] - _aui16FirstSeq[i] + 1);
|
||||
}
|
||||
if(totalSend == 0){
|
||||
return 0;
|
||||
}
|
||||
return 1.0 - (double)totalRecv / totalSend;
|
||||
}
|
||||
|
||||
if(_aui16NowSeq[iTrackIdx] - _aui16FirstSeq[iTrackIdx] + 1 == 0){
|
||||
return 0;
|
||||
}
|
||||
return 1.0 - (double)_aui64RtpRecv[iTrackIdx] / (_aui16NowSeq[iTrackIdx] - _aui16FirstSeq[iTrackIdx] + 1);
|
||||
if(_aui16NowSeq[iTrackIdx] - _aui16FirstSeq[iTrackIdx] + 1 == 0){
|
||||
return 0;
|
||||
}
|
||||
return 1.0 - (double)_aui64RtpRecv[iTrackIdx] / (_aui16NowSeq[iTrackIdx] - _aui16FirstSeq[iTrackIdx] + 1);
|
||||
}
|
||||
|
||||
uint32_t RtspPlayer::getProgressMilliSecond() const{
|
||||
@ -653,70 +653,70 @@ void RtspPlayer::seekToMilliSecond(uint32_t ms) {
|
||||
}
|
||||
|
||||
void RtspPlayer::sendRtspRequest(const string &cmd, const string &url, const std::initializer_list<string> &header) {
|
||||
string key;
|
||||
StrCaseMap header_map;
|
||||
int i = 0;
|
||||
for(auto &val : header){
|
||||
if(++i % 2 == 0){
|
||||
header_map.emplace(key,val);
|
||||
}else{
|
||||
key = val;
|
||||
}
|
||||
}
|
||||
sendRtspRequest(cmd,url,header_map);
|
||||
string key;
|
||||
StrCaseMap header_map;
|
||||
int i = 0;
|
||||
for(auto &val : header){
|
||||
if(++i % 2 == 0){
|
||||
header_map.emplace(key,val);
|
||||
}else{
|
||||
key = val;
|
||||
}
|
||||
}
|
||||
sendRtspRequest(cmd,url,header_map);
|
||||
}
|
||||
void RtspPlayer::sendRtspRequest(const string &cmd, const string &url,const StrCaseMap &header_const) {
|
||||
auto header = header_const;
|
||||
header.emplace("CSeq",StrPrinter << _uiCseq++);
|
||||
header.emplace("User-Agent",SERVER_NAME "(build in " __DATE__ " " __TIME__ ")");
|
||||
auto header = header_const;
|
||||
header.emplace("CSeq",StrPrinter << _uiCseq++);
|
||||
header.emplace("User-Agent",SERVER_NAME "(build in " __DATE__ " " __TIME__ ")");
|
||||
|
||||
if(!_strSession.empty()){
|
||||
header.emplace("Session",_strSession);
|
||||
}
|
||||
if(!_strSession.empty()){
|
||||
header.emplace("Session",_strSession);
|
||||
}
|
||||
|
||||
if(!_rtspRealm.empty() && !(*this)[kRtspUser].empty()){
|
||||
if(!_rtspMd5Nonce.empty()){
|
||||
//MD5认证
|
||||
/*
|
||||
response计算方法如下:
|
||||
RTSP客户端应该使用username + password并计算response如下:
|
||||
(1)当password为MD5编码,则
|
||||
response = md5( password:nonce:md5(public_method:url) );
|
||||
(2)当password为ANSI字符串,则
|
||||
response= md5( md5(username:realm:password):nonce:md5(public_method:url) );
|
||||
*/
|
||||
string encrypted_pwd = (*this)[kRtspPwd];
|
||||
if(!(*this)[kRtspPwdIsMD5].as<bool>()){
|
||||
encrypted_pwd = MD5((*this)[kRtspUser]+ ":" + _rtspRealm + ":" + encrypted_pwd).hexdigest();
|
||||
}
|
||||
auto response = MD5( encrypted_pwd + ":" + _rtspMd5Nonce + ":" + MD5(cmd + ":" + url).hexdigest()).hexdigest();
|
||||
_StrPrinter printer;
|
||||
printer << "Digest ";
|
||||
printer << "username=\"" << (*this)[kRtspUser] << "\", ";
|
||||
printer << "realm=\"" << _rtspRealm << "\", ";
|
||||
printer << "nonce=\"" << _rtspMd5Nonce << "\", ";
|
||||
printer << "uri=\"" << url << "\", ";
|
||||
printer << "response=\"" << response << "\"";
|
||||
header.emplace("Authorization",printer);
|
||||
}else if(!(*this)[kRtspPwdIsMD5].as<bool>()){
|
||||
//base64认证
|
||||
string authStr = StrPrinter << (*this)[kRtspUser] << ":" << (*this)[kRtspPwd];
|
||||
char authStrBase64[1024] = {0};
|
||||
av_base64_encode(authStrBase64,sizeof(authStrBase64),(uint8_t *)authStr.data(),authStr.size());
|
||||
header.emplace("Authorization",StrPrinter << "Basic " << authStrBase64 );
|
||||
}
|
||||
}
|
||||
if(!_rtspRealm.empty() && !(*this)[kRtspUser].empty()){
|
||||
if(!_rtspMd5Nonce.empty()){
|
||||
//MD5认证
|
||||
/*
|
||||
response计算方法如下:
|
||||
RTSP客户端应该使用username + password并计算response如下:
|
||||
(1)当password为MD5编码,则
|
||||
response = md5( password:nonce:md5(public_method:url) );
|
||||
(2)当password为ANSI字符串,则
|
||||
response= md5( md5(username:realm:password):nonce:md5(public_method:url) );
|
||||
*/
|
||||
string encrypted_pwd = (*this)[kRtspPwd];
|
||||
if(!(*this)[kRtspPwdIsMD5].as<bool>()){
|
||||
encrypted_pwd = MD5((*this)[kRtspUser]+ ":" + _rtspRealm + ":" + encrypted_pwd).hexdigest();
|
||||
}
|
||||
auto response = MD5( encrypted_pwd + ":" + _rtspMd5Nonce + ":" + MD5(cmd + ":" + url).hexdigest()).hexdigest();
|
||||
_StrPrinter printer;
|
||||
printer << "Digest ";
|
||||
printer << "username=\"" << (*this)[kRtspUser] << "\", ";
|
||||
printer << "realm=\"" << _rtspRealm << "\", ";
|
||||
printer << "nonce=\"" << _rtspMd5Nonce << "\", ";
|
||||
printer << "uri=\"" << url << "\", ";
|
||||
printer << "response=\"" << response << "\"";
|
||||
header.emplace("Authorization",printer);
|
||||
}else if(!(*this)[kRtspPwdIsMD5].as<bool>()){
|
||||
//base64认证
|
||||
string authStr = StrPrinter << (*this)[kRtspUser] << ":" << (*this)[kRtspPwd];
|
||||
char authStrBase64[1024] = {0};
|
||||
av_base64_encode(authStrBase64,sizeof(authStrBase64),(uint8_t *)authStr.data(),authStr.size());
|
||||
header.emplace("Authorization",StrPrinter << "Basic " << authStrBase64 );
|
||||
}
|
||||
}
|
||||
|
||||
_StrPrinter printer;
|
||||
printer << cmd << " " << url << " RTSP/1.0\r\n";
|
||||
for (auto &pr : header){
|
||||
printer << pr.first << ": " << pr.second << "\r\n";
|
||||
}
|
||||
send(printer << "\r\n");
|
||||
_StrPrinter printer;
|
||||
printer << cmd << " " << url << " RTSP/1.0\r\n";
|
||||
for (auto &pr : header){
|
||||
printer << pr.first << ": " << pr.second << "\r\n";
|
||||
}
|
||||
send(printer << "\r\n");
|
||||
}
|
||||
|
||||
void RtspPlayer::onRecvRTP_l(const RtpPacket::Ptr &pkt, const SdpTrack::Ptr &track) {
|
||||
_rtpTicker.resetTime();
|
||||
_rtpTicker.resetTime();
|
||||
onRecvRTP(pkt,track);
|
||||
|
||||
int iTrackIndex = getTrackIndexByInterleaved(pkt->interleaved);
|
||||
@ -740,22 +740,22 @@ void RtspPlayer::onRecvRTP_l(const RtpPacket::Ptr &pkt, const SdpTrack::Ptr &tra
|
||||
|
||||
}
|
||||
void RtspPlayer::onPlayResult_l(const SockException &ex , bool handshakeCompleted) {
|
||||
WarnL << ex.getErrCode() << " " << ex.what();
|
||||
WarnL << ex.getErrCode() << " " << ex.what();
|
||||
|
||||
if(!ex){
|
||||
//播放成功,恢复rtp接收超时定时器
|
||||
_rtpTicker.resetTime();
|
||||
weak_ptr<RtspPlayer> weakSelf = dynamic_pointer_cast<RtspPlayer>(shared_from_this());
|
||||
int timeoutMS = (*this)[kMediaTimeoutMS].as<int>();
|
||||
//创建rtp数据接收超时检测定时器
|
||||
_pRtpTimer.reset( new Timer(timeoutMS / 2000.0, [weakSelf,timeoutMS]() {
|
||||
//创建rtp数据接收超时检测定时器
|
||||
_pRtpTimer.reset( new Timer(timeoutMS / 2000.0, [weakSelf,timeoutMS]() {
|
||||
auto strongSelf=weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return false;
|
||||
}
|
||||
if(strongSelf->_rtpTicker.elapsedTime()> timeoutMS) {
|
||||
//接收rtp媒体数据包超时
|
||||
strongSelf->onPlayResult_l(SockException(Err_timeout,"receive rtp timeout"), true);
|
||||
//接收rtp媒体数据包超时
|
||||
strongSelf->onPlayResult_l(SockException(Err_timeout,"receive rtp timeout"), true);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -780,27 +780,27 @@ void RtspPlayer::onPlayResult_l(const SockException &ex , bool handshakeComplete
|
||||
}
|
||||
|
||||
int RtspPlayer::getTrackIndexByInterleaved(int interleaved) const{
|
||||
for (unsigned int i = 0; i < _aTrackInfo.size(); i++) {
|
||||
if (_aTrackInfo[i]->_interleaved == interleaved) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
if(_aTrackInfo.size() == 1){
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
for (unsigned int i = 0; i < _aTrackInfo.size(); i++) {
|
||||
if (_aTrackInfo[i]->_interleaved == interleaved) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
if(_aTrackInfo.size() == 1){
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int RtspPlayer::getTrackIndexByTrackType(TrackType trackType) const {
|
||||
for (unsigned int i = 0; i < _aTrackInfo.size(); i++) {
|
||||
if (_aTrackInfo[i]->_type == trackType) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
if(_aTrackInfo.size() == 1){
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
for (unsigned int i = 0; i < _aTrackInfo.size(); i++) {
|
||||
if (_aTrackInfo[i]->_type == trackType) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
if(_aTrackInfo.size() == 1){
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
} /* namespace mediakit */
|
||||
|
@ -50,18 +50,18 @@ namespace mediakit {
|
||||
//实现了rtsp播放器协议部分的功能,及数据接收功能
|
||||
class RtspPlayer: public PlayerBase,public TcpClient, public RtspSplitter, public RtpReceiver {
|
||||
public:
|
||||
typedef std::shared_ptr<RtspPlayer> Ptr;
|
||||
typedef std::shared_ptr<RtspPlayer> Ptr;
|
||||
|
||||
RtspPlayer(const EventPoller::Ptr &poller) ;
|
||||
virtual ~RtspPlayer(void);
|
||||
void play(const string &strUrl) override;
|
||||
void pause(bool bPause) override;
|
||||
void teardown() override;
|
||||
float getPacketLossRate(TrackType type) const override;
|
||||
RtspPlayer(const EventPoller::Ptr &poller) ;
|
||||
virtual ~RtspPlayer(void);
|
||||
void play(const string &strUrl) override;
|
||||
void pause(bool bPause) override;
|
||||
void teardown() override;
|
||||
float getPacketLossRate(TrackType type) const override;
|
||||
protected:
|
||||
//派生类回调函数
|
||||
virtual bool onCheckSDP(const string &strSdp) = 0;
|
||||
virtual void onRecvRTP(const RtpPacket::Ptr &pRtppt, const SdpTrack::Ptr &track) = 0;
|
||||
//派生类回调函数
|
||||
virtual bool onCheckSDP(const string &strSdp) = 0;
|
||||
virtual void onRecvRTP(const RtpPacket::Ptr &pRtppt, const SdpTrack::Ptr &track) = 0;
|
||||
uint32_t getProgressMilliSecond() const;
|
||||
void seekToMilliSecond(uint32_t ms);
|
||||
|
||||
@ -83,7 +83,7 @@ protected:
|
||||
* @param rtppt rtp数据包
|
||||
* @param trackidx track索引
|
||||
*/
|
||||
void onRtpSorted(const RtpPacket::Ptr &rtppt, int trackidx) override;
|
||||
void onRtpSorted(const RtpPacket::Ptr &rtppt, int trackidx) override;
|
||||
|
||||
|
||||
/**
|
||||
@ -95,61 +95,61 @@ protected:
|
||||
*/
|
||||
virtual void onRtcpPacket(int iTrackidx, SdpTrack::Ptr &track, unsigned char *pucData, unsigned int uiLen);
|
||||
|
||||
/////////////TcpClient override/////////////
|
||||
void onConnect(const SockException &err) override;
|
||||
void onRecv(const Buffer::Ptr &pBuf) override;
|
||||
void onErr(const SockException &ex) override;
|
||||
/////////////TcpClient override/////////////
|
||||
void onConnect(const SockException &err) override;
|
||||
void onRecv(const Buffer::Ptr &pBuf) override;
|
||||
void onErr(const SockException &ex) override;
|
||||
private:
|
||||
void onRecvRTP_l(const RtpPacket::Ptr &pRtppt, const SdpTrack::Ptr &track);
|
||||
void onPlayResult_l(const SockException &ex , bool handshakeCompleted);
|
||||
void onRecvRTP_l(const RtpPacket::Ptr &pRtppt, const SdpTrack::Ptr &track);
|
||||
void onPlayResult_l(const SockException &ex , bool handshakeCompleted);
|
||||
|
||||
int getTrackIndexByInterleaved(int interleaved) const;
|
||||
int getTrackIndexByTrackType(TrackType trackType) const;
|
||||
int getTrackIndexByTrackType(TrackType trackType) const;
|
||||
|
||||
void handleResSETUP(const Parser &parser, unsigned int uiTrackIndex);
|
||||
void handleResDESCRIBE(const Parser &parser);
|
||||
bool handleAuthenticationFailure(const string &wwwAuthenticateParamsStr);
|
||||
void handleResPAUSE(const Parser &parser, int type);
|
||||
void handleResSETUP(const Parser &parser, unsigned int uiTrackIndex);
|
||||
void handleResDESCRIBE(const Parser &parser);
|
||||
bool handleAuthenticationFailure(const string &wwwAuthenticateParamsStr);
|
||||
void handleResPAUSE(const Parser &parser, int type);
|
||||
|
||||
//发送SETUP命令
|
||||
void sendSetup(unsigned int uiTrackIndex);
|
||||
void sendPause(int type , uint32_t ms);
|
||||
void sendDescribe();
|
||||
//发送SETUP命令
|
||||
void sendSetup(unsigned int uiTrackIndex);
|
||||
void sendPause(int type , uint32_t ms);
|
||||
void sendDescribe();
|
||||
|
||||
void sendRtspRequest(const string &cmd, const string &url ,const StrCaseMap &header = StrCaseMap());
|
||||
void sendRtspRequest(const string &cmd, const string &url ,const std::initializer_list<string> &header);
|
||||
void sendRtspRequest(const string &cmd, const string &url ,const std::initializer_list<string> &header);
|
||||
void sendReceiverReport(bool overTcp,int iTrackIndex);
|
||||
void createUdpSockIfNecessary(int track_idx);
|
||||
void createUdpSockIfNecessary(int track_idx);
|
||||
private:
|
||||
string _strUrl;
|
||||
vector<SdpTrack::Ptr> _aTrackInfo;
|
||||
function<void(const Parser&)> _onHandshake;
|
||||
string _strUrl;
|
||||
vector<SdpTrack::Ptr> _aTrackInfo;
|
||||
function<void(const Parser&)> _onHandshake;
|
||||
Socket::Ptr _apRtpSock[2]; //RTP端口,trackid idx 为数组下标
|
||||
Socket::Ptr _apRtcpSock[2];//RTCP端口,trackid idx 为数组下标
|
||||
|
||||
//rtsp鉴权相关
|
||||
string _rtspMd5Nonce;
|
||||
string _rtspRealm;
|
||||
//rtsp info
|
||||
string _strSession;
|
||||
unsigned int _uiCseq = 1;
|
||||
string _strContentBase;
|
||||
Rtsp::eRtpType _eType = Rtsp::RTP_TCP;
|
||||
string _rtspMd5Nonce;
|
||||
string _rtspRealm;
|
||||
//rtsp info
|
||||
string _strSession;
|
||||
unsigned int _uiCseq = 1;
|
||||
string _strContentBase;
|
||||
Rtsp::eRtpType _eType = Rtsp::RTP_TCP;
|
||||
|
||||
/* 丢包率统计需要用到的参数 */
|
||||
uint16_t _aui16FirstSeq[2] = { 0 , 0};
|
||||
uint16_t _aui16NowSeq[2] = { 0 , 0 };
|
||||
uint64_t _aui64RtpRecv[2] = { 0 , 0};
|
||||
/* 丢包率统计需要用到的参数 */
|
||||
uint16_t _aui16FirstSeq[2] = { 0 , 0};
|
||||
uint16_t _aui16NowSeq[2] = { 0 , 0 };
|
||||
uint64_t _aui64RtpRecv[2] = { 0 , 0};
|
||||
|
||||
//超时功能实现
|
||||
Ticker _rtpTicker;
|
||||
std::shared_ptr<Timer> _pPlayTimer;
|
||||
std::shared_ptr<Timer> _pRtpTimer;
|
||||
//超时功能实现
|
||||
Ticker _rtpTicker;
|
||||
std::shared_ptr<Timer> _pPlayTimer;
|
||||
std::shared_ptr<Timer> _pRtpTimer;
|
||||
|
||||
//时间戳
|
||||
Stamp _stamp[2];
|
||||
//时间戳
|
||||
Stamp _stamp[2];
|
||||
|
||||
//rtcp相关
|
||||
//rtcp相关
|
||||
RtcpCounter _aRtcpCnt[2]; //rtcp统计,trackid idx 为数组下标
|
||||
Ticker _aRtcpTicker[2]; //rtcp发送时间,trackid idx 为数组下标
|
||||
};
|
||||
|
@ -43,9 +43,9 @@ namespace mediakit {
|
||||
|
||||
class RtspPlayerImp: public PlayerImp<RtspPlayer,RtspDemuxer> {
|
||||
public:
|
||||
typedef std::shared_ptr<RtspPlayerImp> Ptr;
|
||||
RtspPlayerImp(const EventPoller::Ptr &poller) : PlayerImp<RtspPlayer,RtspDemuxer>(poller){}
|
||||
virtual ~RtspPlayerImp(){
|
||||
typedef std::shared_ptr<RtspPlayerImp> Ptr;
|
||||
RtspPlayerImp(const EventPoller::Ptr &poller) : PlayerImp<RtspPlayer,RtspDemuxer>(poller){}
|
||||
virtual ~RtspPlayerImp(){
|
||||
DebugL<<endl;
|
||||
};
|
||||
float getProgress() const override{
|
||||
@ -60,17 +60,17 @@ public:
|
||||
seekToMilliSecond(fProgress * getDuration() * 1000);
|
||||
};
|
||||
private:
|
||||
//派生类回调函数
|
||||
bool onCheckSDP(const string &sdp) override {
|
||||
_pRtspMediaSrc = dynamic_pointer_cast<RtspMediaSource>(_pMediaSrc);
|
||||
if(_pRtspMediaSrc){
|
||||
//派生类回调函数
|
||||
bool onCheckSDP(const string &sdp) override {
|
||||
_pRtspMediaSrc = dynamic_pointer_cast<RtspMediaSource>(_pMediaSrc);
|
||||
if(_pRtspMediaSrc){
|
||||
_pRtspMediaSrc->setSdp(sdp);
|
||||
}
|
||||
}
|
||||
_delegate.reset(new RtspDemuxer);
|
||||
_delegate->loadSdp(sdp);
|
||||
return true;
|
||||
}
|
||||
void onRecvRTP(const RtpPacket::Ptr &rtp, const SdpTrack::Ptr &track) override {
|
||||
}
|
||||
void onRecvRTP(const RtpPacket::Ptr &rtp, const SdpTrack::Ptr &track) override {
|
||||
if(_pRtspMediaSrc){
|
||||
// rtsp直接代理是无法判断该rtp是否是I帧,所以GOP缓存基本是无效的
|
||||
// 为了减少内存使用,那么我们设置为一直关键帧以便清空GOP缓存
|
||||
@ -99,7 +99,7 @@ private:
|
||||
}
|
||||
}
|
||||
private:
|
||||
RtspMediaSource::Ptr _pRtspMediaSrc;
|
||||
RtspMediaSource::Ptr _pRtspMediaSrc;
|
||||
int _maxAnalysisMS = 0;
|
||||
};
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -69,20 +69,20 @@ private:
|
||||
|
||||
class RtspSession: public TcpSession, public RtspSplitter, public RtpReceiver , public MediaSourceEvent{
|
||||
public:
|
||||
typedef std::shared_ptr<RtspSession> Ptr;
|
||||
typedef std::function<void(const string &realm)> onGetRealm;
|
||||
typedef std::shared_ptr<RtspSession> Ptr;
|
||||
typedef std::function<void(const string &realm)> onGetRealm;
|
||||
//encrypted为true是则表明是md5加密的密码,否则是明文密码
|
||||
//在请求明文密码时如果提供md5密码者则会导致认证失败
|
||||
typedef std::function<void(bool encrypted,const string &pwd_or_md5)> onAuth;
|
||||
typedef std::function<void(bool encrypted,const string &pwd_or_md5)> onAuth;
|
||||
|
||||
RtspSession(const Socket::Ptr &pSock);
|
||||
virtual ~RtspSession();
|
||||
////TcpSession override////
|
||||
void onRecv(const Buffer::Ptr &pBuf) override;
|
||||
void onError(const SockException &err) override;
|
||||
void onManager() override;
|
||||
RtspSession(const Socket::Ptr &pSock);
|
||||
virtual ~RtspSession();
|
||||
////TcpSession override////
|
||||
void onRecv(const Buffer::Ptr &pBuf) override;
|
||||
void onError(const SockException &err) override;
|
||||
void onManager() override;
|
||||
protected:
|
||||
//RtspSplitter override
|
||||
//RtspSplitter override
|
||||
/**
|
||||
* 收到完整的rtsp包回调,包括sdp等content数据
|
||||
* @param parser rtsp包
|
||||
@ -103,14 +103,14 @@ protected:
|
||||
*/
|
||||
int64_t getContentLength(Parser &parser) override;
|
||||
|
||||
//RtpReceiver override
|
||||
void onRtpSorted(const RtpPacket::Ptr &rtppt, int trackidx) override;
|
||||
//MediaSourceEvent override
|
||||
bool close(MediaSource &sender,bool force) override ;
|
||||
//RtpReceiver override
|
||||
void onRtpSorted(const RtpPacket::Ptr &rtppt, int trackidx) override;
|
||||
//MediaSourceEvent override
|
||||
bool close(MediaSource &sender,bool force) override ;
|
||||
void onNoneReader(MediaSource &sender) override;
|
||||
int totalReaderCount(MediaSource &sender) override;
|
||||
int totalReaderCount(MediaSource &sender) override;
|
||||
|
||||
//TcpSession override
|
||||
//TcpSession override
|
||||
int send(const Buffer::Ptr &pkt) override;
|
||||
|
||||
/**
|
||||
@ -122,121 +122,121 @@ protected:
|
||||
*/
|
||||
virtual void onRtcpPacket(int iTrackidx, SdpTrack::Ptr &track, unsigned char *pucData, unsigned int uiLen);
|
||||
private:
|
||||
//处理options方法,获取服务器能力
|
||||
//处理options方法,获取服务器能力
|
||||
void handleReq_Options(const Parser &parser);
|
||||
//处理describe方法,请求服务器rtsp sdp信息
|
||||
//处理describe方法,请求服务器rtsp sdp信息
|
||||
void handleReq_Describe(const Parser &parser);
|
||||
//处理ANNOUNCE方法,请求推流,附带sdp
|
||||
//处理ANNOUNCE方法,请求推流,附带sdp
|
||||
void handleReq_ANNOUNCE(const Parser &parser);
|
||||
//处理record方法,开始推流
|
||||
//处理record方法,开始推流
|
||||
void handleReq_RECORD(const Parser &parser);
|
||||
//处理setup方法,播放和推流协商rtp传输方式用
|
||||
//处理setup方法,播放和推流协商rtp传输方式用
|
||||
void handleReq_Setup(const Parser &parser);
|
||||
//处理play方法,开始或恢复播放
|
||||
//处理play方法,开始或恢复播放
|
||||
void handleReq_Play(const Parser &parser);
|
||||
//处理pause方法,暂停播放
|
||||
//处理pause方法,暂停播放
|
||||
void handleReq_Pause(const Parser &parser);
|
||||
//处理teardown方法,结束播放
|
||||
//处理teardown方法,结束播放
|
||||
void handleReq_Teardown(const Parser &parser);
|
||||
//处理Get方法,rtp over http才用到
|
||||
//处理Get方法,rtp over http才用到
|
||||
void handleReq_Get(const Parser &parser);
|
||||
//处理Post方法,rtp over http才用到
|
||||
//处理Post方法,rtp over http才用到
|
||||
void handleReq_Post(const Parser &parser);
|
||||
//处理SET_PARAMETER、GET_PARAMETER方法,一般用于心跳
|
||||
//处理SET_PARAMETER、GET_PARAMETER方法,一般用于心跳
|
||||
void handleReq_SET_PARAMETER(const Parser &parser);
|
||||
|
||||
//rtsp资源未找到
|
||||
void inline send_StreamNotFound();
|
||||
//不支持的传输模式
|
||||
void inline send_UnsupportedTransport();
|
||||
//会话id错误
|
||||
void inline send_SessionNotFound();
|
||||
//一般rtsp服务器打开端口失败时触发
|
||||
void inline send_NotAcceptable();
|
||||
//ssrc转字符串
|
||||
inline string printSSRC(uint32_t ui32Ssrc);
|
||||
//rtsp资源未找到
|
||||
void inline send_StreamNotFound();
|
||||
//不支持的传输模式
|
||||
void inline send_UnsupportedTransport();
|
||||
//会话id错误
|
||||
void inline send_SessionNotFound();
|
||||
//一般rtsp服务器打开端口失败时触发
|
||||
void inline send_NotAcceptable();
|
||||
//ssrc转字符串
|
||||
inline string printSSRC(uint32_t ui32Ssrc);
|
||||
|
||||
//获取track下标
|
||||
inline int getTrackIndexByTrackType(TrackType type);
|
||||
//获取track下标
|
||||
inline int getTrackIndexByTrackType(TrackType type);
|
||||
inline int getTrackIndexByControlSuffix(const string &controlSuffix);
|
||||
inline int getTrackIndexByInterleaved(int interleaved);
|
||||
inline int getTrackIndexByInterleaved(int interleaved);
|
||||
|
||||
//一般用于接收udp打洞包,也用于rtsp推流
|
||||
inline void onRcvPeerUdpData(int intervaled, const Buffer::Ptr &pBuf, const struct sockaddr &addr);
|
||||
//配合onRcvPeerUdpData使用
|
||||
inline void startListenPeerUdpData(int iTrackIdx);
|
||||
//一般用于接收udp打洞包,也用于rtsp推流
|
||||
inline void onRcvPeerUdpData(int intervaled, const Buffer::Ptr &pBuf, const struct sockaddr &addr);
|
||||
//配合onRcvPeerUdpData使用
|
||||
inline void startListenPeerUdpData(int iTrackIdx);
|
||||
|
||||
////rtsp专有认证相关////
|
||||
//认证成功
|
||||
//认证成功
|
||||
void onAuthSuccess();
|
||||
//认证失败
|
||||
//认证失败
|
||||
void onAuthFailed(const string &realm,const string &why,bool close = true);
|
||||
//开始走rtsp专有认证流程
|
||||
//开始走rtsp专有认证流程
|
||||
void onAuthUser(const string &realm,const string &authorization);
|
||||
//校验base64方式的认证加密
|
||||
//校验base64方式的认证加密
|
||||
void onAuthBasic(const string &realm,const string &strBase64);
|
||||
//校验md5方式的认证加密
|
||||
//校验md5方式的认证加密
|
||||
void onAuthDigest(const string &realm,const string &strMd5);
|
||||
|
||||
//发送rtp给客户端
|
||||
//发送rtp给客户端
|
||||
void sendRtpPacket(const RtpPacket::Ptr &pkt);
|
||||
//回复客户端
|
||||
bool sendRtspResponse(const string &res_code,const std::initializer_list<string> &header, const string &sdp = "" , const char *protocol = "RTSP/1.0");
|
||||
bool sendRtspResponse(const string &res_code,const StrCaseMap &header = StrCaseMap(), const string &sdp = "",const char *protocol = "RTSP/1.0");
|
||||
//服务器发送rtcp
|
||||
void sendSenderReport(bool overTcp,int iTrackIndex);
|
||||
//设置socket标志
|
||||
void setSocketFlags();
|
||||
//回复客户端
|
||||
bool sendRtspResponse(const string &res_code,const std::initializer_list<string> &header, const string &sdp = "" , const char *protocol = "RTSP/1.0");
|
||||
bool sendRtspResponse(const string &res_code,const StrCaseMap &header = StrCaseMap(), const string &sdp = "",const char *protocol = "RTSP/1.0");
|
||||
//服务器发送rtcp
|
||||
void sendSenderReport(bool overTcp,int iTrackIndex);
|
||||
//设置socket标志
|
||||
void setSocketFlags();
|
||||
private:
|
||||
//用于判断客户端是否超时
|
||||
Ticker _ticker;
|
||||
//收到的seq,回复时一致
|
||||
int _iCseq = 0;
|
||||
//ContentBase
|
||||
string _strContentBase;
|
||||
//Session号
|
||||
string _strSession;
|
||||
//是否第一次播放,第一次播放需要鉴权,第二次播放属于暂停恢复
|
||||
bool _bFirstPlay = true;
|
||||
//url解析后保存的相关信息
|
||||
//用于判断客户端是否超时
|
||||
Ticker _ticker;
|
||||
//收到的seq,回复时一致
|
||||
int _iCseq = 0;
|
||||
//ContentBase
|
||||
string _strContentBase;
|
||||
//Session号
|
||||
string _strSession;
|
||||
//是否第一次播放,第一次播放需要鉴权,第二次播放属于暂停恢复
|
||||
bool _bFirstPlay = true;
|
||||
//url解析后保存的相关信息
|
||||
MediaInfo _mediaInfo;
|
||||
//rtsp播放器绑定的直播源
|
||||
std::weak_ptr<RtspMediaSource> _pMediaSrc;
|
||||
//直播源读取器
|
||||
RingBuffer<RtpPacket::Ptr>::RingReader::Ptr _pRtpReader;
|
||||
//推流或拉流客户端采用的rtp传输方式
|
||||
Rtsp::eRtpType _rtpType = Rtsp::RTP_Invalid;
|
||||
//sdp里面有效的track,包含音频或视频
|
||||
vector<SdpTrack::Ptr> _aTrackInfo;
|
||||
////////RTP over udp////////
|
||||
//RTP端口,trackid idx 为数组下标
|
||||
Socket::Ptr _apRtpSock[2];
|
||||
//RTCP端口,trackid idx 为数组下标
|
||||
Socket::Ptr _apRtcpSock[2];
|
||||
//标记是否收到播放的udp打洞包,收到播放的udp打洞包后才能知道其外网udp端口号
|
||||
//rtsp播放器绑定的直播源
|
||||
std::weak_ptr<RtspMediaSource> _pMediaSrc;
|
||||
//直播源读取器
|
||||
RingBuffer<RtpPacket::Ptr>::RingReader::Ptr _pRtpReader;
|
||||
//推流或拉流客户端采用的rtp传输方式
|
||||
Rtsp::eRtpType _rtpType = Rtsp::RTP_Invalid;
|
||||
//sdp里面有效的track,包含音频或视频
|
||||
vector<SdpTrack::Ptr> _aTrackInfo;
|
||||
////////RTP over udp////////
|
||||
//RTP端口,trackid idx 为数组下标
|
||||
Socket::Ptr _apRtpSock[2];
|
||||
//RTCP端口,trackid idx 为数组下标
|
||||
Socket::Ptr _apRtcpSock[2];
|
||||
//标记是否收到播放的udp打洞包,收到播放的udp打洞包后才能知道其外网udp端口号
|
||||
unordered_set<int> _udpSockConnected;
|
||||
////////RTP over udp_multicast////////
|
||||
//共享的rtp组播对象
|
||||
RtpMultiCaster::Ptr _multicaster;
|
||||
////////RTP over udp_multicast////////
|
||||
//共享的rtp组播对象
|
||||
RtpMultiCaster::Ptr _multicaster;
|
||||
|
||||
//登录认证
|
||||
//登录认证
|
||||
string _strNonce;
|
||||
//消耗的总流量
|
||||
uint64_t _ui64TotalBytes = 0;
|
||||
|
||||
//RTSP over HTTP
|
||||
//quicktime 请求rtsp会产生两次tcp连接,
|
||||
//一次发送 get 一次发送post,需要通过x-sessioncookie关联起来
|
||||
string _http_x_sessioncookie;
|
||||
function<void(const Buffer::Ptr &pBuf)> _onRecv;
|
||||
//是否开始发送rtp
|
||||
//RTSP over HTTP
|
||||
//quicktime 请求rtsp会产生两次tcp连接,
|
||||
//一次发送 get 一次发送post,需要通过x-sessioncookie关联起来
|
||||
string _http_x_sessioncookie;
|
||||
function<void(const Buffer::Ptr &pBuf)> _onRecv;
|
||||
//是否开始发送rtp
|
||||
bool _enableSendRtp;
|
||||
//rtsp推流相关
|
||||
RtspMediaSourceImp::Ptr _pushSrc;
|
||||
//rtcp统计,trackid idx 为数组下标
|
||||
RtcpCounter _aRtcpCnt[2];
|
||||
//rtcp发送时间,trackid idx 为数组下标
|
||||
Ticker _aRtcpTicker[2];
|
||||
RtspMediaSourceImp::Ptr _pushSrc;
|
||||
//rtcp统计,trackid idx 为数组下标
|
||||
RtcpCounter _aRtcpCnt[2];
|
||||
//rtcp发送时间,trackid idx 为数组下标
|
||||
Ticker _aRtcpTicker[2];
|
||||
//时间戳修整器
|
||||
Stamp _stamp[2];
|
||||
};
|
||||
|
@ -38,76 +38,76 @@ UDPServer::UDPServer() {
|
||||
}
|
||||
|
||||
UDPServer::~UDPServer() {
|
||||
InfoL;
|
||||
InfoL;
|
||||
}
|
||||
|
||||
Socket::Ptr UDPServer::getSock(const EventPoller::Ptr &poller,const char* strLocalIp, int intervaled,uint16_t iLocalPort) {
|
||||
lock_guard<mutex> lck(_mtxUpdSock);
|
||||
string strKey = StrPrinter << strLocalIp << ":" << intervaled << endl;
|
||||
auto it = _mapUpdSock.find(strKey);
|
||||
if (it == _mapUpdSock.end()) {
|
||||
Socket::Ptr pSock(new Socket(poller));
|
||||
//InfoL<<localIp;
|
||||
if (!pSock->bindUdpSock(iLocalPort, strLocalIp)) {
|
||||
//分配失败
|
||||
return nullptr;
|
||||
}
|
||||
lock_guard<mutex> lck(_mtxUpdSock);
|
||||
string strKey = StrPrinter << strLocalIp << ":" << intervaled << endl;
|
||||
auto it = _mapUpdSock.find(strKey);
|
||||
if (it == _mapUpdSock.end()) {
|
||||
Socket::Ptr pSock(new Socket(poller));
|
||||
//InfoL<<localIp;
|
||||
if (!pSock->bindUdpSock(iLocalPort, strLocalIp)) {
|
||||
//分配失败
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
pSock->setOnRead(bind(&UDPServer::onRcvData, this, intervaled, placeholders::_1,placeholders::_2));
|
||||
pSock->setOnErr(bind(&UDPServer::onErr, this, strKey, placeholders::_1));
|
||||
_mapUpdSock[strKey] = pSock;
|
||||
DebugL << strLocalIp << " " << pSock->get_local_port() << " " << intervaled;
|
||||
return pSock;
|
||||
}
|
||||
return it->second;
|
||||
pSock->setOnRead(bind(&UDPServer::onRcvData, this, intervaled, placeholders::_1,placeholders::_2));
|
||||
pSock->setOnErr(bind(&UDPServer::onErr, this, strKey, placeholders::_1));
|
||||
_mapUpdSock[strKey] = pSock;
|
||||
DebugL << strLocalIp << " " << pSock->get_local_port() << " " << intervaled;
|
||||
return pSock;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void UDPServer::listenPeer(const char* strPeerIp, void* pSelf, const onRecvData& cb) {
|
||||
lock_guard<mutex> lck(_mtxDataHandler);
|
||||
auto &mapRef = _mapDataHandler[strPeerIp];
|
||||
mapRef.emplace(pSelf, cb);
|
||||
lock_guard<mutex> lck(_mtxDataHandler);
|
||||
auto &mapRef = _mapDataHandler[strPeerIp];
|
||||
mapRef.emplace(pSelf, cb);
|
||||
}
|
||||
|
||||
void UDPServer::stopListenPeer(const char* strPeerIp, void* pSelf) {
|
||||
lock_guard<mutex> lck(_mtxDataHandler);
|
||||
auto it0 = _mapDataHandler.find(strPeerIp);
|
||||
if (it0 == _mapDataHandler.end()) {
|
||||
return;
|
||||
}
|
||||
auto &mapRef = it0->second;
|
||||
auto it1 = mapRef.find(pSelf);
|
||||
if (it1 != mapRef.end()) {
|
||||
mapRef.erase(it1);
|
||||
}
|
||||
if (mapRef.size() == 0) {
|
||||
_mapDataHandler.erase(it0);
|
||||
}
|
||||
lock_guard<mutex> lck(_mtxDataHandler);
|
||||
auto it0 = _mapDataHandler.find(strPeerIp);
|
||||
if (it0 == _mapDataHandler.end()) {
|
||||
return;
|
||||
}
|
||||
auto &mapRef = it0->second;
|
||||
auto it1 = mapRef.find(pSelf);
|
||||
if (it1 != mapRef.end()) {
|
||||
mapRef.erase(it1);
|
||||
}
|
||||
if (mapRef.size() == 0) {
|
||||
_mapDataHandler.erase(it0);
|
||||
}
|
||||
|
||||
}
|
||||
void UDPServer::onErr(const string& strKey, const SockException& err) {
|
||||
WarnL << err.what();
|
||||
lock_guard<mutex> lck(_mtxUpdSock);
|
||||
_mapUpdSock.erase(strKey);
|
||||
WarnL << err.what();
|
||||
lock_guard<mutex> lck(_mtxUpdSock);
|
||||
_mapUpdSock.erase(strKey);
|
||||
}
|
||||
void UDPServer::onRcvData(int intervaled, const Buffer::Ptr &pBuf, struct sockaddr* pPeerAddr) {
|
||||
//TraceL << trackIndex;
|
||||
struct sockaddr_in *in = (struct sockaddr_in *) pPeerAddr;
|
||||
string peerIp = inet_ntoa(in->sin_addr);
|
||||
lock_guard<mutex> lck(_mtxDataHandler);
|
||||
auto it0 = _mapDataHandler.find(peerIp);
|
||||
if (it0 == _mapDataHandler.end()) {
|
||||
return;
|
||||
}
|
||||
auto &mapRef = it0->second;
|
||||
for (auto it1 = mapRef.begin(); it1 != mapRef.end(); ++it1) {
|
||||
onRecvData &funRef = it1->second;
|
||||
if (!funRef(intervaled, pBuf, pPeerAddr)) {
|
||||
it1 = mapRef.erase(it1);
|
||||
}
|
||||
}
|
||||
if (mapRef.size() == 0) {
|
||||
_mapDataHandler.erase(it0);
|
||||
}
|
||||
//TraceL << trackIndex;
|
||||
struct sockaddr_in *in = (struct sockaddr_in *) pPeerAddr;
|
||||
string peerIp = inet_ntoa(in->sin_addr);
|
||||
lock_guard<mutex> lck(_mtxDataHandler);
|
||||
auto it0 = _mapDataHandler.find(peerIp);
|
||||
if (it0 == _mapDataHandler.end()) {
|
||||
return;
|
||||
}
|
||||
auto &mapRef = it0->second;
|
||||
for (auto it1 = mapRef.begin(); it1 != mapRef.end(); ++it1) {
|
||||
onRecvData &funRef = it1->second;
|
||||
if (!funRef(intervaled, pBuf, pPeerAddr)) {
|
||||
it1 = mapRef.erase(it1);
|
||||
}
|
||||
}
|
||||
if (mapRef.size() == 0) {
|
||||
_mapDataHandler.erase(it0);
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace mediakit */
|
||||
|
@ -43,21 +43,21 @@ namespace mediakit {
|
||||
|
||||
class UDPServer : public std::enable_shared_from_this<UDPServer> {
|
||||
public:
|
||||
typedef function< bool(int intervaled, const Buffer::Ptr &buffer, struct sockaddr *peer_addr)> onRecvData;
|
||||
~UDPServer();
|
||||
static UDPServer &Instance();
|
||||
Socket::Ptr getSock(const EventPoller::Ptr &poller,const char *strLocalIp, int intervaled,uint16_t iLocalPort = 0);
|
||||
void listenPeer(const char *strPeerIp, void *pSelf, const onRecvData &cb);
|
||||
void stopListenPeer(const char *strPeerIp, void *pSelf);
|
||||
typedef function< bool(int intervaled, const Buffer::Ptr &buffer, struct sockaddr *peer_addr)> onRecvData;
|
||||
~UDPServer();
|
||||
static UDPServer &Instance();
|
||||
Socket::Ptr getSock(const EventPoller::Ptr &poller,const char *strLocalIp, int intervaled,uint16_t iLocalPort = 0);
|
||||
void listenPeer(const char *strPeerIp, void *pSelf, const onRecvData &cb);
|
||||
void stopListenPeer(const char *strPeerIp, void *pSelf);
|
||||
private:
|
||||
UDPServer();
|
||||
void onRcvData(int intervaled, const Buffer::Ptr &pBuf,struct sockaddr *pPeerAddr);
|
||||
void onErr(const string &strKey,const SockException &err);
|
||||
unordered_map<string, Socket::Ptr> _mapUpdSock;
|
||||
mutex _mtxUpdSock;
|
||||
UDPServer();
|
||||
void onRcvData(int intervaled, const Buffer::Ptr &pBuf,struct sockaddr *pPeerAddr);
|
||||
void onErr(const string &strKey,const SockException &err);
|
||||
unordered_map<string, Socket::Ptr> _mapUpdSock;
|
||||
mutex _mtxUpdSock;
|
||||
|
||||
unordered_map<string, unordered_map<void *, onRecvData> > _mapDataHandler;
|
||||
mutex _mtxDataHandler;
|
||||
unordered_map<string, unordered_map<void *, onRecvData> > _mapDataHandler;
|
||||
mutex _mtxDataHandler;
|
||||
};
|
||||
|
||||
} /* namespace mediakit */
|
||||
|
@ -48,29 +48,29 @@ ShellSession::~ShellSession() {
|
||||
}
|
||||
|
||||
void ShellSession::onRecv(const Buffer::Ptr&buf) {
|
||||
//DebugL << hexdump(buf->data(), buf->size());
|
||||
//DebugL << hexdump(buf->data(), buf->size());
|
||||
GET_CONFIG(uint32_t,maxReqSize,Shell::kMaxReqSize);
|
||||
if (_strRecvBuf.size() + buf->size() >= maxReqSize) {
|
||||
shutdown(SockException(Err_other,"recv buffer overflow"));
|
||||
return;
|
||||
}
|
||||
_beatTicker.resetTime();
|
||||
_strRecvBuf.append(buf->data(), buf->size());
|
||||
if (_strRecvBuf.find("\xff\xf4\xff\0xfd\x06") != std::string::npos) {
|
||||
send("\033[0m\r\n Bye bye!\r\n");
|
||||
shutdown(SockException(Err_other,"received Ctrl+C"));
|
||||
return;
|
||||
}
|
||||
size_t index;
|
||||
string line;
|
||||
while ((index = _strRecvBuf.find("\r\n")) != std::string::npos) {
|
||||
line = _strRecvBuf.substr(0, index);
|
||||
_strRecvBuf.erase(0, index + 2);
|
||||
if (!onCommandLine(line)) {
|
||||
shutdown(SockException(Err_other,"exit cmd"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
shutdown(SockException(Err_other,"recv buffer overflow"));
|
||||
return;
|
||||
}
|
||||
_beatTicker.resetTime();
|
||||
_strRecvBuf.append(buf->data(), buf->size());
|
||||
if (_strRecvBuf.find("\xff\xf4\xff\0xfd\x06") != std::string::npos) {
|
||||
send("\033[0m\r\n Bye bye!\r\n");
|
||||
shutdown(SockException(Err_other,"received Ctrl+C"));
|
||||
return;
|
||||
}
|
||||
size_t index;
|
||||
string line;
|
||||
while ((index = _strRecvBuf.find("\r\n")) != std::string::npos) {
|
||||
line = _strRecvBuf.substr(0, index);
|
||||
_strRecvBuf.erase(0, index + 2);
|
||||
if (!onCommandLine(line)) {
|
||||
shutdown(SockException(Err_other,"exit cmd"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShellSession::onError(const SockException &err){
|
||||
@ -78,19 +78,19 @@ void ShellSession::onError(const SockException &err){
|
||||
}
|
||||
|
||||
void ShellSession::onManager() {
|
||||
if (_beatTicker.elapsedTime() > 1000 * 60 * 5) {
|
||||
//5 miniutes for alive
|
||||
if (_beatTicker.elapsedTime() > 1000 * 60 * 5) {
|
||||
//5 miniutes for alive
|
||||
shutdown(SockException(Err_timeout,"session timeout"));
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool ShellSession::onCommandLine(const string& line) {
|
||||
auto loginInterceptor = _loginInterceptor;
|
||||
if (loginInterceptor) {
|
||||
bool ret = loginInterceptor(line);
|
||||
return ret;
|
||||
}
|
||||
bool ret = loginInterceptor(line);
|
||||
return ret;
|
||||
}
|
||||
try {
|
||||
std::shared_ptr<stringstream> ss(new stringstream);
|
||||
CMDRegister::Instance()(line,ss);
|
||||
@ -102,21 +102,21 @@ inline bool ShellSession::onCommandLine(const string& line) {
|
||||
send("\r\n");
|
||||
}
|
||||
printShellPrefix();
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void ShellSession::pleaseInputUser() {
|
||||
send("\033[0m");
|
||||
send(StrPrinter << SERVER_NAME << " login: " << endl);
|
||||
_loginInterceptor = [this](const string &user_name) {
|
||||
_strUserName=user_name;
|
||||
send("\033[0m");
|
||||
send(StrPrinter << SERVER_NAME << " login: " << endl);
|
||||
_loginInterceptor = [this](const string &user_name) {
|
||||
_strUserName=user_name;
|
||||
pleaseInputPasswd();
|
||||
return true;
|
||||
};
|
||||
return true;
|
||||
};
|
||||
}
|
||||
inline void ShellSession::pleaseInputPasswd() {
|
||||
send("Password: \033[8m");
|
||||
_loginInterceptor = [this](const string &passwd) {
|
||||
send("Password: \033[8m");
|
||||
_loginInterceptor = [this](const string &passwd) {
|
||||
auto onAuth = [this](const string &errMessage){
|
||||
if(!errMessage.empty()){
|
||||
//鉴权失败
|
||||
@ -157,12 +157,12 @@ inline void ShellSession::pleaseInputPasswd() {
|
||||
//如果无人监听shell登录事件,那么默认shell无法登录
|
||||
onAuth("please listen kBroadcastShellLogin event");
|
||||
}
|
||||
return true;
|
||||
};
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
inline void ShellSession::printShellPrefix() {
|
||||
send(StrPrinter << _strUserName << "@" << SERVER_NAME << "# " << endl);
|
||||
send(StrPrinter << _strUserName << "@" << SERVER_NAME << "# " << endl);
|
||||
}
|
||||
|
||||
}/* namespace mediakit */
|
||||
|
@ -37,23 +37,23 @@ namespace mediakit {
|
||||
|
||||
class ShellSession: public TcpSession {
|
||||
public:
|
||||
ShellSession(const Socket::Ptr &_sock);
|
||||
virtual ~ShellSession();
|
||||
ShellSession(const Socket::Ptr &_sock);
|
||||
virtual ~ShellSession();
|
||||
|
||||
void onRecv(const Buffer::Ptr &) override;
|
||||
void onRecv(const Buffer::Ptr &) override;
|
||||
void onError(const SockException &err) override;
|
||||
void onManager() override;
|
||||
void onManager() override;
|
||||
|
||||
private:
|
||||
inline bool onCommandLine(const string &);
|
||||
inline void pleaseInputUser();
|
||||
inline void pleaseInputPasswd();
|
||||
inline void printShellPrefix();
|
||||
inline bool onCommandLine(const string &);
|
||||
inline void pleaseInputUser();
|
||||
inline void pleaseInputPasswd();
|
||||
inline void printShellPrefix();
|
||||
|
||||
function<bool(const string &)> _loginInterceptor;
|
||||
string _strRecvBuf;
|
||||
Ticker _beatTicker;
|
||||
string _strUserName;
|
||||
function<bool(const string &)> _loginInterceptor;
|
||||
string _strRecvBuf;
|
||||
Ticker _beatTicker;
|
||||
string _strUserName;
|
||||
};
|
||||
|
||||
} /* namespace mediakit */
|
||||
|
@ -32,232 +32,232 @@ namespace mediakit {
|
||||
#define HK_APP_NAME "live"
|
||||
|
||||
DeviceHK::DeviceHK() {
|
||||
InfoL << endl;
|
||||
static onceToken token( []() {
|
||||
NET_DVR_Init();
|
||||
NET_DVR_SetDVRMessageCallBack_V31([](LONG lCommand,NET_DVR_ALARMER *pAlarmer,char *pAlarmInfo,DWORD dwBufLen,void* pUser){
|
||||
WarnL<<pAlarmInfo;
|
||||
return TRUE;
|
||||
},NULL);
|
||||
}, []() {
|
||||
NET_DVR_Cleanup();
|
||||
});
|
||||
InfoL << endl;
|
||||
static onceToken token( []() {
|
||||
NET_DVR_Init();
|
||||
NET_DVR_SetDVRMessageCallBack_V31([](LONG lCommand,NET_DVR_ALARMER *pAlarmer,char *pAlarmInfo,DWORD dwBufLen,void* pUser){
|
||||
WarnL<<pAlarmInfo;
|
||||
return TRUE;
|
||||
},NULL);
|
||||
}, []() {
|
||||
NET_DVR_Cleanup();
|
||||
});
|
||||
}
|
||||
|
||||
DeviceHK::~DeviceHK() {
|
||||
InfoL << endl;
|
||||
InfoL << endl;
|
||||
}
|
||||
|
||||
void DeviceHK::connectDevice(const connectInfo &info, const connectCB& cb, int iTimeOut) {
|
||||
NET_DVR_USER_LOGIN_INFO loginInfo;
|
||||
NET_DVR_DEVICEINFO_V40 loginResult;
|
||||
NET_DVR_USER_LOGIN_INFO loginInfo;
|
||||
NET_DVR_DEVICEINFO_V40 loginResult;
|
||||
|
||||
//login info
|
||||
strcpy(loginInfo.sDeviceAddress, info.strDevIp.c_str());
|
||||
loginInfo.wPort = info.ui16DevPort;
|
||||
strcpy(loginInfo.sUserName, info.strUserName.c_str());
|
||||
strcpy(loginInfo.sPassword, info.strPwd.c_str());
|
||||
//login info
|
||||
strcpy(loginInfo.sDeviceAddress, info.strDevIp.c_str());
|
||||
loginInfo.wPort = info.ui16DevPort;
|
||||
strcpy(loginInfo.sUserName, info.strUserName.c_str());
|
||||
strcpy(loginInfo.sPassword, info.strPwd.c_str());
|
||||
|
||||
//callback info
|
||||
typedef function< void(LONG lUserID, DWORD dwResult, LPNET_DVR_DEVICEINFO_V30 lpDeviceInfo)> hkLoginCB;
|
||||
loginInfo.bUseAsynLogin = TRUE;
|
||||
weak_ptr<Device> weakSelf = shared_from_this();
|
||||
loginInfo.pUser = new hkLoginCB([weakSelf,cb](LONG lUserID, DWORD dwResult, LPNET_DVR_DEVICEINFO_V30 lpDeviceInfo ) {
|
||||
//TraceL<<lUserID<<" "<<dwResult<<" "<<lpDeviceInfo->sSerialNumber;
|
||||
connectResult result;
|
||||
if(dwResult==TRUE) {
|
||||
result.strDevName=(char *)(lpDeviceInfo->sSerialNumber);
|
||||
result.ui16ChnStart=lpDeviceInfo->byStartChan;
|
||||
result.ui16ChnCount=lpDeviceInfo->byChanNum;
|
||||
auto _strongSelf=weakSelf.lock();
|
||||
if(_strongSelf) {
|
||||
auto strongSelf=dynamic_pointer_cast<DeviceHK>(_strongSelf);
|
||||
strongSelf->onConnected(lUserID,lpDeviceInfo);
|
||||
}
|
||||
} else {
|
||||
WarnL<<"connect deviceHK failed:"<<NET_DVR_GetLastError();
|
||||
}
|
||||
cb(dwResult==TRUE,result);
|
||||
});
|
||||
loginInfo.cbLoginResult = [](LONG lUserID, DWORD dwResult, LPNET_DVR_DEVICEINFO_V30 lpDeviceInfo , void* pUser) {
|
||||
auto *fun=static_cast<hkLoginCB *>(pUser);
|
||||
(*fun)(lUserID,dwResult,lpDeviceInfo);
|
||||
delete fun;
|
||||
};
|
||||
NET_DVR_SetConnectTime(iTimeOut * 1000, 3);
|
||||
NET_DVR_Login_V40(&loginInfo, &loginResult);
|
||||
//callback info
|
||||
typedef function< void(LONG lUserID, DWORD dwResult, LPNET_DVR_DEVICEINFO_V30 lpDeviceInfo)> hkLoginCB;
|
||||
loginInfo.bUseAsynLogin = TRUE;
|
||||
weak_ptr<Device> weakSelf = shared_from_this();
|
||||
loginInfo.pUser = new hkLoginCB([weakSelf,cb](LONG lUserID, DWORD dwResult, LPNET_DVR_DEVICEINFO_V30 lpDeviceInfo ) {
|
||||
//TraceL<<lUserID<<" "<<dwResult<<" "<<lpDeviceInfo->sSerialNumber;
|
||||
connectResult result;
|
||||
if(dwResult==TRUE) {
|
||||
result.strDevName=(char *)(lpDeviceInfo->sSerialNumber);
|
||||
result.ui16ChnStart=lpDeviceInfo->byStartChan;
|
||||
result.ui16ChnCount=lpDeviceInfo->byChanNum;
|
||||
auto _strongSelf=weakSelf.lock();
|
||||
if(_strongSelf) {
|
||||
auto strongSelf=dynamic_pointer_cast<DeviceHK>(_strongSelf);
|
||||
strongSelf->onConnected(lUserID,lpDeviceInfo);
|
||||
}
|
||||
} else {
|
||||
WarnL<<"connect deviceHK failed:"<<NET_DVR_GetLastError();
|
||||
}
|
||||
cb(dwResult==TRUE,result);
|
||||
});
|
||||
loginInfo.cbLoginResult = [](LONG lUserID, DWORD dwResult, LPNET_DVR_DEVICEINFO_V30 lpDeviceInfo , void* pUser) {
|
||||
auto *fun=static_cast<hkLoginCB *>(pUser);
|
||||
(*fun)(lUserID,dwResult,lpDeviceInfo);
|
||||
delete fun;
|
||||
};
|
||||
NET_DVR_SetConnectTime(iTimeOut * 1000, 3);
|
||||
NET_DVR_Login_V40(&loginInfo, &loginResult);
|
||||
}
|
||||
|
||||
void DeviceHK::disconnect(const relustCB& cb) {
|
||||
m_mapChannels.clear();
|
||||
if (m_i64LoginId >= 0) {
|
||||
NET_DVR_Logout(m_i64LoginId);
|
||||
m_i64LoginId = -1;
|
||||
Device::onDisconnected(true);
|
||||
}
|
||||
m_mapChannels.clear();
|
||||
if (m_i64LoginId >= 0) {
|
||||
NET_DVR_Logout(m_i64LoginId);
|
||||
m_i64LoginId = -1;
|
||||
Device::onDisconnected(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void DeviceHK::addChannel(int iChn, bool bMainStream) {
|
||||
DevChannel::Ptr channel( new DevChannelHK(m_i64LoginId, (char *) m_deviceInfo.sSerialNumber, iChn, bMainStream));
|
||||
m_mapChannels[iChn] = channel;
|
||||
DevChannel::Ptr channel( new DevChannelHK(m_i64LoginId, (char *) m_deviceInfo.sSerialNumber, iChn, bMainStream));
|
||||
m_mapChannels[iChn] = channel;
|
||||
}
|
||||
|
||||
void DeviceHK::delChannel(int chn) {
|
||||
m_mapChannels.erase(chn);
|
||||
m_mapChannels.erase(chn);
|
||||
}
|
||||
|
||||
void DeviceHK::onConnected(LONG lUserID, LPNET_DVR_DEVICEINFO_V30 lpDeviceInfo) {
|
||||
m_i64LoginId = lUserID;
|
||||
m_deviceInfo = *lpDeviceInfo;
|
||||
Device::onConnected();
|
||||
m_i64LoginId = lUserID;
|
||||
m_deviceInfo = *lpDeviceInfo;
|
||||
Device::onConnected();
|
||||
}
|
||||
|
||||
void DeviceHK::addAllChannel(bool bMainStream) {
|
||||
InfoL << endl;
|
||||
for (int i = 0; i < m_deviceInfo.byChanNum; i++) {
|
||||
addChannel(m_deviceInfo.byStartChan + i, bMainStream);
|
||||
}
|
||||
InfoL << endl;
|
||||
for (int i = 0; i < m_deviceInfo.byChanNum; i++) {
|
||||
addChannel(m_deviceInfo.byStartChan + i, bMainStream);
|
||||
}
|
||||
}
|
||||
|
||||
DevChannelHK::DevChannelHK(int64_t i64LoginId, const char* pcDevName, int iChn, bool bMainStream) :
|
||||
DevChannel(HK_APP_NAME,(StrPrinter<<MD5(pcDevName).hexdigest()<<"_"<<iChn<<endl).data()),
|
||||
m_i64LoginId(i64LoginId) {
|
||||
InfoL << endl;
|
||||
NET_DVR_PREVIEWINFO previewInfo;
|
||||
previewInfo.lChannel = iChn; //通道号
|
||||
previewInfo.dwStreamType = bMainStream ? 0 : 1; // 码流类型,0-主码流,1-子码流,2-码流3,3-码流4 等以此类推
|
||||
previewInfo.dwLinkMode = 1; // 0:TCP方式,1:UDP方式,2:多播方式,3 - RTP方式,4-RTP/RTSP,5-RSTP/HTTP
|
||||
previewInfo.hPlayWnd = 0; //播放窗口的句柄,为NULL表示不播放图象
|
||||
previewInfo.byProtoType = 0; //应用层取流协议,0-私有协议,1-RTSP协议
|
||||
previewInfo.dwDisplayBufNum = 1; //播放库播放缓冲区最大缓冲帧数,范围1-50,置0时默认为1
|
||||
previewInfo.bBlocked = 0;
|
||||
m_i64PreviewHandle = NET_DVR_RealPlay_V40(m_i64LoginId, &previewInfo,
|
||||
[](LONG lPlayHandle,DWORD dwDataType,BYTE *pBuffer,DWORD dwBufSize,void* pUser) {
|
||||
DevChannelHK *self=reinterpret_cast<DevChannelHK *>(pUser);
|
||||
if(self->m_i64PreviewHandle!=(int64_t)lPlayHandle) {
|
||||
return;
|
||||
}
|
||||
self->onPreview(dwDataType,pBuffer,dwBufSize);
|
||||
}, this);
|
||||
if (m_i64PreviewHandle == -1) {
|
||||
throw std::runtime_error( StrPrinter << "设备[" << pcDevName << "/" << iChn << "]开始实时预览失败:"
|
||||
<< NET_DVR_GetLastError() << endl);
|
||||
}
|
||||
DevChannel(HK_APP_NAME,(StrPrinter<<MD5(pcDevName).hexdigest()<<"_"<<iChn<<endl).data()),
|
||||
m_i64LoginId(i64LoginId) {
|
||||
InfoL << endl;
|
||||
NET_DVR_PREVIEWINFO previewInfo;
|
||||
previewInfo.lChannel = iChn; //通道号
|
||||
previewInfo.dwStreamType = bMainStream ? 0 : 1; // 码流类型,0-主码流,1-子码流,2-码流3,3-码流4 等以此类推
|
||||
previewInfo.dwLinkMode = 1; // 0:TCP方式,1:UDP方式,2:多播方式,3 - RTP方式,4-RTP/RTSP,5-RSTP/HTTP
|
||||
previewInfo.hPlayWnd = 0; //播放窗口的句柄,为NULL表示不播放图象
|
||||
previewInfo.byProtoType = 0; //应用层取流协议,0-私有协议,1-RTSP协议
|
||||
previewInfo.dwDisplayBufNum = 1; //播放库播放缓冲区最大缓冲帧数,范围1-50,置0时默认为1
|
||||
previewInfo.bBlocked = 0;
|
||||
m_i64PreviewHandle = NET_DVR_RealPlay_V40(m_i64LoginId, &previewInfo,
|
||||
[](LONG lPlayHandle,DWORD dwDataType,BYTE *pBuffer,DWORD dwBufSize,void* pUser) {
|
||||
DevChannelHK *self=reinterpret_cast<DevChannelHK *>(pUser);
|
||||
if(self->m_i64PreviewHandle!=(int64_t)lPlayHandle) {
|
||||
return;
|
||||
}
|
||||
self->onPreview(dwDataType,pBuffer,dwBufSize);
|
||||
}, this);
|
||||
if (m_i64PreviewHandle == -1) {
|
||||
throw std::runtime_error( StrPrinter << "设备[" << pcDevName << "/" << iChn << "]开始实时预览失败:"
|
||||
<< NET_DVR_GetLastError() << endl);
|
||||
}
|
||||
}
|
||||
|
||||
DevChannelHK::~DevChannelHK() {
|
||||
InfoL << endl;
|
||||
if (m_i64PreviewHandle >= 0) {
|
||||
NET_DVR_StopRealPlay(m_i64PreviewHandle);
|
||||
m_i64PreviewHandle = -1;
|
||||
}
|
||||
if (m_iPlayHandle >= 0) {
|
||||
PlayM4_StopSoundShare(m_iPlayHandle);
|
||||
PlayM4_Stop(m_iPlayHandle);
|
||||
m_iPlayHandle = -1;
|
||||
}
|
||||
InfoL << endl;
|
||||
if (m_i64PreviewHandle >= 0) {
|
||||
NET_DVR_StopRealPlay(m_i64PreviewHandle);
|
||||
m_i64PreviewHandle = -1;
|
||||
}
|
||||
if (m_iPlayHandle >= 0) {
|
||||
PlayM4_StopSoundShare(m_iPlayHandle);
|
||||
PlayM4_Stop(m_iPlayHandle);
|
||||
m_iPlayHandle = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void DevChannelHK::onPreview(DWORD dwDataType, BYTE* pBuffer, DWORD dwBufSize) {
|
||||
//TimeTicker1(-1);
|
||||
switch (dwDataType) {
|
||||
case NET_DVR_SYSHEAD: { //系统头数据
|
||||
if (!PlayM4_GetPort(&m_iPlayHandle)) { //获取播放库未使用的通道号
|
||||
WarnL << "PlayM4_GetPort:" << NET_DVR_GetLastError();
|
||||
break;
|
||||
}
|
||||
if (dwBufSize > 0) {
|
||||
if (!PlayM4_SetStreamOpenMode(m_iPlayHandle, STREAME_REALTIME)) { //设置实时流播放模式
|
||||
WarnL << "PlayM4_SetStreamOpenMode:" << NET_DVR_GetLastError();
|
||||
break;
|
||||
}
|
||||
if (!PlayM4_OpenStream(m_iPlayHandle, pBuffer, dwBufSize,
|
||||
1024 * 1024)) { //打开流接口
|
||||
WarnL << "PlayM4_OpenStream:" << NET_DVR_GetLastError();
|
||||
break;
|
||||
}
|
||||
//TimeTicker1(-1);
|
||||
switch (dwDataType) {
|
||||
case NET_DVR_SYSHEAD: { //系统头数据
|
||||
if (!PlayM4_GetPort(&m_iPlayHandle)) { //获取播放库未使用的通道号
|
||||
WarnL << "PlayM4_GetPort:" << NET_DVR_GetLastError();
|
||||
break;
|
||||
}
|
||||
if (dwBufSize > 0) {
|
||||
if (!PlayM4_SetStreamOpenMode(m_iPlayHandle, STREAME_REALTIME)) { //设置实时流播放模式
|
||||
WarnL << "PlayM4_SetStreamOpenMode:" << NET_DVR_GetLastError();
|
||||
break;
|
||||
}
|
||||
if (!PlayM4_OpenStream(m_iPlayHandle, pBuffer, dwBufSize,
|
||||
1024 * 1024)) { //打开流接口
|
||||
WarnL << "PlayM4_OpenStream:" << NET_DVR_GetLastError();
|
||||
break;
|
||||
}
|
||||
|
||||
PlayM4_SetDecCallBackMend(m_iPlayHandle,
|
||||
[](int nPort,char * pBuf,int nSize,FRAME_INFO * pFrameInfo, void* nUser,int nReserved2) {
|
||||
DevChannelHK *chn=reinterpret_cast<DevChannelHK *>(nUser);
|
||||
if(chn->m_iPlayHandle!=nPort) {
|
||||
return;
|
||||
}
|
||||
chn->onGetDecData(pBuf,nSize,pFrameInfo);
|
||||
}, this);
|
||||
if (!PlayM4_Play(m_iPlayHandle, 0)) { //播放开始
|
||||
WarnL << "PlayM4_Play:" << NET_DVR_GetLastError();
|
||||
break;
|
||||
}
|
||||
InfoL << "设置解码器成功!" << endl;
|
||||
//打开音频解码, 需要码流是复合流
|
||||
if (!PlayM4_PlaySoundShare(m_iPlayHandle)) {
|
||||
WarnL << "PlayM4_PlaySound:" << NET_DVR_GetLastError();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NET_DVR_STREAMDATA: { //流数据(包括复合流或音视频分开的视频流数据)
|
||||
if (dwBufSize > 0 && m_iPlayHandle != -1) {
|
||||
if (!PlayM4_InputData(m_iPlayHandle, pBuffer, dwBufSize)) {
|
||||
WarnL << "PlayM4_InputData:" << NET_DVR_GetLastError();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NET_DVR_AUDIOSTREAMDATA: { //音频数据
|
||||
}
|
||||
break;
|
||||
case NET_DVR_PRIVATE_DATA: { //私有数据,包括智能信息
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
PlayM4_SetDecCallBackMend(m_iPlayHandle,
|
||||
[](int nPort,char * pBuf,int nSize,FRAME_INFO * pFrameInfo, void* nUser,int nReserved2) {
|
||||
DevChannelHK *chn=reinterpret_cast<DevChannelHK *>(nUser);
|
||||
if(chn->m_iPlayHandle!=nPort) {
|
||||
return;
|
||||
}
|
||||
chn->onGetDecData(pBuf,nSize,pFrameInfo);
|
||||
}, this);
|
||||
if (!PlayM4_Play(m_iPlayHandle, 0)) { //播放开始
|
||||
WarnL << "PlayM4_Play:" << NET_DVR_GetLastError();
|
||||
break;
|
||||
}
|
||||
InfoL << "设置解码器成功!" << endl;
|
||||
//打开音频解码, 需要码流是复合流
|
||||
if (!PlayM4_PlaySoundShare(m_iPlayHandle)) {
|
||||
WarnL << "PlayM4_PlaySound:" << NET_DVR_GetLastError();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NET_DVR_STREAMDATA: { //流数据(包括复合流或音视频分开的视频流数据)
|
||||
if (dwBufSize > 0 && m_iPlayHandle != -1) {
|
||||
if (!PlayM4_InputData(m_iPlayHandle, pBuffer, dwBufSize)) {
|
||||
WarnL << "PlayM4_InputData:" << NET_DVR_GetLastError();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NET_DVR_AUDIOSTREAMDATA: { //音频数据
|
||||
}
|
||||
break;
|
||||
case NET_DVR_PRIVATE_DATA: { //私有数据,包括智能信息
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DevChannelHK::onGetDecData(char* pBuf, int nSize, FRAME_INFO* pFrameInfo) {
|
||||
//InfoL << pFrameInfo->nType;
|
||||
switch (pFrameInfo->nType) {
|
||||
case T_YV12: {
|
||||
if (!m_bVideoSeted) {
|
||||
m_bVideoSeted = true;
|
||||
VideoInfo video;
|
||||
video.iWidth = pFrameInfo->nWidth;
|
||||
video.iHeight = pFrameInfo->nHeight;
|
||||
video.iFrameRate = pFrameInfo->nFrameRate;
|
||||
initVideo(video);
|
||||
}
|
||||
char *yuv[3];
|
||||
int yuv_len[3];
|
||||
yuv_len[0] = pFrameInfo->nWidth;
|
||||
yuv_len[1] = pFrameInfo->nWidth / 2;
|
||||
yuv_len[2] = pFrameInfo->nWidth / 2;
|
||||
int dwOffset_Y = pFrameInfo->nWidth * pFrameInfo->nHeight;
|
||||
yuv[0] = pBuf;
|
||||
yuv[2] = yuv[0] + dwOffset_Y;
|
||||
yuv[1] = yuv[2] + dwOffset_Y / 4;
|
||||
inputYUV(yuv, yuv_len, pFrameInfo->nStamp);
|
||||
}
|
||||
break;
|
||||
case T_AUDIO16: {
|
||||
if (!m_bAudioSeted) {
|
||||
m_bAudioSeted = true;
|
||||
AudioInfo audio;
|
||||
audio.iChannel = pFrameInfo->nWidth;
|
||||
audio.iSampleBit = pFrameInfo->nHeight;
|
||||
audio.iSampleRate = pFrameInfo->nFrameRate;
|
||||
initAudio(audio);
|
||||
}
|
||||
inputPCM(pBuf, nSize, pFrameInfo->nStamp);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
//InfoL << pFrameInfo->nType;
|
||||
switch (pFrameInfo->nType) {
|
||||
case T_YV12: {
|
||||
if (!m_bVideoSeted) {
|
||||
m_bVideoSeted = true;
|
||||
VideoInfo video;
|
||||
video.iWidth = pFrameInfo->nWidth;
|
||||
video.iHeight = pFrameInfo->nHeight;
|
||||
video.iFrameRate = pFrameInfo->nFrameRate;
|
||||
initVideo(video);
|
||||
}
|
||||
char *yuv[3];
|
||||
int yuv_len[3];
|
||||
yuv_len[0] = pFrameInfo->nWidth;
|
||||
yuv_len[1] = pFrameInfo->nWidth / 2;
|
||||
yuv_len[2] = pFrameInfo->nWidth / 2;
|
||||
int dwOffset_Y = pFrameInfo->nWidth * pFrameInfo->nHeight;
|
||||
yuv[0] = pBuf;
|
||||
yuv[2] = yuv[0] + dwOffset_Y;
|
||||
yuv[1] = yuv[2] + dwOffset_Y / 4;
|
||||
inputYUV(yuv, yuv_len, pFrameInfo->nStamp);
|
||||
}
|
||||
break;
|
||||
case T_AUDIO16: {
|
||||
if (!m_bAudioSeted) {
|
||||
m_bAudioSeted = true;
|
||||
AudioInfo audio;
|
||||
audio.iChannel = pFrameInfo->nWidth;
|
||||
audio.iSampleBit = pFrameInfo->nHeight;
|
||||
audio.iSampleRate = pFrameInfo->nFrameRate;
|
||||
initAudio(audio);
|
||||
}
|
||||
inputPCM(pBuf, nSize, pFrameInfo->nStamp);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace mediakit */
|
||||
|
@ -41,26 +41,26 @@ namespace mediakit {
|
||||
|
||||
class connectInfo {
|
||||
public:
|
||||
connectInfo(const char *_strDevIp,
|
||||
uint16_t _ui16DevPort,
|
||||
const char *_strUserName,
|
||||
const char *_strPwd) {
|
||||
strDevIp = _strDevIp;
|
||||
ui16DevPort = _ui16DevPort;
|
||||
strUserName = _strUserName;
|
||||
strPwd = _strPwd;
|
||||
}
|
||||
string strDevIp;
|
||||
uint16_t ui16DevPort;
|
||||
string strUserName;
|
||||
string strPwd;
|
||||
connectInfo(const char *_strDevIp,
|
||||
uint16_t _ui16DevPort,
|
||||
const char *_strUserName,
|
||||
const char *_strPwd) {
|
||||
strDevIp = _strDevIp;
|
||||
ui16DevPort = _ui16DevPort;
|
||||
strUserName = _strUserName;
|
||||
strPwd = _strPwd;
|
||||
}
|
||||
string strDevIp;
|
||||
uint16_t ui16DevPort;
|
||||
string strUserName;
|
||||
string strPwd;
|
||||
};
|
||||
|
||||
class connectResult {
|
||||
public:
|
||||
string strDevName;
|
||||
uint16_t ui16ChnStart;
|
||||
uint16_t ui16ChnCount;
|
||||
string strDevName;
|
||||
uint16_t ui16ChnStart;
|
||||
uint16_t ui16ChnCount;
|
||||
};
|
||||
|
||||
typedef function<void(bool success, const connectResult &)> connectCB;
|
||||
@ -68,28 +68,28 @@ typedef function<void(bool success)> relustCB;
|
||||
|
||||
class Device: public enable_shared_from_this<Device> {
|
||||
public:
|
||||
typedef std::shared_ptr<Device> Ptr;
|
||||
Device() {
|
||||
}
|
||||
virtual ~Device(){ disconnect([](bool bSuccess){
|
||||
});};
|
||||
typedef std::shared_ptr<Device> Ptr;
|
||||
Device() {
|
||||
}
|
||||
virtual ~Device(){ disconnect([](bool bSuccess){
|
||||
});};
|
||||
|
||||
virtual void connectDevice(const connectInfo &info, const connectCB &cb, int iTimeOut = 3)=0;
|
||||
virtual void connectDevice(const connectInfo &info, const connectCB &cb, int iTimeOut = 3)=0;
|
||||
|
||||
virtual void disconnect(const relustCB &cb) {
|
||||
}
|
||||
virtual void disconnect(const relustCB &cb) {
|
||||
}
|
||||
|
||||
virtual void addChannel(int iChnIndex, bool bMainStream = true)=0;
|
||||
virtual void addChannel(int iChnIndex, bool bMainStream = true)=0;
|
||||
|
||||
virtual void delChannel(int iChnIndex)=0;
|
||||
virtual void delChannel(int iChnIndex)=0;
|
||||
|
||||
virtual void addAllChannel(bool bMainStream = true)=0;
|
||||
virtual void addAllChannel(bool bMainStream = true)=0;
|
||||
|
||||
protected:
|
||||
void onConnected() {
|
||||
}
|
||||
void onDisconnected(bool bSelfDisconnect) {
|
||||
}
|
||||
void onConnected() {
|
||||
}
|
||||
void onDisconnected(bool bSelfDisconnect) {
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@ -97,36 +97,36 @@ protected:
|
||||
class DevChannelHK;
|
||||
class DeviceHK: public Device {
|
||||
public:
|
||||
typedef std::shared_ptr<DeviceHK> Ptr;
|
||||
DeviceHK();
|
||||
virtual ~DeviceHK();
|
||||
typedef std::shared_ptr<DeviceHK> Ptr;
|
||||
DeviceHK();
|
||||
virtual ~DeviceHK();
|
||||
|
||||
void connectDevice(const connectInfo &info, const connectCB &cb, int iTimeOut = 3) override;
|
||||
void disconnect(const relustCB &cb) override;
|
||||
void connectDevice(const connectInfo &info, const connectCB &cb, int iTimeOut = 3) override;
|
||||
void disconnect(const relustCB &cb) override;
|
||||
|
||||
void addChannel(int iChnIndex, bool bMainStream = true) override;
|
||||
void delChannel(int iChnIndex) override;
|
||||
void addAllChannel(bool bMainStream = true) override;
|
||||
void addChannel(int iChnIndex, bool bMainStream = true) override;
|
||||
void delChannel(int iChnIndex) override;
|
||||
void addAllChannel(bool bMainStream = true) override;
|
||||
private:
|
||||
map<int, std::shared_ptr<DevChannel> > m_mapChannels;
|
||||
int64_t m_i64LoginId = -1;
|
||||
NET_DVR_DEVICEINFO_V30 m_deviceInfo;
|
||||
void onConnected(LONG lUserID, LPNET_DVR_DEVICEINFO_V30 lpDeviceInfo);
|
||||
map<int, std::shared_ptr<DevChannel> > m_mapChannels;
|
||||
int64_t m_i64LoginId = -1;
|
||||
NET_DVR_DEVICEINFO_V30 m_deviceInfo;
|
||||
void onConnected(LONG lUserID, LPNET_DVR_DEVICEINFO_V30 lpDeviceInfo);
|
||||
};
|
||||
|
||||
class DevChannelHK: public DevChannel {
|
||||
public:
|
||||
typedef std::shared_ptr<DevChannel> Ptr;
|
||||
DevChannelHK(int64_t i64LoginId, const char *pcDevName, int iChn, bool bMainStream = true);
|
||||
virtual ~DevChannelHK();
|
||||
typedef std::shared_ptr<DevChannel> Ptr;
|
||||
DevChannelHK(int64_t i64LoginId, const char *pcDevName, int iChn, bool bMainStream = true);
|
||||
virtual ~DevChannelHK();
|
||||
protected:
|
||||
int64_t m_i64LoginId = -1;
|
||||
int64_t m_i64PreviewHandle = -1;
|
||||
int m_iPlayHandle = -1;
|
||||
void onPreview(DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize);
|
||||
void onGetDecData(char * pBuf, int nSize, FRAME_INFO * pFrameInfo);
|
||||
bool m_bVideoSeted = false;
|
||||
bool m_bAudioSeted = false;
|
||||
int64_t m_i64LoginId = -1;
|
||||
int64_t m_i64PreviewHandle = -1;
|
||||
int m_iPlayHandle = -1;
|
||||
void onPreview(DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize);
|
||||
void onGetDecData(char * pBuf, int nSize, FRAME_INFO * pFrameInfo);
|
||||
bool m_bVideoSeted = false;
|
||||
bool m_bAudioSeted = false;
|
||||
};
|
||||
|
||||
} /* namespace mediakit */
|
||||
|
@ -46,51 +46,51 @@ namespace mediakit {
|
||||
class H264Decoder
|
||||
{
|
||||
public:
|
||||
H264Decoder(void){
|
||||
avcodec_register_all();
|
||||
AVCodec *pCodec = avcodec_find_decoder(AV_CODEC_ID_H264);
|
||||
if (!pCodec) {
|
||||
throw std::runtime_error("未找到H264解码器");
|
||||
}
|
||||
m_pContext.reset(avcodec_alloc_context3(pCodec), [](AVCodecContext *pCtx) {
|
||||
avcodec_close(pCtx);
|
||||
avcodec_free_context(&pCtx);
|
||||
});
|
||||
if (!m_pContext) {
|
||||
throw std::runtime_error("创建解码器失败");
|
||||
}
|
||||
if (pCodec->capabilities & AV_CODEC_CAP_TRUNCATED) {
|
||||
/* we do not send complete frames */
|
||||
m_pContext->flags |= AV_CODEC_FLAG_TRUNCATED;
|
||||
}
|
||||
if(avcodec_open2(m_pContext.get(), pCodec, NULL)< 0){
|
||||
throw std::runtime_error("打开编码器失败");
|
||||
}
|
||||
m_pFrame.reset(av_frame_alloc(),[](AVFrame *pFrame){
|
||||
av_frame_free(&pFrame);
|
||||
});
|
||||
if (!m_pFrame) {
|
||||
throw std::runtime_error("创建帧缓存失败");
|
||||
}
|
||||
}
|
||||
virtual ~H264Decoder(void){}
|
||||
bool inputVideo(unsigned char* data,unsigned int dataSize,uint32_t ui32Stamp,AVFrame **ppFrame){
|
||||
AVPacket pkt;
|
||||
av_init_packet(&pkt);
|
||||
pkt.data = data;
|
||||
pkt.size = dataSize;
|
||||
pkt.dts = ui32Stamp;
|
||||
int iGotPicture ;
|
||||
auto iLen = avcodec_decode_video2(m_pContext.get(), m_pFrame.get(), &iGotPicture, &pkt);
|
||||
if (!iGotPicture || iLen < 0) {
|
||||
return false;
|
||||
}
|
||||
*ppFrame = m_pFrame.get();
|
||||
return true;
|
||||
}
|
||||
H264Decoder(void){
|
||||
avcodec_register_all();
|
||||
AVCodec *pCodec = avcodec_find_decoder(AV_CODEC_ID_H264);
|
||||
if (!pCodec) {
|
||||
throw std::runtime_error("未找到H264解码器");
|
||||
}
|
||||
m_pContext.reset(avcodec_alloc_context3(pCodec), [](AVCodecContext *pCtx) {
|
||||
avcodec_close(pCtx);
|
||||
avcodec_free_context(&pCtx);
|
||||
});
|
||||
if (!m_pContext) {
|
||||
throw std::runtime_error("创建解码器失败");
|
||||
}
|
||||
if (pCodec->capabilities & AV_CODEC_CAP_TRUNCATED) {
|
||||
/* we do not send complete frames */
|
||||
m_pContext->flags |= AV_CODEC_FLAG_TRUNCATED;
|
||||
}
|
||||
if(avcodec_open2(m_pContext.get(), pCodec, NULL)< 0){
|
||||
throw std::runtime_error("打开编码器失败");
|
||||
}
|
||||
m_pFrame.reset(av_frame_alloc(),[](AVFrame *pFrame){
|
||||
av_frame_free(&pFrame);
|
||||
});
|
||||
if (!m_pFrame) {
|
||||
throw std::runtime_error("创建帧缓存失败");
|
||||
}
|
||||
}
|
||||
virtual ~H264Decoder(void){}
|
||||
bool inputVideo(unsigned char* data,unsigned int dataSize,uint32_t ui32Stamp,AVFrame **ppFrame){
|
||||
AVPacket pkt;
|
||||
av_init_packet(&pkt);
|
||||
pkt.data = data;
|
||||
pkt.size = dataSize;
|
||||
pkt.dts = ui32Stamp;
|
||||
int iGotPicture ;
|
||||
auto iLen = avcodec_decode_video2(m_pContext.get(), m_pFrame.get(), &iGotPicture, &pkt);
|
||||
if (!iGotPicture || iLen < 0) {
|
||||
return false;
|
||||
}
|
||||
*ppFrame = m_pFrame.get();
|
||||
return true;
|
||||
}
|
||||
private:
|
||||
std::shared_ptr<AVCodecContext> m_pContext;
|
||||
std::shared_ptr<AVFrame> m_pFrame;
|
||||
std::shared_ptr<AVCodecContext> m_pContext;
|
||||
std::shared_ptr<AVFrame> m_pFrame;
|
||||
};
|
||||
|
||||
|
||||
|
@ -65,13 +65,13 @@ public:
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
|
||||
void runLoop(){
|
||||
bool flag = true;
|
||||
std::function<bool ()> task;
|
||||
SDL_Event event;
|
||||
while(flag){
|
||||
SDL_WaitEvent(&event);
|
||||
switch (event.type){
|
||||
void runLoop(){
|
||||
bool flag = true;
|
||||
std::function<bool ()> task;
|
||||
SDL_Event event;
|
||||
while(flag){
|
||||
SDL_WaitEvent(&event);
|
||||
switch (event.type){
|
||||
case REFRESH_EVENT:{
|
||||
{
|
||||
lock_guard<mutex> lck(_mtxTask);
|
||||
@ -91,17 +91,17 @@ public:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void shutdown(){
|
||||
doTask([](){return false;});
|
||||
}
|
||||
void shutdown(){
|
||||
doTask([](){return false;});
|
||||
}
|
||||
private:
|
||||
SDLDisplayerHelper(){
|
||||
};
|
||||
~SDLDisplayerHelper(){
|
||||
shutdown();
|
||||
shutdown();
|
||||
};
|
||||
private:
|
||||
std::deque<std::function<bool ()> > _taskList;
|
||||
@ -112,7 +112,7 @@ private:
|
||||
|
||||
class YuvDisplayer {
|
||||
public:
|
||||
YuvDisplayer(void *hwnd = nullptr,const char *title = "untitled"){
|
||||
YuvDisplayer(void *hwnd = nullptr,const char *title = "untitled"){
|
||||
|
||||
static onceToken token([]() {
|
||||
if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) == -1) {
|
||||
@ -131,39 +131,39 @@ public:
|
||||
SDL_Quit();
|
||||
});
|
||||
|
||||
_title = title;
|
||||
_hwnd = hwnd;
|
||||
}
|
||||
virtual ~YuvDisplayer(){
|
||||
if (_texture) {
|
||||
SDL_DestroyTexture(_texture);
|
||||
_texture = nullptr;
|
||||
}
|
||||
if (_render) {
|
||||
SDL_DestroyRenderer(_render);
|
||||
_render = nullptr;
|
||||
}
|
||||
if (_win) {
|
||||
SDL_DestroyWindow(_win);
|
||||
_win = nullptr;
|
||||
}
|
||||
}
|
||||
bool displayYUV(AVFrame *pFrame){
|
||||
if (!_win) {
|
||||
if (_hwnd) {
|
||||
_win = SDL_CreateWindowFrom(_hwnd);
|
||||
}else {
|
||||
_win = SDL_CreateWindow(_title.data(),
|
||||
SDL_WINDOWPOS_UNDEFINED,
|
||||
SDL_WINDOWPOS_UNDEFINED,
|
||||
pFrame->width,
|
||||
pFrame->height,
|
||||
SDL_WINDOW_OPENGL);
|
||||
}
|
||||
}
|
||||
if (_win && ! _render){
|
||||
_title = title;
|
||||
_hwnd = hwnd;
|
||||
}
|
||||
virtual ~YuvDisplayer(){
|
||||
if (_texture) {
|
||||
SDL_DestroyTexture(_texture);
|
||||
_texture = nullptr;
|
||||
}
|
||||
if (_render) {
|
||||
SDL_DestroyRenderer(_render);
|
||||
_render = nullptr;
|
||||
}
|
||||
if (_win) {
|
||||
SDL_DestroyWindow(_win);
|
||||
_win = nullptr;
|
||||
}
|
||||
}
|
||||
bool displayYUV(AVFrame *pFrame){
|
||||
if (!_win) {
|
||||
if (_hwnd) {
|
||||
_win = SDL_CreateWindowFrom(_hwnd);
|
||||
}else {
|
||||
_win = SDL_CreateWindow(_title.data(),
|
||||
SDL_WINDOWPOS_UNDEFINED,
|
||||
SDL_WINDOWPOS_UNDEFINED,
|
||||
pFrame->width,
|
||||
pFrame->height,
|
||||
SDL_WINDOW_OPENGL);
|
||||
}
|
||||
}
|
||||
if (_win && ! _render){
|
||||
#if 0
|
||||
SDL_RENDERER_SOFTWARE = 0x00000001, /**< The renderer is a software fallback */
|
||||
SDL_RENDERER_SOFTWARE = 0x00000001, /**< The renderer is a software fallback */
|
||||
SDL_RENDERER_ACCELERATED = 0x00000002, /**< The renderer uses hardware
|
||||
acceleration */
|
||||
SDL_RENDERER_PRESENTVSYNC = 0x00000004, /**< Present is synchronized
|
||||
@ -172,34 +172,34 @@ public:
|
||||
rendering to texture */
|
||||
#endif
|
||||
|
||||
_render = SDL_CreateRenderer(_win, -1, SDL_RENDERER_ACCELERATED);
|
||||
}
|
||||
if (_render && !_texture) {
|
||||
_texture = SDL_CreateTexture(_render, SDL_PIXELFORMAT_IYUV,
|
||||
SDL_TEXTUREACCESS_STREAMING,
|
||||
pFrame->width,
|
||||
pFrame->height);
|
||||
}
|
||||
if (_texture) {
|
||||
SDL_UpdateYUVTexture(_texture, nullptr,
|
||||
pFrame->data[0], pFrame->linesize[0],
|
||||
pFrame->data[1], pFrame->linesize[1],
|
||||
pFrame->data[2], pFrame->linesize[2]);
|
||||
_render = SDL_CreateRenderer(_win, -1, SDL_RENDERER_ACCELERATED);
|
||||
}
|
||||
if (_render && !_texture) {
|
||||
_texture = SDL_CreateTexture(_render, SDL_PIXELFORMAT_IYUV,
|
||||
SDL_TEXTUREACCESS_STREAMING,
|
||||
pFrame->width,
|
||||
pFrame->height);
|
||||
}
|
||||
if (_texture) {
|
||||
SDL_UpdateYUVTexture(_texture, nullptr,
|
||||
pFrame->data[0], pFrame->linesize[0],
|
||||
pFrame->data[1], pFrame->linesize[1],
|
||||
pFrame->data[2], pFrame->linesize[2]);
|
||||
|
||||
//SDL_UpdateTexture(_texture, nullptr, pFrame->data[0], pFrame->linesize[0]);
|
||||
SDL_RenderClear(_render);
|
||||
SDL_RenderCopy(_render, _texture, nullptr, nullptr);
|
||||
SDL_RenderPresent(_render);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//SDL_UpdateTexture(_texture, nullptr, pFrame->data[0], pFrame->linesize[0]);
|
||||
SDL_RenderClear(_render);
|
||||
SDL_RenderCopy(_render, _texture, nullptr, nullptr);
|
||||
SDL_RenderPresent(_render);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private:
|
||||
string _title;
|
||||
SDL_Window *_win = nullptr;
|
||||
SDL_Renderer *_render = nullptr;
|
||||
SDL_Texture *_texture = nullptr;
|
||||
void *_hwnd = nullptr;
|
||||
string _title;
|
||||
SDL_Window *_win = nullptr;
|
||||
SDL_Renderer *_render = nullptr;
|
||||
SDL_Texture *_texture = nullptr;
|
||||
void *_hwnd = nullptr;
|
||||
};
|
||||
|
||||
#endif /* YUVDISPLAYER_H_ */
|
@ -126,6 +126,7 @@ void process_file(const char *file,bool rm_bom){
|
||||
InfoL << (rm_bom ? "删除" : "添加") << "bom:" << file;
|
||||
}
|
||||
|
||||
/// 这个程序是为了统一添加或删除utf-8 bom头
|
||||
int main(int argc, char *argv[]) {
|
||||
CMD_main cmd_main;
|
||||
try {
|
||||
@ -148,7 +149,7 @@ int main(int argc, char *argv[]) {
|
||||
bool no_filter = filter_set.find("*") != filter_set.end();
|
||||
//设置日志
|
||||
Logger::Instance().add(std::make_shared<ConsoleChannel>());
|
||||
|
||||
path = File::absolutePath(path, "");
|
||||
for_each_file(path.data(),[&](const char *path){
|
||||
if(!no_filter){
|
||||
//开启了过滤器
|
||||
|
@ -1,18 +1,9 @@
|
||||
#include <stdlib.h>
|
||||
#include <memory.h>
|
||||
|
||||
#if !defined(_WIN32)
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#endif //!defined(_WIN32)
|
||||
|
||||
#include <memory.h>
|
||||
#include <set>
|
||||
#include "Util/CMD.h"
|
||||
#include "Util/util.h"
|
||||
#include "Util/logger.h"
|
||||
#include "Util/File.h"
|
||||
#include "Util/uv_errno.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace toolkit;
|
||||
@ -41,17 +32,40 @@ public:
|
||||
virtual ~CMD_main() {}
|
||||
};
|
||||
|
||||
vector<string> split(const string& s, const char *delim) {
|
||||
vector<string> ret;
|
||||
int last = 0;
|
||||
int index = s.find(delim, last);
|
||||
while (index != string::npos) {
|
||||
if (index - last >= 0) {
|
||||
ret.push_back(s.substr(last, index - last));
|
||||
}
|
||||
last = index + strlen(delim);
|
||||
index = s.find(delim, last);
|
||||
}
|
||||
if (!s.size() || s.size() - last >= 0) {
|
||||
ret.push_back(s.substr(last));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void process_file(const char *file) {
|
||||
auto str = File::loadFile(file);
|
||||
if (str.empty()) {
|
||||
return;
|
||||
}
|
||||
auto lines = split(str, "\n");
|
||||
auto lines = ::split(str, "\n");
|
||||
deque<string> lines_copy;
|
||||
for (auto &line : lines) {
|
||||
if(line.empty()){
|
||||
lines_copy.push_back("");
|
||||
continue;
|
||||
}
|
||||
string line_copy;
|
||||
bool flag = false;
|
||||
int i = 0;
|
||||
for (auto &ch : line) {
|
||||
++i;
|
||||
switch (ch) {
|
||||
case '\t' :
|
||||
line_copy.append(" ");
|
||||
@ -65,6 +79,7 @@ void process_file(const char *file) {
|
||||
break;
|
||||
}
|
||||
if (flag) {
|
||||
line_copy.append(line.substr(i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -75,9 +90,13 @@ void process_file(const char *file) {
|
||||
str.append(line);
|
||||
str.push_back('\n');
|
||||
}
|
||||
if(!lines_copy.empty()){
|
||||
str.pop_back();
|
||||
}
|
||||
File::saveFile(str, file);
|
||||
}
|
||||
|
||||
/// 这个程序是为了统一替换tab为4个空格
|
||||
int main(int argc, char *argv[]) {
|
||||
CMD_main cmd_main;
|
||||
try {
|
||||
@ -87,10 +106,9 @@ int main(int argc, char *argv[]) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool rm_bom = cmd_main.hasKey("rm");
|
||||
string path = cmd_main["in"];
|
||||
string filter = cmd_main["filter"];
|
||||
auto vec = split(filter, ",");
|
||||
auto vec = ::split(filter, ",");
|
||||
|
||||
set<string> filter_set;
|
||||
for (auto ext : vec) {
|
||||
@ -100,6 +118,8 @@ int main(int argc, char *argv[]) {
|
||||
bool no_filter = filter_set.find("*") != filter_set.end();
|
||||
//设置日志
|
||||
Logger::Instance().add(std::make_shared<ConsoleChannel>());
|
||||
path = File::absolutePath(path, "");
|
||||
DebugL << path;
|
||||
File::scanDir(path, [&](const string &path, bool isDir) {
|
||||
if (isDir) {
|
||||
return true;
|
||||
|
@ -50,89 +50,89 @@ const char kPort[] = HTTP_FIELD"port";
|
||||
#define HTTPS_PORT 443
|
||||
extern const char kSSLPort[] = HTTP_FIELD"sslport";
|
||||
onceToken token1([](){
|
||||
mINI::Instance()[kPort] = HTTP_PORT;
|
||||
mINI::Instance()[kSSLPort] = HTTPS_PORT;
|
||||
mINI::Instance()[kPort] = HTTP_PORT;
|
||||
mINI::Instance()[kSSLPort] = HTTPS_PORT;
|
||||
},nullptr);
|
||||
}//namespace Http
|
||||
} // namespace mediakit
|
||||
|
||||
void initEventListener(){
|
||||
static onceToken s_token([](){
|
||||
NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastHttpRequest,[](BroadcastHttpRequestArgs){
|
||||
//const Parser &parser,HttpSession::HttpResponseInvoker &invoker,bool &consumed
|
||||
if(strstr(parser.Url().data(),"/api/") != parser.Url().data()){
|
||||
return;
|
||||
}
|
||||
//url以"/api/起始,说明是http api"
|
||||
consumed = true;//该http请求已被消费
|
||||
static onceToken s_token([](){
|
||||
NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastHttpRequest,[](BroadcastHttpRequestArgs){
|
||||
//const Parser &parser,HttpSession::HttpResponseInvoker &invoker,bool &consumed
|
||||
if(strstr(parser.Url().data(),"/api/") != parser.Url().data()){
|
||||
return;
|
||||
}
|
||||
//url以"/api/起始,说明是http api"
|
||||
consumed = true;//该http请求已被消费
|
||||
|
||||
_StrPrinter printer;
|
||||
////////////////method////////////////////
|
||||
printer << "\r\nmethod:\r\n\t" << parser.Method();
|
||||
////////////////url/////////////////
|
||||
printer << "\r\nurl:\r\n\t" << parser.Url();
|
||||
////////////////protocol/////////////////
|
||||
printer << "\r\nprotocol:\r\n\t" << parser.Tail();
|
||||
///////////////args//////////////////
|
||||
printer << "\r\nargs:\r\n";
|
||||
for(auto &pr : parser.getUrlArgs()){
|
||||
printer << "\t" << pr.first << " : " << pr.second << "\r\n";
|
||||
}
|
||||
///////////////header//////////////////
|
||||
printer << "\r\nheader:\r\n";
|
||||
for(auto &pr : parser.getValues()){
|
||||
printer << "\t" << pr.first << " : " << pr.second << "\r\n";
|
||||
}
|
||||
////////////////content/////////////////
|
||||
printer << "\r\ncontent:\r\n" << parser.Content();
|
||||
auto contentOut = printer << endl;
|
||||
_StrPrinter printer;
|
||||
////////////////method////////////////////
|
||||
printer << "\r\nmethod:\r\n\t" << parser.Method();
|
||||
////////////////url/////////////////
|
||||
printer << "\r\nurl:\r\n\t" << parser.Url();
|
||||
////////////////protocol/////////////////
|
||||
printer << "\r\nprotocol:\r\n\t" << parser.Tail();
|
||||
///////////////args//////////////////
|
||||
printer << "\r\nargs:\r\n";
|
||||
for(auto &pr : parser.getUrlArgs()){
|
||||
printer << "\t" << pr.first << " : " << pr.second << "\r\n";
|
||||
}
|
||||
///////////////header//////////////////
|
||||
printer << "\r\nheader:\r\n";
|
||||
for(auto &pr : parser.getValues()){
|
||||
printer << "\t" << pr.first << " : " << pr.second << "\r\n";
|
||||
}
|
||||
////////////////content/////////////////
|
||||
printer << "\r\ncontent:\r\n" << parser.Content();
|
||||
auto contentOut = printer << endl;
|
||||
|
||||
////////////////我们测算异步回复,当然你也可以同步回复/////////////////
|
||||
EventPollerPool::Instance().getPoller()->async([invoker,contentOut](){
|
||||
HttpSession::KeyValue headerOut;
|
||||
//你可以自定义header,如果跟默认header重名,则会覆盖之
|
||||
//默认header有:Server,Connection,Date,Content-Type,Content-Length
|
||||
//请勿覆盖Connection、Content-Length键
|
||||
//键名覆盖时不区分大小写
|
||||
headerOut["TestHeader"] = "HeaderValue";
|
||||
invoker("200 OK",headerOut,contentOut);
|
||||
});
|
||||
});
|
||||
}, nullptr);
|
||||
////////////////我们测算异步回复,当然你也可以同步回复/////////////////
|
||||
EventPollerPool::Instance().getPoller()->async([invoker,contentOut](){
|
||||
HttpSession::KeyValue headerOut;
|
||||
//你可以自定义header,如果跟默认header重名,则会覆盖之
|
||||
//默认header有:Server,Connection,Date,Content-Type,Content-Length
|
||||
//请勿覆盖Connection、Content-Length键
|
||||
//键名覆盖时不区分大小写
|
||||
headerOut["TestHeader"] = "HeaderValue";
|
||||
invoker("200 OK",headerOut,contentOut);
|
||||
});
|
||||
});
|
||||
}, nullptr);
|
||||
}
|
||||
|
||||
int main(int argc,char *argv[]){
|
||||
//设置退出信号处理函数
|
||||
static semaphore sem;
|
||||
signal(SIGINT, [](int) { sem.post(); });// 设置退出信号
|
||||
//设置退出信号处理函数
|
||||
static semaphore sem;
|
||||
signal(SIGINT, [](int) { sem.post(); });// 设置退出信号
|
||||
|
||||
//设置日志
|
||||
Logger::Instance().add(std::make_shared<ConsoleChannel>());
|
||||
Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());
|
||||
//设置日志
|
||||
Logger::Instance().add(std::make_shared<ConsoleChannel>());
|
||||
Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());
|
||||
|
||||
//加载配置文件,如果配置文件不存在就创建一个
|
||||
loadIniConfig();
|
||||
initEventListener();
|
||||
//加载配置文件,如果配置文件不存在就创建一个
|
||||
loadIniConfig();
|
||||
initEventListener();
|
||||
|
||||
//加载证书,证书包含公钥和私钥
|
||||
SSL_Initor::Instance().loadCertificate((exeDir() + "ssl.p12").data());
|
||||
//信任某个自签名证书
|
||||
SSL_Initor::Instance().trustCertificate((exeDir() + "ssl.p12").data());
|
||||
//不忽略无效证书证书(例如自签名或过期证书)
|
||||
SSL_Initor::Instance().ignoreInvalidCertificate(false);
|
||||
//加载证书,证书包含公钥和私钥
|
||||
SSL_Initor::Instance().loadCertificate((exeDir() + "ssl.p12").data());
|
||||
//信任某个自签名证书
|
||||
SSL_Initor::Instance().trustCertificate((exeDir() + "ssl.p12").data());
|
||||
//不忽略无效证书证书(例如自签名或过期证书)
|
||||
SSL_Initor::Instance().ignoreInvalidCertificate(false);
|
||||
|
||||
|
||||
//开启http服务器
|
||||
TcpServer::Ptr httpSrv(new TcpServer());
|
||||
httpSrv->start<HttpSession>(mINI::Instance()[Http::kPort]);//默认80
|
||||
//开启http服务器
|
||||
TcpServer::Ptr httpSrv(new TcpServer());
|
||||
httpSrv->start<HttpSession>(mINI::Instance()[Http::kPort]);//默认80
|
||||
|
||||
//如果支持ssl,还可以开启https服务器
|
||||
TcpServer::Ptr httpsSrv(new TcpServer());
|
||||
httpsSrv->start<HttpsSession>(mINI::Instance()[Http::kSSLPort]);//默认443
|
||||
TcpServer::Ptr httpsSrv(new TcpServer());
|
||||
httpsSrv->start<HttpsSession>(mINI::Instance()[Http::kSSLPort]);//默认443
|
||||
|
||||
InfoL << "你可以在浏览器输入:http://127.0.0.1/api/my_api?key0=val0&key1=参数1" << endl;
|
||||
InfoL << "你可以在浏览器输入:http://127.0.0.1/api/my_api?key0=val0&key1=参数1" << endl;
|
||||
|
||||
sem.wait();
|
||||
return 0;
|
||||
sem.wait();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -48,92 +48,92 @@ Timer::Ptr g_timer;
|
||||
//推流失败或断开延迟2秒后重试推流
|
||||
void rePushDelay(const EventPoller::Ptr &poller,
|
||||
const string &schema,
|
||||
const string &vhost,
|
||||
const string &app,
|
||||
const string &stream,
|
||||
const string &filePath,
|
||||
const string &url) ;
|
||||
const string &vhost,
|
||||
const string &app,
|
||||
const string &stream,
|
||||
const string &filePath,
|
||||
const string &url) ;
|
||||
|
||||
//创建推流器并开始推流
|
||||
void createPusher(const EventPoller::Ptr &poller,
|
||||
const string &schema,
|
||||
const string &vhost,
|
||||
const string &app,
|
||||
const string &stream,
|
||||
const string &filePath,
|
||||
const string &url) {
|
||||
//不限制APP名,并且指定文件绝对路径
|
||||
auto src = MP4Reader::onMakeMediaSource(schema,vhost,app,stream,filePath, false);
|
||||
const string &vhost,
|
||||
const string &app,
|
||||
const string &stream,
|
||||
const string &filePath,
|
||||
const string &url) {
|
||||
//不限制APP名,并且指定文件绝对路径
|
||||
auto src = MP4Reader::onMakeMediaSource(schema,vhost,app,stream,filePath, false);
|
||||
if(!src){
|
||||
//文件不存在
|
||||
WarnL << "MP4文件不存在:" << filePath;
|
||||
return;
|
||||
}
|
||||
|
||||
//创建推流器并绑定一个MediaSource
|
||||
//创建推流器并绑定一个MediaSource
|
||||
pusher.reset(new MediaPusher(src,poller));
|
||||
//可以指定rtsp推流方式,支持tcp和udp方式,默认tcp
|
||||
//可以指定rtsp推流方式,支持tcp和udp方式,默认tcp
|
||||
// (*pusher)[Client::kRtpType] = Rtsp::RTP_UDP;
|
||||
|
||||
//设置推流中断处理逻辑
|
||||
pusher->setOnShutdown([poller,schema,vhost,app,stream,filePath, url](const SockException &ex) {
|
||||
WarnL << "Server connection is closed:" << ex.getErrCode() << " " << ex.what();
|
||||
//设置推流中断处理逻辑
|
||||
pusher->setOnShutdown([poller,schema,vhost,app,stream,filePath, url](const SockException &ex) {
|
||||
WarnL << "Server connection is closed:" << ex.getErrCode() << " " << ex.what();
|
||||
//重新推流
|
||||
rePushDelay(poller,schema,vhost,app, stream,filePath, url);
|
||||
});
|
||||
//设置发布结果处理逻辑
|
||||
pusher->setOnPublished([poller,schema,vhost,app,stream,filePath, url](const SockException &ex) {
|
||||
if (ex) {
|
||||
WarnL << "Publish fail:" << ex.getErrCode() << " " << ex.what();
|
||||
//如果发布失败,就重试
|
||||
rePushDelay(poller,schema,vhost,app, stream, filePath ,url);
|
||||
}else {
|
||||
InfoL << "Publish success,Please play with player:" << url;
|
||||
}
|
||||
});
|
||||
pusher->publish(url);
|
||||
rePushDelay(poller,schema,vhost,app, stream,filePath, url);
|
||||
});
|
||||
//设置发布结果处理逻辑
|
||||
pusher->setOnPublished([poller,schema,vhost,app,stream,filePath, url](const SockException &ex) {
|
||||
if (ex) {
|
||||
WarnL << "Publish fail:" << ex.getErrCode() << " " << ex.what();
|
||||
//如果发布失败,就重试
|
||||
rePushDelay(poller,schema,vhost,app, stream, filePath ,url);
|
||||
}else {
|
||||
InfoL << "Publish success,Please play with player:" << url;
|
||||
}
|
||||
});
|
||||
pusher->publish(url);
|
||||
}
|
||||
|
||||
//推流失败或断开延迟2秒后重试推流
|
||||
void rePushDelay(const EventPoller::Ptr &poller,
|
||||
const string &schema,
|
||||
const string &vhost,
|
||||
const string &app,
|
||||
const string &stream,
|
||||
const string &filePath,
|
||||
const string &url) {
|
||||
g_timer = std::make_shared<Timer>(2,[poller,schema,vhost,app, stream, filePath,url]() {
|
||||
InfoL << "Re-Publishing...";
|
||||
//重新推流
|
||||
createPusher(poller,schema,vhost,app, stream, filePath,url);
|
||||
//此任务不重复
|
||||
return false;
|
||||
}, poller);
|
||||
const string &vhost,
|
||||
const string &app,
|
||||
const string &stream,
|
||||
const string &filePath,
|
||||
const string &url) {
|
||||
g_timer = std::make_shared<Timer>(2,[poller,schema,vhost,app, stream, filePath,url]() {
|
||||
InfoL << "Re-Publishing...";
|
||||
//重新推流
|
||||
createPusher(poller,schema,vhost,app, stream, filePath,url);
|
||||
//此任务不重复
|
||||
return false;
|
||||
}, poller);
|
||||
}
|
||||
|
||||
//这里才是真正执行main函数,你可以把函数名(domain)改成main,然后就可以输入自定义url了
|
||||
int domain(const string & filePath,const string & pushUrl){
|
||||
//设置日志
|
||||
Logger::Instance().add(std::make_shared<ConsoleChannel>());
|
||||
Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());
|
||||
//设置日志
|
||||
Logger::Instance().add(std::make_shared<ConsoleChannel>());
|
||||
Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());
|
||||
auto poller = EventPollerPool::Instance().getPoller();
|
||||
//vhost/app/stream可以随便自己填,现在不限制app应用名了
|
||||
createPusher(poller,FindField(pushUrl.data(), nullptr,"://"),DEFAULT_VHOST,"live","stream",filePath,pushUrl);
|
||||
|
||||
//设置退出信号处理函数
|
||||
static semaphore sem;
|
||||
signal(SIGINT, [](int) { sem.post(); });// 设置退出信号
|
||||
//设置退出信号处理函数
|
||||
static semaphore sem;
|
||||
signal(SIGINT, [](int) { sem.post(); });// 设置退出信号
|
||||
sem.wait();
|
||||
pusher.reset();
|
||||
g_timer.reset();
|
||||
return 0;
|
||||
pusher.reset();
|
||||
g_timer.reset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main(int argc,char *argv[]){
|
||||
//可以使用test_server生成的mp4文件
|
||||
//文件使用绝对路径,推流url支持rtsp和rtmp
|
||||
//文件使用绝对路径,推流url支持rtsp和rtmp
|
||||
return domain("/Users/xzl/Desktop/bear-1280x720-long.mp4","rtsp://127.0.0.1/live/rtsp_push");
|
||||
}
|
||||
|
||||
|
@ -357,6 +357,6 @@ int main(int argc,char *argv[]) {
|
||||
Recorder::stopAll();
|
||||
lock_guard<mutex> lck(s_mtxFlvRecorder);
|
||||
s_mapFlvRecorder.clear();
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user