static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, int letter, const char *buf) { RTSPState *rt = s->priv_data; char buf1[64], st_type[64]; const char *p; enum AVMediaType codec_type; int payload_type; AVStream *st; RTSPStream *rtsp_st; RTSPSource *rtsp_src; struct sockaddr_storage sdp_ip; int ttl; av_dlog(s, "sdp: %c='%s'\n", letter, buf); p = buf; if (s1->skip_media && letter != 'm') return; switch (letter) { case 'c': get_word(buf1, sizeof(buf1), &p); if (strcmp(buf1, "IN") != 0) return; get_word(buf1, sizeof(buf1), &p); if (strcmp(buf1, "IP4") && strcmp(buf1, "IP6")) return; get_word_sep(buf1, sizeof(buf1), "/", &p); if (get_sockaddr(buf1, &sdp_ip)) return; ttl = 16; if (*p == '/') { p++; get_word_sep(buf1, sizeof(buf1), "/", &p); ttl = atoi(buf1); } if (s->nb_streams == 0) { s1->default_ip = sdp_ip; s1->default_ttl = ttl; } else { rtsp_st = rt->rtsp_streams[rt->nb_rtsp_streams - 1]; rtsp_st->sdp_ip = sdp_ip; rtsp_st->sdp_ttl = ttl; } break; case 's': av_dict_set(&s->metadata, "title", p, 0); break; case 'i': if (s->nb_streams == 0) { av_dict_set(&s->metadata, "comment", p, 0); break; } break; case 'm': /* new stream */ s1->skip_media = 0; s1->seen_fmtp = 0; s1->seen_rtpmap = 0; codec_type = AVMEDIA_TYPE_UNKNOWN; get_word(st_type, sizeof(st_type), &p); if (!strcmp(st_type, "audio")) { codec_type = AVMEDIA_TYPE_AUDIO; } else if (!strcmp(st_type, "video")) { codec_type = AVMEDIA_TYPE_VIDEO; } else if (!strcmp(st_type, "application")) { codec_type = AVMEDIA_TYPE_DATA; } if (codec_type == AVMEDIA_TYPE_UNKNOWN || !(rt->media_type_mask & (1 << codec_type))) { s1->skip_media = 1; return; } rtsp_st = av_mallocz(sizeof(RTSPStream)); if (!rtsp_st) return; rtsp_st->stream_index = -1; dynarray_add(&rt->rtsp_streams, &rt->nb_rtsp_streams, rtsp_st); rtsp_st->sdp_ip = s1->default_ip; rtsp_st->sdp_ttl = s1->default_ttl; copy_default_source_addrs(s1->default_include_source_addrs, s1->nb_default_include_source_addrs, &rtsp_st->include_source_addrs, &rtsp_st->nb_include_source_addrs); copy_default_source_addrs(s1->default_exclude_source_addrs, s1->nb_default_exclude_source_addrs, &rtsp_st->exclude_source_addrs, &rtsp_st->nb_exclude_source_addrs); get_word(buf1, sizeof(buf1), &p); /* port */ rtsp_st->sdp_port = atoi(buf1); get_word(buf1, sizeof(buf1), &p); /* protocol */ if (!strcmp(buf1, "udp")) rt->transport = RTSP_TRANSPORT_RAW; else if (strstr(buf1, "/AVPF") || strstr(buf1, "/SAVPF")) rtsp_st->feedback = 1; /* XXX: handle list of formats */ get_word(buf1, sizeof(buf1), &p); /* format list */ rtsp_st->sdp_payload_type = atoi(buf1); if (!strcmp(ff_rtp_enc_name(rtsp_st->sdp_payload_type), "MP2T")) { /* no corresponding stream */ if (rt->transport == RTSP_TRANSPORT_RAW) { if (!rt->ts && CONFIG_RTPDEC) rt->ts = ff_mpegts_parse_open(s); } else { RTPDynamicProtocolHandler *handler; handler = ff_rtp_handler_find_by_id( rtsp_st->sdp_payload_type, AVMEDIA_TYPE_DATA); init_rtp_handler(handler, rtsp_st, NULL); if (handler && handler->init) handler->init(s, -1, rtsp_st->dynamic_protocol_context); } } else if (rt->server_type == RTSP_SERVER_WMS && codec_type == AVMEDIA_TYPE_DATA) { /* RTX stream, a stream that carries all the other actual * audio/video streams. Don't expose this to the callers. */ } else { st = avformat_new_stream(s, NULL); if (!st) return; st->id = rt->nb_rtsp_streams - 1; rtsp_st->stream_index = st->index; st->codec->codec_type = codec_type; if (rtsp_st->sdp_payload_type < RTP_PT_PRIVATE) { RTPDynamicProtocolHandler *handler; /* if standard payload type, we can find the codec right now */ ff_rtp_get_codec_info(st->codec, rtsp_st->sdp_payload_type); if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO && st->codec->sample_rate > 0) avpriv_set_pts_info(st, 32, 1, st->codec->sample_rate); /* Even static payload types may need a custom depacketizer */ handler = ff_rtp_handler_find_by_id( rtsp_st->sdp_payload_type, st->codec->codec_type); init_rtp_handler(handler, rtsp_st, st->codec); if (handler && handler->init) handler->init(s, st->index, rtsp_st->dynamic_protocol_context); } } /* put a default control url */ av_strlcpy(rtsp_st->control_url, rt->control_uri, sizeof(rtsp_st->control_url)); break; case 'a': if (av_strstart(p, "control:", &p)) { if (s->nb_streams == 0) { if (!strncmp(p, "rtsp://", 7)) av_strlcpy(rt->control_uri, p, sizeof(rt->control_uri)); } else { char proto[32]; /* get the control url */ rtsp_st = rt->rtsp_streams[rt->nb_rtsp_streams - 1]; /* XXX: may need to add full url resolution */ av_url_split(proto, sizeof(proto), NULL, 0, NULL, 0, NULL, NULL, 0, p); if (proto[0] == '\0') { /* relative control URL */ if (rtsp_st->control_url[strlen(rtsp_st->control_url)-1]!='/') av_strlcat(rtsp_st->control_url, "/", sizeof(rtsp_st->control_url)); av_strlcat(rtsp_st->control_url, p, sizeof(rtsp_st->control_url)); } else av_strlcpy(rtsp_st->control_url, p, sizeof(rtsp_st->control_url)); } } else if (av_strstart(p, "rtpmap:", &p) && s->nb_streams > 0) { /* NOTE: rtpmap is only supported AFTER the 'm=' tag */ get_word(buf1, sizeof(buf1), &p); payload_type = atoi(buf1); rtsp_st = rt->rtsp_streams[rt->nb_rtsp_streams - 1]; if (rtsp_st->stream_index >= 0) { st = s->streams[rtsp_st->stream_index]; sdp_parse_rtpmap(s, st, rtsp_st, payload_type, p); } s1->seen_rtpmap = 1; if (s1->seen_fmtp) { parse_fmtp(s, rt, payload_type, s1->delayed_fmtp); } } else if (av_strstart(p, "fmtp:", &p) || av_strstart(p, "framesize:", &p)) { // let dynamic protocol handlers have a stab at the line. get_word(buf1, sizeof(buf1), &p); payload_type = atoi(buf1); if (s1->seen_rtpmap) { parse_fmtp(s, rt, payload_type, buf); } else { s1->seen_fmtp = 1; av_strlcpy(s1->delayed_fmtp, buf, sizeof(s1->delayed_fmtp)); } } else if (av_strstart(p, "range:", &p)) { int64_t start, end; // this is so that seeking on a streamed file can work. rtsp_parse_range_npt(p, &start, &end); s->start_time = start; /* AV_NOPTS_VALUE means live broadcast (and can't seek) */ s->duration = (end == AV_NOPTS_VALUE) ? AV_NOPTS_VALUE : end - start; } else if (av_strstart(p, "IsRealDataType:integer;",&p)) { if (atoi(p) == 1) rt->transport = RTSP_TRANSPORT_RDT; } else if (av_strstart(p, "SampleRate:integer;", &p) && s->nb_streams > 0) { st = s->streams[s->nb_streams - 1]; st->codec->sample_rate = atoi(p); } else if (av_strstart(p, "crypto:", &p) && s->nb_streams > 0) { // RFC 4568 rtsp_st = rt->rtsp_streams[rt->nb_rtsp_streams - 1]; get_word(buf1, sizeof(buf1), &p); // ignore tag get_word(rtsp_st->crypto_suite, sizeof(rtsp_st->crypto_suite), &p); p += strspn(p, SPACE_CHARS); if (av_strstart(p, "inline:", &p)) get_word(rtsp_st->crypto_params, sizeof(rtsp_st->crypto_params), &p); } else if (av_strstart(p, "source-filter:", &p)) { int exclude = 0; get_word(buf1, sizeof(buf1), &p); if (strcmp(buf1, "incl") && strcmp(buf1, "excl")) return; exclude = !strcmp(buf1, "excl"); get_word(buf1, sizeof(buf1), &p); if (strcmp(buf1, "IN") != 0) return; get_word(buf1, sizeof(buf1), &p); if (strcmp(buf1, "IP4") && strcmp(buf1, "IP6") && strcmp(buf1, "*")) return; // not checking that the destination address actually matches or is wildcard get_word(buf1, sizeof(buf1), &p); while (*p != '\0') { rtsp_src = av_mallocz(sizeof(*rtsp_src)); if (!rtsp_src) return; get_word(rtsp_src->addr, sizeof(rtsp_src->addr), &p); if (exclude) { if (s->nb_streams == 0) { dynarray_add(&s1->default_exclude_source_addrs, &s1->nb_default_exclude_source_addrs, rtsp_src); } else { rtsp_st = rt->rtsp_streams[rt->nb_rtsp_streams - 1]; dynarray_add(&rtsp_st->exclude_source_addrs, &rtsp_st->nb_exclude_source_addrs, rtsp_src); } } else { if (s->nb_streams == 0) { dynarray_add(&s1->default_include_source_addrs, &s1->nb_default_include_source_addrs, rtsp_src); } else { rtsp_st = rt->rtsp_streams[rt->nb_rtsp_streams - 1]; dynarray_add(&rtsp_st->include_source_addrs, &rtsp_st->nb_include_source_addrs, rtsp_src); } } } } else { if (rt->server_type == RTSP_SERVER_WMS) ff_wms_parse_sdp_a_line(s, p); if (s->nb_streams > 0) { rtsp_st = rt->rtsp_streams[rt->nb_rtsp_streams - 1]; if (rt->server_type == RTSP_SERVER_REAL) ff_real_parse_sdp_a_line(s, rtsp_st->stream_index, p); if (rtsp_st->dynamic_handler && rtsp_st->dynamic_handler->parse_sdp_a_line) rtsp_st->dynamic_handler->parse_sdp_a_line(s, rtsp_st->stream_index, rtsp_st->dynamic_protocol_context, buf); } } break; } }