From 0ed902509a0e09c4c378a4b9164cd960bf40f359 Mon Sep 17 00:00:00 2001 From: baiyfcu <87684855@qq.com> Date: Mon, 9 Aug 2021 18:28:43 +0800 Subject: [PATCH] =?UTF-8?q?player=E5=A2=9E=E5=8A=A0speed=EF=BC=8Cpause?= =?UTF-8?q?=E6=89=A9=E5=B1=95=EF=BC=8Cseek=E6=94=AF=E6=8C=81=E7=A7=92?= =?UTF-8?q?=E7=BA=A7=E5=AE=9A=E4=BD=8D=EF=BC=8CMP4=E6=8C=89=E6=97=B6?= =?UTF-8?q?=E9=97=B4=E6=88=B3=E7=94=9F=E6=88=90=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + CMakeLists.txt | 33 ++++++++++++--- api/include/mk_media.h | 29 ++++++++++++++ api/include/mk_player.h | 30 +++++++++++++- api/source/mk_media.cpp | 47 +++++++++++++++++++++- api/source/mk_player.cpp | 56 ++++++++++++++++++++++++++ server/WebApi.cpp | 82 ++++++++++++++++++++++++++++---------- server/WebApi.h | 3 +- src/Common/MediaSource.cpp | 36 +++++++++++++++++ src/Common/MediaSource.h | 11 +++++ src/Http/HttpDownloader.h | 5 ++- src/Player/MediaPlayer.cpp | 7 ++++ src/Player/MediaPlayer.h | 1 + src/Player/PlayerBase.h | 31 ++++++++++++++ src/Record/MP4Recorder.cpp | 7 +++- src/Record/MP4Recorder.h | 2 + src/Rtmp/RtmpPlayer.cpp | 5 +++ src/Rtmp/RtmpPlayer.h | 1 + src/Rtmp/RtmpPlayerImp.h | 5 +++ src/Rtmp/RtmpSession.cpp | 31 ++++++++++++++ src/Rtmp/RtmpSession.h | 1 + src/Rtsp/RtspPlayer.cpp | 12 ++++-- src/Rtsp/RtspPlayer.h | 3 +- src/Rtsp/RtspPlayerImp.h | 17 +++++++- src/Rtsp/RtspSession.cpp | 51 ++++++++++++++++++------ 25 files changed, 455 insertions(+), 52 deletions(-) diff --git a/.gitignore b/.gitignore index 37c326cd..482e4a61 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ /cmake-build-release/ /linux/ /.vs/ +/.vscode/ /.idea/ /c_wrapper/.idea/ /release/ diff --git a/CMakeLists.txt b/CMakeLists.txt index ec2a611a..dcf4983f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,16 +17,21 @@ endif () #设置bin和lib库目录 set(RELEASE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/release) +if(CMAKE_CL_64) + set(CL_32_64 64) +else(CMAKE_CL_64) + set(CL_32_64 32) +endif(CMAKE_CL_64) if (CMAKE_SYSTEM_NAME MATCHES "Linux") - SET(LIBRARY_OUTPUT_PATH ${RELEASE_DIR}/linux/${BuildType}) - SET(EXECUTABLE_OUTPUT_PATH ${RELEASE_DIR}/linux/${BuildType}) + SET(LIBRARY_OUTPUT_PATH ${RELEASE_DIR}/linux${CL_32_64}/${BuildType}) + SET(EXECUTABLE_OUTPUT_PATH ${RELEASE_DIR}/linux${CL_32_64}/${BuildType}) add_compile_options(-fPIC -Wall -Wno-unused-variable -Wno-unused-value) elseif (CMAKE_SYSTEM_NAME MATCHES "Windows") - SET(LIBRARY_OUTPUT_PATH ${RELEASE_DIR}/windows/${BuildType}) - SET(EXECUTABLE_OUTPUT_PATH ${RELEASE_DIR}/windows/${BuildType}) + SET(LIBRARY_OUTPUT_PATH ${RELEASE_DIR}/windows${CL_32_64}/${BuildType}) + SET(EXECUTABLE_OUTPUT_PATH ${RELEASE_DIR}/windows${CL_32_64}/${BuildType}) elseif (CMAKE_SYSTEM_NAME MATCHES "Darwin") - SET(LIBRARY_OUTPUT_PATH ${RELEASE_DIR}/mac/${BuildType}) - SET(EXECUTABLE_OUTPUT_PATH ${RELEASE_DIR}/mac/${BuildType}) + SET(LIBRARY_OUTPUT_PATH ${RELEASE_DIR}/mac${CL_32_64}/${BuildType}) + SET(EXECUTABLE_OUTPUT_PATH ${RELEASE_DIR}/mac${CL_32_64/${BuildType}) add_compile_options(-Wall -Wno-unused-variable -Wno-unused-value) endif () @@ -57,6 +62,22 @@ option(ENABLE_MEM_DEBUG "Enable Memory Debug" false) option(ENABLE_ASAN "Enable Address Sanitize" false) option(ENABLE_WEBRTC "Enable WebRTC" false) option(ENABLE_PLAYER "Enable Player" true) +option(ENABLE_MsvcMT "Enable MSVC Mt/Mtd lib" true) + + +if (ENABLE_STATIC AND MSVC) + set(CompilerFlags + CMAKE_CXX_FLAGS + CMAKE_CXX_FLAGS_DEBUG + CMAKE_CXX_FLAGS_RELEASE + CMAKE_C_FLAGS + CMAKE_C_FLAGS_DEBUG + CMAKE_C_FLAGS_RELEASE + ) + foreach(CompilerFlag ${CompilerFlags}) + string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") + endforeach() +endif() # 添加git版本信息 set(COMMIT_HASH "Git_NotFound_Unkown_commit") diff --git a/api/include/mk_media.h b/api/include/mk_media.h index 48a38835..7a8f2a72 100755 --- a/api/include/mk_media.h +++ b/api/include/mk_media.h @@ -142,6 +142,19 @@ API_EXPORT void API_CALL mk_media_set_on_close(mk_media ctx, on_mk_media_close c */ typedef int(API_CALL *on_mk_media_seek)(void *user_data,uint32_t stamp_ms); +/** + * 收到客户端的pause请求时触发该回调 + * @param user_data 用户数据指针,通过mk_media_set_on_pause设置 + */ +typedef int(API_CALL* on_mk_media_pause)(void* user_data); + +/** + * 收到客户端的speed请求时触发该回调 + * @param user_data 用户数据指针,通过mk_media_set_on_pause设置 + * @param speed 0.5 1.0 2.0 + */ +typedef int(API_CALL* on_mk_media_speed)(void* user_data, float speed); + /** * 监听播放器seek请求事件 * @param ctx 对象指针 @@ -150,6 +163,22 @@ typedef int(API_CALL *on_mk_media_seek)(void *user_data,uint32_t stamp_ms); */ API_EXPORT void API_CALL mk_media_set_on_seek(mk_media ctx, on_mk_media_seek cb, void *user_data); +/** + * 监听播放器pause请求事件 + * @param ctx 对象指针 + * @param cb 回调指针 + * @param user_data 用户数据指针 + */ +API_EXPORT void API_CALL mk_media_set_on_pause(mk_media ctx, on_mk_media_pause cb, void* user_data); + +/** + * 监听播放器pause请求事件 + * @param ctx 对象指针 + * @param cb 回调指针 + * @param user_data 用户数据指针 + */ +API_EXPORT void API_CALL mk_media_set_on_speed(mk_media ctx, on_mk_media_speed cb, void* user_data); + /** * 获取总的观看人数 * @param ctx 对象指针 diff --git a/api/include/mk_player.h b/api/include/mk_player.h index c63942c6..81ad87b6 100755 --- a/api/include/mk_player.h +++ b/api/include/mk_player.h @@ -73,6 +73,13 @@ API_EXPORT void API_CALL mk_player_play(mk_player ctx, const char *url); */ API_EXPORT void API_CALL mk_player_pause(mk_player ctx, int pause); +/** + * 倍数播放,仅对点播有用 + * @param ctx 播放器指针 + * @param speed 0.5 1.0 2.0 + */ +API_EXPORT void API_CALL mk_player_speed(mk_player ctx, float speed); + /** * 设置点播进度条 * @param ctx 对象指针 @@ -80,6 +87,13 @@ API_EXPORT void API_CALL mk_player_pause(mk_player ctx, int pause); */ API_EXPORT void API_CALL mk_player_seekto(mk_player ctx, float progress); +/** + * 设置点播进度条 + * @param ctx 对象指针 + * @param seekPos 取值范围 相对于开始时间增量 单位秒 + */ +API_EXPORT void API_CALL mk_player_seektoByPos(mk_player ctx, int seekPos); + /** * 设置播放器开启播放结果回调函数 * @param ctx 播放器指针 @@ -112,6 +126,15 @@ API_EXPORT void API_CALL mk_player_set_on_data(mk_player ctx, on_mk_play_data cb */ API_EXPORT int API_CALL mk_player_video_codecId(mk_player ctx); +/** + * 获取视频codec_id, vendor类型, 私有头数据 codec_id -1:不存在 0:H264,1:H265,2:AAC 3.G711A 4.G711U + * @param ctx 播放器指针 + * @param vendor 输出厂家类型 如果是私有流 应该是H264另外还有厂家类型 + * @param head 厂家SDK头数据 + * @param head 厂家SDK头数据长度 + */ +API_EXPORT int API_CALL mk_player_video_codecId_vendor_head(mk_player ctx, char* vendor, char* head, int* headLen); + /** * 获取视频宽度 */ @@ -154,10 +177,15 @@ API_EXPORT int API_CALL mk_player_audio_channel(mk_player ctx); API_EXPORT float API_CALL mk_player_duration(mk_player ctx); /** - * 获取点播播放进度,取值范围未 0.0~1.0 + * 获取点播播放进度,取值范围 0.0~1.0 */ API_EXPORT float API_CALL mk_player_progress(mk_player ctx); +/** + * 获取点播播放进度位置,取值范围 相对于开始时间增量 单位秒 + */ +API_EXPORT int API_CALL mk_player_progress_pos(mk_player ctx); + /** * 获取丢包率,rtsp时有效 * @param ctx 对象指针 diff --git a/api/source/mk_media.cpp b/api/source/mk_media.cpp index d9eb9634..6e5e629d 100755 --- a/api/source/mk_media.cpp +++ b/api/source/mk_media.cpp @@ -43,6 +43,16 @@ public: _on_seek_data = user_data; } + void setOnPause(on_mk_media_pause cb, void* user_data) { + _on_pause = cb; + _on_pause_data = user_data; + } + + void setOnSpeed(on_mk_media_speed cb, void* user_data) { + _on_speed = cb; + _on_speed_data = user_data; + } + void setOnRegist(on_mk_media_source_regist cb, void *user_data){ _on_regist = cb; _on_regist_data = user_data; @@ -72,6 +82,23 @@ protected: } return _on_seek(_on_seek_data,ui32Stamp); } + // 通知暂停 + bool pause(MediaSource &sender) override { + if (!_on_pause) + { + return false; + } + return _on_pause(_on_pause_data); + } + //通知倍数播放 + bool speed(MediaSource& sender, float speed) override { + if (!_on_speed) + { + return false; + } + return _on_speed(_on_pause_data, speed); + } + // 观看总人数 int totalReaderCount(MediaSource &sender) override{ return _channel->totalReaderCount(); @@ -87,8 +114,12 @@ private: DevChannel::Ptr _channel; on_mk_media_close _on_close = nullptr; on_mk_media_seek _on_seek = nullptr; + on_mk_media_pause _on_pause = nullptr; + on_mk_media_speed _on_speed = nullptr; on_mk_media_source_regist _on_regist = nullptr; - void *_on_seek_data; + void* _on_seek_data; + void* _on_pause_data; + void* _on_speed_data; void *_on_close_data; void *_on_regist_data; }; @@ -105,6 +136,20 @@ API_EXPORT void API_CALL mk_media_set_on_seek(mk_media ctx, on_mk_media_seek cb, (*obj)->setOnSeek(cb, user_data); } +API_EXPORT void API_CALL mk_media_set_on_pause(mk_media ctx, on_mk_media_pause cb, void* user_data) +{ + assert(ctx); + MediaHelper::Ptr* obj = (MediaHelper::Ptr*) ctx; + (*obj)->setOnPause(cb, user_data); +} + +API_EXPORT void API_CALL mk_media_set_on_speed(mk_media ctx, on_mk_media_speed cb, void* user_data) +{ + assert(ctx); + MediaHelper::Ptr* obj = (MediaHelper::Ptr*) ctx; + (*obj)->setOnSpeed(cb, user_data); +} + API_EXPORT void API_CALL mk_media_set_on_regist(mk_media ctx, on_mk_media_source_regist cb, void *user_data){ assert(ctx); MediaHelper::Ptr *obj = (MediaHelper::Ptr *) ctx; diff --git a/api/source/mk_player.cpp b/api/source/mk_player.cpp index 44f373af..d8db3182 100755 --- a/api/source/mk_player.cpp +++ b/api/source/mk_player.cpp @@ -11,6 +11,8 @@ #include "mk_player.h" #include "Util/logger.h" #include "Player/MediaPlayer.h" +#include "Extension/H264.h" + using namespace std; using namespace toolkit; using namespace mediakit; @@ -167,6 +169,16 @@ API_EXPORT void API_CALL mk_player_pause(mk_player ctx, int pause) { }); } +API_EXPORT void API_CALL mk_player_speed(mk_player ctx, float speed) { + assert(ctx); + MediaPlayerForC& obj = **((MediaPlayerForC::Ptr*)ctx); + auto player = obj.getPlayer(); + player->getPoller()->async([speed, player]() { + //切换线程后再操作 + player->speed(speed); + }); +} + API_EXPORT void API_CALL mk_player_seekto(mk_player ctx, float progress) { assert(ctx); MediaPlayerForC &obj = **((MediaPlayerForC::Ptr *)ctx); @@ -177,6 +189,16 @@ API_EXPORT void API_CALL mk_player_seekto(mk_player ctx, float progress) { }); } +API_EXPORT void API_CALL mk_player_seektoByPos(mk_player ctx, int seekPos){ + assert(ctx); + MediaPlayerForC& obj = **((MediaPlayerForC::Ptr*)ctx); + auto player = obj.getPlayer(); + player->getPoller()->async([seekPos, player]() { + //切换线程后再操作 + player->seekTo((uint32_t)seekPos); + }); +} + static void mk_player_set_on_event(mk_player ctx, on_mk_play_event cb, void *user_data, int type) { assert(ctx); MediaPlayerForC &obj = **((MediaPlayerForC::Ptr *)ctx); @@ -204,6 +226,32 @@ API_EXPORT int API_CALL mk_player_video_codecId(mk_player ctx){ return track ? track->getCodecId() : CodecInvalid; } +API_EXPORT int API_CALL mk_player_video_codecId_vendor_head(mk_player ctx, char* vendor, char* head, int* head_len) +{ + assert(ctx); + MediaPlayerForC& obj = **((MediaPlayerForC::Ptr*)ctx); + auto track = dynamic_pointer_cast(obj->getTrack(TrackVideo)); + int codecId = track ? track->getCodecId() : CodecInvalid; + if (codecId == CodecH264) + { + auto h264Track = dynamic_pointer_cast(obj->getTrack(TrackVideo)); + auto pps = h264Track->getPps(); + auto ppsLen = pps.size(); + if (ppsLen >= (4 + 16)) + { + std::string temVendor = std::string(pps.c_str() + 4, 16); + memcpy(vendor, temVendor.c_str(), temVendor.length()); + if (ppsLen > (4 + 16)) + { + std::string temHead = std::string(pps.c_str() + 20, ppsLen - 20); + memcpy(head, temHead.c_str(), temHead.length()); + *head_len = temHead.length(); + } + } + } + return codecId; +} + API_EXPORT int API_CALL mk_player_video_width(mk_player ctx) { assert(ctx); MediaPlayerForC &obj = **((MediaPlayerForC::Ptr *)ctx); @@ -265,6 +313,14 @@ API_EXPORT float API_CALL mk_player_progress(mk_player ctx) { return obj->getProgress(); } + +API_EXPORT int API_CALL mk_player_progress_pos(mk_player ctx) +{ + assert(ctx); + MediaPlayerForC& obj = **((MediaPlayerForC::Ptr*)ctx); + return obj->getProgressPos(); +} + API_EXPORT float API_CALL mk_player_loss_rate(mk_player ctx, int track_type) { assert(ctx); MediaPlayerForC &obj = **((MediaPlayerForC::Ptr *)ctx); diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 8e171eea..9d7bf0f6 100755 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -118,13 +118,15 @@ static HttpApi toApi(const function &cb) { Json::Reader reader; reader.parse(parser.Content(), in); - cb(sender, parser.getHeader(), headerOut, in, out, invoker); + //参数解析成map + auto urlArgs = getAllArgs(parser); + cb(sender, parser.getHeader(), headerOut, in, urlArgs, out, invoker); }; } static HttpApi toApi(const function &cb) { return toApi([cb](API_ARGS_JSON_ASYNC) { - cb(API_ARGS_VALUE); + cb(API_ARGS_JSON_VALUE); invoker(200, headerOut, val.toStyledString()); }); } @@ -228,18 +230,19 @@ static inline void addHttpListener(){ size = body->remainSize(); } + const std::string& content = parser.Content(); if (size && size < 4 * 1024) { string contentOut = body->readData(size)->toString(); DebugL << "\r\n# request:\r\n" << parser.Method() << " " << parser.FullUrl() << "\r\n" - << "# content:\r\n" << parser.Content() << "\r\n" - << "# response:\r\n" - << contentOut << "\r\n"; + << "# content:\r\n" << (content.size() > 4 * 1024 ? content.substr(0, 4 * 1024) : content) << "\r\n" + << "# response:\r\n" + << contentOut << "\r\n"; invoker(code, headerOut, contentOut); } else { DebugL << "\r\n# request:\r\n" << parser.Method() << " " << parser.FullUrl() << "\r\n" - << "# content:\r\n" << parser.Content() << "\r\n" - << "# response size:" - << size << "\r\n"; + << "# content:\r\n" << (content.size() > 4 * 1024 ? content.substr(0, 4 * 1024) : content) << "\r\n" + << "# response size:" + << size << "\r\n"; invoker(code, headerOut, body); } }; @@ -276,8 +279,8 @@ static recursive_mutex s_ffmpegMapMtx; #if defined(ENABLE_RTPPROXY) //rtp服务器列表 -static unordered_map s_rtpServerMap; -static recursive_mutex s_rtpServerMapMtx; +/*static*/ unordered_map s_rtpServerMap; +/*static*/ recursive_mutex s_rtpServerMapMtx; #endif static inline string getProxyKey(const string &vhost, const string &app, const string &stream) { @@ -932,21 +935,56 @@ void installWebApi() { api_regist("/index/api/startSendRtp",[](API_ARGS_MAP_ASYNC){ CHECK_SECRET(); CHECK_ARGS("vhost", "app", "stream", "ssrc", "dst_url", "dst_port", "is_udp"); + bool isAsync = false; + if (checkArgs(allArgs, "is_async")) + isAsync = allArgs["is_async"]; + if (isAsync) + { + MediaInfo info = {}; + info._vhost = allArgs["vhost"]; + info._app = allArgs["app"]; + info._streamid = allArgs["stream"]; + info._schema = RTSP_SCHEMA; + auto session = static_cast(&sender); + auto session_ptr = session->shared_from_this(); - auto src = MediaSource::find(allArgs["vhost"], allArgs["app"], allArgs["stream"]); - if (!src) { - throw ApiRetException("该媒体流不存在", API::OtherFailed); + MediaSource::findAsync(info, session_ptr, [=](const MediaSource::Ptr& src_in) mutable { + if (!src_in) { + val["code"] = API::OtherFailed; + val["msg"] = "该媒体流不存在"; + invoker(200, headerOut, val.toStyledString()); + return; + } + auto src = dynamic_pointer_cast(src_in); + //src_port为空时,则随机本地端口 + src->startSendRtp(allArgs["dst_url"], allArgs["dst_port"], allArgs["ssrc"], allArgs["is_udp"], allArgs["src_port"], [val, headerOut, invoker](uint16_t local_port, const SockException& ex) mutable { + if (ex) { + val["code"] = API::OtherFailed; + val["msg"] = ex.what(); + } + val["local_port"] = local_port; + invoker(200, headerOut, val.toStyledString()); + }); + }); + } + else + { + auto src = MediaSource::find(allArgs["vhost"], allArgs["app"], allArgs["stream"]); + if (!src) { + throw ApiRetException("该媒体流不存在", API::OtherFailed); + } + + //src_port为空时,则随机本地端口 + src->startSendRtp(allArgs["dst_url"], allArgs["dst_port"], allArgs["ssrc"], allArgs["is_udp"], allArgs["src_port"], [val, headerOut, invoker](uint16_t local_port, const SockException& ex) mutable { + if (ex) { + val["code"] = API::OtherFailed; + val["msg"] = ex.what(); + } + val["local_port"] = local_port; + invoker(200, headerOut, val.toStyledString()); + }); } - //src_port为空时,则随机本地端口 - src->startSendRtp(allArgs["dst_url"], allArgs["dst_port"], allArgs["ssrc"], allArgs["is_udp"], allArgs["src_port"], [val, headerOut, invoker](uint16_t local_port, const SockException &ex) mutable{ - if (ex) { - val["code"] = API::OtherFailed; - val["msg"] = ex.what(); - } - val["local_port"] = local_port; - invoker(200, headerOut, val.toStyledString()); - }); }); api_regist("/index/api/stopSendRtp",[](API_ARGS_MAP){ diff --git a/server/WebApi.h b/server/WebApi.h index 48b4580f..a039393f 100755 --- a/server/WebApi.h +++ b/server/WebApi.h @@ -83,11 +83,12 @@ using ApiArgsType = map; #define API_ARGS_MAP SockInfo &sender, HttpSession::KeyValue &headerIn, HttpSession::KeyValue &headerOut, ApiArgsType &allArgs, Json::Value &val #define API_ARGS_MAP_ASYNC API_ARGS_MAP, const HttpSession::HttpResponseInvoker &invoker -#define API_ARGS_JSON SockInfo &sender, HttpSession::KeyValue &headerIn, HttpSession::KeyValue &headerOut, Json::Value &allArgs, Json::Value &val +#define API_ARGS_JSON SockInfo &sender, HttpSession::KeyValue &headerIn, HttpSession::KeyValue &headerOut, Json::Value &bodyArgs, ApiArgsType &allArgs, Json::Value &val #define API_ARGS_JSON_ASYNC API_ARGS_JSON, const HttpSession::HttpResponseInvoker &invoker #define API_ARGS_STRING SockInfo &sender, HttpSession::KeyValue &headerIn, HttpSession::KeyValue &headerOut, const Parser &allArgs, Json::Value &val #define API_ARGS_STRING_ASYNC API_ARGS_STRING, const HttpSession::HttpResponseInvoker &invoker #define API_ARGS_VALUE sender, headerIn, headerOut, allArgs, val +#define API_ARGS_JSON_VALUE sender, headerIn, headerOut, bodyArgs, allArgs, val //注册http请求参数是map类型的http api void api_regist(const string &api_path, const function &func); diff --git a/src/Common/MediaSource.cpp b/src/Common/MediaSource.cpp index 1809f8c5..a29bfd09 100644 --- a/src/Common/MediaSource.cpp +++ b/src/Common/MediaSource.cpp @@ -157,6 +157,24 @@ bool MediaSource::seekTo(uint32_t stamp) { return listener->seekTo(*this, stamp); } +bool MediaSource::pause() +{ + auto listener = _listener.lock(); + if (!listener) { + return false; + } + return listener->pause(*this); +} + +bool MediaSource::speed(float speed) +{ + auto listener = _listener.lock(); + if (!listener) { + return false; + } + return listener->speed(*this, speed); +} + bool MediaSource::close(bool force) { auto listener = _listener.lock(); if(!listener){ @@ -588,6 +606,24 @@ bool MediaSourceEventInterceptor::seekTo(MediaSource &sender, uint32_t stamp) { return listener->seekTo(sender, stamp); } +bool MediaSourceEventInterceptor::pause(MediaSource& sender) +{ + auto listener = _listener.lock(); + if (!listener) { + return false; + } + return listener->pause(sender); +} + +bool MediaSourceEventInterceptor::speed(MediaSource& sender, float speed) +{ + auto listener = _listener.lock(); + if (!listener) { + return false; + } + return listener->speed(sender, speed); +} + bool MediaSourceEventInterceptor::close(MediaSource &sender, bool force) { auto listener = _listener.lock(); if (!listener) { diff --git a/src/Common/MediaSource.h b/src/Common/MediaSource.h index 8e433dfe..c06167ae 100644 --- a/src/Common/MediaSource.h +++ b/src/Common/MediaSource.h @@ -67,6 +67,10 @@ public: // 通知拖动进度条 virtual bool seekTo(MediaSource &sender, uint32_t stamp) { return false; } + // 通知暂停 + virtual bool pause(MediaSource& sender) { return false; } + // 通知倍数 + virtual bool speed(MediaSource& sender, float speed) { return false; } // 通知其停止产生流 virtual bool close(MediaSource &sender, bool force) { return false; } // 获取观看总人数 @@ -106,6 +110,8 @@ public: std::shared_ptr getOriginSock(MediaSource &sender) const override; bool seekTo(MediaSource &sender, uint32_t stamp) override; + bool pause(MediaSource& sender) override; + bool speed(MediaSource& sender, float speed) override; bool close(MediaSource &sender, bool force) override; int totalReaderCount(MediaSource &sender) override; void onReaderChanged(MediaSource &sender, int size) override; @@ -249,6 +255,11 @@ public: // 拖动进度条 bool seekTo(uint32_t stamp); + //暂停 + bool pause(); + + //倍数播放 + bool speed(float speed); // 关闭该流 bool close(bool force); // 该流观看人数变化 diff --git a/src/Http/HttpDownloader.h b/src/Http/HttpDownloader.h index 124994e7..fc9f4b27 100644 --- a/src/Http/HttpDownloader.h +++ b/src/Http/HttpDownloader.h @@ -30,9 +30,12 @@ public: void setOnResult(const onDownloadResult &cb){ _onResult = cb; } + +protected: + void onResponseBody(const char *buf, size_t size, size_t recvedSize, size_t totalSize) override; + private: ssize_t onResponseHeader(const string &status, const HttpHeader &headers) override; - void onResponseBody(const char *buf, size_t size, size_t recvedSize, size_t totalSize) override; void onResponseCompleted() override; void onDisconnect(const SockException &ex) override; void closeFile(); diff --git a/src/Player/MediaPlayer.cpp b/src/Player/MediaPlayer.cpp index 7660c660..5859cc80 100644 --- a/src/Player/MediaPlayer.cpp +++ b/src/Player/MediaPlayer.cpp @@ -64,6 +64,13 @@ void MediaPlayer::pause(bool pause) { } } +void MediaPlayer::speed(float speed) +{ + if (_delegate) { + _delegate->speed(speed); + } +} + void MediaPlayer::teardown() { if (_delegate) { _delegate->teardown(); diff --git a/src/Player/MediaPlayer.h b/src/Player/MediaPlayer.h index b25029b2..84888d92 100644 --- a/src/Player/MediaPlayer.h +++ b/src/Player/MediaPlayer.h @@ -29,6 +29,7 @@ public: virtual ~MediaPlayer(); void play(const string &url) override; void pause(bool pause) override; + void speed(float speed)override; void teardown() override; EventPoller::Ptr getPoller(); void setOnCreateSocket(Socket::onCreateSocket cb); diff --git a/src/Player/PlayerBase.h b/src/Player/PlayerBase.h index f1762a37..8b3791e1 100644 --- a/src/Player/PlayerBase.h +++ b/src/Player/PlayerBase.h @@ -65,6 +65,12 @@ public: */ virtual void pause(bool bPause) {} + /** + * 倍数播放 + * @param speed 1.0 2.0 0.5 + */ + virtual void speed(float speed) {} + /** * 中断播放 */ @@ -94,12 +100,24 @@ public: */ virtual float getProgress() const { return 0;} + /** + * 获取播放进度pos,取值 相对开始时间增量 单位秒 + * @return + */ + virtual uint32_t getProgressPos() const { return 0; } + /** * 拖动进度条 * @param fProgress 进度,取值 0.0 ~ 1.0 */ virtual void seekTo(float fProgress) {} + /** + * 拖动进度条 + * @param seekPos 进度,取值 相对于开始时间的增量 单位秒 + */ + virtual void seekTo(uint32_t seekPos) {} + /** * 设置一个MediaSource,直接生产rtsp/rtmp代理 * @param src @@ -175,6 +193,12 @@ public: } return Parent::getProgress(); } + uint32_t getProgressPos() const override { + if (_delegate) { + return _delegate->getProgressPos(); + } + return Parent::getProgressPos(); + } void seekTo(float fProgress) override{ if (_delegate) { return _delegate->seekTo(fProgress); @@ -182,6 +206,13 @@ public: return Parent::seekTo(fProgress); } + void seekTo(uint32_t seekPos) override { + if (_delegate) { + return _delegate->seekTo(seekPos); + } + return Parent::seekTo(seekPos); + } + void setMediaSource(const MediaSource::Ptr & src) override { if (_delegate) { _delegate->setMediaSource(src); diff --git a/src/Record/MP4Recorder.cpp b/src/Record/MP4Recorder.cpp index 19cbb783..1d5f7f33 100644 --- a/src/Record/MP4Recorder.cpp +++ b/src/Record/MP4Recorder.cpp @@ -107,12 +107,17 @@ void MP4Recorder::closeFile() { } void MP4Recorder::inputFrame(const Frame::Ptr &frame) { - if(!_muxer || ((_createFileTicker.elapsedTime() > _max_second * 1000) && + if (_baseSec == 0) + _baseSec = frame->dts(); + auto dis = frame->dts() - _baseSec; + if(!_muxer || ((dis > _max_second * 1000) && (!_haveVideo || (_haveVideo && frame->keyFrame()))) ){ //成立条件 //1、_muxer为空 //2、到了切片时间,并且只有音频 //3、到了切片时间,有视频并且遇到视频的关键帧 + DebugL << "create file dts:" << frame->dts() << " baseSec:" << _baseSec; + _baseSec = 0; createFile(); } diff --git a/src/Record/MP4Recorder.h b/src/Record/MP4Recorder.h index dfeac3d7..098800f8 100644 --- a/src/Record/MP4Recorder.h +++ b/src/Record/MP4Recorder.h @@ -65,6 +65,8 @@ private: RecordInfo _info; MP4Muxer::Ptr _muxer; list _tracks; + + uint64_t _baseSec = 0; }; #endif ///ENABLE_MP4 diff --git a/src/Rtmp/RtmpPlayer.cpp b/src/Rtmp/RtmpPlayer.cpp index 107988eb..4125359d 100644 --- a/src/Rtmp/RtmpPlayer.cpp +++ b/src/Rtmp/RtmpPlayer.cpp @@ -171,6 +171,11 @@ void RtmpPlayer::pause(bool bPause) { send_pause(bPause); } +void RtmpPlayer::speed(float speed) +{ + //todo +} + inline void RtmpPlayer::send_connect() { AMFValue obj(AMF_OBJECT); obj.set("app", _app); diff --git a/src/Rtmp/RtmpPlayer.h b/src/Rtmp/RtmpPlayer.h index fd24560a..36a770bc 100644 --- a/src/Rtmp/RtmpPlayer.h +++ b/src/Rtmp/RtmpPlayer.h @@ -38,6 +38,7 @@ public: void play(const string &strUrl) override; void pause(bool bPause) override; + void speed(float speed)override; void teardown() override; protected: diff --git a/src/Rtmp/RtmpPlayerImp.h b/src/Rtmp/RtmpPlayerImp.h index 393b0197..184f4cdc 100644 --- a/src/Rtmp/RtmpPlayerImp.h +++ b/src/Rtmp/RtmpPlayerImp.h @@ -46,6 +46,11 @@ public: seekToMilliSecond((uint32_t)(fProgress * getDuration() * 1000)); } + void seekTo(uint32_t seekPos) override { + uint32_t pos = MAX(float(0), MIN(seekPos, getDuration()))*1000; + seekToMilliSecond(pos); + } + void play(const string &strUrl) override { PlayerImp::play(strUrl); } diff --git a/src/Rtmp/RtmpSession.cpp b/src/Rtmp/RtmpSession.cpp index 9a4658a5..72992205 100644 --- a/src/Rtmp/RtmpSession.cpp +++ b/src/Rtmp/RtmpSession.cpp @@ -412,6 +412,36 @@ void RtmpSession::onCmd_pause(AMFDecoder &dec) { //streamBegin sendUserControl(paused ? CONTROL_STREAM_EOF : CONTROL_STREAM_BEGIN, STREAM_MEDIA); _paused = paused; + + auto stongSrc = _player_src.lock(); + if (stongSrc) { + if (_paused) + stongSrc->pause(); + else + stongSrc->seekTo(-1); + } +} + +void RtmpSession::onCmd_playCtrl(AMFDecoder& dec) +{ + dec.load(); + auto ctrlObj = dec.load(); + int ctrlType = ctrlObj["ctrlType"].as_integer(); + float speed = ctrlObj["speed"].as_number(); + + + AMFValue status(AMF_OBJECT); + status.set("level", "status"); + status.set("code", "NetStream.Speed.Notify"); + status.set("description", "Speeding"); + sendReply("onStatus", nullptr, status); + //streamBegin + sendUserControl(CONTROL_STREAM_EOF, STREAM_MEDIA); + + auto stongSrc = _player_src.lock(); + if (stongSrc) { + stongSrc->speed(speed); + } } void RtmpSession::setMetaData(AMFDecoder &dec) { @@ -434,6 +464,7 @@ void RtmpSession::onProcessCmd(AMFDecoder &dec) { s_cmd_functions.emplace("play2", &RtmpSession::onCmd_play2); s_cmd_functions.emplace("seek", &RtmpSession::onCmd_seek); s_cmd_functions.emplace("pause", &RtmpSession::onCmd_pause); + s_cmd_functions.emplace("onPlayCtrl", &RtmpSession::onCmd_playCtrl); }); std::string method = dec.load(); diff --git a/src/Rtmp/RtmpSession.h b/src/Rtmp/RtmpSession.h index 02f2d623..3d8be8c8 100644 --- a/src/Rtmp/RtmpSession.h +++ b/src/Rtmp/RtmpSession.h @@ -53,6 +53,7 @@ private: void onCmd_seek(AMFDecoder &dec); void onCmd_pause(AMFDecoder &dec); + void onCmd_playCtrl(AMFDecoder& dec); void setMetaData(AMFDecoder &dec); void onSendMedia(const RtmpPacket::Ptr &pkt); diff --git a/src/Rtsp/RtspPlayer.cpp b/src/Rtsp/RtspPlayer.cpp index a1926503..6b83a12b 100644 --- a/src/Rtsp/RtspPlayer.cpp +++ b/src/Rtsp/RtspPlayer.cpp @@ -25,7 +25,8 @@ namespace mediakit { enum PlayType { type_play = 0, type_pause, - type_seek + type_seek, + type_speed }; RtspPlayer::RtspPlayer(const EventPoller::Ptr &poller) : TcpClient(poller){ @@ -414,8 +415,13 @@ void RtspPlayer::sendPause(int type , uint32_t seekMS){ } } -void RtspPlayer::pause(bool pause_flag) { - sendPause(pause_flag ? type_pause : type_seek, getProgressMilliSecond()); +void RtspPlayer::pause(bool bPause) { + sendPause(bPause ? type_pause : type_seek, getProgressMilliSecond()); +} + +void RtspPlayer::speed(float speed) +{ + sendRtspRequest("PLAY", _content_base, { "Scale",StrPrinter << speed }); } void RtspPlayer::handleResPAUSE(const Parser& parser,int type) { diff --git a/src/Rtsp/RtspPlayer.h b/src/Rtsp/RtspPlayer.h index 8265ab0c..a745dff4 100644 --- a/src/Rtsp/RtspPlayer.h +++ b/src/Rtsp/RtspPlayer.h @@ -41,7 +41,8 @@ public: ~RtspPlayer() override; void play(const string &strUrl) override; - void pause(bool pause_flag) override; + void pause(bool bPause) override; + void speed(float speed)override; void teardown() override; float getPacketLossRate(TrackType type) const override; diff --git a/src/Rtsp/RtspPlayerImp.h b/src/Rtsp/RtspPlayerImp.h index 304a6790..e02588e7 100644 --- a/src/Rtsp/RtspPlayerImp.h +++ b/src/Rtsp/RtspPlayerImp.h @@ -42,11 +42,24 @@ public: } - void seekTo(float fProgress) override { - fProgress = MAX(float(0), MIN(fProgress, float(1.0))); + uint32_t getProgressPos() const override { + if (getDuration() > 0) { + return getProgressMilliSecond(); + } + return PlayerBase::getProgressPos(); + + }; + + void seekTo(float fProgress) override{ + fProgress = MAX(float(0),MIN(fProgress,float(1.0))); seekToMilliSecond((uint32_t)(fProgress * getDuration() * 1000)); } + void seekTo(uint32_t seekPos) override { + uint32_t pos = MAX(float(0), MIN(seekPos, getDuration()))*1000; + seekToMilliSecond(pos); + } + private: //派生类回调函数 bool onCheckSDP(const string &sdp) override { diff --git a/src/Rtsp/RtspSession.cpp b/src/Rtsp/RtspSession.cpp index 3ab758c6..6f35a13c 100644 --- a/src/Rtsp/RtspSession.cpp +++ b/src/Rtsp/RtspSession.cpp @@ -774,21 +774,36 @@ void RtspSession::handleReq_Play(const Parser &parser) { } bool useGOP = true; - _enable_send_rtp = false; + //_enable_send_rtp = false; float iStartTime = 0; auto strRange = parser["Range"]; - if (strRange.size()) { - //这个是seek操作 - auto strStart = FindField(strRange.data(), "npt=", "-"); - if (strStart == "now") { - strStart = "0"; + auto strScale = parser["Scale"]; + if (strScale.size() == 0) + { + + if (strRange.size()) { + //这个是seek操作 + auto strStart = FindField(strRange.data(), "npt=", "-"); + if (strStart == "now") { + strStart = "0"; + } + iStartTime = 1000 * (float)atof(strStart.data()); + InfoP(this) << "rtsp seekTo(ms):" << iStartTime; + useGOP = !play_src->seekTo((uint32_t)iStartTime); } - iStartTime = 1000 * (float)atof(strStart.data()); - InfoP(this) << "rtsp seekTo(ms):" << iStartTime; - useGOP = !play_src->seekTo((uint32_t)iStartTime); - } else if (play_src->totalReaderCount() == 0) { - //第一个消费者 - play_src->seekTo(0); + else if (play_src->totalReaderCount() == 0) { + //第一个消费者 + play_src->seekTo(0); + } + else if (play_src->totalReaderCount() > 0) + { + _enable_send_rtp = true; + play_src->seekTo(-1); + } + } + else + { + play_src->speed(atof(strScale.data())); } _StrPrinter rtp_info; @@ -845,7 +860,17 @@ void RtspSession::handleReq_Pause(const Parser &parser) { } sendRtspResponse("200 OK"); - _enable_send_rtp = false; + + + auto play_src = _play_src.lock(); + if (!play_src) { + send_StreamNotFound(); + shutdown(SockException(Err_shutdown, "rtsp stream released")); + return; + } + + play_src->pause(); + //_enable_send_rtp = false; } void RtspSession::handleReq_Teardown(const Parser &parser) {