From 5cc794b22ddeedb1a810d7360bb0fb377c3f3fa7 Mon Sep 17 00:00:00 2001 From: Winlin Date: Thu, 23 Mar 2023 10:01:20 +0800 Subject: [PATCH] WHIP: Support DELETE resource for Larix Broadcaster. v5.0.148 v6.0.36 (#3427) * WHIP: Support DELETE resource. * Support push by Larix. * FLV: Disable stash buffer for realtime. * WHEP: Fix muted issue. ------- Co-authored-by: chundonglinlin Co-authored-by: panda <542638787@qq.com> --- trunk/doc/CHANGELOG.md | 1 + trunk/doc/Features.md | 27 ++++----- trunk/research/players/js/srs.sdk.js | 8 +-- trunk/research/players/srs_player.html | 39 +++++++------ trunk/research/players/whep.html | 2 +- trunk/src/app/srs_app_rtc_api.cpp | 78 +++++++++++++++++--------- trunk/src/app/srs_app_rtc_api.hpp | 2 + trunk/src/core/srs_core_version5.hpp | 2 +- 8 files changed, 98 insertions(+), 61 deletions(-) diff --git a/trunk/doc/CHANGELOG.md b/trunk/doc/CHANGELOG.md index 957db8314f..e51a2dcfe7 100644 --- a/trunk/doc/CHANGELOG.md +++ b/trunk/doc/CHANGELOG.md @@ -8,6 +8,7 @@ The changelog for SRS. ## SRS 5.0 Changelog +* v5.0, 2023-03-22, Merge [#3427](https://github.com/ossrs/srs/pull/3427): WHIP: Support DELETE resource for Larix Broadcaster. v5.0.148 (#3427) * v5.0, 2023-03-20, Merge [#3460](https://github.com/ossrs/srs/pull/3460): WebRTC: Support WHIP/WHEP players. v5.0.147 (#3460) * v5.0, 2023-03-07, Merge [#3446](https://github.com/ossrs/srs/pull/3446): WebRTC: Warning if no ideal profile. v5.0.146 (#3446) * v5.0, 2023-03-06, Merge [#3445](https://github.com/ossrs/srs/pull/3445): Support configure for generic linux. v5.0.145 (#3445) diff --git a/trunk/doc/Features.md b/trunk/doc/Features.md index bce3fbbefd..cf5ccbb020 100644 --- a/trunk/doc/Features.md +++ b/trunk/doc/Features.md @@ -53,19 +53,20 @@ The features of SRS. - [x] RTC: [Experimental] Support AV1 codec for WebRTC, [#2324](https://github.com/ossrs/srs/issues/2324). - [x] RTC: [Experimental] Support transmux RTC to RTMP, [#2093](https://github.com/ossrs/srs/issues/2093). - [x] RTC: [Experimental] Support WebRTC over TCP directly, [#2852](https://github.com/ossrs/srs/issues/2852). v5.0.60+ -- [x] RTC: [Experimental] Support WHIP(WebRTC-HTTP ingestion protocol), [#2324](https://github.com/ossrs/srs/issues/2324). v5.0.61+ -- [x] Other: Support ingesting([CN](https://ossrs.net/lts/zh-cn/docs/v4/doc/ingest), [EN](https://ossrs.io/lts/en-us/docs/v4/doc/ingest)) other protocols to SRS by FFMPEG. -- [x] Other: Support forwarding([CN](https://ossrs.net/lts/zh-cn/docs/v4/doc/forward), [EN](https://ossrs.io/lts/en-us/docs/v4/doc/forward)) to other RTMP servers. -- [x] Other: Support transcoding([CN](https://ossrs.net/lts/zh-cn/docs/v4/doc/ffmpeg), [EN](https://ossrs.io/lts/en-us/docs/v4/doc/ffmpeg)) by FFMPEG. -- [x] Other: All wikis are writen in [Chinese](https://ossrs.net) and [English](https://ossrs.io). -- [x] Other: Support valgrind and latest ARM by patching ST, read [ST#1](https://github.com/ossrs/state-threads/issues/1) and [ST#2](https://github.com/ossrs/state-threads/issues/2). -- [x] Other: Enhanced complex error code with description and stack, read [#913](https://github.com/ossrs/srs/issues/913). -- [x] Other: Support test coverage for core/kernel/protocol/service. -- [x] Other: Support a simple [mgmt console](http://ossrs.net/console/), please read [srs-console](https://github.com/ossrs/srs-console). -- [x] Other: Support dynamic forwarding by backend api, [#2799](https://github.com/ossrs/srs/pull/2799). -- [x] Other: Support write log to tencent cloud CLS. -- [x] Other: [Experimental] Support pushing MPEG-TS over UDP, please read [bug #250](https://github.com/ossrs/srs/issues/250). -- [x] Other: [Experimental] Support pushing FLV over HTTP POST, please read wiki([CN](https://ossrs.net/lts/zh-cn/docs/v4/doc/streamer#push-http-flv-to-srs), [EN](https://ossrs.io/lts/en-us/docs/v4/doc/streamer#push-http-flv-to-srs)). +- [x] RTC: [Experimental] Support WHIP(WebRTC-HTTP ingestion protocol), [#3170](https://github.com/ossrs/srs/issues/3170). v5.0.61+ +- [x] RTC: [Experimental] Support [Larix Broadcaster](https://softvelum.com/larix/), [#3476](https://github.com/ossrs/srs/issues/3476). v5.0.148+ +- [x] Other: Support ingesting([CN](https://ossrs.net/lts/zh-cn/docs/v4/doc/ingest), [EN](https://ossrs.io/lts/en-us/docs/v4/doc/ingest)) other protocols to SRS by FFMPEG. v1.0.0+ +- [x] Other: Support forwarding([CN](https://ossrs.net/lts/zh-cn/docs/v4/doc/forward), [EN](https://ossrs.io/lts/en-us/docs/v4/doc/forward)) to other RTMP servers. v1.0.0+ +- [x] Other: Support transcoding([CN](https://ossrs.net/lts/zh-cn/docs/v4/doc/ffmpeg), [EN](https://ossrs.io/lts/en-us/docs/v4/doc/ffmpeg)) by FFMPEG. v1.0.0+ +- [x] Other: All wikis are writen in [Chinese](https://ossrs.net) and [English](https://ossrs.io). v2.0.23+ +- [x] Other: Support valgrind and latest ARM by patching ST, read [ST#1](https://github.com/ossrs/state-threads/issues/1) and [ST#2](https://github.com/ossrs/state-threads/issues/2). v3.0.11+ +- [x] Other: Enhanced complex error code with description and stack, read [#913](https://github.com/ossrs/srs/issues/913). v3.0.26+ +- [x] Other: Support test coverage for core/kernel/protocol/service. v3.0.91+ +- [x] Other: Support a simple [mgmt console](http://ossrs.net/console/), please read [srs-console](https://github.com/ossrs/srs-console). v3.0.43+ +- [x] Other: Support dynamic forwarding by backend api, [#2799](https://github.com/ossrs/srs/pull/2799). v5.0.24+ +- [x] Other: Support write log to tencent cloud CLS. v5.0.44+ +- [x] Other: [Experimental] Support pushing MPEG-TS over UDP, please read [bug #250](https://github.com/ossrs/srs/issues/250). v2.0.111+ +- [x] Other: [Experimental] Support pushing FLV over HTTP POST, please read wiki([CN](https://ossrs.net/lts/zh-cn/docs/v4/doc/streamer#push-http-flv-to-srs), [EN](https://ossrs.io/lts/en-us/docs/v4/doc/streamer#push-http-flv-to-srs)). v2.0.163+ - [x] Other: [Experimental] Support push stream by GB28181, [#3176](https://github.com/ossrs/srs/issues/3176). v5.0.74+ - [x] Other: Support WHIP/WHEP player, [#3460](https://github.com/ossrs/srs/pull/3460). v5.0.147+ - [ ] System: Support Windows/Cygwin 64bits, [#2532](https://github.com/ossrs/srs/issues/2532). diff --git a/trunk/research/players/js/srs.sdk.js b/trunk/research/players/js/srs.sdk.js index bba86ba799..e428ef7f32 100644 --- a/trunk/research/players/js/srs.sdk.js +++ b/trunk/research/players/js/srs.sdk.js @@ -83,7 +83,7 @@ function SrsRtcPublisherAsync() { const xhr = new XMLHttpRequest(); xhr.onload = function() { if (xhr.readyState !== xhr.DONE) return; - if (xhr.status !== 200) return reject(xhr); + if (xhr.status !== 200 && xhr.status !== 201) return reject(xhr); const data = JSON.parse(xhr.responseText); console.log("Got answer: ", data); return data.code ? reject(xhr) : resolve(data); @@ -318,7 +318,7 @@ function SrsRtcPlayerAsync() { const xhr = new XMLHttpRequest(); xhr.onload = function() { if (xhr.readyState !== xhr.DONE) return; - if (xhr.status !== 200) return reject(xhr); + if (xhr.status !== 200 && xhr.status !== 201) return reject(xhr); const data = JSON.parse(xhr.responseText); console.log("Got answer: ", data); return data.code ? reject(xhr) : resolve(data); @@ -553,7 +553,7 @@ function SrsRtcWhipWhepAsync() { const xhr = new XMLHttpRequest(); xhr.onload = function() { if (xhr.readyState !== xhr.DONE) return; - if (xhr.status !== 200) return reject(xhr); + if (xhr.status !== 200 && xhr.status !== 201) return reject(xhr); const data = xhr.responseText; console.log("Got answer: ", data); return data.code ? reject(xhr) : resolve(data); @@ -586,7 +586,7 @@ function SrsRtcWhipWhepAsync() { const xhr = new XMLHttpRequest(); xhr.onload = function() { if (xhr.readyState !== xhr.DONE) return; - if (xhr.status !== 200) return reject(xhr); + if (xhr.status !== 200 && xhr.status !== 201) return reject(xhr); const data = xhr.responseText; console.log("Got answer: ", data); return data.code ? reject(xhr) : resolve(data); diff --git a/trunk/research/players/srs_player.html b/trunk/research/players/srs_player.html index fa1d7c1db7..7d900907f5 100755 --- a/trunk/research/players/srs_player.html +++ b/trunk/research/players/srs_player.html @@ -190,22 +190,6 @@ return; } - // Start play HTTP-FLV. - if (r.stream.indexOf('.flv') > 0) { - if (!mpegts.getFeatureList().mseLivePlayback) { - hide_for_error(); - return; - } - - show_for_ok(); - - flvPlayer = mpegts.createPlayer({type: 'flv', url: r.url, isLive: true}); - flvPlayer.attachMediaElement(document.getElementById('video_player')); - flvPlayer.load(); - flvPlayer.play(); - return; - } - // Start play HTTP-TS. if (r.stream.indexOf('.ts') > 0) { if (!mpegts.getFeatureList().mseLivePlayback) { @@ -215,7 +199,7 @@ show_for_ok(); - tsPlayer = mpegts.createPlayer({type: 'mpegts', url: r.url, isLive: true}); + tsPlayer = mpegts.createPlayer({type: 'mpegts', url: r.url, isLive: true, enableStashBuffer: false}); tsPlayer.attachMediaElement(document.getElementById('video_player')); tsPlayer.load(); tsPlayer.play(); @@ -246,6 +230,27 @@ return; } + // Start play HTTP-FLV. + let isFlv = r.stream.indexOf('.flv') > 0; + // Compatible with NGINX-HTTP-FLV module, see https://github.com/winshining/nginx-http-flv-module and the stream + // url without .flv, such as: + // http://localhost:8080/live?app=live&stream=livestream + isFlv = isFlv || r.stream && r.url.indexOf('http') === 0; + if (isFlv) { + if (!mpegts.getFeatureList().mseLivePlayback) { + hide_for_error(); + return; + } + + show_for_video_ok(); + + flvPlayer = mpegts.createPlayer({type: 'flv', url: r.url, isLive: true, enableStashBuffer: false}); + flvPlayer.attachMediaElement(document.getElementById('video_player')); + flvPlayer.load(); + flvPlayer.play(); + return; + } + console.error('不支持的URL', r.url, r); $('#video_player').hide(); }; diff --git a/trunk/research/players/whep.html b/trunk/research/players/whep.html index e6aa2f305e..5f3aa2d0aa 100644 --- a/trunk/research/players/whep.html +++ b/trunk/research/players/whep.html @@ -52,7 +52,7 @@ - + SessionID: diff --git a/trunk/src/app/srs_app_rtc_api.cpp b/trunk/src/app/srs_app_rtc_api.cpp index f8ca69e54d..aa9398f6ad 100644 --- a/trunk/src/app/srs_app_rtc_api.cpp +++ b/trunk/src/app/srs_app_rtc_api.cpp @@ -600,6 +600,41 @@ srs_error_t SrsGoApiRtcWhip::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessa // For each RTC session, we use short-term HTTP connection. w->header()->set("Connection", "Close"); + // Client stop publish. + // TODO: FIXME: Stop and cleanup the RTC session. + if (r->method() == SRS_CONSTS_HTTP_DELETE) { + srs_trace("WHIP: Delete stream %s", r->url().c_str()); + w->write_header(SRS_CONSTS_HTTP_OK); + return w->write(NULL, 0); + } + + SrsRtcUserConfig ruc; + if ((err = do_serve_http(w, r, &ruc)) != srs_success) { + return srs_error_wrap(err, "serve"); + } + if (ruc.local_sdp_str_.empty()) { + return srs_go_http_error(w, SRS_CONSTS_HTTP_InternalServerError); + } + + // The SDP to response. + string sdp = ruc.local_sdp_str_; + + // Setup the content type to SDP. + w->header()->set("Content-Type", "application/sdp"); + // The location for DELETE resource, not required by SRS, but required by WHIP. + w->header()->set("Location", srs_fmt("/rtc/v1/whip/?app=%s&stream=%s", ruc.req_->app.c_str(), ruc.req_->stream.c_str())); + w->header()->set_content_length((int64_t)sdp.length()); + // Must be 201, see https://datatracker.ietf.org/doc/draft-ietf-wish-whip/ + w->write_header(201); + + // Response the SDP content. + return w->write((char*)sdp.data(), (int)sdp.length()); +} + +srs_error_t SrsGoApiRtcWhip::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, SrsRtcUserConfig* ruc) +{ + srs_error_t err = srs_success; + string remote_sdp_str; if ((err = r->body_read_all(remote_sdp_str)) != srs_success) { return srs_error_wrap(err, "read sdp"); @@ -632,48 +667,41 @@ srs_error_t SrsGoApiRtcWhip::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessa } // The RTC user config object. - SrsRtcUserConfig ruc; - ruc.req_->ip = clientip; - ruc.req_->host = r->host(); - ruc.req_->vhost = ruc.req_->host; - ruc.req_->app = app.empty() ? "live" : app; - ruc.req_->stream = stream.empty() ? "livestream" : stream; - ruc.req_->param = r->query(); + ruc->req_->ip = clientip; + ruc->req_->host = r->host(); + ruc->req_->vhost = ruc->req_->host; + ruc->req_->app = app.empty() ? "live" : app; + ruc->req_->stream = stream.empty() ? "livestream" : stream; + ruc->req_->param = r->query(); // discovery vhost, resolve the vhost from config - SrsConfDirective* parsed_vhost = _srs_config->get_vhost(ruc.req_->vhost); + SrsConfDirective* parsed_vhost = _srs_config->get_vhost(ruc->req_->vhost); if (parsed_vhost) { - ruc.req_->vhost = parsed_vhost->arg0(); + ruc->req_->vhost = parsed_vhost->arg0(); } srs_trace("RTC whip %s %s, clientip=%s, app=%s, stream=%s, offer=%dB, eip=%s, codec=%s, param=%s", - action.c_str(), ruc.req_->get_stream_url().c_str(), clientip.c_str(), ruc.req_->app.c_str(), ruc.req_->stream.c_str(), - remote_sdp_str.length(), eip.c_str(), codec.c_str(), ruc.req_->param.c_str() + action.c_str(), ruc->req_->get_stream_url().c_str(), clientip.c_str(), ruc->req_->app.c_str(), ruc->req_->stream.c_str(), + remote_sdp_str.length(), eip.c_str(), codec.c_str(), ruc->req_->param.c_str() ); - ruc.eip_ = eip; - ruc.codec_ = codec; - ruc.publish_ = (action == "publish"); - ruc.dtls_ = ruc.srtp_ = true; + ruc->eip_ = eip; + ruc->codec_ = codec; + ruc->publish_ = (action == "publish"); + ruc->dtls_ = ruc->srtp_ = true; // TODO: FIXME: It seems remote_sdp doesn't represents the full SDP information. - ruc.remote_sdp_str_ = remote_sdp_str; - if ((err = ruc.remote_sdp_.parse(remote_sdp_str)) != srs_success) { + ruc->remote_sdp_str_ = remote_sdp_str; + if ((err = ruc->remote_sdp_.parse(remote_sdp_str)) != srs_success) { return srs_error_wrap(err, "parse sdp failed: %s", remote_sdp_str.c_str()); } - err = action == "publish" ? publish_->serve_http(w, r, &ruc) : play_->serve_http(w, r, &ruc); + err = action == "publish" ? publish_->serve_http(w, r, ruc) : play_->serve_http(w, r, ruc); if (err != srs_success) { return srs_error_wrap(err, "serve"); } - if (ruc.local_sdp_str_.empty()) { - return srs_go_http_error(w, SRS_CONSTS_HTTP_InternalServerError); - } - - string sdp = ruc.local_sdp_str_; - w->header()->set("Content-Type", "application/sdp"); - return w->write((char*)sdp.data(), (int)sdp.length()); + return err; } SrsGoApiRtcNACK::SrsGoApiRtcNACK(SrsRtcServer* server) diff --git a/trunk/src/app/srs_app_rtc_api.hpp b/trunk/src/app/srs_app_rtc_api.hpp index 7dab7392c3..cf27f8d3d7 100644 --- a/trunk/src/app/srs_app_rtc_api.hpp +++ b/trunk/src/app/srs_app_rtc_api.hpp @@ -65,6 +65,8 @@ class SrsGoApiRtcWhip : public ISrsHttpHandler virtual ~SrsGoApiRtcWhip(); public: virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); +private: + virtual srs_error_t do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, SrsRtcUserConfig* ruc); }; class SrsGoApiRtcNACK : public ISrsHttpHandler diff --git a/trunk/src/core/srs_core_version5.hpp b/trunk/src/core/srs_core_version5.hpp index 61ea271f71..08eb4da339 100644 --- a/trunk/src/core/srs_core_version5.hpp +++ b/trunk/src/core/srs_core_version5.hpp @@ -9,6 +9,6 @@ #define VERSION_MAJOR 5 #define VERSION_MINOR 0 -#define VERSION_REVISION 147 +#define VERSION_REVISION 148 #endif