|
- #include "rtsp-media.h"
- #include "sdp.h"
- #include "sdp-options.h"
- #include "sdp-a-fmtp.h"
- #include "sdp-a-rtpmap.h"
- #include "sys/path.h"
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdarg.h>
- #include <string.h>
- #include <assert.h>
-
- static inline int scopy(struct rtsp_media_t* medias, char** dst, const char* src)
- {
- int n;
- n = snprintf(medias->ptr + medias->offset, sizeof(medias->ptr) - medias->offset, "%s", src);
- if (n < 0 || n >= (int)sizeof(medias->ptr) - medias->offset)
- return -1;
- *dst = medias->ptr + medias->offset;
- medias->offset += n + 1; // with '\0'
- return 0;
- }
-
- //static inline int vscopy(struct rtsp_media_t* medias, char** dst, const char* fmt, ...)
- //{
- // int n;
- // va_list args;
- // va_start(args, fmt);
- // n = vsnprintf(medias->ptr + medias->offset, sizeof(medias->ptr) - medias->offset, fmt, args);
- // va_end(args);
- //
- // if (n < 0 || n >= (int)sizeof(medias->ptr) - medias->offset)
- // return -1;
- // *dst = medias->ptr + medias->offset;
- // medias->offset += n + 1; // with '\0'
- // return 0;
- //}
- //
- //static inline int rtsp_media_aggregate_control_enable(void *sdp)
- //{
- // const char* control;
- //
- // // rfc 2326 C.1.1 Control URL (p80)
- // // If found at the session level, the attribute indicates the URL for aggregate control
- // control = sdp_attribute_find(sdp, "control");
- // return (control && *control) ? 1 : 0;
- //}
-
- // rfc 2326 C.1.1 Control URL (p81)
- // look for a base URL in the following order:
- // 1. The RTSP Content-Base field
- // 2. The RTSP Content-Location field
- // 3. The RTSP request URL
- int rtsp_media_set_url(struct rtsp_media_t* m, const char* base, const char* location, const char* request)
- {
- int r;
- char buffer[256] = { 0 };
-
- // C.1.1 Control URL (p81)
- // If this attribute contains only an asterisk (*), then the URL is
- // treated as if it were an empty embedded URL, and thus inherits the entire base URL.
- if (m->session_uri[0] && '*' != m->session_uri[0])
- {
- snprintf(buffer, sizeof(buffer)-1, "%s", m->session_uri);
- r = path_resolve2(m->session_uri, sizeof(m->session_uri)-1, buffer, base, location, request);
- }
- else if('*' == m->session_uri[0])
- {
- r = snprintf(m->session_uri, sizeof(m->session_uri) - 1, "%s", request);
- }
- else
- {
- // keep session uri empty
- r = 0;
- }
-
- if ('*' != m->uri[0])
- {
- snprintf(buffer, sizeof(buffer) - 1, "%s", m->uri);
- r = path_resolve2(m->uri, sizeof(m->uri) - 1, buffer, base, location, request);
- }
- else
- {
- r = snprintf(m->uri, sizeof(m->uri) - 1, "%s", request);
- }
-
- return r >= 0 ? 0 : r;
- }
-
- // RFC 6184 RTP Payload Format for H.264 Video
- // 8.2.1. Mapping of Payload Type Parameters to SDP
- // m=video 49170 RTP/AVP 98
- // a=rtpmap:98 H264/90000
- // a=fmtp:98 profile-level-id=42A01E;
- // packetization-mode=1;
- // sprop-parameter-sets=<parameter sets data>
- static void rtsp_media_onattr(void* param, const char* name, const char* value)
- {
- int i, n;
- int payload = -1;
- struct rtsp_media_t* media;
-
- media = (struct rtsp_media_t*)param;
-
- if (name)
- {
- if (0 == strcmp("rtpmap", name))
- {
- int rate = 0;
- char channel[64];
- char encoding[16 + sizeof(media->avformats[i].encoding)];
- if (strlen(value) < sizeof(encoding)) // make sure encoding have enough memory space
- {
- channel[0] = '\0';
- sdp_a_rtpmap(value, &payload, encoding, &rate, channel);
- for (i = 0; i < media->avformat_count; i++)
- {
- if (media->avformats[i].fmt == payload)
- {
- media->avformats[i].rate = rate;
- media->avformats[i].channel = *channel ? atoi(channel) : 1; // default 1-channel
- snprintf(media->avformats[i].encoding, sizeof(media->avformats[i].encoding), "%s", encoding);
- break;
- }
- }
- }
- }
- else if (0 == strcmp("fmtp", name))
- {
- n = (int)strlen(value);
- payload = atoi(value);
- for (i = 0; i < media->avformat_count && media->offset + n + 1 < sizeof(media->ptr); i++)
- {
- if (media->avformats[i].fmt != payload)
- continue;
-
- media->avformats[i].fmtp = media->ptr + media->offset;
- strcpy(media->avformats[i].fmtp, value);
- media->offset += n + 1;
- //if(0 == strcmp("H264", media->avformats[i].encoding))
- //{
- // struct sdp_a_fmtp_h264_t h264;
- // memset(&h264, 0, sizeof(h264));
- // sdp_a_fmtp_h264(value, &payload, &h264);
- // if(h264.flags & SDP_A_FMTP_H264_SPROP_PARAMETER_SETS)
- // snprintf(media->avformats[i].ps, sizeof(media->avformats[i].ps), "%s", h264.sprop_parameter_sets);
- //}
- //else if (0 == strcmp("H265", media->avformats[i].encoding))
- //{
- // struct sdp_a_fmtp_h265_t h265;
- // memset(&h265, 0, sizeof(h265));
- // sdp_a_fmtp_h265(value, &payload, &h265);
- // //if (h265.flags & SDP_A_FMTP_H265_SPROP_VPS)
- // // snprintf(media->avformats[i].ps, sizeof(media->avformats[i].ps), "%s", h265.sprop_vps);
- // //if (h265.flags & SDP_A_FMTP_H265_SPROP_SPS)
- // // snprintf(media->avformats[i].ps, sizeof(media->avformats[i].ps), "%s", h265.sprop_sps);
- // //if (h265.flags & SDP_A_FMTP_H265_SPROP_PPS)
- // // snprintf(media->avformats[i].ps, sizeof(media->avformats[i].ps), "%s", h265.sprop_pps);
- //}
- //else if(0 == strcmp("mpeg4-generic", media->avformats[i].encoding))
- //{
- // struct sdp_a_fmtp_mpeg4_t mpeg4;
- // memset(&mpeg4, 0, sizeof(mpeg4));
- // sdp_a_fmtp_mpeg4(value, &payload, &mpeg4);
- //}
- break;
- }
- }
- else if (0 == strcmp("etag", name))
- {
- // C.1.8 Entity Tag
- }
- else if (0 == strcmp("rtcp", name))
- {
- // rfc3605 Real Time Control Protocol (RTCP) attribute in Session Description Protocol (SDP)
- // "a=rtcp:" port [nettype space addrtype space connection-address] CRLF
- // a=rtcp:53020 IN IP6 2001:2345:6789:ABCD:EF01:2345:6789:ABCD
- assert(media->nport <= 2 && sizeof(media->port)/sizeof(media->port[0]) >= 2);
- if (1 == media->nport)
- media->nport++;
- media->port[1] = atoi(value);
-
- // TODO: rtcp address
- }
- else if (0 == strcmp("rtcp-mux", name))
- {
- if (1 == media->nport)
- media->nport++;
- media->port[1] = media->port[0];
- }
- else if (0 == strcmp("rtcp-xr", name))
- {
- // rfc3611 RTP Control Protocol Extended Reports (RTCP XR)
- // "a=rtcp-xr:" [xr-format *(SP xr-format)] CRLF
- }
- else if (0 == strcmp("ice-pwd", name))
- {
- scopy(media, &media->ice.pwd, value);
- }
- else if (0 == strcmp("ice-ufrag", name))
- {
- scopy(media, &media->ice.ufrag, value);
- }
- else if (0 == strcmp("ice-lite", name))
- {
- media->ice.lite = 1;
- }
- else if (0 == strcmp("ice-mismatch", name))
- {
- media->ice.mismatch = 1;
- }
- else if (0 == strcmp("ice-pacing", name))
- {
- media->ice.pacing = atoi(value);
- }
- else if (0 == strcmp("candidate", name))
- {
- if (media->ice.candidate_count + 1 < sizeof(media->ice.candidates) / sizeof(media->ice.candidates[0]) && media->offset + sizeof(*media->ice.candidates[0]) <= sizeof(media->ptr))
- {
- media->ice.candidates[media->ice.candidate_count] = (struct sdp_candidate_t*)(media->ptr + media->offset);
- if (7 == sscanf(value, "%32s %hu %7s %u %63s %hu typ %7s%n",
- media->ice.candidates[media->ice.candidate_count]->foundation,
- &media->ice.candidates[media->ice.candidate_count]->component,
- media->ice.candidates[media->ice.candidate_count]->transport,
- &media->ice.candidates[media->ice.candidate_count]->priority,
- media->ice.candidates[media->ice.candidate_count]->address,
- &media->ice.candidates[media->ice.candidate_count]->port,
- media->ice.candidates[media->ice.candidate_count]->candtype, &n))
- {
- sscanf(value + n, " raddr %63s rport %hu", media->ice.candidates[media->ice.candidate_count]->reladdr, &media->ice.candidates[media->ice.candidate_count]->relport);
- media->offset += sizeof(*media->ice.candidates[0]);
- ++media->ice.candidate_count;
- }
- }
- }
- else if (0 == strcmp("remote-candidates", name))
- {
- while (media->ice.remote_count + 1 < sizeof(media->ice.remotes) / sizeof(media->ice.remotes[0]) && media->offset + sizeof(*media->ice.remotes[0]) <= sizeof(media->ptr))
- {
- media->ice.remotes[media->ice.remote_count] = (struct sdp_candidate_t*)(media->ptr + media->offset);
- if (!value || 3 != sscanf(value, "%hu %63s %hu%n", &media->ice.remotes[media->ice.remote_count]->component, media->ice.remotes[media->ice.remote_count]->address, &media->ice.remotes[media->ice.remote_count]->port, &n))
- break;
-
- value += n;
- ++media->ice.remote_count;
- media->offset += sizeof(*media->ice.remotes[0]);
- }
- }
- else if (0 == strcmp("setup", name))
- {
- media->setup = sdp_option_setup_from(value);
- }
- else if (0 == strcmp("ssrc", name))
- {
- media->ssrc.ssrc = (uint32_t)strtoul(value, NULL, 10);
- // TODO: ssrc attribute
- }
- else if (0 == strcmp("ssrc-group", name))
- {
- // TODO
- }
- }
- }
-
- /*
- v=0
- o=mhandley 2890844526 2890842807 IN IP4 126.16.64.4
- s=SDP Seminar
- i=A Seminar on the session description protocol
- u=http://www.cs.ucl.ac.uk/staff/M.Handley/sdp.03.ps
- e=mjh@isi.edu (Mark Handley)
- c=IN IP4 224.2.17.12/127
- t=2873397496 2873404696
- a=recvonly
- m=audio 3456 RTP/AVP 0
- m=video 2232 RTP/AVP 31
- m=whiteboard 32416 UDP WB
- a=orient:portrait
- */
- int rtsp_media_sdp(const char* s, int len, struct rtsp_media_t* medias, int count)
- {
- int i, j, n;
- int formats[16];
- const char* control;
- const char* start, *stop;
- const char* iceufrag, *icepwd;
- const char* username, *session, *version;
- const char* network, *addrtype, *address, *source;
- struct rtsp_media_t* m;
- struct rtsp_header_range_t range;
- sdp_t* sdp;
-
- sdp = sdp_parse(s, len);
- if (!sdp)
- return -1;
-
- // rfc 2326 C.1.1 Control URL (p80)
- // If found at the session level, the attribute indicates the URL for aggregate control
- control = sdp_attribute_find(sdp, "control");
-
- // C.1.5 Range of presentation
- // The "a=range" attribute defines the total time range of the stored session.
- memset(&range, 0, sizeof(range));
- s = sdp_attribute_find(sdp, "range");
- if(s) rtsp_header_range(s, &range);
-
- // C.1.6 Time of availability
- start = stop = NULL;
- for (i = 0; i < sdp_timing_count(sdp); i++)
- {
- sdp_timing_get(sdp, i, &start, &stop);
- }
-
- // C.1.7 Connection Information
- network = addrtype = source = NULL;
- if (0 != sdp_connection_get(sdp, &network, &addrtype, &source) || 0 == strcmp("0.0.0.0", source))
- sdp_origin_get(sdp, &username, &session, &version, &network, &addrtype, &source);
-
- // session ice-ufrag/ice-pwd
- iceufrag = sdp_attribute_find(sdp, "ice-ufrag");
- icepwd = sdp_attribute_find(sdp, "ice-pwd");
-
- for (i = 0; i < sdp_media_count(sdp) && i < count; i++)
- {
- m = medias + i;
- memset(m, 0, sizeof(struct rtsp_media_t));
- memcpy(&m->range, &range, sizeof(m->range));
- if (control)
- snprintf(m->session_uri, sizeof(m->session_uri), "%s", control);
- if (start && stop)
- {
- m->start = strtoull(start, NULL, 10);
- m->stop = strtoull(stop, NULL, 10);
- }
-
- if(0 == sdp_media_get_connection(sdp, i, &network, &addrtype, &address))
- {
- if (0 == strcmp("IP4", addrtype) && 0 == strcmp("0.0.0.0", address) && source && *source)
- address = source;
- snprintf(m->source, sizeof(m->source), "%s", source && *source ? source : "");
- snprintf(m->network, sizeof(m->network), "%s", network);
- snprintf(m->address, sizeof(m->address), "%s", address);
- snprintf(m->addrtype, sizeof(m->addrtype), "%s", addrtype);
- }
- //media->cseq = rand();
-
- m->nport = sdp_media_port(sdp, i, m->port, sizeof(m->port)/sizeof(m->port[0]));
- snprintf(m->media, sizeof(m->media), "%s", sdp_media_type(sdp, i));
- snprintf(m->proto, sizeof(m->proto), "%s", sdp_media_proto(sdp, i));
- if (1 == m->nport && 0 == strncmp("RTP/", m->proto, 4))
- m->port[m->nport++] = m->port[0] + 1;
-
- // media control url
- s = sdp_media_attribute_find(sdp, i, "control");
- if(s)
- snprintf(m->uri, sizeof(m->uri), "%s", s);
-
- // media format
- j = sizeof(m->avformats) / sizeof(m->avformats[0]);
- assert(sizeof(formats) / sizeof(formats[0]) >= j);
- n = sdp_media_formats(sdp, i, formats, j);
- m->avformat_count = n > j ? j : n;
- for (j = 0; j < m->avformat_count; j++)
- m->avformats[j].fmt = formats[j];
-
- // TODO: plan-B streams
- m->mode = sdp_media_mode(sdp, i);
- m->setup = SDP_A_SETUP_NONE;
-
- // update media encoding
- sdp_media_attribute_list(sdp, i, NULL, rtsp_media_onattr, m);
-
- // use default ice-ufrag/pwd
- if(NULL == m->ice.ufrag && iceufrag)
- scopy(medias, &m->ice.ufrag, iceufrag);
- if(NULL == m->ice.pwd && icepwd)
- scopy(medias, &m->ice.pwd, icepwd);
- }
-
- count = sdp_media_count(sdp);
- sdp_destroy(sdp);
- return count; // should check return value
- }
-
- /// @return -0-no media, >0-ok, <0-error
- int rtsp_media_to_sdp(const struct rtsp_media_t* m, char* line, int bytes)
- {
- int i, n;
- int setup = 0;
- int port = m->port[0];
-
- if (SDP_M_PROTO_TEST_TCP(sdp_option_proto_from(m->proto)))
- {
- // try to set tcp active for sender side
- setup = (SDP_A_SETUP_NONE == m->setup || SDP_A_SETUP_ACTPASS == m->setup) ? SDP_A_SETUP_ACTIVE : m->setup;
- //if (SDP_A_SETUP_PASSIVE == setup || SDP_A_SETUP_ACTPASS == setup)
- // port = options->m[i].port[0];
- }
-
- n = snprintf(line, bytes, "m=%s %d %s", m->media, port, m->proto);
- for (i = 0; i < m->avformat_count; i++)
- {
- if (m->avformats[i].fmt >= 96 && !m->avformats[i].encoding[0])
- continue; // ignore empty encoding
- n += snprintf(line + n, bytes - n, " %d", m->avformats[i].fmt);
- }
- n += snprintf(line + n, bytes - n, "\n");
-
- for (i = 0; i < m->avformat_count && n >= 0 && n < bytes; i++)
- {
- if (!m->avformats[i].encoding[0])
- continue;
-
- if (SDP_M_MEDIA_VIDEO == sdp_option_media_from(m->media))
- {
- n += snprintf(line + n, bytes - n, "a=rtpmap:%d %s/%d\n", m->avformats[i].fmt, m->avformats[i].encoding, m->avformats[i].rate ? m->avformats[i].rate : 90000);
- if(n >= 0 && n < bytes && m->avformats[i].fmtp && m->avformats[i].fmtp[0])
- n += snprintf(line + n, bytes - n, "a=fmtp:%s\n", m->avformats[i].fmtp);
- }
- else if (SDP_M_MEDIA_AUDIO == sdp_option_media_from(m->media))
- {
- if(m->avformats[i].channel > 0)
- n += snprintf(line + n, bytes - n, "a=rtpmap:%d %s/%d/%d\n", m->avformats[i].fmt, m->avformats[i].encoding, m->avformats[i].rate, m->avformats[i].channel);
- else
- n += snprintf(line + n, bytes - n, "a=rtpmap:%d %s/%d\n", m->avformats[i].fmt, m->avformats[i].encoding, m->avformats[i].rate);
-
- if (n >= 0 && n < bytes && m->avformats[i].fmtp && m->avformats[i].fmtp[0])
- n += snprintf(line + n, bytes - n, "a=fmtp:%s\n", m->avformats[i].fmtp);
- }
- }
-
- //for (int j = 0; j < 128 && j < 8 * sizeof(m->payloads) / sizeof(m->payloads[0]); j++)
- //{
- // if(m->payloads[j/8] & (1<<(j%8)))
- // n += snprintf(answer+n, sizeof(answer)-n, " %d", j);
- //}
-
- if (SDP_M_PROTO_TEST_TCP(sdp_option_proto_from(m->proto)))
- {
- n += snprintf(line + n, bytes - n, "a=setup:%s\n", sdp_option_setup_to(setup));
- }
-
- if (m->nport < 2 || m->port[0] == m->port[1])
- {
- n += snprintf(line + n, bytes - n, "a=rtcp-mux\n");
- }
-
- n += snprintf(line + n, bytes - n, "a=%s\n", sdp_option_mode_to(m->mode));
-
- if (m->ssrc.ssrc)
- {
- n += snprintf(line + n, bytes - n, "a=ssrc:%u\n", m->ssrc.ssrc);
- }
-
- return n > 0 && n < bytes ? n : -1;
- }
|