diff --git a/CMakeLists.txt b/CMakeLists.txt index de0ca74..c80b1d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,59 +1,84 @@ -cmake_minimum_required(VERSION 3.5.0) -project(RtspServer) +project(RTSPServer) -include_directories("${CMAKE_CURRENT_SOURCE_DIR}/src/net/" "${CMAKE_CURRENT_SOURCE_DIR}/src/xop/") +# Cmake needs some version information +CMAKE_MINIMUM_REQUIRED(VERSION 3.16) -file(GLOB rtspserver_sources - "${CMAKE_CURRENT_SOURCE_DIR}/src/net/*.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/src/net/*.h" - "${CMAKE_CURRENT_SOURCE_DIR}/src/xop/*.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/src/xop/*.h" - ) +set(CMAKE_CXX_STANDARD 14) + +SET(ARCHSTR ${ANDROID_ABI}) +message(STATUS "Architecture: ${ARCHSTR}") + +set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "Configs" FORCE) +#======================================= +# USER DEFINED VARIABLES + +IF(NOT CMAKE_BUILD_TYPE) + MESSAGE(STATUS "CMAKE_BUILD_TYPE was not specified. Building Debug Version.") + SET(CMAKE_BUILD_TYPE Debug) +ENDIF(NOT CMAKE_BUILD_TYPE) + +#========================================================== + +INCLUDE_DIRECTORIES( + ${CMAKE_CURRENT_SOURCE_DIR}/src/ + ${CMAKE_CURRENT_SOURCE_DIR}/src/3rdpart +) + +FILE(GLOB NetSources ${CMAKE_CURRENT_SOURCE_DIR}/src/net/*.cpp) +FILE(GLOB NetHeaders ${CMAKE_CURRENT_SOURCE_DIR}/src/net/*.h) +ADD_LIBRARY(net SHARED ${NetSources} ${NetHeaders}) +SET_TARGET_PROPERTIES(net PROPERTIES + DEBUG_POSTFIX d + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lib/${ARCHSTR}" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lib/${ARCHSTR}" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin/${ARCHSTR}" +) -set(RTSPSERVER_SOURCES - ${CMAKE_CURRENT_SOURCE_DIR}/src/net/Acceptor.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/net/BufferReader.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/net/BufferWriter.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/net/EpollTaskScheduler.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/net/EventLoop.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/net/Logger.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/net/MemoryManager.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/net/NetInterface.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/net/Pipe.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/net/SelectTaskScheduler.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/net/SocketUtil.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/net/Socket.h - ${CMAKE_CURRENT_SOURCE_DIR}/src/net/TcpConnection.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/net/TcpServer.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/net/Timer.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/net/Timestamp.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/net/TaskScheduler.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/net/TcpSocket.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/xop/AACSource.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/xop/DigestAuthentication.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/xop/G711ASource.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/xop/H264Source.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/xop/MediaSession.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/xop/H264Parser.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/xop/H265Source.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/xop/RtpConnection.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/xop/RtspConnection.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/xop/RtspMessage.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/xop/RtspPusher.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/xop/RtspServer.cpp +FILE(GLOB XopSources ${CMAKE_CURRENT_SOURCE_DIR}/src/xop/*.cpp) +FILE(GLOB XopHeaders ${CMAKE_CURRENT_SOURCE_DIR}/src/xop/*.h) +ADD_LIBRARY(xop SHARED ${XopSources} ${XopHeaders}) +ADD_DEPENDENCIES(xop net) +TARGET_LINK_LIBRARIES(xop net) +SET_TARGET_PROPERTIES(xop PROPERTIES + DEBUG_POSTFIX d + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lib/${ARCHSTR}" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lib/${ARCHSTR}" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin/${ARCHSTR}" ) -add_library(RtspServer STATIC ${rtspserver_sources}) -add_library(RtspServer::RtspServer ALIAS RtspServer) +IF(ANDROID) + TARGET_LINK_LIBRARIES(xop log) + TARGET_LINK_LIBRARIES(net log) +ENDIF(ANDROID) -target_include_directories(RtspServer - PUBLIC - "${CMAKE_CURRENT_SOURCE_DIR}/src/" - "${CMAKE_CURRENT_SOURCE_DIR}/src/xop/" - "${CMAKE_CURRENT_SOURCE_DIR}/src/3rdpart/" - ) +IF(BUILD_EXECUTABLE) + ADD_EXECUTABLE(rtsp_pusher ${CMAKE_CURRENT_SOURCE_DIR}/example/rtsp_pusher.cpp ) + ADD_DEPENDENCIES(rtsp_pusher net xop) + TARGET_LINK_LIBRARIES(rtsp_pusher net xop) + SET_TARGET_PROPERTIES(rtsp_pusher PROPERTIES + DEBUG_POSTFIX d + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lib/${ARCHSTR}" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lib/${ARCHSTR}" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin/${ARCHSTR}" + ) -target_link_libraries(RtspServer - PRIVATE - zm-dependency-interface) + ADD_EXECUTABLE(rtsp_h264_file ${CMAKE_CURRENT_SOURCE_DIR}/example/rtsp_h264_file.cpp ) + ADD_DEPENDENCIES(rtsp_h264_file net xop) + TARGET_LINK_LIBRARIES(rtsp_h264_file net xop) + SET_TARGET_PROPERTIES(rtsp_h264_file PROPERTIES + DEBUG_POSTFIX d + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lib/${ARCHSTR}" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lib/${ARCHSTR}" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin/${ARCHSTR}" + ) + ADD_EXECUTABLE(rtsp_server ${CMAKE_CURRENT_SOURCE_DIR}/example/rtsp_server.cpp ) + ADD_DEPENDENCIES(rtsp_server net xop) + TARGET_LINK_LIBRARIES(rtsp_server net xop) + SET_TARGET_PROPERTIES(rtsp_server PROPERTIES + DEBUG_POSTFIX d + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lib/${ARCHSTR}" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lib/${ARCHSTR}" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin/${ARCHSTR}" + ) +ENDIF(BUILD_EXECUTABLE) diff --git a/example/rtsp_h264_file.cpp b/example/rtsp_h264_file.cpp index 5b07ad4..f95874c 100644 --- a/example/rtsp_h264_file.cpp +++ b/example/rtsp_h264_file.cpp @@ -20,7 +20,7 @@ class H264File { return (m_file != NULL); } int ReadFrame(char* in_buf, int in_buf_size, bool* end); - + private: FILE *m_file = NULL; char *m_buf = NULL; @@ -32,7 +32,7 @@ class H264File void SendFrameThread(xop::RtspServer* rtsp_server, xop::MediaSessionId session_id, H264File* h264_file); int main(int argc, char **argv) -{ +{ if(argc != 2) { printf("Usage: %s test.h264 \n", argv[0]); return 0; @@ -48,7 +48,7 @@ int main(int argc, char **argv) std::string ip = "127.0.0.1"; std::string port = "554"; std::string rtsp_url = "rtsp://" + ip + ":" + port + "/" + suffix; - + std::shared_ptr event_loop(new xop::EventLoop()); std::shared_ptr server = xop::RtspServer::Create(event_loop.get()); @@ -61,21 +61,21 @@ int main(int argc, char **argv) server->SetAuthConfig("-_-", "admin", "12345"); #endif - xop::MediaSession *session = xop::MediaSession::CreateNew("live"); - session->AddSource(xop::channel_0, xop::H264Source::CreateNew()); - //session->StartMulticast(); + xop::MediaSession *session = xop::MediaSession::CreateNew("live"); + session->AddSource(xop::channel_0, xop::H264Source::CreateNew()); + //session->StartMulticast(); session->AddNotifyConnectedCallback([] (xop::MediaSessionId sessionId, std::string peer_ip, uint16_t peer_port){ printf("RTSP client connect, ip=%s, port=%hu \n", peer_ip.c_str(), peer_port); }); - + session->AddNotifyDisconnectedCallback([](xop::MediaSessionId sessionId, std::string peer_ip, uint16_t peer_port) { printf("RTSP client disconnect, ip=%s, port=%hu \n", peer_ip.c_str(), peer_port); }); xop::MediaSessionId session_id = server->AddSession(session); - + std::thread t1(SendFrameThread, server.get(), session_id, &h264_file); - t1.detach(); + t1.detach(); std::cout << "Play URL: " << rtsp_url << std::endl; @@ -88,7 +88,7 @@ int main(int argc, char **argv) } void SendFrameThread(xop::RtspServer* rtsp_server, xop::MediaSessionId session_id, H264File* h264_file) -{ +{ int buf_size = 2000000; std::unique_ptr frame_buf(new uint8_t[buf_size]); @@ -96,19 +96,20 @@ void SendFrameThread(xop::RtspServer* rtsp_server, xop::MediaSessionId session_i bool end_of_frame = false; int frame_size = h264_file->ReadFrame((char*)frame_buf.get(), buf_size, &end_of_frame); if(frame_size > 0) { - xop::AVFrame videoFrame = {0}; - videoFrame.type = 0; - videoFrame.size = frame_size; + xop::AVFrame videoFrame; + videoFrame.type = 0; videoFrame.timestamp = xop::H264Source::GetTimestamp(); - videoFrame.buffer.reset(new uint8_t[videoFrame.size]); - memcpy(videoFrame.buffer.get(), frame_buf.get(), videoFrame.size); + //videoFrame.buffer.reset(new uint8_t[videoFrame.size]); + //memcpy(videoFrame.buffer.get(), frame_buf.get(), videoFrame.size); + videoFrame.buffer.resize(frame_size); + memcpy(videoFrame.buffer.data(), frame_buf.get(), frame_size); rtsp_server->PushFrame(session_id, xop::channel_0, videoFrame); } else { break; } - - xop::Timer::Sleep(40); + + xop::Timer::Sleep(40); }; } @@ -125,8 +126,12 @@ H264File::~H264File() bool H264File::Open(const char *path) { +#ifdef WIN32 + fopen_s(&m_file, path, "rb"); +#else m_file = fopen(path, "rb"); - if(m_file == NULL) { +#endif + if(m_file == NULL) { return false; } @@ -151,11 +156,11 @@ int H264File::ReadFrame(char* in_buf, int in_buf_size, bool* end) int bytes_read = (int)fread(m_buf, 1, m_buf_size, m_file); if(bytes_read == 0) { - fseek(m_file, 0, SEEK_SET); + fseek(m_file, 0, SEEK_SET); m_count = 0; m_bytes_used = 0; bytes_read = (int)fread(m_buf, 1, m_buf_size, m_file); - if(bytes_read == 0) { + if(bytes_read == 0) { this->Close(); return -1; } @@ -175,8 +180,8 @@ int H264File::ReadFrame(char* in_buf, int in_buf_size, bool* end) else { continue; } - - if (((m_buf[i+start_code]&0x1F) == 0x5 || (m_buf[i+start_code]&0x1F) == 0x1) + + if (((m_buf[i+start_code]&0x1F) == 0x5 || (m_buf[i+start_code]&0x1F) == 0x1) && ((m_buf[i+start_code+1]&0x80) == 0x80)) { is_find_start = true; i += 4; @@ -195,9 +200,9 @@ int H264File::ReadFrame(char* in_buf, int in_buf_size, bool* end) else { continue; } - - if (((m_buf[i+start_code]&0x1F) == 0x7) || ((m_buf[i+start_code]&0x1F) == 0x8) - || ((m_buf[i+start_code]&0x1F) == 0x6)|| (((m_buf[i+start_code]&0x1F) == 0x5 + + if (((m_buf[i+start_code]&0x1F) == 0x7) || ((m_buf[i+start_code]&0x1F) == 0x8) + || ((m_buf[i+start_code]&0x1F) == 0x6)|| (((m_buf[i+start_code]&0x1F) == 0x5 || (m_buf[i+start_code]&0x1F) == 0x1) &&((m_buf[i+start_code+1]&0x80) == 0x80))) { is_find_end = true; break; @@ -205,7 +210,7 @@ int H264File::ReadFrame(char* in_buf, int in_buf_size, bool* end) } bool flag = false; - if(is_find_start && !is_find_end && m_count>0) { + if(is_find_start && !is_find_end && m_count>0) { flag = is_find_end = true; i = bytes_read; *end = true; @@ -217,7 +222,7 @@ int H264File::ReadFrame(char* in_buf, int in_buf_size, bool* end) } int size = (i<=in_buf_size ? i : in_buf_size); - memcpy(in_buf, m_buf, size); + memcpy(in_buf, m_buf, size); if(!flag) { m_count += 1; @@ -231,5 +236,3 @@ int H264File::ReadFrame(char* in_buf, int in_buf_size, bool* end) fseek(m_file, m_bytes_used, SEEK_SET); return size; } - - diff --git a/example/rtsp_pusher.cpp b/example/rtsp_pusher.cpp index 1f15118..18f3b66 100755 --- a/example/rtsp_pusher.cpp +++ b/example/rtsp_pusher.cpp @@ -1,35 +1,116 @@ -// RTSP Pusher +// RTSP Pusher #include "xop/RtspPusher.h" +#include "xop/DigestAuthenticator.h" #include "net/Timer.h" #include #include #include #include -#define PUSH_TEST "rtsp://10.11.165.203:554/test" +#include "md5/md5.hpp" -void snedFrameThread(xop::RtspPusher* rtspPusher); +// #define PUSH_TEST "rtsp://10.11.165.203:554/test" + +class H264File +{ +public: + H264File(int buf_size=500000); + ~H264File(); + + bool Open(const char *path); + void Close(); + + bool IsOpened() const + { return (m_file != NULL); } + + int ReadFrame(char* in_buf, int in_buf_size, bool* end); + +private: + FILE *m_file = NULL; + char *m_buf = NULL; + int m_buf_size = 0; + int m_bytes_used = 0; + int m_count = 0; +}; + +void sendFrameThread(xop::RtspPusher* rtspPusher, H264File* h264_file); int main(int argc, char **argv) -{ - std::shared_ptr event_loop(new xop::EventLoop()); +{ + if(argc != 3) { + printf("Usage: %s \n", argv[0]); + return 0; + } + + H264File h264_file; + if(!h264_file.Open(argv[1])) { + printf("Open %s failed.\n", argv[1]); + return 0; + } + + std::shared_ptr event_loop(new xop::EventLoop()); std::shared_ptr rtsp_pusher = xop::RtspPusher::Create(event_loop.get()); - xop::MediaSession *session = xop::MediaSession::CreateNew(); - session->AddSource(xop::channel_0, xop::H264Source::CreateNew()); - session->AddSource(xop::channel_1, xop::AACSource::CreateNew(44100, 2, false)); + xop::MediaSession *session = xop::MediaSession::CreateNew(); + session->AddSource(xop::channel_0, xop::H264Source::CreateNew()); + // session->AddSource(xop::channel_1, xop::AACSource::CreateNew(44100, 2, false)); rtsp_pusher->AddSession(session); - if (rtsp_pusher->OpenUrl(PUSH_TEST, 3000) < 0) { - std::cout << "Open " << PUSH_TEST << " failed." << std::endl; +/* + std::string serverIP("127.0.0.1"); + uint16_t port = 8009; + std::string channel("ar4-stream"); +*/ + std::string username(""); + std::string password(""); + + std::string rtspaddr(argv[2]); + size_t it = rtspaddr.find("rtsp://") + strlen("rtsp://"); + std::string tstr = rtspaddr.substr(it,40); // should be within the first 40 chars after rtsp:// + // std::cout << tstr << std::endl; + size_t it2 = tstr.find("@"); + if(it2 != tstr.npos) { + size_t it2 = rtspaddr.find(":",it); + std::cout << "==== user: " << rtspaddr.substr(it, it2-it) << std::endl; + username = rtspaddr.substr(it, it2-it); + size_t it3 = rtspaddr.find("@",it2); + std::cout << "==== pass: " << rtspaddr.substr(it2+1, it3-it2-1) << std::endl; + password = rtspaddr.substr(it2+1, it3-it2-1); + it = it3+1; + } + it2 = rtspaddr.find(":", it); + std::cout << "==== server: " << rtspaddr.substr(it, it2-it) << std::endl; + std::string serverIP = rtspaddr.substr(it, it2-it); + size_t it3 = rtspaddr.find("/",it2+1); + std::cout << "==== port: " << rtspaddr.substr(it2+1, it3-it2-1) << std::endl; + uint16_t port = atoi(rtspaddr.substr(it2+1, it3-it2-1).c_str()); + std::string channel(rtspaddr.substr(it3+1)); + std::cout << "==== channel: " << channel << std::endl; + +/* + std::string serverIP("192.168.0.32"); + uint16_t port = 8554; + std::string channel("live"); + std::string username(""); + std::string password(""); + */ + if(username.size() && password.size()) + { + std::shared_ptr auth = std::shared_ptr(new xop::DigestAuthenticator("-_-", username, password)); // "visocon.rtsp" + rtsp_pusher->SetAuthenticator(auth); + } + + std::string connectString = "rtsp://" + serverIP + ":" + std::to_string(port) + "/" + channel; + if (rtsp_pusher->OpenUrl(connectString.c_str(), 3000) < 0) { + std::cout << "Open " << connectString.c_str() << " failed." << std::endl; getchar(); return 0; } - std::cout << "Push stream to " << PUSH_TEST << " ..." << std::endl; - - std::thread thread(snedFrameThread, rtsp_pusher.get()); + std::cout << "Push stream to " << connectString.c_str() << " ..." << std::endl; + + std::thread thread(sendFrameThread, rtsp_pusher.get(),&h264_file); thread.detach(); while (1) { @@ -40,36 +121,168 @@ int main(int argc, char **argv) return 0; } -void snedFrameThread(xop::RtspPusher* rtsp_pusher) -{ +void sendFrameThread(xop::RtspPusher* rtsp_pusher, H264File* h264_file) +{ while(rtsp_pusher->IsConnected()) - { - { - /* - //获取一帧 H264, 打包 - xop::AVFrame videoFrame = {0}; - //videoFrame.size = video frame size; // 视频帧大小 - videoFrame.timestamp = xop::H264Source::GetTimestamp(); // 时间戳, 建议使用编码器提供的时间戳 - videoFrame.buffer.reset(new uint8_t[videoFrame.size]); - //memcpy(videoFrame.buffer.get(), video frame data, videoFrame.size); - - rtsp_pusher->PushFrame(xop::channel_0, videoFrame); //推流到服务器, 接口线程安全 - */ + { + int buf_size = 2000000; + std::unique_ptr frame_buf(new uint8_t[buf_size]); + + bool end_of_frame = false; + int frame_size = h264_file->ReadFrame((char*)frame_buf.get(), buf_size, &end_of_frame); + if(frame_size > 0) { + xop::AVFrame videoFrame; + videoFrame.type = 0; + auto time_point = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + int64_t tp = (int64_t)((time_point.time_since_epoch().count() + 500) / 1000 * 90 ); + videoFrame.timestamp = xop::H264Source::GetTimestamp(); // tp; + //videoFrame.buffer.reset(new uint8_t[videoFrame.size]); + //memcpy(videoFrame.buffer.get(), frame_buf.get(), videoFrame.size); + videoFrame.buffer.resize(frame_size); + memcpy(videoFrame.buffer.data(), frame_buf.get(), frame_size); + rtsp_pusher->PushFrame(xop::channel_0, videoFrame); } - - { + else { + break; + } + + { /* //获取一帧 AAC, 打包 xop::AVFrame audioFrame = {0}; - //audioFrame.size = audio frame size; // 音频帧大小 + //audioFrame.size = audio frame size; // 音频帧大小 audioFrame.timestamp = xop::AACSource::GetTimestamp(44100); // 时间戳 audioFrame.buffer.reset(new uint8_t[audioFrame.size]); //memcpy(audioFrame.buffer.get(), audio frame data, audioFrame.size); rtsp_pusher->PushFrame(xop::channel_1, audioFrame); //推流到服务器, 接口线程安全 */ - } + } + + xop::Timer::Sleep(40); + } +} + +H264File::H264File(int buf_size) + : m_buf_size(buf_size) +{ + m_buf = new char[m_buf_size]; +} + +H264File::~H264File() +{ + delete m_buf; +} + +bool H264File::Open(const char *path) +{ +#if WIN32 + errno_t err = fopen_s(&m_file, path, "rb"); +#else + m_file = fopen(path, "rb"); +#endif + if(m_file == NULL) { + return false; + } + + return true; +} + +void H264File::Close() +{ + if(m_file) { + fclose(m_file); + m_file = NULL; + m_count = 0; + m_bytes_used = 0; + } +} + +int H264File::ReadFrame(char* in_buf, int in_buf_size, bool* end) +{ + if(m_file == NULL) { + return -1; + } + + int bytes_read = (int)fread(m_buf, 1, m_buf_size, m_file); + if(bytes_read == 0) { + fseek(m_file, 0, SEEK_SET); + m_count = 0; + m_bytes_used = 0; + bytes_read = (int)fread(m_buf, 1, m_buf_size, m_file); + if(bytes_read == 0) { + this->Close(); + return -1; + } + } + + bool is_find_start = false, is_find_end = false; + int i = 0, start_code = 3; + *end = false; + + for (i=0; i0) { + flag = is_find_end = true; + i = bytes_read; + *end = true; + } + + if(!is_find_start || !is_find_end) { + this->Close(); + return -1; + } + + int size = (i<=in_buf_size ? i : in_buf_size); + memcpy(in_buf, m_buf, size); + + if(!flag) { + m_count += 1; + m_bytes_used += i; + } + else { + m_count = 0; + m_bytes_used = 0; + } + + fseek(m_file, m_bytes_used, SEEK_SET); + return size; } diff --git a/example/rtsp_server.cpp b/example/rtsp_server.cpp index d055a8c..ddebe2a 100755 --- a/example/rtsp_server.cpp +++ b/example/rtsp_server.cpp @@ -1,4 +1,4 @@ -// RTSP Server +// RTSP Server #include "xop/RtspServer.h" #include "net/Timer.h" @@ -7,17 +7,50 @@ #include #include -void SendFrameThread(xop::RtspServer* rtsp_server, xop::MediaSessionId session_id, int& clients); +class H264File +{ +public: + H264File(int buf_size=500000); + ~H264File(); + + bool Open(const char *path); + void Close(); + + bool IsOpened() const + { return (m_file != NULL); } + + int ReadFrame(char* in_buf, int in_buf_size, bool* end); + +private: + FILE *m_file = NULL; + char *m_buf = NULL; + int m_buf_size = 0; + int m_bytes_used = 0; + int m_count = 0; +}; + +void SendFrameThread(xop::RtspServer* rtsp_server, xop::MediaSessionId session_id, H264File* file, int& clients); int main(int argc, char **argv) -{ +{ + if(argc != 2) { + printf("Usage: %s test.h264 \n", argv[0]); + return 0; + } + int clients = 0; std::string ip = "0.0.0.0"; - std::string rtsp_url = "rtsp://127.0.0.1:554/live"; + std::string rtsp_url = "rtsp://127.0.0.1:8554/live"; + H264File h264_file; + if(!h264_file.Open(argv[1])) { + printf("Open %s failed.\n", argv[1]); + return 0; + } + std::shared_ptr event_loop(new xop::EventLoop()); std::shared_ptr server = xop::RtspServer::Create(event_loop.get()); - if (!server->Start(ip, 554)) { + if (!server->Start(ip, 8554)) { return -1; } @@ -27,15 +60,17 @@ int main(int argc, char **argv) xop::MediaSession *session = xop::MediaSession::CreateNew("live"); // url: rtsp://ip/live session->AddSource(xop::channel_0, xop::H264Source::CreateNew()); - session->AddSource(xop::channel_1, xop::AACSource::CreateNew(44100,2)); + // session->AddSource(xop::channel_1, xop::AACSource::CreateNew(44100,2)); // session->startMulticast(); /* 开启组播(ip,端口随机生成), 默认使用 RTP_OVER_UDP, RTP_OVER_RTSP */ - session->AddNotifyConnectedCallback([] (xop::MediaSessionId sessionId, std::string peer_ip, uint16_t peer_port){ + session->AddNotifyConnectedCallback([&clients] (xop::MediaSessionId sessionId, std::string peer_ip, uint16_t peer_port){ printf("RTSP client connect, ip=%s, port=%hu \n", peer_ip.c_str(), peer_port); + clients++; }); - session->AddNotifyDisconnectedCallback([](xop::MediaSessionId sessionId, std::string peer_ip, uint16_t peer_port) { + session->AddNotifyDisconnectedCallback([&clients](xop::MediaSessionId sessionId, std::string peer_ip, uint16_t peer_port) { printf("RTSP client disconnect, ip=%s, port=%hu \n", peer_ip.c_str(), peer_port); + clients--; }); std::cout << "URL: " << rtsp_url << std::endl; @@ -43,7 +78,7 @@ int main(int argc, char **argv) xop::MediaSessionId session_id = server->AddSession(session); //server->removeMeidaSession(session_id); /* 取消会话, 接口线程安全 */ - std::thread thread(SendFrameThread, server.get(), session_id, std::ref(clients)); + std::thread thread(SendFrameThread, server.get(), session_id, &h264_file, std::ref(clients)); thread.detach(); while(1) { @@ -54,12 +89,34 @@ int main(int argc, char **argv) return 0; } -void SendFrameThread(xop::RtspServer* rtsp_server, xop::MediaSessionId session_id, int& clients) -{ +void SendFrameThread(xop::RtspServer* rtsp_server, xop::MediaSessionId session_id, H264File* h264_file, int& clients) +{ + int buf_size = 2000000; + std::unique_ptr frame_buf(new uint8_t[buf_size]); + while(1) { if(clients > 0) /* 会话有客户端在线, 发送音视频数据 */ { + { + bool end_of_frame = false; + int frame_size = h264_file->ReadFrame((char*)frame_buf.get(), buf_size, &end_of_frame); + if(frame_size > 0) { + xop::AVFrame videoFrame; + videoFrame.type = 0; + videoFrame.timestamp = xop::H264Source::GetTimestamp(); + //videoFrame.buffer.reset(new uint8_t[videoFrame.size]); + //memcpy(videoFrame.buffer.get(), frame_buf.get(), videoFrame.size); + videoFrame.buffer.resize(frame_size); + memcpy(videoFrame.buffer.data(), frame_buf.get(), frame_size); + rtsp_server->PushFrame(session_id, xop::channel_0, videoFrame); + } + else { + break; + } + + xop::Timer::Sleep(40); + } { /* //获取一帧 H264, 打包 @@ -92,3 +149,127 @@ void SendFrameThread(xop::RtspServer* rtsp_server, xop::MediaSessionId session_i xop::Timer::Sleep(1); /* 实际使用需要根据帧率计算延时! */ } } + +H264File::H264File(int buf_size) + : m_buf_size(buf_size) +{ + m_buf = new char[m_buf_size]; +} + +H264File::~H264File() +{ + delete m_buf; +} + +bool H264File::Open(const char *path) +{ +#if WIN32 + errno_t err = fopen_s(&m_file, path, "rb"); +#else + m_file = fopen(path, "rb"); +#endif + if(m_file == NULL) { + return false; + } + + return true; +} + +void H264File::Close() +{ + if(m_file) { + fclose(m_file); + m_file = NULL; + m_count = 0; + m_bytes_used = 0; + } +} + +int H264File::ReadFrame(char* in_buf, int in_buf_size, bool* end) +{ + if(m_file == NULL) { + return -1; + } + + int bytes_read = (int)fread(m_buf, 1, m_buf_size, m_file); + if(bytes_read == 0) { + fseek(m_file, 0, SEEK_SET); + m_count = 0; + m_bytes_used = 0; + bytes_read = (int)fread(m_buf, 1, m_buf_size, m_file); + if(bytes_read == 0) { + this->Close(); + return -1; + } + } + + bool is_find_start = false, is_find_end = false; + int i = 0, start_code = 3; + *end = false; + + for (i=0; i0) { + flag = is_find_end = true; + i = bytes_read; + *end = true; + } + + if(!is_find_start || !is_find_end) { + this->Close(); + return -1; + } + + int size = (i<=in_buf_size ? i : in_buf_size); + memcpy(in_buf, m_buf, size); + + if(!flag) { + m_count += 1; + m_bytes_used += i; + } + else { + m_count = 0; + m_bytes_used = 0; + } + + fseek(m_file, m_bytes_used, SEEK_SET); + return size; +} diff --git a/src/net/BufferReader.cpp b/src/net/BufferReader.cpp index b94ad98..deb8594 100644 --- a/src/net/BufferReader.cpp +++ b/src/net/BufferReader.cpp @@ -48,8 +48,6 @@ uint16_t xop::ReadUint16LE(char* data) return value; } -const char BufferReader::kCRLF[] = "\r\n"; - BufferReader::BufferReader(uint32_t initial_size) { buffer_.resize(initial_size); diff --git a/src/net/BufferReader.h b/src/net/BufferReader.h index fe91437..71501af 100644 --- a/src/net/BufferReader.h +++ b/src/net/BufferReader.h @@ -21,7 +21,7 @@ uint32_t ReadUint24LE(char* data); uint16_t ReadUint16BE(char* data); uint16_t ReadUint16LE(char* data); -class BufferReader +class DLL_API BufferReader { public: BufferReader(uint32_t initial_size = 2048); @@ -40,11 +40,13 @@ class BufferReader { return Begin() + reader_index_; } const char* FindFirstCrlf() const { + char kCRLF[] = "\r\n"; const char* crlf = std::search(Peek(), BeginWrite(), kCRLF, kCRLF+2); return crlf == BeginWrite() ? nullptr : crlf; } const char* FindLastCrlf() const { + char kCRLF[] = "\r\n"; const char* crlf = std::find_end(Peek(), BeginWrite(), kCRLF, kCRLF+2); return crlf == BeginWrite() ? nullptr : crlf; } @@ -84,6 +86,7 @@ class BufferReader { return (uint32_t)buffer_.size(); } private: + char* Begin() { return &*buffer_.begin(); } @@ -100,7 +103,6 @@ class BufferReader size_t reader_index_ = 0; size_t writer_index_ = 0; - static const char kCRLF[]; static const uint32_t MAX_BYTES_PER_READ = 4096; static const uint32_t MAX_BUFFER_SIZE = 1024 * 100000; }; diff --git a/src/net/BufferWriter.cpp b/src/net/BufferWriter.cpp index 941d337..a2e278f 100644 --- a/src/net/BufferWriter.cpp +++ b/src/net/BufferWriter.cpp @@ -49,22 +49,22 @@ void xop::WriteUint16LE(char* p, uint16_t value) p[1] = value >> 8; } -BufferWriter::BufferWriter(int capacity) +BufferWriter::BufferWriter(int capacity) : max_queue_length_(capacity) { - -} + +} bool BufferWriter::Append(std::shared_ptr data, uint32_t size, uint32_t index) { if (size <= index) { return false; } - + if ((int)buffer_.size() >= max_queue_length_) { return false; } - + Packet pkt = { data, size, index }; buffer_.emplace(std::move(pkt)); return true; @@ -75,11 +75,11 @@ bool BufferWriter::Append(const char* data, uint32_t size, uint32_t index) if (size <= index) { return false; } - + if ((int)buffer_.size() >= max_queue_length_) { return false; } - + Packet pkt; pkt.data.reset(new char[size+512]); memcpy(pkt.data.get(), data, size); @@ -90,11 +90,11 @@ bool BufferWriter::Append(const char* data, uint32_t size, uint32_t index) } int BufferWriter::Send(SOCKET sockfd, int timeout) -{ +{ if (timeout > 0) { - SocketUtil::SetBlock(sockfd, timeout); + SocketUtil::SetBlock(sockfd, timeout); } - + int ret = 0; int count = 1; @@ -103,7 +103,7 @@ int BufferWriter::Send(SOCKET sockfd, int timeout) if (buffer_.empty()) { return 0; } - + count -= 1; Packet &pkt = buffer_.front(); ret = ::send(sockfd, pkt.data.get() + pkt.writeIndex, pkt.size - pkt.writeIndex, 0); @@ -115,8 +115,8 @@ int BufferWriter::Send(SOCKET sockfd, int timeout) } } else if (ret < 0) { -#if defined(__linux) || defined(__linux__) || defined(__FreeBSD__) - if (errno == EINTR || errno == EAGAIN) +#if defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) + if (errno == EINTR || errno == EAGAIN) #elif defined(WIN32) || defined(_WIN32) int error = WSAGetLastError(); if (error == WSAEWOULDBLOCK || error == WSAEINPROGRESS || error == 0) @@ -130,8 +130,6 @@ int BufferWriter::Send(SOCKET sockfd, int timeout) if (timeout > 0) { SocketUtil::SetNonBlock(sockfd); } - + return ret; } - - diff --git a/src/net/BufferWriter.h b/src/net/BufferWriter.h index c4f92b4..2bcbad3 100644 --- a/src/net/BufferWriter.h +++ b/src/net/BufferWriter.h @@ -20,7 +20,7 @@ void WriteUint24LE(char* p, uint32_t value); void WriteUint16BE(char* p, uint16_t value); void WriteUint16LE(char* p, uint16_t value); -class BufferWriter +class DLL_API BufferWriter { public: BufferWriter(int capacity = kMaxQueueLength); diff --git a/src/net/EpollTaskScheduler.cpp b/src/net/EpollTaskScheduler.cpp index 5dfa3a0..6be1488 100644 --- a/src/net/EpollTaskScheduler.cpp +++ b/src/net/EpollTaskScheduler.cpp @@ -3,9 +3,10 @@ #include "EpollTaskScheduler.h" -#if defined(__linux) || defined(__linux__) +#if defined(__linux) || defined(__linux__) || defined(ANDROID) #include #include +#elif defined(__APPLE__) #endif using namespace xop; @@ -13,21 +14,23 @@ using namespace xop; EpollTaskScheduler::EpollTaskScheduler(int id) : TaskScheduler(id) { -#if defined(__linux) || defined(__linux__) +#if defined(__linux) || defined(__linux__) || defined(ANDROID) epollfd_ = epoll_create(1024); - #endif +#elif defined(__APPLE__) + +#endif this->UpdateChannel(wakeup_channel_); } EpollTaskScheduler::~EpollTaskScheduler() { - + } void EpollTaskScheduler::UpdateChannel(ChannelPtr channel) { std::lock_guard lock(mutex_); -#if defined(__linux) || defined(__linux__) +#if defined(__linux) || defined(__linux__) || defined(ANDROID) int fd = channel->GetSocket(); if(channels_.find(fd) != channels_.end()) { if(channel->IsNoneEvent()) { @@ -42,14 +45,16 @@ void EpollTaskScheduler::UpdateChannel(ChannelPtr channel) if(!channel->IsNoneEvent()) { channels_.emplace(fd, channel); Update(EPOLL_CTL_ADD, channel); - } - } + } + } +#elif defined(__APPLE__) + #endif } void EpollTaskScheduler::Update(int operation, ChannelPtr& channel) { -#if defined(__linux) || defined(__linux__) +#if defined(__linux) || defined(__linux__) || defined(ANDROID) struct epoll_event event = {0}; if(operation != EPOLL_CTL_DEL) { @@ -60,25 +65,29 @@ void EpollTaskScheduler::Update(int operation, ChannelPtr& channel) if(::epoll_ctl(epollfd_, operation, channel->GetSocket(), &event) < 0) { } +#elif defined(__APPLE__) + #endif } void EpollTaskScheduler::RemoveChannel(ChannelPtr& channel) { std::lock_guard lock(mutex_); -#if defined(__linux) || defined(__linux__) +#if defined(__linux) || defined(__linux__) || defined(ANDROID) int fd = channel->GetSocket(); if(channels_.find(fd) != channels_.end()) { Update(EPOLL_CTL_DEL, channel); channels_.erase(fd); } +#elif defined(__APPLE__) + #endif } bool EpollTaskScheduler::HandleEvent(int timeout) { -#if defined(__linux) || defined(__linux__) +#if defined(__linux) || defined(__linux__) || defined(ANDROID) struct epoll_event events[512] = {0}; int num_events = -1; @@ -86,18 +95,18 @@ bool EpollTaskScheduler::HandleEvent(int timeout) if(num_events < 0) { if(errno != EINTR) { return false; - } + } } for(int n=0; nHandleEvent(events[n].events); } - } + } return true; +#elif defined(__APPLE__) + return false; #else return false; #endif } - - diff --git a/src/net/EventLoop.cpp b/src/net/EventLoop.cpp index 5f99c53..a0e42ff 100644 --- a/src/net/EventLoop.cpp +++ b/src/net/EventLoop.cpp @@ -3,14 +3,14 @@ #include "EventLoop.h" -#if defined(WIN32) || defined(_WIN32) +#if defined(WIN32) || defined(_WIN32) #include #endif -#if defined(WIN32) || defined(_WIN32) +#if defined(WIN32) || defined(_WIN32) #pragma comment(lib, "Ws2_32.lib") #pragma comment(lib,"Iphlpapi.lib") -#endif +#endif using namespace xop; @@ -41,7 +41,7 @@ std::shared_ptr EventLoop::GetTaskScheduler() index_++; if (index_ >= task_schedulers_.size()) { index_ = 1; - } + } return task_scheduler; } @@ -56,11 +56,11 @@ void EventLoop::Loop() return ; } - for (uint32_t n = 0; n < num_threads_; n++) + for (uint32_t n = 0; n < num_threads_; n++) { #if defined(__linux) || defined(__linux__) std::shared_ptr task_scheduler_ptr(new EpollTaskScheduler(n)); -#elif defined(WIN32) || defined(_WIN32) || defined(__FreeBSD__) +#elif defined(WIN32) || defined(_WIN32) || defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) std::shared_ptr task_scheduler_ptr(new SelectTaskScheduler(n)); #endif task_schedulers_.push_back(task_scheduler_ptr); @@ -71,12 +71,12 @@ void EventLoop::Loop() const int priority = TASK_SCHEDULER_PRIORITY_REALTIME; - for (auto iter : threads_) + for (auto iter : threads_) { -#if defined(__linux) || defined(__linux__) || defined(__FreeBSD__) +#if defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) -#elif defined(WIN32) || defined(_WIN32) - switch (priority) +#elif defined(WIN32) || defined(_WIN32) + switch (priority) { case TASK_SCHEDULER_PRIORITY_LOW: SetThreadPriority(iter->native_handle(), THREAD_PRIORITY_BELOW_NORMAL); @@ -113,13 +113,13 @@ void EventLoop::Quit() task_schedulers_.clear(); threads_.clear(); } - + void EventLoop::UpdateChannel(ChannelPtr channel) { std::lock_guard locker(mutex_); if (task_schedulers_.size() > 0) { task_schedulers_[0]->UpdateChannel(channel); - } + } } void EventLoop::RemoveChannel(ChannelPtr& channel) @@ -127,7 +127,7 @@ void EventLoop::RemoveChannel(ChannelPtr& channel) std::lock_guard locker(mutex_); if (task_schedulers_.size() > 0) { task_schedulers_[0]->RemoveChannel(channel); - } + } } TimerId EventLoop::AddTimer(TimerEvent timerEvent, uint32_t msec) @@ -144,11 +144,11 @@ void EventLoop::RemoveTimer(TimerId timerId) std::lock_guard locker(mutex_); if (task_schedulers_.size() > 0) { task_schedulers_[0]->RemoveTimer(timerId); - } + } } bool EventLoop::AddTriggerEvent(TriggerEvent callback) -{ +{ std::lock_guard locker(mutex_); if (task_schedulers_.size() > 0) { return task_schedulers_[0]->AddTriggerEvent(callback); diff --git a/src/net/EventLoop.h b/src/net/EventLoop.h index f11a1d8..60ea62b 100644 --- a/src/net/EventLoop.h +++ b/src/net/EventLoop.h @@ -27,7 +27,7 @@ namespace xop { -class EventLoop +class DLL_API EventLoop { public: EventLoop(const EventLoop&) = delete; diff --git a/src/net/Logger.h b/src/net/Logger.h index 74fcb07..a08bc7c 100644 --- a/src/net/Logger.h +++ b/src/net/Logger.h @@ -12,6 +12,16 @@ #include #include +#if(WIN32) +#ifdef DLL_EXPORTS +#define DLL_API __declspec(dllexport) +#else +#define DLL_API __declspec(dllimport) +#endif +#else +#define DLL_API +#endif + namespace xop { @@ -20,7 +30,7 @@ enum Priority LOG_DEBUG, LOG_STATE, LOG_INFO, LOG_WARNING, LOG_ERROR, }; -class Logger +class DLL_API Logger { public: Logger &operator=(const Logger &) = delete; diff --git a/src/net/NetInterface.cpp b/src/net/NetInterface.cpp index d4a0bda..2bb1e3c 100644 --- a/src/net/NetInterface.cpp +++ b/src/net/NetInterface.cpp @@ -8,7 +8,7 @@ using namespace xop; std::string NetInterface::GetLocalIPAddress() { -#if defined(__linux) || defined(__linux__) || defined(__FreeBSD__) +#if defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) SOCKET sockfd = 0; char buf[512] = { 0 }; struct ifconf ifconf; @@ -69,12 +69,12 @@ std::string NetInterface::GetLocalIPAddress() if (strcmp(pIpAddrString->IpAddress.String, "127.0.0.1")!=0 && strcmp(pIpAddrString->IpAddress.String, "0.0.0.0")!=0) { - // pIpAddrString->IpMask.String + // pIpAddrString->IpMask.String //pIpAdapterInfo->GatewayList.IpAddress.String std::string ip(pIpAddrString->IpAddress.String); //delete pIpAdapterInfo; return ip; - } + } pIpAddrString = pIpAddrString->Next; } while (pIpAddrString); pIpAdapterInfo = pIpAdapterInfo->Next; @@ -86,5 +86,3 @@ std::string NetInterface::GetLocalIPAddress() return "0.0.0.0"; #endif } - - diff --git a/src/net/Pipe.cpp b/src/net/Pipe.cpp index e34e850..8d4d9c8 100644 --- a/src/net/Pipe.cpp +++ b/src/net/Pipe.cpp @@ -7,6 +7,10 @@ #include #include +#if defined(__APPLE__) + #include +#endif + using namespace xop; Pipe::Pipe() @@ -16,7 +20,7 @@ Pipe::Pipe() bool Pipe::Create() { -#if defined(WIN32) || defined(_WIN32) +#if defined(WIN32) || defined(_WIN32) TcpSocket rp(socket(AF_INET, SOCK_STREAM, 0)), wp(socket(AF_INET, SOCK_STREAM, 0)); std::random_device rd; @@ -26,20 +30,20 @@ bool Pipe::Create() int again = 5; while(again--) { - port = rd(); + port = rd(); if (rp.Bind("127.0.0.1", port)) { break; - } + } } if (again == 0) { return false; } - + if (!rp.Listen(1)) { return false; } - + if (!wp.Connect("127.0.0.1", port)) { return false; } @@ -55,34 +59,44 @@ bool Pipe::Create() if (pipe2(pipe_fd_, O_NONBLOCK | O_CLOEXEC) < 0) { return false; } +#elif defined(__APPLE__) || defined(ANDROID) + if(pipe(pipe_fd_) < 0) + return false; + if(fcntl(pipe_fd_[0],F_SETFL,O_NONBLOCK | O_CLOEXEC) < 0) + return false; + if(fcntl(pipe_fd_[1],F_SETFL,O_NONBLOCK | O_CLOEXEC) < 0) + return false; + //if (pipe2(pipe_fd_, O_NONBLOCK | O_CLOEXEC) < 0) { + // return false; + //} #endif return true; } int Pipe::Write(void *buf, int len) { -#if defined(WIN32) || defined(_WIN32) +#if defined(WIN32) || defined(_WIN32) return ::send(pipe_fd_[1], (char *)buf, len, 0); -#elif defined(__linux) || defined(__linux__) || defined(__FreeBSD__) +#elif defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) return ::write(pipe_fd_[1], buf, len); -#endif +#endif } int Pipe::Read(void *buf, int len) { -#if defined(WIN32) || defined(_WIN32) +#if defined(WIN32) || defined(_WIN32) return recv(pipe_fd_[0], (char *)buf, len, 0); -#elif defined(__linux) || defined(__linux__) || defined(__FreeBSD__) +#elif defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) return ::read(pipe_fd_[0], buf, len); -#endif +#endif } void Pipe::Close() { -#if defined(WIN32) || defined(_WIN32) +#if defined(WIN32) || defined(_WIN32) closesocket(pipe_fd_[0]); closesocket(pipe_fd_[1]); -#elif defined(__linux) || defined(__linux__) || defined(__FreeBSD__) +#elif defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) ::close(pipe_fd_[0]); ::close(pipe_fd_[1]); #endif diff --git a/src/net/SelectTaskScheduler.cpp b/src/net/SelectTaskScheduler.cpp index acce1c9..0ad3958 100644 --- a/src/net/SelectTaskScheduler.cpp +++ b/src/net/SelectTaskScheduler.cpp @@ -25,7 +25,7 @@ SelectTaskScheduler::SelectTaskScheduler(int id) SelectTaskScheduler::~SelectTaskScheduler() { - + } void SelectTaskScheduler::UpdateChannel(ChannelPtr channel) @@ -52,8 +52,8 @@ void SelectTaskScheduler::UpdateChannel(ChannelPtr channel) is_fd_read_reset_ = true; is_fd_write_reset_ = true; is_fd_exp_reset_ = true; - } - } + } + } } void SelectTaskScheduler::RemoveChannel(ChannelPtr& channel) @@ -71,12 +71,12 @@ void SelectTaskScheduler::RemoveChannel(ChannelPtr& channel) } bool SelectTaskScheduler::HandleEvent(int timeout) -{ +{ if(channels_.empty()) { if (timeout <= 0) { timeout = 10; } - + Timer::Sleep(timeout); return true; } @@ -95,7 +95,7 @@ bool SelectTaskScheduler::HandleEvent(int timeout) if (is_fd_exp_reset_) { maxfd_ = 0; } - + std::lock_guard lock(mutex_); for(auto iter : channels_) { int events = iter.second->GetEvents(); @@ -114,9 +114,9 @@ bool SelectTaskScheduler::HandleEvent(int timeout) if(fd > maxfd_) { maxfd_ = fd; } - } + } } - + fd_read_reset = is_fd_read_reset_; fd_write_reset = is_fd_write_reset_; fd_exp_reset = is_fd_exp_reset_; @@ -124,15 +124,15 @@ bool SelectTaskScheduler::HandleEvent(int timeout) is_fd_write_reset_ = false; is_fd_exp_reset_ = false; } - + if(fd_read_reset) { FD_ZERO(&fd_read_backup_); - memcpy(&fd_read_backup_, &fd_read, sizeof(fd_set)); + memcpy(&fd_read_backup_, &fd_read, sizeof(fd_set)); } else { memcpy(&fd_read, &fd_read_backup_, sizeof(fd_set)); } - + if(fd_write_reset) { FD_ZERO(&fd_write_backup_); @@ -141,7 +141,7 @@ bool SelectTaskScheduler::HandleEvent(int timeout) else { memcpy(&fd_write, &fd_write_backup_, sizeof(fd_set)); } - + if(fd_exp_reset) { FD_ZERO(&fd_exp_backup_); @@ -156,13 +156,13 @@ bool SelectTaskScheduler::HandleEvent(int timeout) } struct timeval tv = { timeout/1000, timeout%1000*1000 }; - int ret = select((int)maxfd_+1, &fd_read, &fd_write, &fd_exp, &tv); + int ret = select((int)maxfd_+1, &fd_read, &fd_write, &fd_exp, &tv); if (ret < 0) { -#if defined(__linux) || defined(__linux__) || defined(__FreeBSD__) +#if defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) if(errno == EINTR) { return true; - } -#endif + } +#endif return false; } @@ -189,7 +189,7 @@ bool SelectTaskScheduler::HandleEvent(int timeout) event_list.emplace_front(iter.second, events); } } - } + } for(auto& iter: event_list) { iter.first->HandleEvent(iter.second); @@ -197,6 +197,3 @@ bool SelectTaskScheduler::HandleEvent(int timeout) return true; } - - - diff --git a/src/net/SelectTaskScheduler.h b/src/net/SelectTaskScheduler.h index 6e753e9..1610ae9 100644 --- a/src/net/SelectTaskScheduler.h +++ b/src/net/SelectTaskScheduler.h @@ -9,7 +9,7 @@ #include #include -#if defined(__linux) || defined(__linux__) || defined(__FreeBSD__) +#if defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) #include #include #include @@ -17,7 +17,7 @@ #endif namespace xop -{ +{ class SelectTaskScheduler : public TaskScheduler { @@ -28,7 +28,7 @@ class SelectTaskScheduler : public TaskScheduler void UpdateChannel(ChannelPtr channel); void RemoveChannel(ChannelPtr& channel); bool HandleEvent(int timeout); - + private: fd_set fd_read_backup_; fd_set fd_write_backup_; diff --git a/src/net/Socket.h b/src/net/Socket.h index d923cdd..2da6328 100644 --- a/src/net/Socket.h +++ b/src/net/Socket.h @@ -4,15 +4,18 @@ #ifndef XOP_SOCKET_H #define XOP_SOCKET_H -#if defined(__linux) || defined(__linux__) || defined(__FreeBSD__) -#include +#if defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) +#include #include #include -#include +#include +#if !defined(__APPLE__) + #include + #include + #include +#endif #include #include -#include -#include #include #include #include @@ -21,9 +24,11 @@ #include #define SOCKET int #define INVALID_SOCKET (-1) -#define SOCKET_ERROR (-1) +#define SOCKET_ERROR (-1) + +#define DLL_API -#elif defined(WIN32) || defined(_WIN32) +#elif defined(WIN32) || defined(_WIN32) #define FD_SETSIZE 1024 #define WIN32_LEAN_AND_MEAN #define _WINSOCK_DEPRECATED_NO_WARNINGS @@ -32,11 +37,24 @@ #include #include #define SHUT_RD 0 -#define SHUT_WR 1 +#define SHUT_WR 1 #define SHUT_RDWR 2 +#ifdef DLL_EXPORTS +#define DLL_API __declspec(dllexport) #else +#define DLL_API __declspec(dllimport) +#endif + +#else + +#endif +#if defined(ANDROID) + #include + #ifndef MODULE_NAME + #define MODULE_NAME "RTSPSERVER" + #endif #endif #include diff --git a/src/net/SocketUtil.cpp b/src/net/SocketUtil.cpp index 6c99930..2c98278 100644 --- a/src/net/SocketUtil.cpp +++ b/src/net/SocketUtil.cpp @@ -9,12 +9,12 @@ using namespace xop; bool SocketUtil::Bind(SOCKET sockfd, std::string ip, uint16_t port) { - struct sockaddr_in addr = {0}; - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = inet_addr(ip.c_str()); - addr.sin_port = htons(port); + struct sockaddr_in addr = {0}; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr(ip.c_str()); + addr.sin_port = htons(port); - if(::bind(sockfd, (struct sockaddr*)&addr, sizeof addr) == SOCKET_ERROR) { + if(::bind(sockfd, (struct sockaddr*)&addr, sizeof addr) == SOCKET_ERROR) { return false; } @@ -23,7 +23,7 @@ bool SocketUtil::Bind(SOCKET sockfd, std::string ip, uint16_t port) void SocketUtil::SetNonBlock(SOCKET fd) { -#if defined(__linux) || defined(__linux__) || defined(__FreeBSD__) +#if defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) int flags = fcntl(fd, F_GETFL, 0); fcntl(fd, F_SETFL, flags | O_NONBLOCK); #elif defined(WIN32) || defined(_WIN32) @@ -34,7 +34,7 @@ void SocketUtil::SetNonBlock(SOCKET fd) void SocketUtil::SetBlock(SOCKET fd, int write_timeout) { -#if defined(__linux) || defined(__linux__) || defined(__FreeBSD__) +#if defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) int flags = fcntl(fd, F_GETFL, 0); fcntl(fd, F_SETFL, flags&(~O_NONBLOCK)); #elif defined(WIN32) || defined(_WIN32) @@ -45,14 +45,14 @@ void SocketUtil::SetBlock(SOCKET fd, int write_timeout) if(write_timeout > 0) { #ifdef SO_SNDTIMEO -#if defined(__linux) || defined(__linux__) || defined(__FreeBSD__) +#if defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) struct timeval tv = {write_timeout/1000, (write_timeout%1000)*1000}; setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&tv, sizeof tv); #elif defined(WIN32) || defined(_WIN32) unsigned long ms = (unsigned long)write_timeout; setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char *)&ms, sizeof(unsigned long)); #else -#endif +#endif #endif } } @@ -68,7 +68,7 @@ void SocketUtil::SetReusePort(SOCKET sockfd) #ifdef SO_REUSEPORT int on = 1; setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, (const char*)&on, sizeof(on)); -#endif +#endif } void SocketUtil::SetNoDelay(SOCKET sockfd) @@ -100,25 +100,54 @@ void SocketUtil::SetSendBufSize(SOCKET sockfd, int size) void SocketUtil::SetRecvBufSize(SOCKET sockfd, int size) { - if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (char *)&size, sizeof(size)) < 0) + if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (char *)&size, sizeof(size)) < 0) { +#ifdef DEBUG + #if defined(ANDROID) + __android_log_print(ANDROID_LOG_ERROR, MODULE_NAME, "SocketUtil: Error setting SO_RCVBUF to %d",size); + #else std::cerr << "Error setting SO_RCVBUF to " << size << std::endl; + #endif +#endif + } } int SocketUtil::GetSendBufSize(SOCKET sockfd) { +#ifdef WIN32 + char optval; +#else int optval; - socklen_t optlen = sizeof(optval); - if (getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &optval, &optlen) < 0) +#endif + socklen_t optlen = sizeof(optval); + if (getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &optval, &optlen) < 0) { +#ifdef DEBUG + #if defined(ANDROID) + __android_log_print(ANDROID_LOG_ERROR, MODULE_NAME, "SocketUtil: Error getting SO_SNDBUF"); + #else std::cerr << "Error getting SO_SNDBUF " << std::endl; + #endif +#endif + } return optval; } int SocketUtil::GetRecvBufSize(SOCKET sockfd) { - int optval; +#ifdef WIN32 + char optval; +#else + int optval; +#endif socklen_t optlen = sizeof(optval); - if (getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &optval, &optlen) < 0) + if (getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &optval, &optlen) < 0) { +#ifdef DEBUG + #if defined(ANDROID) + __android_log_print(ANDROID_LOG_ERROR, MODULE_NAME, "SocketUtil: Error getting SO_RCVBUF"); + #else std::cerr << "Error getting SO_RCVBUF " << std::endl; + #endif +#endif + } return optval; } @@ -168,7 +197,7 @@ int SocketUtil::GetPeerAddr(SOCKET sockfd, struct sockaddr_in *addr) void SocketUtil::Close(SOCKET sockfd) { -#if defined(__linux) || defined(__linux__) || defined(__FreeBSD__) +#if defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) ::close(sockfd); #elif defined(WIN32) || defined(_WIN32) ::closesocket(sockfd); @@ -189,7 +218,7 @@ bool SocketUtil::Connect(SOCKET sockfd, std::string ip, uint16_t port, int timeo addr.sin_port = htons(port); addr.sin_addr.s_addr = inet_addr(ip.c_str()); - if (::connect(sockfd, (struct sockaddr*)&addr, addrlen) == SOCKET_ERROR) { + if (::connect(sockfd, (struct sockaddr*)&addr, addrlen) == SOCKET_ERROR) { if (timeout > 0) { is_connected = false; fd_set fd_write; @@ -204,9 +233,8 @@ bool SocketUtil::Connect(SOCKET sockfd, std::string ip, uint16_t port, int timeo } else { is_connected = false; - } + } } - + return is_connected; } - diff --git a/src/net/SocketUtil.h b/src/net/SocketUtil.h index f161309..0a990c5 100644 --- a/src/net/SocketUtil.h +++ b/src/net/SocketUtil.h @@ -10,7 +10,7 @@ namespace xop { -class SocketUtil +class DLL_API SocketUtil { public: static bool Bind(SOCKET sockfd, std::string ip, uint16_t port); diff --git a/src/net/TaskScheduler.cpp b/src/net/TaskScheduler.cpp index ab5d91d..10d2b3f 100644 --- a/src/net/TaskScheduler.cpp +++ b/src/net/TaskScheduler.cpp @@ -1,5 +1,5 @@ #include "TaskScheduler.h" -#if defined(__linux) || defined(__linux__) || defined(__FreeBSD__) +#if defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) #include #endif @@ -7,13 +7,13 @@ using namespace xop; TaskScheduler::TaskScheduler(int id) : id_(id) - , is_shutdown_(false) + , is_shutdown_(false) , wakeup_pipe_(new Pipe()) , trigger_events_(new xop::RingBuffer(kMaxTriggetEvents)) { static std::once_flag flag; std::call_once(flag, [] { -#if defined(WIN32) || defined(_WIN32) +#if defined(WIN32) || defined(_WIN32) WSADATA wsa_data; if (WSAStartup(MAKEWORD(2, 2), &wsa_data)) { WSACleanup(); @@ -24,26 +24,26 @@ TaskScheduler::TaskScheduler(int id) if (wakeup_pipe_->Create()) { wakeup_channel_.reset(new Channel(wakeup_pipe_->Read())); wakeup_channel_->EnableReading(); - wakeup_channel_->SetReadCallback([this]() { this->Wake(); }); - } + wakeup_channel_->SetReadCallback([this]() { this->Wake(); }); + } } TaskScheduler::~TaskScheduler() { - + } void TaskScheduler::Start() { #if 0 -#if defined(__linux) || defined(__linux__) || defined(__FreeBSD__) +#if defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) signal(SIGPIPE, SIG_IGN); signal(SIGQUIT, SIG_IGN); signal(SIGUSR1, SIG_IGN); signal(SIGTERM, SIG_IGN); signal(SIGKILL, SIG_IGN); -#endif -#endif +#endif +#endif is_shutdown_ = false; while (!is_shutdown_) { this->HandleTriggerEvent(); @@ -92,7 +92,7 @@ void TaskScheduler::Wake() void TaskScheduler::HandleTriggerEvent() { - do + do { TriggerEvent callback; if (trigger_events_->Pop(callback)) { diff --git a/src/net/TaskScheduler.h b/src/net/TaskScheduler.h index ff6fd01..451717c 100644 --- a/src/net/TaskScheduler.h +++ b/src/net/TaskScheduler.h @@ -14,7 +14,7 @@ namespace xop typedef std::function TriggerEvent; -class TaskScheduler +class DLL_API TaskScheduler { public: TaskScheduler(int id=1); diff --git a/src/net/TcpConnection.cpp b/src/net/TcpConnection.cpp index 7ee7850..dbb720c 100644 --- a/src/net/TcpConnection.cpp +++ b/src/net/TcpConnection.cpp @@ -36,8 +36,15 @@ void TcpConnection::Send(std::shared_ptr data, uint32_t size) { if (!is_closed_) { mutex_.lock(); - if (!write_buffer_->Append(data, size)) + if (!write_buffer_->Append(data, size)) { +#ifdef DEBUG +#if defined(ANDROID) + __android_log_print(ANDROID_LOG_ERROR, MODULE_NAME, "TcpConnection: write_buffer failed..."); +#else std::cerr << "write_buffer failed" << std::endl; +#endif +#endif + } mutex_.unlock(); this->HandleWrite(); @@ -48,8 +55,15 @@ void TcpConnection::Send(const char *data, uint32_t size) { if (!is_closed_) { mutex_.lock(); - if ( !write_buffer_->Append(data, size)) + if ( !write_buffer_->Append(data, size)) { +#ifdef DEBUG +#if defined(ANDROID) + __android_log_print(ANDROID_LOG_ERROR, MODULE_NAME, "TcpConnection: write_buffer failed..."); +#else std::cerr << "write_buffer failed" << std::endl; +#endif +#endif + } mutex_.unlock(); this->HandleWrite(); @@ -73,7 +87,7 @@ void TcpConnection::HandleRead() if (is_closed_) { return; } - + int ret = read_buffer_->Read(channel_->GetSocket()); if (ret <= 0) { this->Close(); @@ -95,7 +109,7 @@ void TcpConnection::HandleWrite() if (is_closed_) { return; } - + //std::lock_guard lock(mutex_); if (!mutex_.try_lock()) { return; @@ -136,11 +150,11 @@ void TcpConnection::Close() if (close_cb_) { close_cb_(shared_from_this()); - } + } if (disconnect_cb_) { disconnect_cb_(shared_from_this()); - } + } } } diff --git a/src/net/TcpConnection.h b/src/net/TcpConnection.h index 97c244d..1b1e8ad 100644 --- a/src/net/TcpConnection.h +++ b/src/net/TcpConnection.h @@ -12,7 +12,7 @@ namespace xop { -class TcpConnection : public std::enable_shared_from_this +class DLL_API TcpConnection : public std::enable_shared_from_this { public: using Ptr = std::shared_ptr; diff --git a/src/net/TcpServer.cpp b/src/net/TcpServer.cpp index 3c53d25..23c274f 100644 --- a/src/net/TcpServer.cpp +++ b/src/net/TcpServer.cpp @@ -2,7 +2,7 @@ #include "Acceptor.h" #include "EventLoop.h" #include "Logger.h" -#include +#include using namespace xop; using namespace std; @@ -53,7 +53,7 @@ bool TcpServer::Start(std::string ip, uint16_t port) void TcpServer::Stop() { - if (is_started_) { + if (is_started_) { mutex_.lock(); for (auto iter : connections_) { iter.second->Disconnect(); @@ -69,11 +69,16 @@ void TcpServer::Stop() break; } } - } + } } TcpConnection::Ptr TcpServer::OnConnect(SOCKET sockfd) { +#ifdef DEBUG + #if defined(ANDROID) + __android_log_print(ANDROID_LOG_VERBOSE, MODULE_NAME, "TcpServer: received connect request..."); + #endif +#endif return std::make_shared(event_loop_->GetTaskScheduler().get(), sockfd); } diff --git a/src/net/TcpServer.h b/src/net/TcpServer.h index 78f37ae..bed3493 100644 --- a/src/net/TcpServer.h +++ b/src/net/TcpServer.h @@ -17,7 +17,7 @@ namespace xop class Acceptor; class EventLoop; -class TcpServer +class DLL_API TcpServer { public: TcpServer(EventLoop* event_loop); diff --git a/src/net/TcpSocket.cpp b/src/net/TcpSocket.cpp index 5178678..aaf8c8d 100644 --- a/src/net/TcpSocket.cpp +++ b/src/net/TcpSocket.cpp @@ -11,12 +11,12 @@ using namespace xop; TcpSocket::TcpSocket(SOCKET sockfd) : sockfd_(sockfd) { - + } TcpSocket::~TcpSocket() { - + } SOCKET TcpSocket::Create() @@ -27,10 +27,10 @@ SOCKET TcpSocket::Create() bool TcpSocket::Bind(std::string ip, uint16_t port) { - struct sockaddr_in addr = {0}; - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = inet_addr(ip.c_str()); - addr.sin_port = htons(port); + struct sockaddr_in addr = {0}; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr(ip.c_str()); + addr.sin_port = htons(port); if(::bind(sockfd_, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) { LOG_DEBUG(" bind <%s:%u> failed.\n", sockfd_, ip.c_str(), port); @@ -60,7 +60,7 @@ SOCKET TcpSocket::Accept() } bool TcpSocket::Connect(std::string ip, uint16_t port, int timeout) -{ +{ if(!SocketUtil::Connect(sockfd_, ip, port, timeout)) { LOG_DEBUG(" connect failed.\n", sockfd_); return false; @@ -71,12 +71,12 @@ bool TcpSocket::Connect(std::string ip, uint16_t port, int timeout) void TcpSocket::Close() { -#if defined(__linux) || defined(__linux__) || defined(__FreeBSD__) +#if defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) ::close(sockfd_); #elif defined(WIN32) || defined(_WIN32) closesocket(sockfd_); #else - + #endif sockfd_ = 0; } diff --git a/src/net/TcpSocket.h b/src/net/TcpSocket.h index ace8776..cab45e3 100644 --- a/src/net/TcpSocket.h +++ b/src/net/TcpSocket.h @@ -11,7 +11,7 @@ namespace xop { -class TcpSocket +class DLL_API TcpSocket { public: TcpSocket(SOCKET sockfd=-1); diff --git a/src/net/Timestamp.cpp b/src/net/Timestamp.cpp index 7f1c981..6c0e47b 100644 --- a/src/net/Timestamp.cpp +++ b/src/net/Timestamp.cpp @@ -1,6 +1,6 @@ #include "Timestamp.h" #include -#include +#include #include using namespace xop; @@ -12,16 +12,16 @@ std::string Timestamp::Localtime() std::ostringstream stream; auto now = system_clock::now(); time_t tt = system_clock::to_time_t(now); - + #if defined(WIN32) || defined(_WIN32) struct tm tm; localtime_s(&tm, &tt); stream << std::put_time(&tm, "%F %T"); -#elif defined(__linux) || defined(__linux__) || defined(__FreeBSD__) +#elif defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) char buffer[200] = {0}; std::string timeString; std::strftime(buffer, 200, "%F %T", std::localtime(&tt)); stream << buffer; -#endif +#endif return stream.str(); -} \ No newline at end of file +} diff --git a/src/xop/AACSource.cpp b/src/xop/AACSource.cpp index 3dcdd30..dc3f7ff 100644 --- a/src/xop/AACSource.cpp +++ b/src/xop/AACSource.cpp @@ -1,4 +1,4 @@ -// PHZ +// PHZ // 2018-5-16 #if defined(WIN32) || defined(_WIN32) @@ -10,7 +10,7 @@ #include #include #include -#if defined(__linux) || defined(__linux__) +#if defined(__linux) || defined(__linux__) || defined(__APPLE__) || defined(ANDROID) #include #endif @@ -61,13 +61,13 @@ string AACSource::GetAttribute() // RFC 3640 for (index = 0; index < 16; index++) { if (AACSampleRate[index] == samplerate_) { break; - } + } } if (index == 16) { return ""; // error } - + uint8_t profile = 1; char config[10] = {0}; diff --git a/src/xop/Authenticator.h b/src/xop/Authenticator.h index 63c0d40..714bdcd 100644 --- a/src/xop/Authenticator.h +++ b/src/xop/Authenticator.h @@ -3,6 +3,7 @@ #include #include "RtspMessage.h" +#include "net/BufferReader.h" namespace xop { @@ -13,8 +14,13 @@ class Authenticator Authenticator() {}; virtual ~Authenticator() {}; - virtual bool Authenticate(std::shared_ptr request, std::string &nonce) = 0; - virtual size_t GetFailedResponse(std::shared_ptr request, std::shared_ptr buf, size_t size) = 0; + virtual std::string GetUsername() const = 0; + virtual std::string GetRealm() const = 0; + + virtual bool Authenticate(std::shared_ptr request, std::string &nonce) = 0; + virtual bool HandleUnauthorized(BufferReader* buffer, std::string& nonce, bool& unauthorized) = 0; + virtual std::string GetResponse(std::string nonce, std::string cmd, std::string url) = 0; + virtual size_t GetFailedResponse(std::shared_ptr request, std::shared_ptr buf, size_t size) = 0; private: diff --git a/src/xop/DigestAuthenticator.cpp b/src/xop/DigestAuthenticator.cpp index 7aae4cf..67a1622 100644 --- a/src/xop/DigestAuthenticator.cpp +++ b/src/xop/DigestAuthenticator.cpp @@ -1,5 +1,7 @@ #include "DigestAuthenticator.h" -#include "md5/md5.hpp" +#include "md5/md5.hpp" + +#include using namespace xop; @@ -8,7 +10,13 @@ DigestAuthenticator::DigestAuthenticator(std::string realm, std::string username , username_(username) , password_(password) { - +#ifdef DEBUG + #if defined(ANDROID) + __android_log_print(ANDROID_LOG_ERROR, MODULE_NAME, "DigestAuthenticator Constructor"); + #else + std::cout << "DigestAuthenticator Constructor" << std::endl; + #endif +#endif } DigestAuthenticator::~DigestAuthenticator() @@ -18,6 +26,13 @@ DigestAuthenticator::~DigestAuthenticator() std::string DigestAuthenticator::GetNonce() { +#ifdef DEBUG + #if defined(ANDROID) + __android_log_print(ANDROID_LOG_ERROR, MODULE_NAME, "DigestAuthenticator GetNonce"); + #else + std::cout << "DigestAuthenticator GetNonce" << std::endl; + #endif +#endif return md5::generate_nonce(); } @@ -28,9 +43,66 @@ std::string DigestAuthenticator::GetResponse(std::string nonce, std::string cmd, auto hex1 = md5::md5_hash_hex(username_ + ":" + realm_ + ":" + password_); auto hex2 = md5::md5_hash_hex(cmd + ":" + url); auto response = md5::md5_hash_hex(hex1 + ":" + nonce + ":" + hex2); +#ifdef DEBUG + #if defined(ANDROID) + __android_log_print(ANDROID_LOG_ERROR, MODULE_NAME, "DigestAuthenticator GetResponse"); + #else + std::cout << "DigestAuthenticator GetResponse" << std::endl; + #endif +#endif + return response; } +bool parseParam(std::string& line, std::string param, std::string& value) +{ + size_t idx = line.find(param); + if(idx != line.npos) + { + int cnt1 = idx + param.size(); + while(line[cnt1++] != '\"'); + int cnt2 = cnt1+1; + while(line[cnt2++] != '\"'); + value = line.substr(cnt1, cnt2 - cnt1 - 1); + std::cout << "Value = " << value << std::endl; + return true; + } + return false; +} + +bool DigestAuthenticator::HandleUnauthorized(BufferReader* buffer, std::string& nonce, bool& unauthorized) +{ + bool complete = false; + unauthorized = false; + // TRY PARSE STUFF OF THIS FORM: + // "RTSP/1.0 401 Unauthorized" + // "Content-Length: 91" + // "Content-Type: text/html" + // "WWW-Authenticate: Digest realm=\"visocon.rtsp\" charset=\"UTF-8\" nonce=\"761ca587865b0c3ba7af2a0e95876833\"" + while(!complete) + { + const char* firstCrlf = buffer->FindFirstCrlf(); + const char* start = buffer->Peek(); + if(firstCrlf != nullptr) + { + std::string line(start, firstCrlf); + if(line.find("Unauthorized") != line.npos) + unauthorized = true; + + if(line.find("WWW-Authenticate") != line.npos && line.find("Digest") != line.npos) + { + if(parseParam(line, "realm", realm_) && + parseParam(line, "nonce", nonce)) + complete = true; + } + buffer->RetrieveUntil(firstCrlf + 2); + } + else + break; + } + return complete; +} + bool DigestAuthenticator::Authenticate( std::shared_ptr rtsp_request, std::string &nonce) @@ -38,10 +110,23 @@ bool DigestAuthenticator::Authenticate( std::string cmd = rtsp_request->MethodToString[rtsp_request->GetMethod()]; std::string url = rtsp_request->GetRtspUrl(); - if (nonce.size() > 0 && (GetResponse(nonce, cmd, url) == rtsp_request->GetAuthResponse())) { + if (nonce.size() > 0 && (GetResponse(nonce, cmd, url) == rtsp_request->GetAuthResponse())) + { +#ifdef DEBUG + #if defined(ANDROID) + __android_log_print(ANDROID_LOG_ERROR, MODULE_NAME, "DigestAuthenticator Authenticate success %s - %s - %s", cmd.c_str(), url.c_str(), nonce.c_str()); + #else + std::cout << "DigestAuthenticator Authenticate success" << cmd << " - " << url.c_str() << " - " << nonce.c_str() << std::endl; + #endif +#endif return true; } else { -#if 0 +#ifdef DEBUG + #if defined(ANDROID) + __android_log_print(ANDROID_LOG_ERROR, MODULE_NAME, "DigestAuthenticator Authenticate failed %s - %s - %s", cmd.c_str(), url.c_str(), nonce.c_str()); + #else + std::cout << "DigestAuthenticator Authenticate failed" << cmd << " - " << url.c_str() << " - " << nonce.c_str() << std::endl; + #endif #endif return false; } @@ -53,5 +138,12 @@ size_t DigestAuthenticator::GetFailedResponse( size_t size) { std::string nonce = md5::generate_nonce(); +#ifdef DEBUG +#if defined(ANDROID) + __android_log_print(ANDROID_LOG_ERROR, MODULE_NAME, "DigestAuthenticator GetFailedResponse %s - %s - %s", buf.get(), realm_.c_str(), nonce.c_str()); +#else + std::cout << "DigestAuthenticator GetFailedResponse" << buf.get() << " - " << realm_.c_str() << " - " << nonce.c_str() << std::endl; +#endif +#endif return rtsp_request->BuildUnauthorizedRes(buf.get(), size, realm_.c_str(), nonce.c_str()); } diff --git a/src/xop/DigestAuthenticator.h b/src/xop/DigestAuthenticator.h index a584cd1..281616c 100644 --- a/src/xop/DigestAuthenticator.h +++ b/src/xop/DigestAuthenticator.h @@ -12,7 +12,7 @@ namespace xop { -class DigestAuthenticator : public Authenticator +class DLL_API DigestAuthenticator : public Authenticator { public: DigestAuthenticator(std::string realm, std::string username, std::string password); @@ -30,6 +30,8 @@ class DigestAuthenticator : public Authenticator std::string GetNonce(); std::string GetResponse(std::string nonce, std::string cmd, std::string url); + bool HandleUnauthorized(BufferReader* buffer,std::string& nonce, bool& unauthorized); + bool Authenticate(std::shared_ptr request, std::string &nonce); size_t GetFailedResponse(std::shared_ptr request, std::shared_ptr buf, size_t size); diff --git a/src/xop/G711ASource.cpp b/src/xop/G711ASource.cpp index 2d3400b..d570789 100644 --- a/src/xop/G711ASource.cpp +++ b/src/xop/G711ASource.cpp @@ -1,7 +1,7 @@ -// PHZ +// PHZ // 2018-5-16 -#if defined(WIN32) || defined(_WIN32) +#if defined(WIN32) || defined(_WIN32) #ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #endif @@ -9,9 +9,9 @@ #include "G711ASource.h" #include #include -#if defined(__linux) || defined(__linux__) +#if defined(__linux) || defined(__linux__) || defined(__APPLE__) || defined(ANDROID) #include -#endif +#endif using namespace xop; using namespace std; @@ -30,7 +30,7 @@ G711ASource* G711ASource::CreateNew() G711ASource::~G711ASource() { - + } string G711ASource::GetMediaDescription(uint16_t port) @@ -39,7 +39,7 @@ string G711ASource::GetMediaDescription(uint16_t port) sprintf(buf, "m=audio %hu RTP/AVP 8", port); return string(buf); } - + string G711ASource::GetAttribute() { return string("a=rtpmap:8 PCMA/8000/1"); @@ -74,4 +74,3 @@ int64_t G711ASource::GetTimestamp() auto time_point = chrono::time_point_cast(chrono::steady_clock::now()); return (int64_t)((time_point.time_since_epoch().count()+500)/1000*8); } - diff --git a/src/xop/H264Source.cpp b/src/xop/H264Source.cpp index dd64e55..052f170 100644 --- a/src/xop/H264Source.cpp +++ b/src/xop/H264Source.cpp @@ -10,7 +10,7 @@ #include "H264Source.h" #include #include -#if defined(__linux) || defined(__linux__) +#if defined(__linux) || defined(__linux__) || defined(__APPLE__) || defined(ANDROID) #include #endif @@ -20,9 +20,12 @@ using namespace std; H264Source::H264Source(uint32_t framerate) : framerate_(framerate) { - payload_ = 96; + payload_ = 96; media_type_ = H264; clock_rate_ = 90000; + + nalUnitChecked_ = false; + decodeNAL_ = false; } H264Source* H264Source::CreateNew(uint32_t framerate) @@ -47,75 +50,264 @@ string H264Source::GetAttribute() return string("a=rtpmap:96 H264/90000"); } + +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | RTP Header | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |STAP-B NAL HDR | DON | NALU 1 Size | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | NALU 1 Size | NALU 1 HDR | NALU 1 Data | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +// : : +// + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | NALU 2 Size | NALU 2 HDR | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | NALU 2 Data | +// : : +// | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | :...OPTIONAL RTP padding | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +const string naluTypesStrings2[] = +{ + "0: Unspecified (non-VCL)", + "1: Coded slice of a non-IDR picture (VCL)", // P frame + "2: Coded slice data partition A (VCL)", + "3: Coded slice data partition B (VCL)", + "4: Coded slice data partition C (VCL)", + "5: Coded slice of an IDR picture (VCL)", // I frame + "6: Supplemental enhancement information (SEI) (non-VCL)", + "7: Sequence parameter set (non-VCL)", // SPS parameter + "8: Picture parameter set (non-VCL)", // PPS parameter + "9: Access unit delimiter (non-VCL)", + "10: End of sequence (non-VCL)", + "11: End of stream (non-VCL)", + "12: Filler data (non-VCL)", + "13: Sequence parameter set extension (non-VCL)", + "14: Prefix NAL unit (non-VCL)", + "15: Subset sequence parameter set (non-VCL)", + "16: Reserved (non-VCL)", + "17: Reserved (non-VCL)", + "18: Reserved (non-VCL)", + "19: Coded slice of an auxiliary coded picture without partitioning (non-VCL)", + "20: Coded slice extension (non-VCL)", + "21: Coded slice extension for depth view components (non-VCL)", + "22: Reserved (non-VCL)", + "23: Reserved (non-VCL)", + "24: STAP-A Single-time aggregation packet (non-VCL)", + "25: STAP-B Single-time aggregation packet (non-VCL)", + "26: MTAP16 Multi-time aggregation packet (non-VCL)", + "27: MTAP24 Multi-time aggregation packet (non-VCL)", + "28: FU-A Fragmentation unit (non-VCL)", + "29: FU-B Fragmentation unit (non-VCL)", + "30: Unspecified (non-VCL)", + "31: Unspecified (non-VCL)", +}; + bool H264Source::HandleFrame(MediaChannelId channel_id, AVFrame frame) { uint8_t* frame_buf = frame.buffer.data(); uint32_t frame_size = frame.buffer.size(); - - if (frame.timestamp == 0) { - frame.timestamp = GetTimestamp(); - } - - if (frame_size <= MAX_RTP_PAYLOAD_SIZE) { - RtpPacket rtp_pkt; - rtp_pkt.type = frame.type; - rtp_pkt.timestamp = frame.timestamp; - rtp_pkt.size = frame_size + 4 + RTP_HEADER_SIZE; - rtp_pkt.last = 1; - memcpy(rtp_pkt.data.get()+4+RTP_HEADER_SIZE, frame_buf, frame_size); - - if (send_frame_callback_) { - if (!send_frame_callback_(channel_id, rtp_pkt)) { - return false; - } - } - } else { - char FU_A[2] = {0}; - - FU_A[0] = (frame_buf[0] & 0xE0) | 28; - FU_A[1] = 0x80 | (frame_buf[0] & 0x1f); - - frame_buf += 1; - frame_size -= 1; - - while (frame_size + 2 > MAX_RTP_PAYLOAD_SIZE) { - RtpPacket rtp_pkt; - rtp_pkt.type = frame.type; - rtp_pkt.timestamp = frame.timestamp; - rtp_pkt.size = 4 + RTP_HEADER_SIZE + MAX_RTP_PAYLOAD_SIZE; - rtp_pkt.last = 0; - - rtp_pkt.data.get()[RTP_HEADER_SIZE+4] = FU_A[0]; - rtp_pkt.data.get()[RTP_HEADER_SIZE+5] = FU_A[1]; - memcpy(rtp_pkt.data.get()+4+RTP_HEADER_SIZE+2, frame_buf, MAX_RTP_PAYLOAD_SIZE-2); - - if (send_frame_callback_) { - if (!send_frame_callback_(channel_id, rtp_pkt)) - return false; + + if(!nalUnitChecked_) + { + if(frame_buf[0] == 0 && frame_buf[1] == 0 && frame_buf[2] == 1) { + decodeNAL_ = true; + } + else if(frame_buf[0] == 0 && frame_buf[1] == 0 && frame_buf[2] == 0 && frame_buf[3] == 1) { + decodeNAL_ = true; + } + } + if(decodeNAL_) + { + if (frame.timestamp == 0) { + frame.timestamp = GetTimestamp(); + } + + // we received the raw byte stream - we need to packetize it into individual single NALU units (essentially + // just have RTP header + data of NAL + // parse full buffer + int nalutypes[32]; + int nalustart[32]; + int naluend[32]; + int cnt = 0; + for (int i = 0; i < frame_size-1; i++) { + if (i >= 3) { + if(frame_buf[i] == 0x01 && frame_buf[i-1] == 0x00 && frame_buf[i-2] == 0x00) + { + int nalu_type = ((uint8_t)frame_buf[i+1] & 0x1F); +#if DEBUG +#if ANDROID + __android_log_print(ANDROID_LOG_VERBOSE, MODULE_NAME,"=== FOUND NAL UNIT \"%@\" FOUND at %i", naluTypesStrings2[nalu_type], i); +#else + printf("=== FOUND NAL UNIT \"%@\" FOUND at %i", naluTypesStrings2[nalu_type], i); +#endif +#endif + nalutypes[cnt] = nalu_type; + nalustart[cnt] = i+1; // first element + if(cnt > 0) + naluend[cnt-1] = i-3; // last element that counts! + cnt++; + } + else if (frame_buf[i] == 0x01 && frame_buf[i-1] == 0x00 && frame_buf[i-2] == 0x00 && frame_buf[i-3] == 0x00) + { + int nalu_type = ((uint8_t)frame_buf[i+1] & 0x1F); +#if DEBUG +#if ANDROID + __android_log_print(ANDROID_LOG_VERBOSE, MODULE_NAME,"=== FOUND NAL UNIT \"%@\" FOUND at %i", naluTypesStrings2[nalu_type], i); +#else + printf("=== FOUND NAL UNIT \"%@\" FOUND at %i", naluTypesStrings2[nalu_type], i); +#endif +#endif + nalutypes[cnt] = nalu_type; + nalustart[cnt] = i+1; // first element + if(cnt > 0) + naluend[cnt-1] = i-4; // last element that counts! + cnt++; + } } + } + naluend[cnt-1] = frame_size-1; + + // now we have the individual NAL units - lets send them individually... + for(int i = 0; i < cnt; i++) + { + int this_size = naluend[i] - nalustart[i] + 1; + uint8_t* this_buf = frame_buf + nalustart[i]; + + if (this_size <= MAX_RTP_PAYLOAD_SIZE) { + RtpPacket rtp_pkt; + rtp_pkt.type = frame.type; + rtp_pkt.timestamp = frame.timestamp; + rtp_pkt.size = this_size + 4 + RTP_HEADER_SIZE; + rtp_pkt.last = 1; + memcpy(rtp_pkt.data.get()+4+RTP_HEADER_SIZE, this_buf, this_size); + + if (send_frame_callback_) { + if (!send_frame_callback_(channel_id, rtp_pkt)) { + return false; + } + } + } else { + char FU_A[2] = {0}; + + FU_A[0] = (this_buf[0] & 0xE0) | 28; + FU_A[1] = 0x80 | (this_buf[0] & 0x1f); + + this_buf += 1; + this_size -= 1; + + while (this_size + 2 > MAX_RTP_PAYLOAD_SIZE) { + RtpPacket rtp_pkt; + rtp_pkt.type = frame.type; + rtp_pkt.timestamp = frame.timestamp; + rtp_pkt.size = 4 + RTP_HEADER_SIZE + MAX_RTP_PAYLOAD_SIZE; + rtp_pkt.last = 0; + + rtp_pkt.data.get()[RTP_HEADER_SIZE+4] = FU_A[0]; + rtp_pkt.data.get()[RTP_HEADER_SIZE+5] = FU_A[1]; + memcpy(rtp_pkt.data.get()+4+RTP_HEADER_SIZE+2, this_buf, MAX_RTP_PAYLOAD_SIZE-2); + + if (send_frame_callback_) { + if (!send_frame_callback_(channel_id, rtp_pkt)) + return false; + } - frame_buf += MAX_RTP_PAYLOAD_SIZE - 2; - frame_size -= MAX_RTP_PAYLOAD_SIZE - 2; + this_buf += MAX_RTP_PAYLOAD_SIZE - 2; + this_size -= MAX_RTP_PAYLOAD_SIZE - 2; - FU_A[1] &= ~0x80; + FU_A[1] &= ~0x80; + } + + { + RtpPacket rtp_pkt; + rtp_pkt.type = frame.type; + rtp_pkt.timestamp = frame.timestamp; + rtp_pkt.size = 4 + RTP_HEADER_SIZE + 2 + this_size; + rtp_pkt.last = 1; + + FU_A[1] |= 0x40; + rtp_pkt.data.get()[RTP_HEADER_SIZE+4] = FU_A[0]; + rtp_pkt.data.get()[RTP_HEADER_SIZE+5] = FU_A[1]; + memcpy(rtp_pkt.data.get()+4+RTP_HEADER_SIZE+2, this_buf, this_size); + + if (send_frame_callback_) { + if (!send_frame_callback_(channel_id, rtp_pkt)) { + return false; + } + } + } + } + } + } + else // WE RECEIVED THE BUFS ALREADY - JUST SEND NALUS AS BEFORE + { + if (frame.timestamp == 0) { + frame.timestamp = GetTimestamp(); } - { - RtpPacket rtp_pkt; - rtp_pkt.type = frame.type; - rtp_pkt.timestamp = frame.timestamp; - rtp_pkt.size = 4 + RTP_HEADER_SIZE + 2 + frame_size; - rtp_pkt.last = 1; - - FU_A[1] |= 0x40; - rtp_pkt.data.get()[RTP_HEADER_SIZE+4] = FU_A[0]; - rtp_pkt.data.get()[RTP_HEADER_SIZE+5] = FU_A[1]; - memcpy(rtp_pkt.data.get()+4+RTP_HEADER_SIZE+2, frame_buf, frame_size); - - if (send_frame_callback_) { - if (!send_frame_callback_(channel_id, rtp_pkt)) { - return false; - } + if (frame_size <= MAX_RTP_PAYLOAD_SIZE) { + RtpPacket rtp_pkt; + rtp_pkt.type = frame.type; + rtp_pkt.timestamp = frame.timestamp; + rtp_pkt.size = frame_size + 4 + RTP_HEADER_SIZE; + rtp_pkt.last = 1; + memcpy(rtp_pkt.data.get()+4+RTP_HEADER_SIZE, frame_buf, frame_size); + + if (send_frame_callback_) { + if (!send_frame_callback_(channel_id, rtp_pkt)) { + return false; + } + } + } else { + char FU_A[2] = {0}; + + FU_A[0] = (frame_buf[0] & 0xE0) | 28; + FU_A[1] = 0x80 | (frame_buf[0] & 0x1f); + + frame_buf += 1; + frame_size -= 1; + + while (frame_size + 2 > MAX_RTP_PAYLOAD_SIZE) { + RtpPacket rtp_pkt; + rtp_pkt.type = frame.type; + rtp_pkt.timestamp = frame.timestamp; + rtp_pkt.size = 4 + RTP_HEADER_SIZE + MAX_RTP_PAYLOAD_SIZE; + rtp_pkt.last = 0; + + rtp_pkt.data.get()[RTP_HEADER_SIZE+4] = FU_A[0]; + rtp_pkt.data.get()[RTP_HEADER_SIZE+5] = FU_A[1]; + memcpy(rtp_pkt.data.get()+4+RTP_HEADER_SIZE+2, frame_buf, MAX_RTP_PAYLOAD_SIZE-2); + + if (send_frame_callback_) { + if (!send_frame_callback_(channel_id, rtp_pkt)) + return false; + } + + frame_buf += MAX_RTP_PAYLOAD_SIZE - 2; + frame_size -= MAX_RTP_PAYLOAD_SIZE - 2; + + FU_A[1] &= ~0x80; + } + + { + RtpPacket rtp_pkt; + rtp_pkt.type = frame.type; + rtp_pkt.timestamp = frame.timestamp; + rtp_pkt.size = 4 + RTP_HEADER_SIZE + 2 + frame_size; + rtp_pkt.last = 1; + + FU_A[1] |= 0x40; + rtp_pkt.data.get()[RTP_HEADER_SIZE+4] = FU_A[0]; + rtp_pkt.data.get()[RTP_HEADER_SIZE+5] = FU_A[1]; + memcpy(rtp_pkt.data.get()+4+RTP_HEADER_SIZE+2, frame_buf, frame_size); + + if (send_frame_callback_) { + if (!send_frame_callback_(channel_id, rtp_pkt)) { + return false; + } + } } } } @@ -125,7 +317,7 @@ bool H264Source::HandleFrame(MediaChannelId channel_id, AVFrame frame) int64_t H264Source::GetTimestamp() { -/* #if defined(__linux) || defined(__linux__) +/* #if defined(__linux) || defined(__linux__) || defined(__APPLE__) || defined(ANDROID) struct timeval tv = {0}; gettimeofday(&tv, NULL); uint32_t ts = ((tv.tv_sec*1000)+((tv.tv_usec+500)/1000))*90; // 90: _clockRate/1000; @@ -135,4 +327,3 @@ int64_t H264Source::GetTimestamp() return (int64_t)((time_point.time_since_epoch().count() + 500) / 1000 * 90 ); //#endif } - diff --git a/src/xop/H264Source.h b/src/xop/H264Source.h index 576095b..e26fea8 100644 --- a/src/xop/H264Source.h +++ b/src/xop/H264Source.h @@ -10,7 +10,7 @@ namespace xop { -class H264Source : public MediaSource +class DLL_API H264Source : public MediaSource { public: static H264Source* CreateNew(uint32_t framerate=25); @@ -33,6 +33,9 @@ class H264Source : public MediaSource private: H264Source(uint32_t framerate); + bool nalUnitChecked_; + bool decodeNAL_; + uint32_t framerate_ = 25; }; diff --git a/src/xop/H265Source.cpp b/src/xop/H265Source.cpp index 3089e88..9da35c3 100644 --- a/src/xop/H265Source.cpp +++ b/src/xop/H265Source.cpp @@ -1,7 +1,7 @@ -// PHZ +// PHZ // 2018-6-7 -#if defined(WIN32) || defined(_WIN32) +#if defined(WIN32) || defined(_WIN32) #ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #endif @@ -10,7 +10,7 @@ #include "H265Source.h" #include #include -#if defined(__linux) || defined(__linux__) +#if defined(__linux) || defined(__linux__) || defined(__APPLE__) || defined(ANDROID) #include #endif @@ -32,7 +32,7 @@ H265Source* H265Source::CreateNew(uint32_t framerate) H265Source::~H265Source() { - + } string H265Source::GetMediaDescription(uint16_t port) @@ -41,7 +41,7 @@ string H265Source::GetMediaDescription(uint16_t port) sprintf(buf, "m=video %hu RTP/AVP 96", port); return string(buf); } - + string H265Source::GetAttribute() { return string("a=rtpmap:96 H265/90000"); @@ -55,7 +55,7 @@ bool H265Source::HandleFrame(MediaChannelId channelId, AVFrame frame) if (frame.timestamp == 0) { frame.timestamp = GetTimestamp(); } - + if (frame_size <= MAX_RTP_PAYLOAD_SIZE) { RtpPacket rtp_pkt; rtp_pkt.type = frame.type; @@ -64,23 +64,23 @@ bool H265Source::HandleFrame(MediaChannelId channelId, AVFrame frame) rtp_pkt.last = 1; memcpy(rtp_pkt.data.get()+4+RTP_HEADER_SIZE, frame_buf, frame_size); - + if (send_frame_callback_) { if (!send_frame_callback_(channelId, rtp_pkt)) { return false; - } + } } - } - else { - char FU[3] = {0}; - char nalUnitType = (frame_buf[0] & 0x7E) >> 1; - FU[0] = (frame_buf[0] & 0x81) | (49<<1); - FU[1] = frame_buf[1]; - FU[2] = (0x80 | nalUnitType); - + } + else { + char FU[3] = {0}; + char nalUnitType = (frame_buf[0] & 0x7E) >> 1; + FU[0] = (frame_buf[0] & 0x81) | (49<<1); + FU[1] = frame_buf[1]; + FU[2] = (0x80 | nalUnitType); + frame_buf += 2; frame_size -= 2; - + while (frame_size + 3 > MAX_RTP_PAYLOAD_SIZE) { RtpPacket rtp_pkt; rtp_pkt.type = frame.type; @@ -92,19 +92,19 @@ bool H265Source::HandleFrame(MediaChannelId channelId, AVFrame frame) rtp_pkt.data.get()[RTP_HEADER_SIZE+5] = FU[1]; rtp_pkt.data.get()[RTP_HEADER_SIZE+6] = FU[2]; memcpy(rtp_pkt.data.get()+4+RTP_HEADER_SIZE+3, frame_buf, MAX_RTP_PAYLOAD_SIZE-3); - + if (send_frame_callback_) { if (!send_frame_callback_(channelId, rtp_pkt)) { return false; - } + } } - + frame_buf += (MAX_RTP_PAYLOAD_SIZE - 3); frame_size -= (MAX_RTP_PAYLOAD_SIZE - 3); - - FU[2] &= ~0x80; + + FU[2] &= ~0x80; } - + { RtpPacket rtp_pkt; rtp_pkt.type = frame.type; @@ -117,13 +117,13 @@ bool H265Source::HandleFrame(MediaChannelId channelId, AVFrame frame) rtp_pkt.data.get()[RTP_HEADER_SIZE+5] = FU[1]; rtp_pkt.data.get()[RTP_HEADER_SIZE+6] = FU[2]; memcpy(rtp_pkt.data.get()+4+RTP_HEADER_SIZE+3, frame_buf, frame_size); - + if (send_frame_callback_) { if (!send_frame_callback_(channelId, rtp_pkt)) { return false; - } + } } - } + } } return true; @@ -132,7 +132,7 @@ bool H265Source::HandleFrame(MediaChannelId channelId, AVFrame frame) int64_t H265Source::GetTimestamp() { -/* #if defined(__linux) || defined(__linux__) +/* #if defined(__linux) || defined(__linux__) || defined(__APPLE__) || defined(ANDROID) struct timeval tv = {0}; gettimeofday(&tv, NULL); uint32_t ts = ((tv.tv_sec*1000)+((tv.tv_usec+500)/1000))*90; // 90: _clockRate/1000; @@ -142,5 +142,5 @@ int64_t H265Source::GetTimestamp() //auto time_point = chrono::time_point_cast(chrono::steady_clock::now()); auto time_point = chrono::time_point_cast(chrono::steady_clock::now()); return (int64_t)((time_point.time_since_epoch().count() + 500) / 1000 * 90); -//#endif +//#endif } diff --git a/src/xop/MediaSession.cpp b/src/xop/MediaSession.cpp index 2282eb6..95d36d8 100644 --- a/src/xop/MediaSession.cpp +++ b/src/xop/MediaSession.cpp @@ -1,4 +1,4 @@ -// PHZ +// PHZ // 2018-9-30 #include "MediaSession.h" @@ -139,13 +139,27 @@ std::string MediaSession::GetSdpMessage(std::string ip, std::string session_name } char buf[2048] = {0}; - +/* + snprintf(buf, sizeof(buf), + "v=0\r\n" + "o=- 0 0 IN IP4 127.0.0.1\r\n" + "s=No Name\r\n" + "c=IN IP4 54.155.36.131\r\n" + "t=0 0\r\n" + "a=tool:libavformat 58.76.100\r\n" + "m=video 0 RTP/AVP 96\r\n" + "a=rtpmap:96 H264/90000\r\n" + "a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z2QAH6yyAeBr8v/gIgAiIgAAAwACAAADAHgeMGSQ,aOvDyyLA; profile-level-id=64001F\r\n" + "a=control:streamid=0\r\n"); +*/ + snprintf(buf, sizeof(buf), "v=0\r\n" "o=- 9%ld 1 IN IP4 %s\r\n" "t=0 0\r\n" "a=control:*\r\n" , - (long)std::time(NULL), ip.c_str()); + (long)std::time(NULL), + ip.c_str()); if(session_name != "") { snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), @@ -175,16 +189,21 @@ std::string MediaSession::GetSdpMessage(std::string ip, std::string session_name "%s\r\n", media_sources_[chn]->GetMediaDescription(0).c_str()); } + //snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), + // "c=IN IP4 54.155.36.131\r\n"); snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%s\r\n", media_sources_[chn]->GetAttribute().c_str()); - + + //snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf),"a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z2QAH6yyAeBr8v/gIgAiIgAAAwACAAADAHgeMGSQ,aOvDyyLA; profile-level-id=64001F\r\n"); + snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), - "a=control:track%d\r\n", chn); + // "a=control:track%d\r\n", chn); + "a=control:streamid=%d\r\n", chn); } } - + sdp_ = buf; return sdp_; } diff --git a/src/xop/MediaSession.h b/src/xop/MediaSession.h index 48814b7..fb90ab2 100644 --- a/src/xop/MediaSession.h +++ b/src/xop/MediaSession.h @@ -26,7 +26,7 @@ namespace xop class RtpConnection; -class MediaSession +class DLL_API MediaSession { public: using Ptr = std::shared_ptr; diff --git a/src/xop/RtpConnection.cpp b/src/xop/RtpConnection.cpp index cbab596..c2562c6 100644 --- a/src/xop/RtpConnection.cpp +++ b/src/xop/RtpConnection.cpp @@ -1,4 +1,4 @@ -// PHZ +// PHZ // 2018-9-30 #include "RtpConnection.h" @@ -88,7 +88,7 @@ bool RtpConnection::SetupRtpOverUdp(MediaChannelId channel_id, uint16_t rtp_port if (n == 10) { return false; } - + local_rtp_port_[channel_id] = rd() & 0xfffe; local_rtcp_port_[channel_id] =local_rtp_port_[channel_id] + 1; @@ -109,8 +109,13 @@ bool RtpConnection::SetupRtpOverUdp(MediaChannelId channel_id, uint16_t rtp_port } SocketUtil::SetSendBufSize(rtpfd_[channel_id], 200*1024); +#ifdef DEBUG +#if defined(ANDROID) + __android_log_print(ANDROID_LOG_VERBOSE, MODULE_NAME, "SocketUtil: SendBufSize is %d",SocketUtil::GetSendBufSize(rtpfd_[channel_id])); +#else std::cerr << "SendBufSize is " << SocketUtil::GetSendBufSize(rtpfd_[channel_id]) << std::endl; - +#endif +#endif peer_rtp_addr_[channel_id].sin_family = AF_INET; peer_rtp_addr_[channel_id].sin_addr.s_addr = peer_addr_.sin_addr.s_addr; peer_rtp_addr_[channel_id].sin_port = htons(media_channel_info_[channel_id].rtp_port); @@ -132,7 +137,7 @@ bool RtpConnection::SetupRtpOverMulticast(MediaChannelId channel_id, std::string if (n == 10) { return false; } - + local_rtp_port_[channel_id] = rd() & 0xfffe; rtpfd_[channel_id] = ::socket(AF_INET, SOCK_DGRAM, 0); if (!SocketUtil::Bind(rtpfd_[channel_id], "0.0.0.0", local_rtp_port_[channel_id])) { @@ -164,6 +169,82 @@ void RtpConnection::Play() } } +const uint32_t NTP_EPOCH_OFFSET = 2208992400u; +timeval NTP2Timeval(uint32_t msw, uint32_t lsw) +{ + struct timeval t; + t.tv_sec = msw - NTP_EPOCH_OFFSET; + t.tv_usec = (uint32_t)((((double)lsw) * 1000000.0) / ((uint32_t)(~0))); + return t; +} + +void RtpConnection::AssembleRTCPMessage() +{ +// RTP: +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |V=2|P|X| CC |M| PT | sequence number | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | timestamp | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | synchronization source (SSRC) identifier | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// | contributing source (CSRC) identifiers | +// | .... | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +// RTCP: +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// header |V=2|P| RC | PT=SR=200 | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of sender | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// sender | NTP timestamp, most significant word | +// info +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | NTP timestamp, least significant word | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | RTP timestamp | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | sender's packet count | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | sender's octet count | + + RtpPacket rpkt; + rtcp_packet *pkt = reinterpret_cast(rpkt.data.get() + 4); + pkt->header.version = 2; + pkt->header.p = 0; + pkt->header.count = 0; + pkt->header.pt = 200; + pkt->header.length = htons(6); + pkt->ssrc = media_channel_info_[0].rtp_header.ssrc; + // + int32_t* blk = (int32_t*)(rpkt.data.get() + 12); // + 12); + + const std::chrono::time_point now = std::chrono::system_clock::now(); + uint32_t secs = std::chrono::duration_cast(now.time_since_epoch()).count(); + uint64_t nsecs = std::chrono::duration_cast(now.time_since_epoch()).count() - (1e9 * secs); + + auto time_point = chrono::time_point_cast(chrono::steady_clock::now()); + uint32_t rtptm = (int64_t)((time_point.time_since_epoch().count() + 500) / 1000 * 90 ); + + // usecs = (uint32)((((double)lsw) * 1000000.0) / ((uint32)(~0))) + // NTP time stamp is seconds since 1970 as MSW + blk[0] = htonl(secs + NTP_EPOCH_OFFSET); + blk[1] = htonl((uint32_t)((nsecs * 4294967295) / 1000000000)); + blk[2] = htonl(rtptm); // is based on 10 microsecond intervals + blk[3] = htonl(0x00); + blk[4] = htonl(0x00); + + rpkt.last = 1; + rpkt.size = 32; + + SendRtpPacket(xop::channel_0, rpkt, true); +} + void RtpConnection::Record() { for (int chn=0; chntask_scheduler_->AddTriggerEvent([this, channel_id, pkt] { - this->SetFrameType(pkt.type); - this->SetRtpHeader(channel_id, pkt); - if((media_channel_info_[channel_id].is_play || media_channel_info_[channel_id].is_record) && has_key_frame_ ) { + bool ret = rtsp_conn->task_scheduler_->AddTriggerEvent([this, channel_id, pkt, isRtcp] { + if(!isRtcp) + { + this->SetFrameType(pkt.type); + this->SetRtpHeader(channel_id, pkt); + } + if((media_channel_info_[channel_id].is_play || media_channel_info_[channel_id].is_record) && (has_key_frame_ || isRtcp) ) { if(transport_mode_ == RTP_OVER_TCP) { - SendRtpOverTcp(channel_id, pkt); + SendRtpOverTcp(channel_id, pkt, isRtcp); } else { SendRtpOverUdp(channel_id, pkt); } - + //media_channel_info_[channel_id].octetCount += pkt.size; //media_channel_info_[channel_id].packetCount += 1; } @@ -264,7 +350,7 @@ int RtpConnection::SendRtpPacket(MediaChannelId channel_id, RtpPacket pkt) return ret ? 0 : -1; } -int RtpConnection::SendRtpOverTcp(MediaChannelId channel_id, RtpPacket pkt) +int RtpConnection::SendRtpOverTcp(MediaChannelId channel_id, RtpPacket pkt, bool isRtcp) { auto conn = rtsp_connection_.lock(); if (!conn) { @@ -273,7 +359,10 @@ int RtpConnection::SendRtpOverTcp(MediaChannelId channel_id, RtpPacket pkt) uint8_t* rtpPktPtr = pkt.data.get(); rtpPktPtr[0] = '$'; - rtpPktPtr[1] = (char)media_channel_info_[channel_id].rtp_channel; + if(isRtcp) + rtpPktPtr[1] = (char)media_channel_info_[channel_id].rtcp_channel; + else + rtpPktPtr[1] = (char)media_channel_info_[channel_id].rtp_channel; rtpPktPtr[2] = (char)(((pkt.size-4)&0xFF00)>>8); rtpPktPtr[3] = (char)((pkt.size -4)&0xFF); @@ -285,8 +374,8 @@ int RtpConnection::SendRtpOverUdp(MediaChannelId channel_id, RtpPacket pkt) { int ret = sendto(rtpfd_[channel_id], (const char*)pkt.data.get()+4, pkt.size-4, 0, (struct sockaddr *)&(peer_rtp_addr_[channel_id]), sizeof(struct sockaddr_in)); - - if(ret < 0) { + + if(ret < 0) { Teardown(); return -1; } diff --git a/src/xop/RtpConnection.h b/src/xop/RtpConnection.h index 1eacc89..6c95d27 100644 --- a/src/xop/RtpConnection.h +++ b/src/xop/RtpConnection.h @@ -1,4 +1,4 @@ -// PHZ +// PHZ // 2018-6-8 #ifndef XOP_RTP_CONNECTION_H @@ -69,7 +69,7 @@ class RtpConnection void Teardown(); std::string GetRtpInfo(const std::string& rtsp_url); - int SendRtpPacket(MediaChannelId channel_id, RtpPacket pkt); + int SendRtpPacket(MediaChannelId channel_id, RtpPacket pkt, bool isRtcp = false); bool IsClosed() const { return is_closed_; } @@ -82,9 +82,10 @@ class RtpConnection private: friend class RtspConnection; friend class MediaSession; + void AssembleRTCPMessage(); void SetFrameType(uint8_t frameType = 0); void SetRtpHeader(MediaChannelId channel_id, RtpPacket pkt); - int SendRtpOverTcp(MediaChannelId channel_id, RtpPacket pkt); + int SendRtpOverTcp(MediaChannelId channel_id, RtpPacket pkt, bool isRtcp = false); int SendRtpOverUdp(MediaChannelId channel_id, RtpPacket pkt); std::weak_ptr rtsp_connection_; diff --git a/src/xop/RtspConnection.cpp b/src/xop/RtspConnection.cpp index 4ce502a..8fc61af 100644 --- a/src/xop/RtspConnection.cpp +++ b/src/xop/RtspConnection.cpp @@ -1,4 +1,4 @@ -// PHZ +// PHZ // 2018-6-10 #include "RtspConnection.h" @@ -7,7 +7,7 @@ #include "MediaSource.h" #include "net/SocketUtil.h" -#define USER_AGENT "-_-" +#define USER_AGENT "ar4rtsp" //Lavf57.83.100" //"-_-" #define RTSP_DEBUG 0 #define MAX_RTSP_MESSAGE_SIZE 2048 @@ -22,6 +22,13 @@ RtspConnection::RtspConnection(std::shared_ptr rtsp, TaskScheduler *task_s , rtsp_request_(new RtspRequest) , rtsp_response_(new RtspResponse) { +#if RTSP_DEBUG + #if defined(ANDROID) + __android_log_print(ANDROID_LOG_ERROR, MODULE_NAME, "RtspRequest Constructor"); + #else + std::cout << "RtspConnection Constructor" << std::endl; + #endif +#endif this->SetReadCallback([this](std::shared_ptr conn, xop::BufferReader& buffer) { return this->OnRead(buffer); }); @@ -76,7 +83,6 @@ bool RtspConnection::OnRead(BufferReader& buffer) if (buffer.ReadableBytes() > MAX_RTSP_MESSAGE_SIZE) { buffer.RetrieveAll(); } - return true; } @@ -105,7 +111,11 @@ bool RtspConnection::HandleRtspRequest(BufferReader& buffer) string str(buffer.Peek(), buffer.ReadableBytes()); if (str.find("rtsp") != string::npos || str.find("RTSP") != string::npos) { - std::cout << str << std::endl; + #if defined(ANDROID) + __android_log_print(ANDROID_LOG_VERBOSE, MODULE_NAME, "RtspConnection::HandleRtspRequest: %s",str.c_str()); + #else + std::cout << "RtspConnection::HandleRtspRequest: " << str << std::endl; + #endif } #endif @@ -121,26 +131,26 @@ bool RtspConnection::HandleRtspRequest(BufferReader& buffer) switch (method) { - case RtspRequest::OPTIONS: - HandleCmdOption(); - break; - case RtspRequest::DESCRIBE: - HandleCmdDescribe(); - break; - case RtspRequest::SETUP: - HandleCmdSetup(); - break; - case RtspRequest::PLAY: - HandleCmdPlay(); - break; - case RtspRequest::TEARDOWN: - HandleCmdTeardown(); - break; - case RtspRequest::GET_PARAMETER: - HandleCmdGetParamter(); - break; - default: - break; + case RtspRequest::OPTIONS: + HandleCmdOption(); + break; + case RtspRequest::DESCRIBE: + HandleCmdDescribe(); + break; + case RtspRequest::SETUP: + HandleCmdSetup(); + break; + case RtspRequest::PLAY: + HandleCmdPlay(); + break; + case RtspRequest::TEARDOWN: + HandleCmdTeardown(); + break; + case RtspRequest::GET_PARAMETER: + HandleCmdGetParamter(); + break; + default: + break; } if (rtsp_request_->GotAll()) { @@ -159,11 +169,15 @@ bool RtspConnection::HandleRtspResponse(BufferReader& buffer) #if RTSP_DEBUG string str(buffer.Peek(), buffer.ReadableBytes()); if (str.find("rtsp") != string::npos || str.find("RTSP") != string::npos) { - cout << str << endl; + #if defined(ANDROID) + __android_log_print(ANDROID_LOG_VERBOSE, MODULE_NAME, "RtspConnection::HandleRtspResponse: \n%s",str.c_str()); + #else + cout << "RtspConnection::HandleRtspResponse: " << endl << str << endl; + #endif } #endif - if (rtsp_response_->ParseResponse(&buffer)) { + if (rtsp_response_->ParseResponse(&buffer)) { RtspResponse::Method method = rtsp_response_->GetMethod(); switch (method) { @@ -187,16 +201,66 @@ bool RtspConnection::HandleRtspResponse(BufferReader& buffer) } } else { +#if RTSP_DEBUG + #if defined(ANDROID) + __android_log_print(ANDROID_LOG_VERBOSE, MODULE_NAME, "RtspConnection::HandleRtspResponse: ParseResponse failed!"); + #else + cout << "RtspConnection::HandleRtspResponse: ParseResponse failed!" << endl; + #endif +#endif + //========================================================================= + if (conn_mode_ == RTSP_PUSHER) + { + // check if unauthorized + // if yes, try fix it with authenticator + // else just return false + + // parse auth info + if (authenticator_!=nullptr) + { + bool unauthorized = false; + if(authenticator_->HandleUnauthorized(&buffer, _nonce, unauthorized)) + { + if(unauthorized) + { +#if RTSP_DEBUG + #if defined(ANDROID) + __android_log_print(ANDROID_LOG_VERBOSE, MODULE_NAME, "RtspConnection::HandleRtspResponse: Resending Options!"); + #else + cout << "RtspConnection::HandleRtspResponse: Resending Options!" << endl; + #endif +#endif + rtsp_response_->SetCSeq(rtsp_response_->GetCSeq() + 1); + SendOptions(RtspConnection::RTSP_PUSHER); + //task_scheduler_->AddTriggerEvent([this]() { + // SendOptions(RtspConnection::RTSP_PUSHER); + //}); + return true; + } + } + } + } return false; + //========================================================================= } - +#if RTSP_DEBUG + #if defined(ANDROID) + __android_log_print(ANDROID_LOG_VERBOSE, MODULE_NAME, "RtspConnection::HandleRtspResponse: ParseResponse ok!"); + #else + cout << "RtspConnection::HandleRtspResponse: ParseResponse ok!" << endl; + #endif +#endif return true; } void RtspConnection::SendRtspMessage(std::shared_ptr buf, uint32_t size) { #if RTSP_DEBUG - cout << buf.get() << endl; + #if defined(ANDROID) + __android_log_print(ANDROID_LOG_VERBOSE, MODULE_NAME, "RtspConnection::SendRtspMessage: \n%s",buf.get()); + #else + cout << "RtspConnection::SendRtspMessage: " << endl << buf.get() << endl; + #endif #endif this->Send(buf, size); @@ -411,6 +475,13 @@ void RtspConnection::HandleCmdGetParamter() bool RtspConnection::HandleAuthentication() { +#if RTSP_DEBUG + #if defined(ANDROID) + __android_log_print(ANDROID_LOG_ERROR, MODULE_NAME, "RtspConnection HandleAuthentication"); + #else + std::cout << "RtspConnection HandleAuthentication" << std::endl; + #endif +#endif if (authenticator_ != nullptr && !has_auth_) { if (authenticator_->Authenticate(rtsp_request_, _nonce)) { has_auth_ = true; @@ -442,10 +513,46 @@ void RtspConnection::SendOptions(ConnectionMode mode) rtsp_response_->SetRtspUrl(rtsp->GetRtspUrl().c_str()); std::shared_ptr req(new char[2048], std::default_delete()); - int size = rtsp_response_->BuildOptionReq(req.get(), 2048); + int size = rtsp_response_->BuildOptionReq(req.get(), 2048, _nonce, authenticator_.get()); +#if RTSP_DEBUG + #if defined(ANDROID) + __android_log_print(ANDROID_LOG_VERBOSE, MODULE_NAME, "RtspConnection::SendOptions:"); + #else + cout << "RtspConnection::SendOptions: " << endl; + #endif +#endif SendRtspMessage(req, size); } +void RtspConnection::SendTeardown(ConnectionMode mode) +{ + if (rtp_conn_ == nullptr) { + rtp_conn_.reset(new RtpConnection(shared_from_this())); + } + + auto rtsp = rtsp_.lock(); + if (!rtsp) { + HandleClose(); + return; + } + + conn_mode_ = mode; + rtsp_response_->SetUserAgent(USER_AGENT); + rtsp_response_->SetRtspUrl(rtsp->GetRtspUrl().c_str()); + + std::shared_ptr req(new char[2048], std::default_delete()); + int size = rtsp_response_->BuildTeardownReq(req.get(), 2048, _nonce, authenticator_.get()); +#if RTSP_DEBUG + #if defined(ANDROID) + __android_log_print(ANDROID_LOG_VERBOSE, MODULE_NAME, "RtspConnection::SendTeardown:"); + #else + cout << "RtspConnection::SendTeardown: " << endl; + #endif +#endif + SendRtspMessage(req, size); + rtp_conn_->Teardown(); +} + void RtspConnection::SendAnnounce() { MediaSession::Ptr media_session = nullptr; @@ -479,14 +586,14 @@ void RtspConnection::SendAnnounce() } std::shared_ptr req(new char[4096], std::default_delete()); - int size = rtsp_response_->BuildAnnounceReq(req.get(), 4096, sdp.c_str()); + int size = rtsp_response_->BuildAnnounceReq(req.get(), 4096, sdp.c_str(), _nonce, authenticator_.get()); SendRtspMessage(req, size); } void RtspConnection::SendDescribe() { std::shared_ptr req(new char[2048], std::default_delete()); - int size = rtsp_response_->BuildDescribeReq(req.get(), 2048); + int size = rtsp_response_->BuildDescribeReq(req.get(), 2048, _nonce, authenticator_.get()); SendRtspMessage(req, size); } @@ -508,14 +615,14 @@ void RtspConnection::SendSetup() if (media_session->GetMediaSource(channel_0) && !rtp_conn_->IsSetup(channel_0)) { rtp_conn_->SetupRtpOverTcp(channel_0, 0, 1); - size = rtsp_response_->BuildSetupTcpReq(buf.get(), 2048, channel_0); + size = rtsp_response_->BuildSetupTcpReq(buf.get(), 2048, channel_0, _nonce, authenticator_.get()); } else if (media_session->GetMediaSource(channel_1) && !rtp_conn_->IsSetup(channel_1)) { rtp_conn_->SetupRtpOverTcp(channel_1, 2, 3); - size = rtsp_response_->BuildSetupTcpReq(buf.get(), 2048, channel_1); + size = rtsp_response_->BuildSetupTcpReq(buf.get(), 2048, channel_1, _nonce, authenticator_.get()); } else { - size = rtsp_response_->BuildRecordReq(buf.get(), 2048); + size = rtsp_response_->BuildRecordReq(buf.get(), 2048, _nonce, authenticator_.get()); } SendRtspMessage(buf, size); diff --git a/src/xop/RtspConnection.h b/src/xop/RtspConnection.h index 8299c03..88323d4 100644 --- a/src/xop/RtspConnection.h +++ b/src/xop/RtspConnection.h @@ -1,4 +1,4 @@ -// PHZ +// PHZ // 2018-6-8 #ifndef _RTSP_CONNECTION_H @@ -75,6 +75,20 @@ class RtspConnection : public TcpConnection int GetId() const { return task_scheduler_->GetId(); } + std::string GetNonce() const + { return _nonce; } + void SetNonce(std::string nonce) + { _nonce = nonce; } + + uint32_t GetCseq() const + { + return rtsp_response_->GetCSeq(); + } + void SetCseq(uint32_t cseq) + { + rtsp_response_->SetCSeq(cseq); + } + bool IsPlay() const { return conn_state_ == START_PLAY; } @@ -105,6 +119,7 @@ class RtspConnection : public TcpConnection bool HandleAuthentication(); void SendOptions(ConnectionMode mode= RTSP_SERVER); + void SendTeardown(ConnectionMode mode= RTSP_SERVER); void SendDescribe(); void SendAnnounce(); void SendSetup(); diff --git a/src/xop/RtspMessage.cpp b/src/xop/RtspMessage.cpp index 8df851d..41bef3d 100644 --- a/src/xop/RtspMessage.cpp +++ b/src/xop/RtspMessage.cpp @@ -1,15 +1,20 @@ -// PHZ +// PHZ // 2018-5-16 #if defined(WIN32) || defined(_WIN32) #ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS +#include +#define getpid _getpid #endif #endif #include "RtspMessage.h" +#include "DigestAuthenticator.h" #include "media.h" +#include + using namespace std; using namespace xop; @@ -19,7 +24,7 @@ bool RtspRequest::ParseRequest(BufferReader *buffer) method_ = RTCP; return true; } - + bool ret = true; while(1) { if(state_ == kParseRequestLine) { @@ -32,10 +37,10 @@ bool RtspRequest::ParseRequest(BufferReader *buffer) if (state_ == kParseHeadersLine) { continue; - } + } else { break; - } + } } else if(state_ == kParseHeadersLine) { const char* lastCrlf = buffer->FindLastCrlf(); @@ -62,7 +67,7 @@ bool RtspRequest::ParseRequestLine(const char* begin, const char* end) char version[64] = {0}; if(sscanf(message.c_str(), "%s %s %s", method, url, version) != 3) { - return true; + return true; } string method_str(method); @@ -125,7 +130,7 @@ bool RtspRequest::ParseHeadersLine(const char* begin, const char* end) if(!ParseCSeq(message)) { if (header_line_param_.find("cseq") == header_line_param_.end()) { return false; - } + } } if (method_ == DESCRIBE || method_ == SETUP || method_ == PLAY) { @@ -247,7 +252,7 @@ bool RtspRequest::ParseSessionId(std::string& message) uint32_t session_id = 0; if (sscanf(message.c_str() + pos, "%*[^:]: %u", &session_id) != 1) { return false; - } + } return true; } @@ -263,14 +268,14 @@ bool RtspRequest::ParseMediaChannel(std::string& message) std::size_t pos = iter->second.first.find("track1"); if (pos != std::string::npos) { channel_id_ = channel_1; - } + } } return true; } bool RtspRequest::ParseAuthorization(std::string& message) -{ +{ std::size_t pos = message.find("Authorization"); if (pos != std::string::npos) { if ((pos = message.find("response=")) != std::string::npos) { @@ -326,7 +331,7 @@ std::string RtspRequest::GetRtspUrlSuffix() const return ""; } -std::string RtspRequest::GetRtspUrlSession() const +std::string RtspRequest::GetRtspUrlSession() const { auto iter = request_line_param_.find("url_suffix"); if(iter != request_line_param_.end()) { @@ -339,7 +344,7 @@ std::string RtspRequest::GetRtspUrlSession() const return ""; } -std::string RtspRequest::GetRtspUrlQueryString() const +std::string RtspRequest::GetRtspUrlQueryString() const { auto iter = request_line_param_.find("url_suffix"); if(iter != request_line_param_.end()) { @@ -409,6 +414,24 @@ int RtspRequest::BuildOptionRes(const char* buf, int buf_size) return (int)strlen(buf); } +/* +int RtspRequest::BuildTeardownMSG(const char* buf, int buf_size, uint32_t session_id) +{ + memset((void*)buf, 0, buf_size); + snprintf((char*)buf, buf_size, + "TEARDOWN rtsp://127.0.0.1:8554/live RTSP/1.0\r\n" + "CSeq: %u\r\n" + "Session: %llu\r\n" + "\r\n", + this->GetCSeq(), + session_id); + + method_ = TEARDOWN; + + return (int)strlen(buf); +} +*/ + int RtspRequest::BuildDescribeRes(const char* buf, int buf_size, const char* sdp) { memset((void*)buf, 0, buf_size); @@ -419,21 +442,21 @@ int RtspRequest::BuildDescribeRes(const char* buf, int buf_size, const char* sdp "Content-Type: application/sdp\r\n" "\r\n" "%s", - this->GetCSeq(), - (int)strlen(sdp), + this->GetCSeq(), + (int)strlen(sdp), sdp); return (int)strlen(buf); } -int RtspRequest::BuildSetupMulticastRes(const char* buf, int buf_size, const char* multicast_ip, uint16_t port, uint32_t session_id) -{ +int RtspRequest::BuildSetupMulticastRes(const char* buf, int buf_size, const char* multicast_ip, uint16_t port, uint64_t session_id) +{ memset((void*)buf, 0, buf_size); snprintf((char*)buf, buf_size, "RTSP/1.0 200 OK\r\n" "CSeq: %u\r\n" "Transport: RTP/AVP;multicast;destination=%s;source=%s;port=%u-0;ttl=255\r\n" - "Session: %u\r\n" + "Session: %llu\r\n" "\r\n", this->GetCSeq(), multicast_ip, @@ -444,33 +467,33 @@ int RtspRequest::BuildSetupMulticastRes(const char* buf, int buf_size, const cha return (int)strlen(buf); } -int RtspRequest::BuildSetupUdpRes(const char* buf, int buf_size, uint16_t rtp_chn, uint16_t rtcp_chn, uint32_t session_id) +int RtspRequest::BuildSetupUdpRes(const char* buf, int buf_size, uint16_t rtp_chn, uint16_t rtcp_chn, uint64_t session_id) { memset((void*)buf, 0, buf_size); snprintf((char*)buf, buf_size, "RTSP/1.0 200 OK\r\n" "CSeq: %u\r\n" "Transport: RTP/AVP;unicast;client_port=%hu-%hu;server_port=%hu-%hu\r\n" - "Session: %u\r\n" + "Session: %llu\r\n" "\r\n", this->GetCSeq(), this->GetRtpPort(), this->GetRtcpPort(), - rtp_chn, + rtp_chn, rtcp_chn, session_id); return (int)strlen(buf); } -int RtspRequest::BuildSetupTcpRes(const char* buf, int buf_size, uint16_t rtp_chn, uint16_t rtcp_chn, uint32_t session_id) +int RtspRequest::BuildSetupTcpRes(const char* buf, int buf_size, uint16_t rtp_chn, uint16_t rtcp_chn, uint64_t session_id) { memset((void*)buf, 0, buf_size); snprintf((char*)buf, buf_size, "RTSP/1.0 200 OK\r\n" "CSeq: %u\r\n" "Transport: RTP/AVP/TCP;unicast;interleaved=%d-%d\r\n" - "Session: %u\r\n" + "Session: %llu\r\n" "\r\n", this->GetCSeq(), rtp_chn, rtcp_chn, @@ -479,14 +502,14 @@ int RtspRequest::BuildSetupTcpRes(const char* buf, int buf_size, uint16_t rtp_ch return (int)strlen(buf); } -int RtspRequest::BuildPlayRes(const char* buf, int buf_size, const char* rtpInfo, uint32_t session_id) +int RtspRequest::BuildPlayRes(const char* buf, int buf_size, const char* rtpInfo, uint64_t session_id) { memset((void*)buf, 0, buf_size); snprintf((char*)buf, buf_size, "RTSP/1.0 200 OK\r\n" "CSeq: %d\r\n" "Range: npt=0.000-\r\n" - "Session: %u; timeout=60\r\n", + "Session: %llu; timeout=60\r\n", this->GetCSeq(), session_id); @@ -498,13 +521,13 @@ int RtspRequest::BuildPlayRes(const char* buf, int buf_size, const char* rtpInfo return (int)strlen(buf); } -int RtspRequest::BuildTeardownRes(const char* buf, int buf_size, uint32_t session_id) +int RtspRequest::BuildTeardownRes(const char* buf, int buf_size, uint64_t session_id) { memset((void*)buf, 0, buf_size); snprintf((char*)buf, buf_size, "RTSP/1.0 200 OK\r\n" "CSeq: %d\r\n" - "Session: %u\r\n" + "Session: %llu\r\n" "\r\n", this->GetCSeq(), session_id); @@ -512,13 +535,13 @@ int RtspRequest::BuildTeardownRes(const char* buf, int buf_size, uint32_t sessio return (int)strlen(buf); } -int RtspRequest::BuildGetParamterRes(const char* buf, int buf_size, uint32_t session_id) +int RtspRequest::BuildGetParamterRes(const char* buf, int buf_size, uint64_t session_id) { memset((void*)buf, 0, buf_size); snprintf((char*)buf, buf_size, "RTSP/1.0 200 OK\r\n" "CSeq: %d\r\n" - "Session: %u\r\n" + "Session: %llu\r\n" "\r\n", this->GetCSeq(), session_id); @@ -576,6 +599,13 @@ size_t RtspRequest::BuildUnauthorizedRes(const char *buf, size_t buf_size) size_t RtspRequest::BuildUnauthorizedRes(const char* buf, size_t buf_size, const char* realm, const char* nonce) { +#ifdef DEBUG + #if defined(ANDROID) + __android_log_print(ANDROID_LOG_ERROR, MODULE_NAME, "RtspRequest BuildUnauthorizedRes"); + #else + std::cout << "RtspRequest BuildUnauthorizedRes" << std::endl; + #endif +#endif memset((void*)buf, 0, buf_size); snprintf((char*)buf, buf_size, "RTSP/1.0 401 Unauthorized\r\n" @@ -589,6 +619,49 @@ size_t RtspRequest::BuildUnauthorizedRes(const char* buf, size_t buf_size, const return strlen(buf); } +uint32_t gen_random(const int len) { + + string tmp_s; + static const char num[] = + "0123456789"; + + srand( (unsigned) time(NULL) * getpid()); + + tmp_s.reserve(len); + + for (int i = 0; i < len; ++i) + tmp_s += num[rand() % (sizeof(num) - 1)]; + + + return std::stoi(tmp_s); + +} + +string gen_randomString(const int len) { + + string tmp_s; + static const char alphanum[] = + "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"; + + srand( (unsigned) time(NULL) * getpid()); + + tmp_s.reserve(len); + + for (int i = 0; i < len; ++i) + tmp_s += alphanum[rand() % (sizeof(alphanum) - 1)]; + + + return tmp_s; + +} + +RtspResponse::RtspResponse() +{ + session_ = gen_random(8); +} + bool RtspResponse::ParseResponse(xop::BufferReader *buffer) { if (strstr(buffer->Peek(), "\r\n\r\n") != NULL) { @@ -600,7 +673,14 @@ bool RtspResponse::ParseResponse(xop::BufferReader *buffer) if (ptr != NULL) { char session_id[50] = {0}; if (sscanf(ptr, "%*[^:]: %s", session_id) == 1) - session_ = session_id; + try + { + session_ = std::stoull(session_id); + } + catch (const std::out_of_range& oor) + { + return false; + } } cseq_++; @@ -610,105 +690,249 @@ bool RtspResponse::ParseResponse(xop::BufferReader *buffer) return true; } -int RtspResponse::BuildOptionReq(const char* buf, int buf_size) -{ - memset((void*)buf, 0, buf_size); - snprintf((char*)buf, buf_size, - "OPTIONS %s RTSP/1.0\r\n" - "CSeq: %u\r\n" - "User-Agent: %s\r\n" - "\r\n", - rtsp_url_.c_str(), - this->GetCSeq() + 1, - user_agent_.c_str()); - +int RtspResponse::BuildOptionReq(const char* buf, int buf_size, std::string& nonce, Authenticator* auth) +{ + if(auth != nullptr && nonce.size() > 0) + { + memset((void*)buf, 0, buf_size); + snprintf((char*)buf, buf_size, + "OPTIONS %s RTSP/1.0\r\n" + "CSeq: %u\r\n" + "User-Agent: %s\r\n" + "Authorization: Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"\r\n" + "\r\n", + rtsp_url_.c_str(), + this->GetCSeq() + 1, + user_agent_.c_str(), + auth->GetUsername().c_str(), auth->GetRealm().c_str(), nonce.c_str(), rtsp_url_.c_str(), + auth->GetResponse(nonce, "OPTIONS", rtsp_url_.c_str()).c_str()); + } + else + { + memset((void*)buf, 0, buf_size); + snprintf((char*)buf, buf_size, + "OPTIONS %s RTSP/1.0\r\n" + "CSeq: %u\r\n" + "User-Agent: %s\r\n" + "\r\n", + rtsp_url_.c_str(), + this->GetCSeq() + 1, + user_agent_.c_str()); + } method_ = OPTIONS; return (int)strlen(buf); } -int RtspResponse::BuildAnnounceReq(const char* buf, int buf_size, const char *sdp) -{ - memset((void*)buf, 0, buf_size); - snprintf((char*)buf, buf_size, - "ANNOUNCE %s RTSP/1.0\r\n" - "Content-Type: application/sdp\r\n" - "CSeq: %u\r\n" - "User-Agent: %s\r\n" - "Session: %s\r\n" - "Content-Length: %d\r\n" - "\r\n" - "%s", - rtsp_url_.c_str(), - this->GetCSeq() + 1, - user_agent_.c_str(), - this->GetSession().c_str(), - (int)strlen(sdp), - sdp); - +int RtspResponse::BuildTeardownReq(const char* buf, int buf_size, std::string& nonce, Authenticator* auth) +{ + if(auth != nullptr && nonce.size() > 0) + { + memset((void*)buf, 0, buf_size); + snprintf((char*)buf, buf_size, + "TEARDOWN %s RTSP/1.0\r\n" + "CSeq: %u\r\n" + "User-Agent: %s\r\n" + "Authorization: Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"\r\n" + "Session: %llu\r\n" + "\r\n", + rtsp_url_.c_str(), + this->GetCSeq() + 1, + user_agent_.c_str(), + auth->GetUsername().c_str(), auth->GetRealm().c_str(), nonce.c_str(), rtsp_url_.c_str(), + auth->GetResponse(nonce, "TEARDOWN", rtsp_url_.c_str()).c_str(), + this->GetSession()); + } + else + { + memset((void*)buf, 0, buf_size); + snprintf((char*)buf, buf_size, + "TEARDOWN %s RTSP/1.0\r\n" + "CSeq: %u\r\n" + "User-Agent: %s\r\n" + "Session: %llu\r\n" + "\r\n", + rtsp_url_.c_str(), + this->GetCSeq() + 1, + user_agent_.c_str(), + this->GetSession()); + } + method_ = TEARDOWN; + return (int)strlen(buf); +} + +int RtspResponse::BuildAnnounceReq(const char* buf, int buf_size, const char *sdp, std::string& nonce, Authenticator* auth) +{ + if(auth != nullptr && nonce.size() > 0) + { + memset((void*)buf, 0, buf_size); + snprintf((char*)buf, buf_size, + "ANNOUNCE %s RTSP/1.0\r\n" + "Content-Type: application/sdp\r\n" + "CSeq: %u\r\n" + "User-Agent: %s\r\n" + "Authorization: Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"\r\n" + "Session: %llu\r\n" + "Content-Length: %d\r\n" + "\r\n" + "%s", + rtsp_url_.c_str(), + this->GetCSeq() + 1, + user_agent_.c_str(), + auth->GetUsername().c_str(), auth->GetRealm().c_str(), nonce.c_str(), rtsp_url_.c_str(), + auth->GetResponse(nonce, "ANNOUNCE", rtsp_url_.c_str()).c_str(), + this->GetSession(), + (int)strlen(sdp), + sdp); + } + else + { + memset((void*)buf, 0, buf_size); + snprintf((char*)buf, buf_size, + "ANNOUNCE %s RTSP/1.0\r\n" + "Content-Type: application/sdp\r\n" + "CSeq: %u\r\n" + "User-Agent: %s\r\n" + "Session: %llu\r\n" + "Content-Length: %d\r\n" + "\r\n" + "%s", + rtsp_url_.c_str(), + this->GetCSeq() + 1, + user_agent_.c_str(), + this->GetSession(), + (int)strlen(sdp), + sdp); + } method_ = ANNOUNCE; return (int)strlen(buf); } -int RtspResponse::BuildDescribeReq(const char* buf, int buf_size) -{ - memset((void*)buf, 0, buf_size); - snprintf((char*)buf, buf_size, - "DESCRIBE %s RTSP/1.0\r\n" - "CSeq: %u\r\n" - "Accept: application/sdp\r\n" - "User-Agent: %s\r\n" - "\r\n", - rtsp_url_.c_str(), - this->GetCSeq() + 1, - user_agent_.c_str()); - +int RtspResponse::BuildDescribeReq(const char* buf, int buf_size, std::string& nonce, Authenticator* auth) +{ + if(auth != nullptr && nonce.size() > 0) + { + memset((void*)buf, 0, buf_size); + snprintf((char*)buf, buf_size, + "DESCRIBE %s RTSP/1.0\r\n" + "CSeq: %u\r\n" + "Accept: application/sdp\r\n" + "User-Agent: %s\r\n" + "Authorization: Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"\r\n" + "Session: %llu\r\n" + "\r\n", + rtsp_url_.c_str(), + this->GetCSeq() + 1, + user_agent_.c_str(), + auth->GetUsername().c_str(), auth->GetRealm().c_str(), nonce.c_str(), rtsp_url_.c_str(), + auth->GetResponse(nonce, "DESCRIBE", rtsp_url_.c_str()).c_str(), + this->GetSession()); + } + else + { + memset((void*)buf, 0, buf_size); + snprintf((char*)buf, buf_size, + "DESCRIBE %s RTSP/1.0\r\n" + "CSeq: %u\r\n" + "Accept: application/sdp\r\n" + "User-Agent: %s\r\n" + "Session: %llu\r\n" + "\r\n", + rtsp_url_.c_str(), + this->GetCSeq() + 1, + user_agent_.c_str(), + this->GetSession()); + } method_ = DESCRIBE; return (int)strlen(buf); } -int RtspResponse::BuildSetupTcpReq(const char* buf, int buf_size, int trackId) +int RtspResponse::BuildSetupTcpReq(const char* buf, int buf_size, int trackId, std::string& nonce, Authenticator* auth) { int interleaved[2] = { 0, 1 }; if (trackId == 1) { interleaved[0] = 2; interleaved[1] = 3; } - - memset((void*)buf, 0, buf_size); - snprintf((char*)buf, buf_size, - "SETUP %s/track%d RTSP/1.0\r\n" - "Transport: RTP/AVP/TCP;unicast;mode=record;interleaved=%d-%d\r\n" - "CSeq: %u\r\n" - "User-Agent: %s\r\n" - "Session: %s\r\n" - "\r\n", - rtsp_url_.c_str(), - trackId, - interleaved[0], - interleaved[1], - this->GetCSeq() + 1, - user_agent_.c_str(), - this->GetSession().c_str()); - + if(auth != nullptr && nonce.size() > 0) + { + memset((void*)buf, 0, buf_size); + snprintf((char*)buf, buf_size, + // "SETUP %s/track%d RTSP/1.0\r\n" + "SETUP %s/streamid=%d RTSP/1.0\r\n" + "Transport: RTP/AVP/TCP;unicast;mode=record;interleaved=%d-%d\r\n" + "CSeq: %u\r\n" + "User-Agent: %s\r\n" + "Session: %llu\r\n" + "Authorization: Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s/streamid=%d\", response=\"%s\"\r\n" + "\r\n", + rtsp_url_.c_str(), + trackId, + interleaved[0], + interleaved[1], + this->GetCSeq() + 1, + user_agent_.c_str(), + this->GetSession(), + auth->GetUsername().c_str(), auth->GetRealm().c_str(), nonce.c_str(), rtsp_url_.c_str(), trackId, + auth->GetResponse(nonce, "SETUP", (rtsp_url_ + "/streamid=" + std::to_string(trackId)).c_str()).c_str()); + } + else + { + memset((void*)buf, 0, buf_size); + snprintf((char*)buf, buf_size, + // "SETUP %s/track%d RTSP/1.0\r\n" + "SETUP %s/streamid=%d RTSP/1.0\r\n" + "Transport: RTP/AVP/TCP;unicast;mode=record;interleaved=%d-%d\r\n" + "CSeq: %u\r\n" + "User-Agent: %s\r\n" + "Session: %llu\r\n" + "\r\n", + rtsp_url_.c_str(), + trackId, + interleaved[0], + interleaved[1], + this->GetCSeq() + 1, + user_agent_.c_str(), + this->GetSession()); + } method_ = SETUP; return (int)strlen(buf); } -int RtspResponse::BuildRecordReq(const char* buf, int buf_size) -{ - memset((void*)buf, 0, buf_size); - snprintf((char*)buf, buf_size, - "RECORD %s RTSP/1.0\r\n" - "Range: npt=0.000-\r\n" - "CSeq: %u\r\n" - "User-Agent: %s\r\n" - "Session: %s\r\n" - "\r\n", - rtsp_url_.c_str(), - this->GetCSeq() + 1, - user_agent_.c_str(), - this->GetSession().c_str()); - +int RtspResponse::BuildRecordReq(const char* buf, int buf_size, std::string& nonce, Authenticator* auth) +{ + if(auth != nullptr && nonce.size() > 0) + { + memset((void*)buf, 0, buf_size); + snprintf((char*)buf, buf_size, + "RECORD %s RTSP/1.0\r\n" + "Range: npt=0.000-\r\n" + "CSeq: %u\r\n" + "User-Agent: %s\r\n" + "Session: %llu\r\n" + "Authorization: Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"\r\n" + "\r\n", + rtsp_url_.c_str(), + this->GetCSeq() + 1, + user_agent_.c_str(), + this->GetSession(), + auth->GetUsername().c_str(), auth->GetRealm().c_str(), nonce.c_str(), rtsp_url_.c_str(), + auth->GetResponse(nonce, "RECORD", rtsp_url_.c_str()).c_str()); + } + else + { + memset((void*)buf, 0, buf_size); + snprintf((char*)buf, buf_size, + "RECORD %s RTSP/1.0\r\n" + "Range: npt=0.000-\r\n" + "CSeq: %u\r\n" + "User-Agent: %s\r\n" + "Session: %llu\r\n" + "\r\n", + rtsp_url_.c_str(), + this->GetCSeq() + 1, + user_agent_.c_str(), + this->GetSession()); + } method_ = RECORD; return (int)strlen(buf); } diff --git a/src/xop/RtspMessage.h b/src/xop/RtspMessage.h index 8f2d2ca..e5ddfc2 100644 --- a/src/xop/RtspMessage.h +++ b/src/xop/RtspMessage.h @@ -1,4 +1,4 @@ -// PHZ +// PHZ // 2018-6-8 #ifndef XOP_RTSP_MESSAGE_H @@ -12,8 +12,11 @@ #include "media.h" #include "net/BufferReader.h" + + namespace xop { +class Authenticator; class RtspRequest { @@ -52,6 +55,9 @@ class RtspRequest Method GetMethod() const { return method_; } + + std::string GetMethodAsString() const + { return std::string(MethodToString[method_]); } uint32_t GetCSeq() const; @@ -78,12 +84,13 @@ class RtspRequest int BuildOptionRes(const char* buf, int buf_size); int BuildDescribeRes(const char* buf, int buf_size, const char* sdp); - int BuildSetupMulticastRes(const char* buf, int buf_size, const char* multicast_ip, uint16_t port, uint32_t session_id); - int BuildSetupTcpRes(const char* buf, int buf_size, uint16_t rtp_chn, uint16_t rtcp_chn, uint32_t session_id); - int BuildSetupUdpRes(const char* buf, int buf_size, uint16_t rtp_chn, uint16_t rtcp_chn, uint32_t session_id); - int BuildPlayRes(const char* buf, int buf_size, const char* rtp_info, uint32_t session_id); - int BuildTeardownRes(const char* buf, int buf_size, uint32_t session_id); - int BuildGetParamterRes(const char* buf, int buf_size, uint32_t session_id); + int BuildSetupMulticastRes(const char* buf, int buf_size, const char* multicast_ip, uint16_t port, uint64_t session_id); + int BuildSetupTcpRes(const char* buf, int buf_size, uint16_t rtp_chn, uint16_t rtcp_chn, uint64_t session_id); + int BuildSetupUdpRes(const char* buf, int buf_size, uint16_t rtp_chn, uint16_t rtcp_chn, uint64_t session_id); + int BuildPlayRes(const char* buf, int buf_size, const char* rtp_info, uint64_t session_id); + int BuildTeardownRes(const char* buf, int buf_size, uint64_t session_id); + //int BuildTeardownMSG(const char* buf, int buf_size, uint32_t session_id); + int BuildGetParamterRes(const char* buf, int buf_size, uint64_t session_id); int BuildNotFoundRes(const char* buf, int buf_size); int BuildServerErrorRes(const char* buf, int buf_size); int BuildUnsupportedRes(const char* buf, int buf_size); @@ -115,19 +122,31 @@ class RtspResponse public: enum Method { - OPTIONS=0, DESCRIBE, ANNOUNCE, SETUP, RECORD, RTCP, + OPTIONS=0, DESCRIBE, ANNOUNCE, SETUP, RECORD, RTCP, TEARDOWN, NONE, }; + + const char* MethodToString[8] = + { + "OPTIONS", "DESCRIBE", "ANNOUNCE", "SETUP", "RECORD", "RTCP", + "TEARDOWN", "NONE" + }; + + RtspResponse(); bool ParseResponse(xop::BufferReader *buffer); Method GetMethod() const { return method_; } + + std::string GetMethodAsString() const + { return std::string(MethodToString[method_]); } uint32_t GetCSeq() const { return cseq_; } + void SetCSeq(uint32_t cseq) { cseq_ = cseq; } - std::string GetSession() const + uint64_t GetSession() const { return session_; } void SetUserAgent(const char *user_agent) @@ -136,18 +155,19 @@ class RtspResponse void SetRtspUrl(const char *url) { rtsp_url_ = std::string(url); } - int BuildOptionReq(const char* buf, int buf_size); - int BuildDescribeReq(const char* buf, int buf_size); - int BuildAnnounceReq(const char* buf, int buf_size, const char *sdp); - int BuildSetupTcpReq(const char* buf, int buf_size, int channel); - int BuildRecordReq(const char* buf, int buf_size); - + int BuildOptionReq(const char* buf, int buf_size, std::string& nonce, Authenticator* auth = nullptr); + int BuildDescribeReq(const char* buf, int buf_size, std::string& nonce, Authenticator* auth); + int BuildAnnounceReq(const char* buf, int buf_size, const char *sdp, std::string& nonce, Authenticator* auth); + int BuildSetupTcpReq(const char* buf, int buf_size, int channel, std::string& nonce, Authenticator* auth); + int BuildTeardownReq(const char* buf, int buf_size, std::string& nonce, Authenticator* auth); + int BuildRecordReq(const char* buf, int buf_size, std::string& nonce, Authenticator* auth); + private: Method method_; uint32_t cseq_ = 0; std::string user_agent_; std::string rtsp_url_; - std::string session_; + uint64_t session_; }; } diff --git a/src/xop/RtspPusher.cpp b/src/xop/RtspPusher.cpp index 638160b..c88945a 100644 --- a/src/xop/RtspPusher.cpp +++ b/src/xop/RtspPusher.cpp @@ -1,4 +1,4 @@ -#include "RtspPusher.h" +#include "RtspPusher.h" #include "RtspConnection.h" #include "net/Logger.h" #include "net/TcpSocket.h" @@ -45,63 +45,79 @@ int RtspPusher::OpenUrl(std::string url, int msec) { std::lock_guard lock(mutex_); - static xop::Timestamp timestamp; - int timeout = msec; - if (timeout <= 0) { - timeout = 10000; - } - - timestamp.Reset(); - - if (!this->ParseRtspUrl(url)) { - LOG_ERROR("rtsp url(%s) was illegal.\n", url.c_str()); - return -1; - } - - if (rtsp_conn_ != nullptr) { - std::shared_ptr rtspConn = rtsp_conn_; - SOCKET sockfd = rtspConn->GetSocket(); - task_scheduler_->AddTriggerEvent([sockfd, rtspConn]() { - rtspConn->Disconnect(); - }); - rtsp_conn_ = nullptr; - } - - TcpSocket tcpSocket; - tcpSocket.Create(); - if (!tcpSocket.Connect(rtsp_url_info_.ip, rtsp_url_info_.port, timeout)) - { - tcpSocket.Close(); - return -1; - } - - task_scheduler_ = event_loop_->GetTaskScheduler().get(); - rtsp_conn_.reset(new RtspConnection(shared_from_this(), task_scheduler_, tcpSocket.GetSocket())); - event_loop_->AddTriggerEvent([this]() { - rtsp_conn_->SendOptions(RtspConnection::RTSP_PUSHER); - }); - - timeout -= (int)timestamp.Elapsed(); - if (timeout < 0) { - timeout = 1000; - } - - do - { - xop::Timer::Sleep(100); - timeout -= 100; - } while (!rtsp_conn_->IsRecord() && timeout > 0); - - if (!rtsp_conn_->IsRecord()) { - std::shared_ptr rtspConn = rtsp_conn_; - SOCKET sockfd = rtspConn->GetSocket(); - task_scheduler_->AddTriggerEvent([sockfd, rtspConn]() { - rtspConn->Disconnect(); - }); - rtsp_conn_ = nullptr; - return -1; - } - + int retry = false; std::string nonce; uint32_t cseq = 0; +// while(true) +// { + static xop::Timestamp timestamp; + int timeout = msec; + if (timeout <= 0) { + timeout = 5000; + } + + timestamp.Reset(); + + if (!this->ParseRtspUrl(url)) { + LOG_ERROR("rtsp url(%s) was illegal.\n", url.c_str()); + return -1; + } + + if (rtsp_conn_ != nullptr) { + std::shared_ptr rtspConn = rtsp_conn_; + SOCKET sockfd = rtspConn->GetSocket(); + task_scheduler_->AddTriggerEvent([sockfd, rtspConn]() { + rtspConn->Disconnect(); + }); + nonce = rtsp_conn_->GetNonce(); + cseq = rtsp_conn_->GetCseq() + 1; + rtsp_conn_ = nullptr; + } + + TcpSocket tcpSocket; + tcpSocket.Create(); + if (!tcpSocket.Connect(rtsp_url_info_.ip, rtsp_url_info_.port, timeout)) + { + tcpSocket.Close(); + return -1; + } + + task_scheduler_ = event_loop_->GetTaskScheduler().get(); + rtsp_conn_.reset(new RtspConnection(shared_from_this(), task_scheduler_, tcpSocket.GetSocket())); + rtsp_conn_->SetNonce(nonce); + rtsp_conn_->SetCseq(cseq); + event_loop_->AddTriggerEvent([this]() { + rtsp_conn_->SendOptions(RtspConnection::RTSP_PUSHER); + }); + + timeout -= (int)timestamp.Elapsed(); + if (timeout < 0) { + timeout = 1000; + } + // we should check authentication status here! + do + { + xop::Timer::Sleep(100); + timeout -= 100; + } while (!rtsp_conn_->IsRecord() && timeout > 0); + + if (!rtsp_conn_->IsRecord()) { + std::shared_ptr rtspConn = rtsp_conn_; + SOCKET sockfd = rtspConn->GetSocket(); + task_scheduler_->AddTriggerEvent([sockfd, rtspConn]() { + rtspConn->Disconnect(); + }); + + // here we need to save some states + nonce = rtsp_conn_->GetNonce(); + cseq = rtsp_conn_->GetCseq() + 1; + + rtsp_conn_ = nullptr; + if(!retry) + { + return -1; + } + retry = false; + } +// } return 0; } @@ -110,6 +126,31 @@ void RtspPusher::Close() std::lock_guard lock(mutex_); if (rtsp_conn_ != nullptr) { + + // send tear-down + //TEARDOWN + task_scheduler_ = event_loop_->GetTaskScheduler().get(); + event_loop_->AddTriggerEvent([this]() { + rtsp_conn_->SendTeardown(RtspConnection::RTSP_PUSHER); + }); + + static xop::Timestamp timestamp; + int timeout = 2000; + if (timeout <= 0) { + timeout = 5000; + } + + timeout -= (int)timestamp.Elapsed(); + if (timeout < 0) { + timeout = 1000; + } + // we should check authentication status here! + do + { + xop::Timer::Sleep(100); + timeout -= 100; + } while (rtsp_conn_->IsRecord() && timeout > 0); + std::shared_ptr rtsp_conn = rtsp_conn_; SOCKET sockfd = rtsp_conn->GetSocket(); task_scheduler_->AddTriggerEvent([sockfd, rtsp_conn]() { @@ -137,4 +178,4 @@ bool RtspPusher::PushFrame(MediaChannelId channelId, AVFrame frame) } return media_session_->HandleFrame(channelId, frame); -} \ No newline at end of file +} diff --git a/src/xop/RtspPusher.h b/src/xop/RtspPusher.h index 566f7bf..c5f75c2 100644 --- a/src/xop/RtspPusher.h +++ b/src/xop/RtspPusher.h @@ -10,7 +10,7 @@ namespace xop class RtspConnection; -class RtspPusher : public Rtsp +class DLL_API RtspPusher : public Rtsp { public: static std::shared_ptr Create(xop::EventLoop* loop); diff --git a/src/xop/RtspServer.cpp b/src/xop/RtspServer.cpp index b7bcf27..3f28e47 100644 --- a/src/xop/RtspServer.cpp +++ b/src/xop/RtspServer.cpp @@ -6,32 +6,60 @@ using namespace xop; using namespace std; +#define RTSP_DEBUG 0 + RtspServer::RtspServer(EventLoop* loop) : TcpServer(loop) { - +#if RTSP_DEBUG + #if defined(ANDROID) + __android_log_print(ANDROID_LOG_ERROR, MODULE_NAME, "RtspServer Constructor"); + #else + std::cout << "RtspServer Constructor" << std::endl; + #endif +#endif } RtspServer::~RtspServer() { - +#if RTSP_DEBUG + #if defined(ANDROID) + __android_log_print(ANDROID_LOG_ERROR, MODULE_NAME, "RtspServer Destructor"); + #else + std::cout << "RtspServer Destructor" << std::endl; + #endif +#endif } std::shared_ptr RtspServer::Create(xop::EventLoop* loop) { +#if RTSP_DEBUG + #if defined(ANDROID) + __android_log_print(ANDROID_LOG_ERROR, MODULE_NAME, "RtspServer Create"); + #else + std::cout << "RtspServer Create" << std::endl; + #endif +#endif std::shared_ptr server(new RtspServer(loop)); return server; } MediaSessionId RtspServer::AddSession(MediaSession* session) { +#if RTSP_DEBUG + #if defined(ANDROID) + __android_log_print(ANDROID_LOG_ERROR, MODULE_NAME, "RtspServer AddSession"); + #else + std::cout << "RtspServer AddSession" << std::endl; + #endif +#endif std::lock_guard locker(mutex_); if (rtsp_suffix_map_.find(session->GetRtspUrlSuffix()) != rtsp_suffix_map_.end()) { return 0; } - std::shared_ptr media_session(session); + std::shared_ptr media_session(session); MediaSessionId sessionId = media_session->GetMediaSessionId(); rtsp_suffix_map_.emplace(std::move(media_session->GetRtspUrlSuffix()), sessionId); media_sessions_.emplace(sessionId, std::move(media_session)); @@ -41,6 +69,13 @@ MediaSessionId RtspServer::AddSession(MediaSession* session) void RtspServer::RemoveSession(MediaSessionId sessionId) { +#if RTSP_DEBUG + #if defined(ANDROID) + __android_log_print(ANDROID_LOG_VERBOSE, MODULE_NAME, "RtspServer: RemoveSession"); + #else + std::cout << "RtspServer: RemoveSession" << std::endl; + #endif +#endif std::lock_guard locker(mutex_); auto iter = media_sessions_.find(sessionId); @@ -52,6 +87,13 @@ void RtspServer::RemoveSession(MediaSessionId sessionId) MediaSession::Ptr RtspServer::LookMediaSession(const std::string& suffix) { +#if RTSP_DEBUG + #if defined(ANDROID) + __android_log_print(ANDROID_LOG_VERBOSE, MODULE_NAME, "RtspServer: LookMediaSession by suffix..."); + #else + std::cout << "RtspServer: LookMediaSession by suffix..." << std::endl; + #endif +#endif std::lock_guard locker(mutex_); auto iter = rtsp_suffix_map_.find(suffix); @@ -65,6 +107,13 @@ MediaSession::Ptr RtspServer::LookMediaSession(const std::string& suffix) MediaSession::Ptr RtspServer::LookMediaSession(MediaSessionId session_Id) { +#if RTSP_DEBUG + #if defined(ANDROID) + __android_log_print(ANDROID_LOG_VERBOSE, MODULE_NAME, "RtspServer: LookMediaSession..."); + #else + std::cout << "RtspServer: LookMediaSession..." << std::endl; + #endif +#endif std::lock_guard locker(mutex_); auto iter = media_sessions_.find(session_Id); @@ -77,8 +126,14 @@ MediaSession::Ptr RtspServer::LookMediaSession(MediaSessionId session_Id) bool RtspServer::PushFrame(MediaSessionId session_id, MediaChannelId channel_id, AVFrame frame) { +#if RTSP_DEBUG + #if defined(ANDROID) + __android_log_print(ANDROID_LOG_ERROR, MODULE_NAME, "RtspServer PushFrame"); + #else + std::cout << "RtspServer PushFrame" << std::endl; + #endif +#endif std::shared_ptr sessionPtr = nullptr; - { std::lock_guard locker(mutex_); auto iter = media_sessions_.find(session_id); @@ -98,7 +153,13 @@ bool RtspServer::PushFrame(MediaSessionId session_id, MediaChannelId channel_id, } TcpConnection::Ptr RtspServer::OnConnect(SOCKET sockfd) -{ +{ +#if RTSP_DEBUG + #if defined(ANDROID) + __android_log_print(ANDROID_LOG_VERBOSE, MODULE_NAME, "RtspServer: received connect request..."); + #else + std::cout << "RtspServer: received connect request..." << std::endl; + #endif +#endif return std::make_shared(shared_from_this(), event_loop_->GetTaskScheduler().get(), sockfd); } - diff --git a/src/xop/RtspServer.h b/src/xop/RtspServer.h index 2806203..e7eaa22 100644 --- a/src/xop/RtspServer.h +++ b/src/xop/RtspServer.h @@ -16,7 +16,7 @@ namespace xop class RtspConnection; -class RtspServer : public Rtsp, public TcpServer +class DLL_API RtspServer : public Rtsp, public TcpServer { public: static std::shared_ptr Create(xop::EventLoop* loop); diff --git a/src/xop/rtp.h b/src/xop/rtp.h index 35ca770..6559c1e 100644 --- a/src/xop/rtp.h +++ b/src/xop/rtp.h @@ -1,4 +1,4 @@ -// PHZ +// PHZ // 2018-6-11 #ifndef XOP_RTP_H @@ -15,27 +15,58 @@ namespace xop { -enum TransportMode -{ - RTP_OVER_TCP = 1, - RTP_OVER_UDP = 2, - RTP_OVER_MULTICAST = 3, + enum TransportMode + { + RTP_OVER_TCP = 1, + RTP_OVER_UDP = 2, + RTP_OVER_MULTICAST = 3, + }; + + typedef struct _RTP_header + { + /* 小端序 */ + unsigned char csrc : 4; + unsigned char extension : 1; + unsigned char padding : 1; + unsigned char version : 2; + unsigned char payload : 7; + unsigned char marker : 1; + + unsigned short seq; + unsigned int ts; + unsigned int ssrc; + } RtpHeader; + +#ifdef WIN32 +#pragma pack(push,1) +#endif + +//=============== TAKEN FROM https://github.com/sipwise/rtpengine +struct rtcp_header { + unsigned count : 5; /**< varies by payload type */ + unsigned p : 1; /**< padding flag */ + unsigned version : 2; /**< packet type */ + unsigned char pt; + uint16_t length; +#ifndef WIN32 +} __attribute__((packed)); +#else }; +#endif + +struct rtcp_packet { + struct rtcp_header header; + uint32_t ssrc; +#ifndef WIN32 +} __attribute__((packed)); +#else +}; +#pragma pack(pop) +#endif + +//=============================== + -typedef struct _RTP_header -{ - /* 小端序 */ - unsigned char csrc:4; - unsigned char extension:1; - unsigned char padding:1; - unsigned char version:2; - unsigned char payload:7; - unsigned char marker:1; - - unsigned short seq; - unsigned int ts; - unsigned int ssrc; -} RtpHeader; struct MediaChannelInfo { diff --git a/src/xop/rtsp.h b/src/xop/rtsp.h index 75b444e..190af00 100644 --- a/src/xop/rtsp.h +++ b/src/xop/rtsp.h @@ -13,6 +13,13 @@ #include "net/Socket.h" #include "net/Timer.h" +#if defined(ANDROID) + #include + #ifndef MODULE_NAME + #define MODULE_NAME "RTSPSERVER" + #endif +#endif + namespace xop { @@ -24,13 +31,13 @@ struct RtspUrlInfo std::string suffix; }; -class Rtsp : public std::enable_shared_from_this +class DLL_API Rtsp : public std::enable_shared_from_this { public: Rtsp() : has_auth_info_(false), authenticator_(nullptr) {} virtual ~Rtsp() {} - virtual void SetAuthenticator(std::shared_ptrauthenticator) + virtual void SetAuthenticator(std::shared_ptrauthenticator) { std::cerr << "Setting Authenticator" << std::endl; authenticator_ = authenticator; @@ -51,7 +58,7 @@ class Rtsp : public std::enable_shared_from_this char ip[100] = { 0 }; char suffix[100] = { 0 }; uint16_t port = 0; -#if defined(__linux) || defined(__linux__) || defined(__FreeBSD__) +#if defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) if (sscanf(url.c_str() + 7, "%[^:]:%hu/%s", ip, &port, suffix) == 3) #elif defined(WIN32) || defined(_WIN32) if (sscanf_s(url.c_str() + 7, "%[^:]:%hu/%s", ip, 100, &port, suffix, 100) == 3) @@ -59,7 +66,7 @@ class Rtsp : public std::enable_shared_from_this { rtsp_url_info_.port = port; } -#if defined(__linux) || defined(__linux__) || defined(__FreeBSD__) +#if defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) else if (sscanf(url.c_str() + 7, "%[^/]/%s", ip, suffix) == 2) #elif defined(WIN32) || defined(_WIN32) else if (sscanf_s(url.c_str() + 7, "%[^/]/%s", ip, 100, suffix, 100) == 2) @@ -97,5 +104,3 @@ class Rtsp : public std::enable_shared_from_this } #endif - -