ossrs / srs

Compare e0c8c19 ... +5 ... 686f577

Coverage Reach
src/app/srs_app_config.cpp src/app/srs_app_rtc_conn.cpp src/app/srs_app_rtc_source.cpp src/app/srs_app_source.cpp src/app/srs_app_server.cpp src/app/srs_app_utility.cpp src/app/srs_app_rtmp_conn.cpp src/app/srs_app_hls.cpp src/app/srs_app_http_api.cpp src/app/srs_app_rtc_sdp.cpp src/app/srs_app_http_stream.cpp src/app/srs_app_dvr.cpp src/app/srs_app_rtc_dtls.cpp src/app/srs_app_conn.cpp src/app/srs_app_rtc_server.cpp src/app/srs_app_edge.cpp src/app/srs_app_statistic.cpp src/app/srs_app_rtc_api.cpp src/app/srs_app_http_hooks.cpp src/app/srs_app_dash.cpp src/app/srs_app_listener.cpp src/app/srs_app_ingest.cpp src/app/srs_app_mpegts_udp.cpp src/app/srs_app_rtc_codec.cpp src/app/srs_app_recv_thread.cpp src/app/srs_app_http_conn.cpp src/app/srs_app_http_static.cpp src/app/srs_app_bandwidth.cpp src/app/srs_app_ffmpeg.cpp src/app/srs_app_uuid.cpp src/app/srs_app_caster_flv.cpp src/app/srs_app_encoder.cpp src/app/srs_app_process.cpp src/app/srs_app_latest_version.cpp src/app/srs_app_hybrid.cpp src/app/srs_app_rtc_queue.cpp src/app/srs_app_st.cpp src/app/srs_app_log.cpp src/app/srs_app_pithy_print.cpp src/app/srs_app_threads.cpp src/app/srs_app_forward.cpp src/app/srs_app_fragment.cpp src/app/srs_app_ng_exec.cpp src/app/srs_app_hourglass.cpp src/app/srs_app_reload.cpp src/app/srs_app_coworkers.cpp src/app/srs_app_security.cpp src/app/srs_app_async_call.cpp src/app/srs_app_heartbeat.cpp src/app/srs_app_refer.cpp src/app/srs_app_rtc_sdp.hpp src/app/srs_app_config.hpp src/app/srs_app_rtc_source.hpp src/app/srs_app_conn.hpp src/app/srs_app_st.hpp src/utest/srs_utest_kernel.cpp src/utest/srs_utest_protocol.cpp src/utest/srs_utest_config.cpp src/utest/srs_utest_rtmp.cpp src/utest/srs_utest_amf0.cpp src/utest/srs_utest_http.cpp src/utest/srs_utest_mp4.cpp src/utest/srs_utest_service.cpp src/utest/srs_utest_rtc.cpp src/utest/srs_utest_reload.cpp src/utest/srs_utest_app.cpp src/utest/srs_utest_avc.cpp src/utest/srs_utest.cpp src/utest/srs_utest_srt.cpp src/utest/srs_utest_core.cpp src/kernel/srs_kernel_mp4.cpp src/kernel/srs_kernel_ts.cpp src/kernel/srs_kernel_rtc_rtcp.cpp src/kernel/srs_kernel_rtc_rtp.cpp src/kernel/srs_kernel_utility.cpp src/kernel/srs_kernel_codec.cpp src/kernel/srs_kernel_flv.cpp src/kernel/srs_kernel_buffer.cpp src/kernel/srs_kernel_file.cpp src/kernel/srs_kernel_error.cpp src/kernel/srs_kernel_aac.cpp src/kernel/srs_kernel_kbps.cpp src/kernel/srs_kernel_mp3.cpp src/kernel/srs_kernel_rtc_rtp.hpp src/kernel/srs_kernel_stream.cpp src/kernel/srs_kernel_rtc_rtcp.hpp src/kernel/srs_kernel_io.cpp src/kernel/srs_kernel_balance.cpp src/kernel/srs_kernel_log.cpp src/kernel/srs_kernel_mp4.hpp src/kernel/srs_kernel_flv.hpp src/protocol/srs_rtmp_stack.cpp src/protocol/srs_protocol_amf0.cpp src/protocol/srs_http_stack.cpp src/protocol/srs_rtsp_stack.cpp src/protocol/srs_rtmp_handshake.cpp src/protocol/srs_service_http_conn.cpp src/protocol/srs_protocol_json.cpp src/protocol/srs_service_st.cpp src/protocol/srs_service_http_client.cpp src/protocol/srs_raw_avc.cpp src/protocol/srs_protocol_utility.cpp src/protocol/srs_service_utility.cpp src/protocol/srs_rtc_stun_stack.cpp src/protocol/srs_protocol_kbps.cpp src/protocol/srs_service_rtmp_conn.cpp src/protocol/srs_protocol_stream.cpp src/protocol/srs_service_log.cpp src/protocol/srs_rtmp_stack.hpp src/protocol/srs_protocol_format.cpp src/protocol/srs_rtmp_msg_array.cpp src/protocol/srs_protocol_io.cpp src/protocol/srs_service_st.hpp src/protocol/srs_protocol_utility.hpp src/protocol/srs_service_conn.cpp src/protocol/srs_service_conn.hpp src/protocol/srs_service_utility.hpp src/protocol/srs_http_stack.hpp src/srt/srt_to_rtmp.cpp src/srt/ts_demux.cpp src/srt/srt_handle.cpp src/srt/srt_server.cpp src/srt/srt_conn.cpp src/srt/srt_data.cpp src/srt/srt_log.cpp src/srt/stringex.hpp src/srt/ts_demux.hpp src/srt/time_help.h src/main/srs_main_server.cpp src/core/srs_core.cpp src/core/srs_core_autofree.hpp src/core/srs_core_time.cpp 3rdparty/ffmpeg-4-fit/libavutil/error.h

No flags found

Use flags to group coverage reports by test type, project and/or folders.
Then setup custom commit statuses and notifications for each flag.

e.g., #unittest #integration

#production #enterprise

#frontend #backend

Learn more about Codecov Flags here.


@@ -121,7 +121,7 @@
Loading
121 121
122 122
    srs_parse_rtmp_url(streamurl, ruc.req_->tcUrl, ruc.req_->stream);
123 123
124 -
    srs_discovery_tc_url(ruc.req_->tcUrl, ruc.req_->schema, ruc.req_->host, ruc.req_->vhost, 
124 +
    srs_discovery_tc_url(ruc.req_->tcUrl, ruc.req_->schema, ruc.req_->host, ruc.req_->vhost,
125 125
                         ruc.req_->app, ruc.req_->stream, ruc.req_->port, ruc.req_->param);
126 126
127 127
    // discovery vhost, resolve the vhost from config
@@ -130,10 +130,6 @@
Loading
130 130
        ruc.req_->vhost = parsed_vhost->arg0();
131 131
    }
132 132
133 -
    if ((err = http_hooks_on_play(ruc.req_)) != srs_success) {
134 -
        return srs_error_wrap(err, "RTC: http_hooks_on_play");
135 -
    }
136 -
137 133
    // For client to specifies the candidate(EIP) of server.
138 134
    string eip = r->query_get("eip");
139 135
    if (eip.empty()) {
@@ -144,9 +140,11 @@
Loading
144 140
    string srtp = r->query_get("encrypt");
145 141
    string dtls = r->query_get("dtls");
146 142
147 -
    srs_trace("RTC play %s, api=%s, tid=%s, clientip=%s, app=%s, stream=%s, offer=%dB, eip=%s, codec=%s, srtp=%s, dtls=%s",
148 -
        streamurl.c_str(), api.c_str(), tid.c_str(), clientip.c_str(), ruc.req_->app.c_str(), ruc.req_->stream.c_str(), remote_sdp_str.length(),
149 -
        eip.c_str(), codec.c_str(), srtp.c_str(), dtls.c_str()
143 +
    srs_trace(
144 +
            "RTC play %s, api=%s, tid=%s, clientip=%s, app=%s, stream=%s, offer=%dB, eip=%s, codec=%s, srtp=%s, dtls=%s",
145 +
            streamurl.c_str(), api.c_str(), tid.c_str(), clientip.c_str(), ruc.req_->app.c_str(),
146 +
            ruc.req_->stream.c_str(), remote_sdp_str.length(),
147 +
            eip.c_str(), codec.c_str(), srtp.c_str(), dtls.c_str()
150 148
    );
151 149
152 150
    ruc.eip_ = eip;
@@ -161,6 +159,7 @@
Loading
161 159
    }
162 160
163 161
    // TODO: FIXME: It seems remote_sdp doesn't represents the full SDP information.
162 +
    ruc.remote_sdp_str_ = remote_sdp_str;
164 163
    if ((err = ruc.remote_sdp_.parse(remote_sdp_str)) != srs_success) {
165 164
        return srs_error_wrap(err, "parse sdp failed: %s", remote_sdp_str.c_str());
166 165
    }
@@ -169,42 +168,64 @@
Loading
169 168
        return srs_error_wrap(err, "remote sdp check failed");
170 169
    }
171 170
171 +
    if ((err = http_hooks_on_play(ruc.req_)) != srs_success) {
172 +
        return srs_error_wrap(err, "RTC: http_hooks_on_play");
173 +
    }
174 +
175 +
    if ((err = serve_http(w, r, &ruc)) != srs_success) {
176 +
        return srs_error_wrap(err, "serve");
177 +
    }
178 +
179 +
    res->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
180 +
    res->set("server", SrsJsonAny::str(SrsStatistic::instance()->server_id().c_str()));
181 +
182 +
    // TODO: add candidates in response json?
183 +
    res->set("sdp", SrsJsonAny::str(ruc.local_sdp_str_.c_str()));
184 +
    res->set("sessionid", SrsJsonAny::str(ruc.session_id_.c_str()));
185 +
186 +
    return err;
187 +
}
188 +
189 +
srs_error_t SrsGoApiRtcPlay::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, SrsRtcUserConfig* ruc)
190 +
{
191 +
    srs_error_t  err = srs_success;
192 +
172 193
    SrsSdp local_sdp;
173 194
174 195
    // Config for SDP and session.
175 -
    local_sdp.session_config_.dtls_role = _srs_config->get_rtc_dtls_role(ruc.req_->vhost);
176 -
    local_sdp.session_config_.dtls_version = _srs_config->get_rtc_dtls_version(ruc.req_->vhost);
196 +
    local_sdp.session_config_.dtls_role = _srs_config->get_rtc_dtls_role(ruc->req_->vhost);
197 +
    local_sdp.session_config_.dtls_version = _srs_config->get_rtc_dtls_version(ruc->req_->vhost);
177 198
178 199
    // Whether enabled.
179 200
    bool server_enabled = _srs_config->get_rtc_server_enabled();
180 -
    bool rtc_enabled = _srs_config->get_rtc_enabled(ruc.req_->vhost);
201 +
    bool rtc_enabled = _srs_config->get_rtc_enabled(ruc->req_->vhost);
181 202
    if (server_enabled && !rtc_enabled) {
182 -
        srs_warn("RTC disabled in vhost %s", ruc.req_->vhost.c_str());
203 +
        srs_warn("RTC disabled in vhost %s", ruc->req_->vhost.c_str());
183 204
    }
184 205
    if (!server_enabled || !rtc_enabled) {
185 206
        return srs_error_new(ERROR_RTC_DISABLED, "Disabled server=%d, rtc=%d, vhost=%s",
186 -
            server_enabled, rtc_enabled, ruc.req_->vhost.c_str());
207 +
            server_enabled, rtc_enabled, ruc->req_->vhost.c_str());
187 208
    }
188 209
189 210
    // Whether RTC stream is active.
190 211
    bool is_rtc_stream_active = false;
191 212
    if (true) {
192 -
        SrsRtcSource* source = _srs_rtc_sources->fetch(ruc.req_);
213 +
        SrsRtcSource* source = _srs_rtc_sources->fetch(ruc->req_);
193 214
        is_rtc_stream_active = (source && !source->can_publish());
194 215
    }
195 216
196 217
    // For RTMP to RTC, fail if disabled and RTMP is active, see https://github.com/ossrs/srs/issues/2728
197 -
    if (!is_rtc_stream_active && !_srs_config->get_rtc_from_rtmp(ruc.req_->vhost)) {
198 -
        SrsLiveSource* rtmp = _srs_sources->fetch(ruc.req_);
218 +
    if (!is_rtc_stream_active && !_srs_config->get_rtc_from_rtmp(ruc->req_->vhost)) {
219 +
        SrsLiveSource* rtmp = _srs_sources->fetch(ruc->req_);
199 220
        if (rtmp && !rtmp->inactive()) {
200 -
            return srs_error_new(ERROR_RTC_DISABLED, "Disabled rtmp_to_rtc of %s, see #2728", ruc.req_->vhost.c_str());
221 +
            return srs_error_new(ERROR_RTC_DISABLED, "Disabled rtmp_to_rtc of %s, see #2728", ruc->req_->vhost.c_str());
201 222
        }
202 223
    }
203 224
204 225
    // TODO: FIXME: When server enabled, but vhost disabled, should report error.
205 226
    SrsRtcConnection* session = NULL;
206 -
    if ((err = server_->create_session(&ruc, local_sdp, &session)) != srs_success) {
207 -
        return srs_error_wrap(err, "create session, dtls=%u, srtp=%u, eip=%s", ruc.dtls_, ruc.srtp_, eip.c_str());
227 +
    if ((err = server_->create_session(ruc, local_sdp, &session)) != srs_success) {
228 +
        return srs_error_wrap(err, "create session, dtls=%u, srtp=%u, eip=%s", ruc->dtls_, ruc->srtp_, ruc->eip_.c_str());
208 229
    }
209 230
210 231
    ostringstream os;
@@ -216,17 +237,12 @@
Loading
216 237
    // Filter the \r\n to \\r\\n for JSON.
217 238
    string local_sdp_escaped = srs_string_replace(local_sdp_str.c_str(), "\r\n", "\\r\\n");
218 239
219 -
    res->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
220 -
    res->set("server", SrsJsonAny::str(SrsStatistic::instance()->server_id().c_str()));
221 -
222 -
    // TODO: add candidates in response json?
223 -
224 -
    res->set("sdp", SrsJsonAny::str(local_sdp_str.c_str()));
225 -
    res->set("sessionid", SrsJsonAny::str(session->username().c_str()));
240 +
    ruc->local_sdp_str_ = local_sdp_str;
241 +
    ruc->session_id_ = session->username();
226 242
227 243
    srs_trace("RTC username=%s, dtls=%u, srtp=%u, offer=%dB, answer=%dB", session->username().c_str(),
228 -
        ruc.dtls_, ruc.srtp_, remote_sdp_str.length(), local_sdp_escaped.length());
229 -
    srs_trace("RTC remote offer: %s", srs_string_replace(remote_sdp_str.c_str(), "\r\n", "\\r\\n").c_str());
244 +
        ruc->dtls_, ruc->srtp_, ruc->remote_sdp_str_.length(), local_sdp_escaped.length());
245 +
    srs_trace("RTC remote offer: %s", srs_string_replace(ruc->remote_sdp_str_.c_str(), "\r\n", "\\r\\n").c_str());
230 246
    srs_trace("RTC local answer: %s", local_sdp_escaped.c_str());
231 247
232 248
    return err;
@@ -333,8 +349,7 @@
Loading
333 349
    srs_error_t err = srs_success;
334 350
335 351
    // For each RTC session, we use short-term HTTP connection.
336 -
    SrsHttpHeader* hdr = w->header();
337 -
    hdr->set("Connection", "Close");
352 +
    w->header()->set("Connection", "Close");
338 353
339 354
    // Parse req, the request json object, from body.
340 355
    SrsJsonObject* req = NULL;
@@ -406,10 +421,6 @@
Loading
406 421
        ruc.req_->vhost = parsed_vhost->arg0();
407 422
    }
408 423
409 -
	if ((err = http_hooks_on_publish(ruc.req_)) != srs_success) {
410 -
        return srs_error_wrap(err, "RTC: http_hooks_on_publish");
411 -
    }
412 -
413 424
    // For client to specifies the candidate(EIP) of server.
414 425
    string eip = r->query_get("eip");
415 426
    if (eip.empty()) {
@@ -428,35 +439,58 @@
Loading
428 439
    ruc.dtls_ = ruc.srtp_ = true;
429 440
430 441
    // TODO: FIXME: It seems remote_sdp doesn't represents the full SDP information.
442 +
    ruc.remote_sdp_str_ = remote_sdp_str;
431 443
    if ((err = ruc.remote_sdp_.parse(remote_sdp_str)) != srs_success) {
432 444
        return srs_error_wrap(err, "parse sdp failed: %s", remote_sdp_str.c_str());
433 445
    }
434 446
435 -
    if ((err = check_remote_sdp(ruc.remote_sdp_)) != srs_success) {
447 +
    if ((err = serve_http(w, r, &ruc)) != srs_success) {
448 +
        return srs_error_wrap(err, "serve");
449 +
    }
450 +
451 +
    res->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
452 +
    res->set("server", SrsJsonAny::str(SrsStatistic::instance()->server_id().c_str()));
453 +
454 +
    // TODO: add candidates in response json?
455 +
    res->set("sdp", SrsJsonAny::str(ruc.local_sdp_str_.c_str()));
456 +
    res->set("sessionid", SrsJsonAny::str(ruc.session_id_.c_str()));
457 +
458 +
    return err;
459 +
}
460 +
461 +
srs_error_t SrsGoApiRtcPublish::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, SrsRtcUserConfig* ruc)
462 +
{
463 +
    srs_error_t  err = srs_success;
464 +
465 +
    if ((err = check_remote_sdp(ruc->remote_sdp_)) != srs_success) {
436 466
        return srs_error_wrap(err, "remote sdp check failed");
437 467
    }
438 468
469 +
    if ((err = http_hooks_on_publish(ruc->req_)) != srs_success) {
470 +
        return srs_error_wrap(err, "RTC: http_hooks_on_publish");
471 +
    }
472 +
439 473
    SrsSdp local_sdp;
440 474
441 475
    // TODO: FIXME: move to create_session.
442 476
    // Config for SDP and session.
443 -
    local_sdp.session_config_.dtls_role = _srs_config->get_rtc_dtls_role(ruc.req_->vhost);
444 -
    local_sdp.session_config_.dtls_version = _srs_config->get_rtc_dtls_version(ruc.req_->vhost);
477 +
    local_sdp.session_config_.dtls_role = _srs_config->get_rtc_dtls_role(ruc->req_->vhost);
478 +
    local_sdp.session_config_.dtls_version = _srs_config->get_rtc_dtls_version(ruc->req_->vhost);
445 479
446 480
    // Whether enabled.
447 481
    bool server_enabled = _srs_config->get_rtc_server_enabled();
448 -
    bool rtc_enabled = _srs_config->get_rtc_enabled(ruc.req_->vhost);
482 +
    bool rtc_enabled = _srs_config->get_rtc_enabled(ruc->req_->vhost);
449 483
    if (server_enabled && !rtc_enabled) {
450 -
        srs_warn("RTC disabled in vhost %s", ruc.req_->vhost.c_str());
484 +
        srs_warn("RTC disabled in vhost %s", ruc->req_->vhost.c_str());
451 485
    }
452 486
    if (!server_enabled || !rtc_enabled) {
453 487
        return srs_error_new(ERROR_RTC_DISABLED, "Disabled server=%d, rtc=%d, vhost=%s",
454 -
            server_enabled, rtc_enabled, ruc.req_->vhost.c_str());
488 +
            server_enabled, rtc_enabled, ruc->req_->vhost.c_str());
455 489
    }
456 490
457 491
    // TODO: FIXME: When server enabled, but vhost disabled, should report error.
458 492
    SrsRtcConnection* session = NULL;
459 -
    if ((err = server_->create_session(&ruc, local_sdp, &session)) != srs_success) {
493 +
    if ((err = server_->create_session(ruc, local_sdp, &session)) != srs_success) {
460 494
        return srs_error_wrap(err, "create session");
461 495
    }
462 496
@@ -469,17 +503,12 @@
Loading
469 503
    // Filter the \r\n to \\r\\n for JSON.
470 504
    string local_sdp_escaped = srs_string_replace(local_sdp_str.c_str(), "\r\n", "\\r\\n");
471 505
472 -
    res->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
473 -
    res->set("server", SrsJsonAny::str(SrsStatistic::instance()->server_id().c_str()));
474 -
475 -
    // TODO: add candidates in response json?
476 -
477 -
    res->set("sdp", SrsJsonAny::str(local_sdp_str.c_str()));
478 -
    res->set("sessionid", SrsJsonAny::str(session->username().c_str()));
506 +
    ruc->local_sdp_str_ = local_sdp_str;
507 +
    ruc->session_id_ = session->username();
479 508
480 509
    srs_trace("RTC username=%s, offer=%dB, answer=%dB", session->username().c_str(),
481 -
        remote_sdp_str.length(), local_sdp_escaped.length());
482 -
    srs_trace("RTC remote offer: %s", srs_string_replace(remote_sdp_str.c_str(), "\r\n", "\\r\\n").c_str());
510 +
        ruc->remote_sdp_str_.length(), local_sdp_escaped.length());
511 +
    srs_trace("RTC remote offer: %s", srs_string_replace(ruc->remote_sdp_str_.c_str(), "\r\n", "\\r\\n").c_str());
483 512
    srs_trace("RTC local answer: %s", local_sdp_escaped.c_str());
484 513
485 514
    return err;
@@ -545,6 +574,100 @@
Loading
545 574
    return err;
546 575
}
547 576
577 +
SrsGoApiRtcWhip::SrsGoApiRtcWhip(SrsRtcServer* server)
578 +
{
579 +
    publish_ = new SrsGoApiRtcPublish(server);
580 +
    play_ = new SrsGoApiRtcPlay(server);
581 +
}
582 +
583 +
SrsGoApiRtcWhip::~SrsGoApiRtcWhip()
584 +
{
585 +
    srs_freep(publish_);
586 +
    srs_freep(play_);
587 +
}
588 +
589 +
srs_error_t SrsGoApiRtcWhip::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
590 +
{
591 +
    srs_error_t err = srs_success;
592 +
593 +
    // For each RTC session, we use short-term HTTP connection.
594 +
    w->header()->set("Connection", "Close");
595 +
596 +
    string remote_sdp_str;
597 +
    if ((err = r->body_read_all(remote_sdp_str)) != srs_success) {
598 +
        return srs_error_wrap(err, "read sdp");
599 +
    }
600 +
601 +
    string clientip;
602 +
    if (clientip.empty()){
603 +
        clientip = dynamic_cast<SrsHttpMessage*>(r)->connection()->remote_ip();
604 +
        // Overwrite by ip from proxy.
605 +
        string oip = srs_get_original_ip(r);
606 +
        if (!oip.empty()) {
607 +
            clientip = oip;
608 +
        }
609 +
    }
610 +
611 +
    // For client to specifies the candidate(EIP) of server.
612 +
    string eip = r->query_get("eip");
613 +
    if (eip.empty()) {
614 +
        eip = r->query_get("candidate");
615 +
    }
616 +
    string codec = r->query_get("codec");
617 +
    string app = r->query_get("app");
618 +
    string stream = r->query_get("stream");
619 +
    string action = r->query_get("action");
620 +
    if (action.empty()) {
621 +
        action = "publish";
622 +
    }
623 +
    if (srs_string_ends_with(r->path(), "/whip-play/")) {
624 +
        action = "play";
625 +
    }
626 +
627 +
    // The RTC user config object.
628 +
    SrsRtcUserConfig ruc;
629 +
    ruc.req_->ip = clientip;
630 +
    ruc.req_->host = r->host();
631 +
    ruc.req_->vhost = ruc.req_->host;
632 +
    ruc.req_->app = app.empty() ? "live" : app;
633 +
    ruc.req_->stream = stream.empty() ? "livestream" : stream;
634 +
635 +
    // discovery vhost, resolve the vhost from config
636 +
    SrsConfDirective* parsed_vhost = _srs_config->get_vhost(ruc.req_->vhost);
637 +
    if (parsed_vhost) {
638 +
        ruc.req_->vhost = parsed_vhost->arg0();
639 +
    }
640 +
641 +
    srs_trace("RTC whip %s %s, clientip=%s, app=%s, stream=%s, offer=%dB, eip=%s, codec=%s",
642 +
        action.c_str(), ruc.req_->get_stream_url().c_str(), clientip.c_str(), ruc.req_->app.c_str(), ruc.req_->stream.c_str(),
643 +
        remote_sdp_str.length(), eip.c_str(), codec.c_str()
644 +
    );
645 +
646 +
    ruc.eip_ = eip;
647 +
    ruc.codec_ = codec;
648 +
    ruc.publish_ = (action == "publish");
649 +
    ruc.dtls_ = ruc.srtp_ = true;
650 +
651 +
    // TODO: FIXME: It seems remote_sdp doesn't represents the full SDP information.
652 +
    ruc.remote_sdp_str_ = remote_sdp_str;
653 +
    if ((err = ruc.remote_sdp_.parse(remote_sdp_str)) != srs_success) {
654 +
        return srs_error_wrap(err, "parse sdp failed: %s", remote_sdp_str.c_str());
655 +
    }
656 +
657 +
    err = action == "publish" ? publish_->serve_http(w, r, &ruc) : play_->serve_http(w, r, &ruc);
658 +
    if (err != srs_success) {
659 +
        return srs_error_wrap(err, "serve");
660 +
    }
661 +
662 +
    if (ruc.local_sdp_str_.empty()) {
663 +
        return srs_go_http_error(w, SRS_CONSTS_HTTP_InternalServerError);
664 +
    }
665 +
666 +
    string sdp = ruc.local_sdp_str_;
667 +
    w->header()->set("Content-Type", "application/sdp");
668 +
    return w->write((char*)sdp.data(), (int)sdp.length());
669 +
}
670 +
548 671
SrsGoApiRtcNACK::SrsGoApiRtcNACK(SrsRtcServer* server)
549 672
{
550 673
    server_ = server;

@@ -357,13 +357,7 @@
Loading
357 357
class SrsVideoPayload : public SrsCodecPayload
358 358
{
359 359
public:
360 -
    struct H264SpecificParameter
361 -
    {
362 -
        std::string profile_level_id;
363 -
        std::string packetization_mode;
364 -
        std::string level_asymmerty_allow;
365 -
    };
366 -
    H264SpecificParameter h264_param_;
360 +
    H264SpecificParam h264_param_;
367 361
368 362
public:
369 363
    SrsVideoPayload();

@@ -290,6 +290,7 @@
Loading
290 290
    req_ = new SrsRequest();
291 291
    publish_ = false;
292 292
    dtls_ = srtp_ = true;
293 +
    audio_before_video_ = false;
293 294
}
294 295
295 296
SrsRtcUserConfig::~SrsRtcUserConfig()
@@ -505,6 +506,16 @@
Loading
505 506
        return srs_error_wrap(err, "handle publish");
506 507
    }
507 508
509 +
    // Generally, WHIP is a publishing protocol, but it can be also used as playing.
510 +
    if ((err = http_api_mux->handle("/rtc/v1/whip/", new SrsGoApiRtcWhip(this))) != srs_success) {
511 +
        return srs_error_wrap(err, "handle whip");
512 +
    }
513 +
514 +
    // We create another mount, to support play with the same query string as publish.
515 +
    if ((err = http_api_mux->handle("/rtc/v1/whip-play/", new SrsGoApiRtcWhip(this))) != srs_success) {
516 +
        return srs_error_wrap(err, "handle whip play");
517 +
    }
518 +
508 519
#ifdef SRS_SIMULATOR
509 520
    if ((err = http_api_mux->handle("/rtc/v1/nack/", new SrsGoApiRtcNACK(this))) != srs_success) {
510 521
        return srs_error_wrap(err, "handle nack");

@@ -1992,7 +1992,7 @@
Loading
1992 1992
        return srs_error_wrap(err, "publish negotiate");
1993 1993
    }
1994 1994
1995 -
    if ((err = generate_publish_local_sdp(req, local_sdp, stream_desc, ruc->remote_sdp_.is_unified())) != srs_success) {
1995 +
    if ((err = generate_publish_local_sdp(req, local_sdp, stream_desc, ruc->remote_sdp_.is_unified(), ruc->audio_before_video_)) != srs_success) {
1996 1996
        return srs_error_wrap(err, "generate local sdp");
1997 1997
    }
1998 1998
@@ -2058,7 +2058,7 @@
Loading
2058 2058
        ++it;
2059 2059
    }
2060 2060
2061 -
    if ((err = generate_play_local_sdp(req, local_sdp, stream_desc, ruc->remote_sdp_.is_unified())) != srs_success) {
2061 +
    if ((err = generate_play_local_sdp(req, local_sdp, stream_desc, ruc->remote_sdp_.is_unified(), ruc->audio_before_video_)) != srs_success) {
2062 2062
        return srs_error_wrap(err, "generate local sdp");
2063 2063
    }
2064 2064
@@ -2853,9 +2853,14 @@
Loading
2853 2853
    // TODO: FIME: Should check packetization-mode=1 also.
2854 2854
    bool has_42e01f = srs_sdp_has_h264_profile(remote_sdp, "42e01f");
2855 2855
2856 +
    // How many video descriptions we have parsed.
2857 +
    int nn_any_video_parsed = 0;
2858 +
2856 2859
    for (int i = 0; i < (int)remote_sdp.media_descs_.size(); ++i) {
2857 2860
        const SrsMediaDesc& remote_media_desc = remote_sdp.media_descs_.at(i);
2858 2861
2862 +
        if (remote_media_desc.is_video()) nn_any_video_parsed++;
2863 +
2859 2864
        SrsRtcTrackDescription* track_desc = new SrsRtcTrackDescription();
2860 2865
        SrsAutoFree(SrsRtcTrackDescription, track_desc);
2861 2866
@@ -2878,6 +2883,9 @@
Loading
2878 2883
        }
2879 2884
2880 2885
        if (remote_media_desc.is_audio()) {
2886 +
            // Update the ruc, which is about user specified configuration.
2887 +
            ruc->audio_before_video_ = !nn_any_video_parsed;
2888 +
2881 2889
            // TODO: check opus format specific param
2882 2890
            std::vector<SrsMediaPayloadType> payloads = remote_media_desc.find_media_with_encoding_name("opus");
2883 2891
            if (payloads.empty()) {
@@ -2977,6 +2985,9 @@
Loading
2977 2985
                    SrsVideoPayload* video_payload = new SrsVideoPayload(payload.payload_type_, payload.encoding_name_, payload.clock_rate_);
2978 2986
                    video_payload->set_h264_param_desc(payload.format_specific_param_);
2979 2987
2988 +
                    // Set the codec parameter for H.264, to make Unity happy.
2989 +
                    video_payload->h264_param_ = h264_param;
2990 +
2980 2991
                    // TODO: FIXME: Only support some transport algorithms.
2981 2992
                    for (int k = 0; k < (int)payload.rtcp_fb_.size(); ++k) {
2982 2993
                        const string& rtcp_fb = payload.rtcp_fb_.at(k);
@@ -3026,6 +3037,7 @@
Loading
3026 3037
                    }
3027 3038
                }
3028 3039
3040 +
                track_desc->type_ = "video";
3029 3041
                track_desc->set_codec_payload((SrsCodecPayload*)video_payload);
3030 3042
                srs_warn("choose backup H.264 payload type=%d", payload.payload_type_);
3031 3043
            }
@@ -3034,6 +3046,11 @@
Loading
3034 3046
            //local_media_desc.payload_types_.back().rtcp_fb_.push_back("rrtr");
3035 3047
        }
3036 3048
3049 +
        // Error if track desc is invalid, that is failed to match SDP, for example, we require H264 but no H264 found.
3050 +
        if (track_desc->type_.empty() || !track_desc->media_) {
3051 +
            return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no match for track=%s, mid=%s, tracker=%s", remote_media_desc.type_.c_str(), remote_media_desc.mid_.c_str(), remote_media_desc.msid_tracker_.c_str());
3052 +
        }
3053 +
3037 3054
        // TODO: FIXME: use one parse payload from sdp.
3038 3055
3039 3056
        track_desc->create_auxiliary_payload(remote_media_desc.find_media_with_encoding_name("red"));
@@ -3055,6 +3072,8 @@
Loading
3055 3072
                    stream_desc->audio_track_desc_ = track_desc_copy;
3056 3073
                } else if (remote_media_desc.is_video()) {
3057 3074
                    stream_desc->video_track_descs_.push_back(track_desc_copy);
3075 +
                } else {
3076 +
                    srs_freep(track_desc_copy);
3058 3077
                }
3059 3078
            }
3060 3079
            track_id = ssrc_info.msid_tracker_;
@@ -3080,7 +3099,7 @@
Loading
3080 3099
    return err;
3081 3100
}
3082 3101
3083 -
srs_error_t SrsRtcConnection::generate_publish_local_sdp(SrsRequest* req, SrsSdp& local_sdp, SrsRtcSourceDescription* stream_desc, bool unified_plan)
3102 +
srs_error_t SrsRtcConnection::generate_publish_local_sdp(SrsRequest* req, SrsSdp& local_sdp, SrsRtcSourceDescription* stream_desc, bool unified_plan, bool audio_before_video)
3084 3103
{
3085 3104
    srs_error_t err = srs_success;
3086 3105
@@ -3105,6 +3124,29 @@
Loading
3105 3124
3106 3125
    local_sdp.group_policy_ = "BUNDLE";
3107 3126
3127 +
    if (audio_before_video) {
3128 +
        if ((err = generate_publish_local_sdp_for_audio(local_sdp, stream_desc)) != srs_success) {
3129 +
            return srs_error_wrap(err, "audio");
3130 +
        }
3131 +
        if ((err = generate_publish_local_sdp_for_video(local_sdp, stream_desc, unified_plan)) != srs_success) {
3132 +
            return srs_error_wrap(err, "video");
3133 +
        }
3134 +
    } else {
3135 +
        if ((err = generate_publish_local_sdp_for_video(local_sdp, stream_desc, unified_plan)) != srs_success) {
3136 +
            return srs_error_wrap(err, "video");
3137 +
        }
3138 +
        if ((err = generate_publish_local_sdp_for_audio(local_sdp, stream_desc)) != srs_success) {
3139 +
            return srs_error_wrap(err, "audio");
3140 +
        }
3141 +
    }
3142 +
3143 +
    return err;
3144 +
}
3145 +
3146 +
srs_error_t SrsRtcConnection::generate_publish_local_sdp_for_audio(SrsSdp& local_sdp, SrsRtcSourceDescription* stream_desc)
3147 +
{
3148 +
    srs_error_t err = srs_success;
3149 +
3108 3150
    // generate audio media desc
3109 3151
    if (stream_desc->audio_track_desc_) {
3110 3152
        SrsRtcTrackDescription* audio_track = stream_desc->audio_track_desc_;
@@ -3139,6 +3181,13 @@
Loading
3139 3181
        local_media_desc.payload_types_.push_back(payload->generate_media_payload_type());
3140 3182
    }
3141 3183
3184 +
    return err;
3185 +
}
3186 +
3187 +
srs_error_t SrsRtcConnection::generate_publish_local_sdp_for_video(SrsSdp& local_sdp, SrsRtcSourceDescription* stream_desc, bool unified_plan)
3188 +
{
3189 +
    srs_error_t err = srs_success;
3190 +
3142 3191
    for (int i = 0;  i < (int)stream_desc->video_track_descs_.size(); ++i) {
3143 3192
        SrsRtcTrackDescription* video_track = stream_desc->video_track_descs_.at(i);
3144 3193
@@ -3202,9 +3251,14 @@
Loading
3202 3251
        return srs_error_wrap(err, "fetch rtc source");
3203 3252
    }
3204 3253
3254 +
    // How many video descriptions we have parsed.
3255 +
    int nn_any_video_parsed = 0;
3256 +
3205 3257
    for (int i = 0; i < (int)remote_sdp.media_descs_.size(); ++i) {
3206 3258
        const SrsMediaDesc& remote_media_desc = remote_sdp.media_descs_.at(i);
3207 3259
3260 +
        if (remote_media_desc.is_video()) nn_any_video_parsed++;
3261 +
3208 3262
        // Whether feature enabled in remote extmap.
3209 3263
        int remote_twcc_id = 0;
3210 3264
        if (true) {
@@ -3220,6 +3274,9 @@
Loading
3220 3274
        std::vector<SrsRtcTrackDescription*> track_descs;
3221 3275
        SrsMediaPayloadType remote_payload(0);
3222 3276
        if (remote_media_desc.is_audio()) {
3277 +
            // Update the ruc, which is about user specified configuration.
3278 +
            ruc->audio_before_video_ = !nn_any_video_parsed;
3279 +
3223 3280
            // TODO: check opus format specific param
3224 3281
            vector<SrsMediaPayloadType> payloads = remote_media_desc.find_media_with_encoding_name("opus");
3225 3282
            if (payloads.empty()) {
@@ -3379,7 +3436,7 @@
Loading
3379 3436
    }
3380 3437
}
3381 3438
3382 -
srs_error_t SrsRtcConnection::generate_play_local_sdp(SrsRequest* req, SrsSdp& local_sdp, SrsRtcSourceDescription* stream_desc, bool unified_plan)
3439 +
srs_error_t SrsRtcConnection::generate_play_local_sdp(SrsRequest* req, SrsSdp& local_sdp, SrsRtcSourceDescription* stream_desc, bool unified_plan, bool audio_before_video)
3383 3440
{
3384 3441
    srs_error_t err = srs_success;
3385 3442
@@ -3406,6 +3463,29 @@
Loading
3406 3463
3407 3464
    std::string cname = srs_random_str(16);
3408 3465
3466 +
    if (audio_before_video) {
3467 +
        if ((err = generate_play_local_sdp_for_audio(local_sdp, stream_desc, cname)) != srs_success) {
3468 +
            return srs_error_wrap(err, "audio");
3469 +
        }
3470 +
        if ((err = generate_play_local_sdp_for_video(local_sdp, stream_desc, unified_plan, cname)) != srs_success) {
3471 +
            return srs_error_wrap(err, "video");
3472 +
        }
3473 +
    } else {
3474 +
        if ((err = generate_play_local_sdp_for_video(local_sdp, stream_desc, unified_plan, cname)) != srs_success) {
3475 +
            return srs_error_wrap(err, "video");
3476 +
        }
3477 +
        if ((err = generate_play_local_sdp_for_audio(local_sdp, stream_desc, cname)) != srs_success) {
3478 +
            return srs_error_wrap(err, "audio");
3479 +
        }
3480 +
    }
3481 +
3482 +
    return err;
3483 +
}
3484 +
3485 +
srs_error_t SrsRtcConnection::generate_play_local_sdp_for_audio(SrsSdp& local_sdp, SrsRtcSourceDescription* stream_desc, std::string cname)
3486 +
{
3487 +
    srs_error_t err = srs_success;
3488 +
3409 3489
    // generate audio media desc
3410 3490
    if (stream_desc->audio_track_desc_) {
3411 3491
        SrsRtcTrackDescription* audio_track = stream_desc->audio_track_desc_;
@@ -3465,6 +3545,13 @@
Loading
3465 3545
        }
3466 3546
    }
3467 3547
3548 +
    return err;
3549 +
}
3550 +
3551 +
srs_error_t SrsRtcConnection::generate_play_local_sdp_for_video(SrsSdp& local_sdp, SrsRtcSourceDescription* stream_desc, bool unified_plan, std::string cname)
3552 +
{
3553 +
    srs_error_t err = srs_success;
3554 +
3468 3555
    for (int i = 0;  i < (int)stream_desc->video_track_descs_.size(); ++i) {
3469 3556
        SrsRtcTrackDescription* track = stream_desc->video_track_descs_[i];
3470 3557

@@ -56,32 +56,39 @@
Loading
56 56
srs_error_t srs_parse_h264_fmtp(const std::string& fmtp, H264SpecificParam& h264_param)
57 57
{
58 58
    srs_error_t err = srs_success;
59 -
    std::vector<std::string> vec = split_str(fmtp, ";");
59 +
60 +
    std::vector<std::string> vec = srs_string_split(fmtp, ";");
60 61
    for (size_t i = 0; i < vec.size(); ++i) {
61 -
        std::vector<std::string> kv = split_str(vec[i], "=");
62 -
        if (kv.size() == 2) {
63 -
            if (kv[0] == "profile-level-id") {
64 -
                h264_param.profile_level_id = kv[1];
65 -
            } else if (kv[0] == "packetization-mode") {
66 -
                // 6.3.  Non-Interleaved Mode
67 -
                // This mode is in use when the value of the OPTIONAL packetization-mode
68 -
                // media type parameter is equal to 1.  This mode SHOULD be supported.
69 -
                // It is primarily intended for low-delay applications.  Only single NAL
70 -
                // unit packets, STAP-As, and FU-As MAY be used in this mode.  STAP-Bs,
71 -
                // MTAPs, and FU-Bs MUST NOT be used.  The transmission order of NAL
72 -
                // units MUST comply with the NAL unit decoding order.
73 -
                // @see https://tools.ietf.org/html/rfc6184#section-6.3
74 -
                h264_param.packetization_mode = kv[1];
75 -
            } else if (kv[0] == "level-asymmetry-allowed") {
76 -
                h264_param.level_asymmerty_allow = kv[1];
77 -
            } else {
78 -
                return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid h264 param=%s", kv[0].c_str());
79 -
            }
80 -
        } else {
81 -
            return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid h264 param=%s", vec[i].c_str());
62 +
        std::vector<std::string> kv = srs_string_split(vec[i], "=");
63 +
        if (kv.size() != 2) continue;
64 +
65 +
        if (kv[0] == "profile-level-id") {
66 +
            h264_param.profile_level_id = kv[1];
67 +
        } else if (kv[0] == "packetization-mode") {
68 +
            // 6.3.  Non-Interleaved Mode
69 +
            // This mode is in use when the value of the OPTIONAL packetization-mode
70 +
            // media type parameter is equal to 1.  This mode SHOULD be supported.
71 +
            // It is primarily intended for low-delay applications.  Only single NAL
72 +
            // unit packets, STAP-As, and FU-As MAY be used in this mode.  STAP-Bs,
73 +
            // MTAPs, and FU-Bs MUST NOT be used.  The transmission order of NAL
74 +
            // units MUST comply with the NAL unit decoding order.
75 +
            // @see https://tools.ietf.org/html/rfc6184#section-6.3
76 +
            h264_param.packetization_mode = kv[1];
77 +
        } else if (kv[0] == "level-asymmetry-allowed") {
78 +
            h264_param.level_asymmerty_allow = kv[1];
82 79
        }
83 80
    }
84 81
82 +
    if (h264_param.profile_level_id.empty()) {
83 +
        return srs_error_new(ERROR_RTC_SDP_DECODE, "no h264 param: profile-level-id");
84 +
    }
85 +
    if (h264_param.packetization_mode.empty()) {
86 +
        return srs_error_new(ERROR_RTC_SDP_DECODE, "no h264 param: packetization-mode");
87 +
    }
88 +
    if (h264_param.level_asymmerty_allow.empty()) {
89 +
        return srs_error_new(ERROR_RTC_SDP_DECODE, "no h264 param: level-asymmetry-allowed");
90 +
    }
91 +
85 92
    return err;
86 93
}
87 94

Everything is accounted for!

No changes detected that need to be reviewed.
What changes does Codecov check for?
Lines, not adjusted in diff, that have changed coverage data.
Files that introduced coverage data that had none before.
Files that have missing coverage data that once were tracked.
Files Coverage
app -0.10% 17.16%
core 85.71%
kernel 74.96%
protocol 77.75%
srt 3.70%
utest 98.88%
main/srs_main_server.cpp 0.00%
Folder Totals (7 files) 58.46%
Project Totals (133 files) 58.46%
Loading