00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "libavutil/base64.h"
00023 #include "libavutil/avstring.h"
00024 #include "libavutil/intreadwrite.h"
00025 #include "avformat.h"
00026
00027 #include <sys/time.h>
00028 #if HAVE_SYS_SELECT_H
00029 #include <sys/select.h>
00030 #endif
00031 #include <strings.h>
00032 #include "internal.h"
00033 #include "network.h"
00034 #include "os_support.h"
00035 #include "rtsp.h"
00036
00037 #include "rtpdec.h"
00038 #include "rdt.h"
00039 #include "rtpdec_asf.h"
00040
00041
00042
00043
00044 #if LIBAVFORMAT_VERSION_INT < (53 << 16)
00045 int rtsp_default_protocols = (1 << RTSP_LOWER_TRANSPORT_UDP);
00046 #endif
00047
00048
00049
00050 #define SELECT_TIMEOUT_MS 100
00051 #define READ_PACKET_TIMEOUT_S 10
00052 #define MAX_TIMEOUTS READ_PACKET_TIMEOUT_S * 1000 / SELECT_TIMEOUT_MS
00053
00054 #define SPACE_CHARS " \t\r\n"
00055
00056
00057 #define redir_isspace(c) memchr(SPACE_CHARS, c, 4)
00058 static void skip_spaces(const char **pp)
00059 {
00060 const char *p;
00061 p = *pp;
00062 while (redir_isspace(*p))
00063 p++;
00064 *pp = p;
00065 }
00066
00067 static void get_word_until_chars(char *buf, int buf_size,
00068 const char *sep, const char **pp)
00069 {
00070 const char *p;
00071 char *q;
00072
00073 p = *pp;
00074 skip_spaces(&p);
00075 q = buf;
00076 while (!strchr(sep, *p) && *p != '\0') {
00077 if ((q - buf) < buf_size - 1)
00078 *q++ = *p;
00079 p++;
00080 }
00081 if (buf_size > 0)
00082 *q = '\0';
00083 *pp = p;
00084 }
00085
00086 static void get_word_sep(char *buf, int buf_size, const char *sep,
00087 const char **pp)
00088 {
00089 if (**pp == '/') (*pp)++;
00090 get_word_until_chars(buf, buf_size, sep, pp);
00091 }
00092
00093 static void get_word(char *buf, int buf_size, const char **pp)
00094 {
00095 get_word_until_chars(buf, buf_size, SPACE_CHARS, pp);
00096 }
00097
00098
00099 static int sdp_parse_rtpmap(AVFormatContext *s,
00100 AVCodecContext *codec, RTSPStream *rtsp_st,
00101 int payload_type, const char *p)
00102 {
00103 char buf[256];
00104 int i;
00105 AVCodec *c;
00106 const char *c_name;
00107
00108
00109
00110
00111
00112
00113 get_word_sep(buf, sizeof(buf), "/ ", &p);
00114 if (payload_type >= RTP_PT_PRIVATE) {
00115 RTPDynamicProtocolHandler *handler;
00116 for (handler = RTPFirstDynamicPayloadHandler;
00117 handler; handler = handler->next) {
00118 if (!strcasecmp(buf, handler->enc_name) &&
00119 codec->codec_type == handler->codec_type) {
00120 codec->codec_id = handler->codec_id;
00121 rtsp_st->dynamic_handler = handler;
00122 if (handler->open)
00123 rtsp_st->dynamic_protocol_context = handler->open();
00124 break;
00125 }
00126 }
00127 } else {
00128
00129
00130
00131 codec->codec_id = ff_rtp_codec_id(buf, codec->codec_type);
00132 }
00133
00134 c = avcodec_find_decoder(codec->codec_id);
00135 if (c && c->name)
00136 c_name = c->name;
00137 else
00138 c_name = "(null)";
00139
00140 get_word_sep(buf, sizeof(buf), "/", &p);
00141 i = atoi(buf);
00142 switch (codec->codec_type) {
00143 case AVMEDIA_TYPE_AUDIO:
00144 av_log(s, AV_LOG_DEBUG, "audio codec set to: %s\n", c_name);
00145 codec->sample_rate = RTSP_DEFAULT_AUDIO_SAMPLERATE;
00146 codec->channels = RTSP_DEFAULT_NB_AUDIO_CHANNELS;
00147 if (i > 0) {
00148 codec->sample_rate = i;
00149 get_word_sep(buf, sizeof(buf), "/", &p);
00150 i = atoi(buf);
00151 if (i > 0)
00152 codec->channels = i;
00153
00154
00155
00156
00157 }
00158 av_log(s, AV_LOG_DEBUG, "audio samplerate set to: %i\n",
00159 codec->sample_rate);
00160 av_log(s, AV_LOG_DEBUG, "audio channels set to: %i\n",
00161 codec->channels);
00162 break;
00163 case AVMEDIA_TYPE_VIDEO:
00164 av_log(s, AV_LOG_DEBUG, "video codec set to: %s\n", c_name);
00165 break;
00166 default:
00167 break;
00168 }
00169 return 0;
00170 }
00171
00172
00173 static int hex_to_data(uint8_t *data, const char *p)
00174 {
00175 int c, len, v;
00176
00177 len = 0;
00178 v = 1;
00179 for (;;) {
00180 skip_spaces(&p);
00181 if (*p == '\0')
00182 break;
00183 c = toupper((unsigned char) *p++);
00184 if (c >= '0' && c <= '9')
00185 c = c - '0';
00186 else if (c >= 'A' && c <= 'F')
00187 c = c - 'A' + 10;
00188 else
00189 break;
00190 v = (v << 4) | c;
00191 if (v & 0x100) {
00192 if (data)
00193 data[len] = v;
00194 len++;
00195 v = 1;
00196 }
00197 }
00198 return len;
00199 }
00200
00201 static void sdp_parse_fmtp_config(AVCodecContext * codec, void *ctx,
00202 char *attr, char *value)
00203 {
00204 switch (codec->codec_id) {
00205 case CODEC_ID_MPEG4:
00206 case CODEC_ID_AAC:
00207 if (!strcmp(attr, "config")) {
00208
00209 int len = hex_to_data(NULL, value);
00210 if (codec->extradata)
00211 av_free(codec->extradata);
00212 codec->extradata = av_mallocz(len + FF_INPUT_BUFFER_PADDING_SIZE);
00213 if (!codec->extradata)
00214 return;
00215 codec->extradata_size = len;
00216 hex_to_data(codec->extradata, value);
00217 }
00218 break;
00219 default:
00220 break;
00221 }
00222 return;
00223 }
00224
00225 typedef struct {
00226 const char *str;
00227 uint16_t type;
00228 uint32_t offset;
00229 } AttrNameMap;
00230
00231
00232 #define ATTR_NAME_TYPE_INT 0
00233 #define ATTR_NAME_TYPE_STR 1
00234 static const AttrNameMap attr_names[]=
00235 {
00236 { "SizeLength", ATTR_NAME_TYPE_INT,
00237 offsetof(RTPPayloadData, sizelength) },
00238 { "IndexLength", ATTR_NAME_TYPE_INT,
00239 offsetof(RTPPayloadData, indexlength) },
00240 { "IndexDeltaLength", ATTR_NAME_TYPE_INT,
00241 offsetof(RTPPayloadData, indexdeltalength) },
00242 { "profile-level-id", ATTR_NAME_TYPE_INT,
00243 offsetof(RTPPayloadData, profile_level_id) },
00244 { "StreamType", ATTR_NAME_TYPE_INT,
00245 offsetof(RTPPayloadData, streamtype) },
00246 { "mode", ATTR_NAME_TYPE_STR,
00247 offsetof(RTPPayloadData, mode) },
00248 { NULL, -1, -1 },
00249 };
00250
00251
00252
00253
00254 int ff_rtsp_next_attr_and_value(const char **p, char *attr, int attr_size,
00255 char *value, int value_size)
00256 {
00257 skip_spaces(p);
00258 if (**p) {
00259 get_word_sep(attr, attr_size, "=", p);
00260 if (**p == '=')
00261 (*p)++;
00262 get_word_sep(value, value_size, ";", p);
00263 if (**p == ';')
00264 (*p)++;
00265 return 1;
00266 }
00267 return 0;
00268 }
00269
00270
00271 static void sdp_parse_fmtp(AVStream *st, const char *p)
00272 {
00273 char attr[256];
00274
00275
00276 char value[16384];
00277 int i;
00278 RTSPStream *rtsp_st = st->priv_data;
00279 AVCodecContext *codec = st->codec;
00280 RTPPayloadData *rtp_payload_data = &rtsp_st->rtp_payload_data;
00281
00282
00283 while (ff_rtsp_next_attr_and_value(&p, attr, sizeof(attr),
00284 value, sizeof(value))) {
00285
00286
00287 sdp_parse_fmtp_config(codec, rtsp_st->dynamic_protocol_context,
00288 attr, value);
00289
00290 for (i = 0; attr_names[i].str; ++i) {
00291 if (!strcasecmp(attr, attr_names[i].str)) {
00292 if (attr_names[i].type == ATTR_NAME_TYPE_INT) {
00293 *(int *)((char *)rtp_payload_data +
00294 attr_names[i].offset) = atoi(value);
00295 } else if (attr_names[i].type == ATTR_NAME_TYPE_STR)
00296 *(char **)((char *)rtp_payload_data +
00297 attr_names[i].offset) = av_strdup(value);
00298 }
00299 }
00300 }
00301 }
00302
00307 static void rtsp_parse_range_npt(const char *p, int64_t *start, int64_t *end)
00308 {
00309 char buf[256];
00310
00311 skip_spaces(&p);
00312 if (!av_stristart(p, "npt=", &p))
00313 return;
00314
00315 *start = AV_NOPTS_VALUE;
00316 *end = AV_NOPTS_VALUE;
00317
00318 get_word_sep(buf, sizeof(buf), "-", &p);
00319 *start = parse_date(buf, 1);
00320 if (*p == '-') {
00321 p++;
00322 get_word_sep(buf, sizeof(buf), "-", &p);
00323 *end = parse_date(buf, 1);
00324 }
00325
00326
00327 }
00328
00329 typedef struct SDPParseState {
00330
00331 struct in_addr default_ip;
00332 int default_ttl;
00333 int skip_media;
00334 } SDPParseState;
00335
00336 static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1,
00337 int letter, const char *buf)
00338 {
00339 RTSPState *rt = s->priv_data;
00340 char buf1[64], st_type[64];
00341 const char *p;
00342 enum AVMediaType codec_type;
00343 int payload_type, i;
00344 AVStream *st;
00345 RTSPStream *rtsp_st;
00346 struct in_addr sdp_ip;
00347 int ttl;
00348
00349 dprintf(s, "sdp: %c='%s'\n", letter, buf);
00350
00351 p = buf;
00352 if (s1->skip_media && letter != 'm')
00353 return;
00354 switch (letter) {
00355 case 'c':
00356 get_word(buf1, sizeof(buf1), &p);
00357 if (strcmp(buf1, "IN") != 0)
00358 return;
00359 get_word(buf1, sizeof(buf1), &p);
00360 if (strcmp(buf1, "IP4") != 0)
00361 return;
00362 get_word_sep(buf1, sizeof(buf1), "/", &p);
00363 if (ff_inet_aton(buf1, &sdp_ip) == 0)
00364 return;
00365 ttl = 16;
00366 if (*p == '/') {
00367 p++;
00368 get_word_sep(buf1, sizeof(buf1), "/", &p);
00369 ttl = atoi(buf1);
00370 }
00371 if (s->nb_streams == 0) {
00372 s1->default_ip = sdp_ip;
00373 s1->default_ttl = ttl;
00374 } else {
00375 st = s->streams[s->nb_streams - 1];
00376 rtsp_st = st->priv_data;
00377 rtsp_st->sdp_ip = sdp_ip;
00378 rtsp_st->sdp_ttl = ttl;
00379 }
00380 break;
00381 case 's':
00382 av_metadata_set(&s->metadata, "title", p);
00383 break;
00384 case 'i':
00385 if (s->nb_streams == 0) {
00386 av_metadata_set(&s->metadata, "comment", p);
00387 break;
00388 }
00389 break;
00390 case 'm':
00391
00392 s1->skip_media = 0;
00393 get_word(st_type, sizeof(st_type), &p);
00394 if (!strcmp(st_type, "audio")) {
00395 codec_type = AVMEDIA_TYPE_AUDIO;
00396 } else if (!strcmp(st_type, "video")) {
00397 codec_type = AVMEDIA_TYPE_VIDEO;
00398 } else if (!strcmp(st_type, "application")) {
00399 codec_type = AVMEDIA_TYPE_DATA;
00400 } else {
00401 s1->skip_media = 1;
00402 return;
00403 }
00404 rtsp_st = av_mallocz(sizeof(RTSPStream));
00405 if (!rtsp_st)
00406 return;
00407 rtsp_st->stream_index = -1;
00408 dynarray_add(&rt->rtsp_streams, &rt->nb_rtsp_streams, rtsp_st);
00409
00410 rtsp_st->sdp_ip = s1->default_ip;
00411 rtsp_st->sdp_ttl = s1->default_ttl;
00412
00413 get_word(buf1, sizeof(buf1), &p);
00414 rtsp_st->sdp_port = atoi(buf1);
00415
00416 get_word(buf1, sizeof(buf1), &p);
00417
00418
00419 get_word(buf1, sizeof(buf1), &p);
00420 rtsp_st->sdp_payload_type = atoi(buf1);
00421
00422 if (!strcmp(ff_rtp_enc_name(rtsp_st->sdp_payload_type), "MP2T")) {
00423
00424 } else {
00425 st = av_new_stream(s, 0);
00426 if (!st)
00427 return;
00428 st->priv_data = rtsp_st;
00429 rtsp_st->stream_index = st->index;
00430 st->codec->codec_type = codec_type;
00431 if (rtsp_st->sdp_payload_type < RTP_PT_PRIVATE) {
00432
00433 ff_rtp_get_codec_info(st->codec, rtsp_st->sdp_payload_type);
00434 }
00435 }
00436
00437 av_strlcpy(rtsp_st->control_url, rt->control_uri,
00438 sizeof(rtsp_st->control_url));
00439 break;
00440 case 'a':
00441 if (av_strstart(p, "control:", &p)) {
00442 if (s->nb_streams == 0) {
00443 if (!strncmp(p, "rtsp://", 7))
00444 av_strlcpy(rt->control_uri, p,
00445 sizeof(rt->control_uri));
00446 } else {
00447 char proto[32];
00448
00449 st = s->streams[s->nb_streams - 1];
00450 rtsp_st = st->priv_data;
00451
00452
00453 ff_url_split(proto, sizeof(proto), NULL, 0, NULL, 0,
00454 NULL, NULL, 0, p);
00455 if (proto[0] == '\0') {
00456
00457 if (rtsp_st->control_url[strlen(rtsp_st->control_url)-1]!='/')
00458 av_strlcat(rtsp_st->control_url, "/",
00459 sizeof(rtsp_st->control_url));
00460 av_strlcat(rtsp_st->control_url, p,
00461 sizeof(rtsp_st->control_url));
00462 } else
00463 av_strlcpy(rtsp_st->control_url, p,
00464 sizeof(rtsp_st->control_url));
00465 }
00466 } else if (av_strstart(p, "rtpmap:", &p) && s->nb_streams > 0) {
00467
00468 get_word(buf1, sizeof(buf1), &p);
00469 payload_type = atoi(buf1);
00470 st = s->streams[s->nb_streams - 1];
00471 rtsp_st = st->priv_data;
00472 sdp_parse_rtpmap(s, st->codec, rtsp_st, payload_type, p);
00473 } else if (av_strstart(p, "fmtp:", &p)) {
00474
00475 get_word(buf1, sizeof(buf1), &p);
00476 payload_type = atoi(buf1);
00477 for (i = 0; i < s->nb_streams; i++) {
00478 st = s->streams[i];
00479 rtsp_st = st->priv_data;
00480 if (rtsp_st->sdp_payload_type == payload_type) {
00481 if (!(rtsp_st->dynamic_handler &&
00482 rtsp_st->dynamic_handler->parse_sdp_a_line &&
00483 rtsp_st->dynamic_handler->parse_sdp_a_line(s,
00484 i, rtsp_st->dynamic_protocol_context, buf)))
00485 sdp_parse_fmtp(st, p);
00486 }
00487 }
00488 } else if (av_strstart(p, "framesize:", &p)) {
00489
00490 get_word(buf1, sizeof(buf1), &p);
00491 payload_type = atoi(buf1);
00492 for (i = 0; i < s->nb_streams; i++) {
00493 st = s->streams[i];
00494 rtsp_st = st->priv_data;
00495 if (rtsp_st->sdp_payload_type == payload_type &&
00496 rtsp_st->dynamic_handler &&
00497 rtsp_st->dynamic_handler->parse_sdp_a_line)
00498 rtsp_st->dynamic_handler->parse_sdp_a_line(s, i,
00499 rtsp_st->dynamic_protocol_context, buf);
00500 }
00501 } else if (av_strstart(p, "range:", &p)) {
00502 int64_t start, end;
00503
00504
00505 rtsp_parse_range_npt(p, &start, &end);
00506 s->start_time = start;
00507
00508 s->duration = (end == AV_NOPTS_VALUE) ?
00509 AV_NOPTS_VALUE : end - start;
00510 } else if (av_strstart(p, "IsRealDataType:integer;",&p)) {
00511 if (atoi(p) == 1)
00512 rt->transport = RTSP_TRANSPORT_RDT;
00513 } else {
00514 if (rt->server_type == RTSP_SERVER_WMS)
00515 ff_wms_parse_sdp_a_line(s, p);
00516 if (s->nb_streams > 0) {
00517 if (rt->server_type == RTSP_SERVER_REAL)
00518 ff_real_parse_sdp_a_line(s, s->nb_streams - 1, p);
00519
00520 rtsp_st = s->streams[s->nb_streams - 1]->priv_data;
00521 if (rtsp_st->dynamic_handler &&
00522 rtsp_st->dynamic_handler->parse_sdp_a_line)
00523 rtsp_st->dynamic_handler->parse_sdp_a_line(s,
00524 s->nb_streams - 1,
00525 rtsp_st->dynamic_protocol_context, buf);
00526 }
00527 }
00528 break;
00529 }
00530 }
00531
00532 static int sdp_parse(AVFormatContext *s, const char *content)
00533 {
00534 const char *p;
00535 int letter;
00536
00537
00538
00539
00540
00541
00542
00543 char buf[16384], *q;
00544 SDPParseState sdp_parse_state, *s1 = &sdp_parse_state;
00545
00546 memset(s1, 0, sizeof(SDPParseState));
00547 p = content;
00548 for (;;) {
00549 skip_spaces(&p);
00550 letter = *p;
00551 if (letter == '\0')
00552 break;
00553 p++;
00554 if (*p != '=')
00555 goto next_line;
00556 p++;
00557
00558 q = buf;
00559 while (*p != '\n' && *p != '\r' && *p != '\0') {
00560 if ((q - buf) < sizeof(buf) - 1)
00561 *q++ = *p;
00562 p++;
00563 }
00564 *q = '\0';
00565 sdp_parse_line(s, s1, letter, buf);
00566 next_line:
00567 while (*p != '\n' && *p != '\0')
00568 p++;
00569 if (*p == '\n')
00570 p++;
00571 }
00572 return 0;
00573 }
00574
00575
00576 void ff_rtsp_close_streams(AVFormatContext *s)
00577 {
00578 RTSPState *rt = s->priv_data;
00579 int i;
00580 RTSPStream *rtsp_st;
00581
00582 for (i = 0; i < rt->nb_rtsp_streams; i++) {
00583 rtsp_st = rt->rtsp_streams[i];
00584 if (rtsp_st) {
00585 if (rtsp_st->transport_priv) {
00586 if (s->oformat) {
00587 AVFormatContext *rtpctx = rtsp_st->transport_priv;
00588 av_write_trailer(rtpctx);
00589 if (rt->lower_transport == RTSP_LOWER_TRANSPORT_TCP) {
00590 uint8_t *ptr;
00591 url_close_dyn_buf(rtpctx->pb, &ptr);
00592 av_free(ptr);
00593 } else {
00594 url_fclose(rtpctx->pb);
00595 }
00596 av_metadata_free(&rtpctx->streams[0]->metadata);
00597 av_metadata_free(&rtpctx->metadata);
00598 av_free(rtpctx->streams[0]);
00599 av_free(rtpctx);
00600 } else if (rt->transport == RTSP_TRANSPORT_RDT)
00601 ff_rdt_parse_close(rtsp_st->transport_priv);
00602 else
00603 rtp_parse_close(rtsp_st->transport_priv);
00604 }
00605 if (rtsp_st->rtp_handle)
00606 url_close(rtsp_st->rtp_handle);
00607 if (rtsp_st->dynamic_handler && rtsp_st->dynamic_protocol_context)
00608 rtsp_st->dynamic_handler->close(
00609 rtsp_st->dynamic_protocol_context);
00610 }
00611 }
00612 av_free(rt->rtsp_streams);
00613 if (rt->asf_ctx) {
00614 av_close_input_stream (rt->asf_ctx);
00615 rt->asf_ctx = NULL;
00616 }
00617 }
00618
00619 static void *rtsp_rtp_mux_open(AVFormatContext *s, AVStream *st,
00620 URLContext *handle)
00621 {
00622 RTSPState *rt = s->priv_data;
00623 AVFormatContext *rtpctx;
00624 int ret;
00625 AVOutputFormat *rtp_format = av_guess_format("rtp", NULL, NULL);
00626
00627 if (!rtp_format)
00628 return NULL;
00629
00630
00631 rtpctx = avformat_alloc_context();
00632 if (!rtpctx)
00633 return NULL;
00634
00635 rtpctx->oformat = rtp_format;
00636 if (!av_new_stream(rtpctx, 0)) {
00637 av_free(rtpctx);
00638 return NULL;
00639 }
00640
00641 rtpctx->max_delay = s->max_delay;
00642
00643 rtpctx->streams[0]->sample_aspect_ratio = st->sample_aspect_ratio;
00644
00645
00646 rtpctx->start_time_realtime = rt->start_time;
00647
00648
00649
00650
00651 av_free(rtpctx->streams[0]->codec);
00652 rtpctx->streams[0]->codec = st->codec;
00653
00654 if (handle) {
00655 url_fdopen(&rtpctx->pb, handle);
00656 } else
00657 url_open_dyn_packet_buf(&rtpctx->pb, RTSP_TCP_MAX_PACKET_SIZE);
00658 ret = av_write_header(rtpctx);
00659
00660 if (ret) {
00661 if (handle) {
00662 url_fclose(rtpctx->pb);
00663 } else {
00664 uint8_t *ptr;
00665 url_close_dyn_buf(rtpctx->pb, &ptr);
00666 av_free(ptr);
00667 }
00668 av_free(rtpctx->streams[0]);
00669 av_free(rtpctx);
00670 return NULL;
00671 }
00672
00673
00674 st->time_base = rtpctx->streams[0]->time_base;
00675 return rtpctx;
00676 }
00677
00678 static int rtsp_open_transport_ctx(AVFormatContext *s, RTSPStream *rtsp_st)
00679 {
00680 RTSPState *rt = s->priv_data;
00681 AVStream *st = NULL;
00682
00683
00684 if (rtsp_st->stream_index >= 0)
00685 st = s->streams[rtsp_st->stream_index];
00686 if (!st)
00687 s->ctx_flags |= AVFMTCTX_NOHEADER;
00688
00689 if (s->oformat) {
00690 rtsp_st->transport_priv = rtsp_rtp_mux_open(s, st, rtsp_st->rtp_handle);
00691
00692 rtsp_st->rtp_handle = NULL;
00693 } else if (rt->transport == RTSP_TRANSPORT_RDT)
00694 rtsp_st->transport_priv = ff_rdt_parse_open(s, st->index,
00695 rtsp_st->dynamic_protocol_context,
00696 rtsp_st->dynamic_handler);
00697 else
00698 rtsp_st->transport_priv = rtp_parse_open(s, st, rtsp_st->rtp_handle,
00699 rtsp_st->sdp_payload_type,
00700 &rtsp_st->rtp_payload_data);
00701
00702 if (!rtsp_st->transport_priv) {
00703 return AVERROR(ENOMEM);
00704 } else if (rt->transport != RTSP_TRANSPORT_RDT) {
00705 if (rtsp_st->dynamic_handler) {
00706 rtp_parse_set_dynamic_protocol(rtsp_st->transport_priv,
00707 rtsp_st->dynamic_protocol_context,
00708 rtsp_st->dynamic_handler);
00709 }
00710 }
00711
00712 return 0;
00713 }
00714
00715 #if CONFIG_RTSP_DEMUXER || CONFIG_RTSP_MUXER
00716 static int rtsp_probe(AVProbeData *p)
00717 {
00718 if (av_strstart(p->filename, "rtsp:", NULL))
00719 return AVPROBE_SCORE_MAX;
00720 return 0;
00721 }
00722
00723 static void rtsp_parse_range(int *min_ptr, int *max_ptr, const char **pp)
00724 {
00725 const char *p;
00726 int v;
00727
00728 p = *pp;
00729 skip_spaces(&p);
00730 v = strtol(p, (char **)&p, 10);
00731 if (*p == '-') {
00732 p++;
00733 *min_ptr = v;
00734 v = strtol(p, (char **)&p, 10);
00735 *max_ptr = v;
00736 } else {
00737 *min_ptr = v;
00738 *max_ptr = v;
00739 }
00740 *pp = p;
00741 }
00742
00743
00744 static void rtsp_parse_transport(RTSPMessageHeader *reply, const char *p)
00745 {
00746 char transport_protocol[16];
00747 char profile[16];
00748 char lower_transport[16];
00749 char parameter[16];
00750 RTSPTransportField *th;
00751 char buf[256];
00752
00753 reply->nb_transports = 0;
00754
00755 for (;;) {
00756 skip_spaces(&p);
00757 if (*p == '\0')
00758 break;
00759
00760 th = &reply->transports[reply->nb_transports];
00761
00762 get_word_sep(transport_protocol, sizeof(transport_protocol),
00763 "/", &p);
00764 if (!strcasecmp (transport_protocol, "rtp")) {
00765 get_word_sep(profile, sizeof(profile), "/;,", &p);
00766 lower_transport[0] = '\0';
00767
00768 if (*p == '/') {
00769 get_word_sep(lower_transport, sizeof(lower_transport),
00770 ";,", &p);
00771 }
00772 th->transport = RTSP_TRANSPORT_RTP;
00773 } else if (!strcasecmp (transport_protocol, "x-pn-tng") ||
00774 !strcasecmp (transport_protocol, "x-real-rdt")) {
00775
00776 get_word_sep(lower_transport, sizeof(lower_transport), "/;,", &p);
00777 profile[0] = '\0';
00778 th->transport = RTSP_TRANSPORT_RDT;
00779 }
00780 if (!strcasecmp(lower_transport, "TCP"))
00781 th->lower_transport = RTSP_LOWER_TRANSPORT_TCP;
00782 else
00783 th->lower_transport = RTSP_LOWER_TRANSPORT_UDP;
00784
00785 if (*p == ';')
00786 p++;
00787
00788 while (*p != '\0' && *p != ',') {
00789 get_word_sep(parameter, sizeof(parameter), "=;,", &p);
00790 if (!strcmp(parameter, "port")) {
00791 if (*p == '=') {
00792 p++;
00793 rtsp_parse_range(&th->port_min, &th->port_max, &p);
00794 }
00795 } else if (!strcmp(parameter, "client_port")) {
00796 if (*p == '=') {
00797 p++;
00798 rtsp_parse_range(&th->client_port_min,
00799 &th->client_port_max, &p);
00800 }
00801 } else if (!strcmp(parameter, "server_port")) {
00802 if (*p == '=') {
00803 p++;
00804 rtsp_parse_range(&th->server_port_min,
00805 &th->server_port_max, &p);
00806 }
00807 } else if (!strcmp(parameter, "interleaved")) {
00808 if (*p == '=') {
00809 p++;
00810 rtsp_parse_range(&th->interleaved_min,
00811 &th->interleaved_max, &p);
00812 }
00813 } else if (!strcmp(parameter, "multicast")) {
00814 if (th->lower_transport == RTSP_LOWER_TRANSPORT_UDP)
00815 th->lower_transport = RTSP_LOWER_TRANSPORT_UDP_MULTICAST;
00816 } else if (!strcmp(parameter, "ttl")) {
00817 if (*p == '=') {
00818 p++;
00819 th->ttl = strtol(p, (char **)&p, 10);
00820 }
00821 } else if (!strcmp(parameter, "destination")) {
00822 struct in_addr ipaddr;
00823
00824 if (*p == '=') {
00825 p++;
00826 get_word_sep(buf, sizeof(buf), ";,", &p);
00827 if (ff_inet_aton(buf, &ipaddr))
00828 th->destination = ntohl(ipaddr.s_addr);
00829 }
00830 }
00831 while (*p != ';' && *p != '\0' && *p != ',')
00832 p++;
00833 if (*p == ';')
00834 p++;
00835 }
00836 if (*p == ',')
00837 p++;
00838
00839 reply->nb_transports++;
00840 }
00841 }
00842
00843 void ff_rtsp_parse_line(RTSPMessageHeader *reply, const char *buf,
00844 HTTPAuthState *auth_state)
00845 {
00846 const char *p;
00847
00848
00849 p = buf;
00850 if (av_stristart(p, "Session:", &p)) {
00851 int t;
00852 get_word_sep(reply->session_id, sizeof(reply->session_id), ";", &p);
00853 if (av_stristart(p, ";timeout=", &p) &&
00854 (t = strtol(p, NULL, 10)) > 0) {
00855 reply->timeout = t;
00856 }
00857 } else if (av_stristart(p, "Content-Length:", &p)) {
00858 reply->content_length = strtol(p, NULL, 10);
00859 } else if (av_stristart(p, "Transport:", &p)) {
00860 rtsp_parse_transport(reply, p);
00861 } else if (av_stristart(p, "CSeq:", &p)) {
00862 reply->seq = strtol(p, NULL, 10);
00863 } else if (av_stristart(p, "Range:", &p)) {
00864 rtsp_parse_range_npt(p, &reply->range_start, &reply->range_end);
00865 } else if (av_stristart(p, "RealChallenge1:", &p)) {
00866 skip_spaces(&p);
00867 av_strlcpy(reply->real_challenge, p, sizeof(reply->real_challenge));
00868 } else if (av_stristart(p, "Server:", &p)) {
00869 skip_spaces(&p);
00870 av_strlcpy(reply->server, p, sizeof(reply->server));
00871 } else if (av_stristart(p, "Notice:", &p) ||
00872 av_stristart(p, "X-Notice:", &p)) {
00873 reply->notice = strtol(p, NULL, 10);
00874 } else if (av_stristart(p, "Location:", &p)) {
00875 skip_spaces(&p);
00876 av_strlcpy(reply->location, p , sizeof(reply->location));
00877 } else if (av_stristart(p, "WWW-Authenticate:", &p) && auth_state) {
00878 skip_spaces(&p);
00879 ff_http_auth_handle_header(auth_state, "WWW-Authenticate", p);
00880 } else if (av_stristart(p, "Authentication-Info:", &p) && auth_state) {
00881 skip_spaces(&p);
00882 ff_http_auth_handle_header(auth_state, "Authentication-Info", p);
00883 }
00884 }
00885
00886
00887 void ff_rtsp_skip_packet(AVFormatContext *s)
00888 {
00889 RTSPState *rt = s->priv_data;
00890 int ret, len, len1;
00891 uint8_t buf[1024];
00892
00893 ret = url_read_complete(rt->rtsp_hd, buf, 3);
00894 if (ret != 3)
00895 return;
00896 len = AV_RB16(buf + 1);
00897
00898 dprintf(s, "skipping RTP packet len=%d\n", len);
00899
00900
00901 while (len > 0) {
00902 len1 = len;
00903 if (len1 > sizeof(buf))
00904 len1 = sizeof(buf);
00905 ret = url_read_complete(rt->rtsp_hd, buf, len1);
00906 if (ret != len1)
00907 return;
00908 len -= len1;
00909 }
00910 }
00911
00912 int ff_rtsp_read_reply(AVFormatContext *s, RTSPMessageHeader *reply,
00913 unsigned char **content_ptr,
00914 int return_on_interleaved_data)
00915 {
00916 RTSPState *rt = s->priv_data;
00917 char buf[4096], buf1[1024], *q;
00918 unsigned char ch;
00919 const char *p;
00920 int ret, content_length, line_count = 0;
00921 unsigned char *content = NULL;
00922
00923 memset(reply, 0, sizeof(*reply));
00924
00925
00926 rt->last_reply[0] = '\0';
00927 for (;;) {
00928 q = buf;
00929 for (;;) {
00930 ret = url_read_complete(rt->rtsp_hd, &ch, 1);
00931 #ifdef DEBUG_RTP_TCP
00932 dprintf(s, "ret=%d c=%02x [%c]\n", ret, ch, ch);
00933 #endif
00934 if (ret != 1)
00935 return -1;
00936 if (ch == '\n')
00937 break;
00938 if (ch == '$') {
00939
00940 if (return_on_interleaved_data) {
00941 return 1;
00942 } else
00943 ff_rtsp_skip_packet(s);
00944 } else if (ch != '\r') {
00945 if ((q - buf) < sizeof(buf) - 1)
00946 *q++ = ch;
00947 }
00948 }
00949 *q = '\0';
00950
00951 dprintf(s, "line='%s'\n", buf);
00952
00953
00954 if (buf[0] == '\0')
00955 break;
00956 p = buf;
00957 if (line_count == 0) {
00958
00959 get_word(buf1, sizeof(buf1), &p);
00960 get_word(buf1, sizeof(buf1), &p);
00961 reply->status_code = atoi(buf1);
00962 } else {
00963 ff_rtsp_parse_line(reply, p, &rt->auth_state);
00964 av_strlcat(rt->last_reply, p, sizeof(rt->last_reply));
00965 av_strlcat(rt->last_reply, "\n", sizeof(rt->last_reply));
00966 }
00967 line_count++;
00968 }
00969
00970 if (rt->session_id[0] == '\0' && reply->session_id[0] != '\0')
00971 av_strlcpy(rt->session_id, reply->session_id, sizeof(rt->session_id));
00972
00973 content_length = reply->content_length;
00974 if (content_length > 0) {
00975
00976 content = av_malloc(content_length + 1);
00977 (void)url_read_complete(rt->rtsp_hd, content, content_length);
00978 content[content_length] = '\0';
00979 }
00980 if (content_ptr)
00981 *content_ptr = content;
00982 else
00983 av_free(content);
00984
00985 if (rt->seq != reply->seq) {
00986 av_log(s, AV_LOG_WARNING, "CSeq %d expected, %d received.\n",
00987 rt->seq, reply->seq);
00988 }
00989
00990
00991 if (reply->notice == 2101 ||
00992 reply->notice == 2104 ||
00993 reply->notice == 2306 ) {
00994 rt->state = RTSP_STATE_IDLE;
00995 } else if (reply->notice >= 4400 && reply->notice < 5500) {
00996 return AVERROR(EIO);
00997 } else if (reply->notice == 2401 ||
00998 (reply->notice >= 5500 && reply->notice < 5600) )
00999 return AVERROR(EPERM);
01000
01001 return 0;
01002 }
01003
01004 void ff_rtsp_send_cmd_with_content_async(AVFormatContext *s,
01005 const char *method, const char *url,
01006 const char *headers,
01007 const unsigned char *send_content,
01008 int send_content_length)
01009 {
01010 RTSPState *rt = s->priv_data;
01011 char buf[4096];
01012
01013 rt->seq++;
01014 snprintf(buf, sizeof(buf), "%s %s RTSP/1.0\r\n", method, url);
01015 if (headers)
01016 av_strlcat(buf, headers, sizeof(buf));
01017 av_strlcatf(buf, sizeof(buf), "CSeq: %d\r\n", rt->seq);
01018 if (rt->session_id[0] != '\0' && (!headers ||
01019 !strstr(headers, "\nIf-Match:"))) {
01020 av_strlcatf(buf, sizeof(buf), "Session: %s\r\n", rt->session_id);
01021 }
01022 if (rt->auth[0]) {
01023 char *str = ff_http_auth_create_response(&rt->auth_state,
01024 rt->auth, url, method);
01025 if (str)
01026 av_strlcat(buf, str, sizeof(buf));
01027 av_free(str);
01028 }
01029 if (send_content_length > 0 && send_content)
01030 av_strlcatf(buf, sizeof(buf), "Content-Length: %d\r\n", send_content_length);
01031 av_strlcat(buf, "\r\n", sizeof(buf));
01032
01033 dprintf(s, "Sending:\n%s--\n", buf);
01034
01035 url_write(rt->rtsp_hd, buf, strlen(buf));
01036 if (send_content_length > 0 && send_content)
01037 url_write(rt->rtsp_hd, send_content, send_content_length);
01038 rt->last_cmd_time = av_gettime();
01039 }
01040
01041 void ff_rtsp_send_cmd_async(AVFormatContext *s, const char *method,
01042 const char *url, const char *headers)
01043 {
01044 ff_rtsp_send_cmd_with_content_async(s, method, url, headers, NULL, 0);
01045 }
01046
01047 void ff_rtsp_send_cmd(AVFormatContext *s, const char *method, const char *url,
01048 const char *headers, RTSPMessageHeader *reply,
01049 unsigned char **content_ptr)
01050 {
01051 ff_rtsp_send_cmd_with_content(s, method, url, headers, reply,
01052 content_ptr, NULL, 0);
01053 }
01054
01055 void ff_rtsp_send_cmd_with_content(AVFormatContext *s,
01056 const char *method, const char *url,
01057 const char *header,
01058 RTSPMessageHeader *reply,
01059 unsigned char **content_ptr,
01060 const unsigned char *send_content,
01061 int send_content_length)
01062 {
01063 RTSPState *rt = s->priv_data;
01064 HTTPAuthType cur_auth_type;
01065
01066 retry:
01067 cur_auth_type = rt->auth_state.auth_type;
01068 ff_rtsp_send_cmd_with_content_async(s, method, url, header,
01069 send_content, send_content_length);
01070
01071 ff_rtsp_read_reply(s, reply, content_ptr, 0);
01072
01073 if (reply->status_code == 401 && cur_auth_type == HTTP_AUTH_NONE &&
01074 rt->auth_state.auth_type != HTTP_AUTH_NONE)
01075 goto retry;
01076 }
01077
01081 static int make_setup_request(AVFormatContext *s, const char *host, int port,
01082 int lower_transport, const char *real_challenge)
01083 {
01084 RTSPState *rt = s->priv_data;
01085 int rtx, j, i, err, interleave = 0;
01086 RTSPStream *rtsp_st;
01087 RTSPMessageHeader reply1, *reply = &reply1;
01088 char cmd[2048];
01089 const char *trans_pref;
01090
01091 if (rt->transport == RTSP_TRANSPORT_RDT)
01092 trans_pref = "x-pn-tng";
01093 else
01094 trans_pref = "RTP/AVP";
01095
01096
01097 rt->timeout = 60;
01098
01099
01100
01101
01102
01103 for (j = RTSP_RTP_PORT_MIN, i = 0; i < rt->nb_rtsp_streams; ++i) {
01104 char transport[2048];
01105
01111 if (lower_transport == RTSP_LOWER_TRANSPORT_UDP &&
01112 rt->server_type == RTSP_SERVER_WMS) {
01113 if (i == 0) {
01114
01115 for (rtx = 0; rtx < rt->nb_rtsp_streams; rtx++) {
01116 int len = strlen(rt->rtsp_streams[rtx]->control_url);
01117 if (len >= 4 &&
01118 !strcmp(rt->rtsp_streams[rtx]->control_url + len - 4,
01119 "/rtx"))
01120 break;
01121 }
01122 if (rtx == rt->nb_rtsp_streams)
01123 return -1;
01124 rtsp_st = rt->rtsp_streams[rtx];
01125 } else
01126 rtsp_st = rt->rtsp_streams[i > rtx ? i : i - 1];
01127 } else
01128 rtsp_st = rt->rtsp_streams[i];
01129
01130
01131 if (lower_transport == RTSP_LOWER_TRANSPORT_UDP) {
01132 char buf[256];
01133
01134 if (rt->server_type == RTSP_SERVER_WMS && i > 1) {
01135 port = reply->transports[0].client_port_min;
01136 goto have_port;
01137 }
01138
01139
01140 if (RTSP_RTP_PORT_MIN != 0) {
01141 while (j <= RTSP_RTP_PORT_MAX) {
01142 ff_url_join(buf, sizeof(buf), "rtp", NULL, host, -1,
01143 "?localport=%d", j);
01144
01145 j += 2;
01146 if (url_open(&rtsp_st->rtp_handle, buf, URL_RDWR) == 0)
01147 goto rtp_opened;
01148 }
01149 }
01150
01151 #if 0
01152
01153 if (url_open(&rtsp_st->rtp_handle, "rtp://", URL_RDONLY) < 0) {
01154 err = AVERROR_INVALIDDATA;
01155 goto fail;
01156 }
01157 #endif
01158
01159 rtp_opened:
01160 port = rtp_get_local_port(rtsp_st->rtp_handle);
01161 have_port:
01162 snprintf(transport, sizeof(transport) - 1,
01163 "%s/UDP;", trans_pref);
01164 if (rt->server_type != RTSP_SERVER_REAL)
01165 av_strlcat(transport, "unicast;", sizeof(transport));
01166 av_strlcatf(transport, sizeof(transport),
01167 "client_port=%d", port);
01168 if (rt->transport == RTSP_TRANSPORT_RTP &&
01169 !(rt->server_type == RTSP_SERVER_WMS && i > 0))
01170 av_strlcatf(transport, sizeof(transport), "-%d", port + 1);
01171 }
01172
01173
01174 else if (lower_transport == RTSP_LOWER_TRANSPORT_TCP) {
01178 if (rt->server_type == RTSP_SERVER_WMS &&
01179 s->streams[rtsp_st->stream_index]->codec->codec_type ==
01180 AVMEDIA_TYPE_DATA)
01181 continue;
01182 snprintf(transport, sizeof(transport) - 1,
01183 "%s/TCP;", trans_pref);
01184 if (rt->server_type == RTSP_SERVER_WMS)
01185 av_strlcat(transport, "unicast;", sizeof(transport));
01186 av_strlcatf(transport, sizeof(transport),
01187 "interleaved=%d-%d",
01188 interleave, interleave + 1);
01189 interleave += 2;
01190 }
01191
01192 else if (lower_transport == RTSP_LOWER_TRANSPORT_UDP_MULTICAST) {
01193 snprintf(transport, sizeof(transport) - 1,
01194 "%s/UDP;multicast", trans_pref);
01195 }
01196 if (s->oformat) {
01197 av_strlcat(transport, ";mode=receive", sizeof(transport));
01198 } else if (rt->server_type == RTSP_SERVER_REAL ||
01199 rt->server_type == RTSP_SERVER_WMS)
01200 av_strlcat(transport, ";mode=play", sizeof(transport));
01201 snprintf(cmd, sizeof(cmd),
01202 "Transport: %s\r\n",
01203 transport);
01204 if (i == 0 && rt->server_type == RTSP_SERVER_REAL) {
01205 char real_res[41], real_csum[9];
01206 ff_rdt_calc_response_and_checksum(real_res, real_csum,
01207 real_challenge);
01208 av_strlcatf(cmd, sizeof(cmd),
01209 "If-Match: %s\r\n"
01210 "RealChallenge2: %s, sd=%s\r\n",
01211 rt->session_id, real_res, real_csum);
01212 }
01213 ff_rtsp_send_cmd(s, "SETUP", rtsp_st->control_url, cmd, reply, NULL);
01214 if (reply->status_code == 461 && i == 0) {
01215 err = 1;
01216 goto fail;
01217 } else if (reply->status_code != RTSP_STATUS_OK ||
01218 reply->nb_transports != 1) {
01219 err = AVERROR_INVALIDDATA;
01220 goto fail;
01221 }
01222
01223
01224 if (i > 0) {
01225 if (reply->transports[0].lower_transport != rt->lower_transport ||
01226 reply->transports[0].transport != rt->transport) {
01227 err = AVERROR_INVALIDDATA;
01228 goto fail;
01229 }
01230 } else {
01231 rt->lower_transport = reply->transports[0].lower_transport;
01232 rt->transport = reply->transports[0].transport;
01233 }
01234
01235
01236 if (reply->transports[0].lower_transport != RTSP_LOWER_TRANSPORT_UDP &&
01237 (lower_transport == RTSP_LOWER_TRANSPORT_UDP)) {
01238 url_close(rtsp_st->rtp_handle);
01239 rtsp_st->rtp_handle = NULL;
01240 }
01241
01242 switch(reply->transports[0].lower_transport) {
01243 case RTSP_LOWER_TRANSPORT_TCP:
01244 rtsp_st->interleaved_min = reply->transports[0].interleaved_min;
01245 rtsp_st->interleaved_max = reply->transports[0].interleaved_max;
01246 break;
01247
01248 case RTSP_LOWER_TRANSPORT_UDP: {
01249 char url[1024];
01250
01251
01252 ff_url_join(url, sizeof(url), "rtp", NULL, host,
01253 reply->transports[0].server_port_min, NULL);
01254 if (!(rt->server_type == RTSP_SERVER_WMS && i > 1) &&
01255 rtp_set_remote_url(rtsp_st->rtp_handle, url) < 0) {
01256 err = AVERROR_INVALIDDATA;
01257 goto fail;
01258 }
01259
01260
01261
01262
01263 if (!(rt->server_type == RTSP_SERVER_WMS && i > 1) && s->iformat)
01264 rtp_send_punch_packets(rtsp_st->rtp_handle);
01265 break;
01266 }
01267 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST: {
01268 char url[1024];
01269 struct in_addr in;
01270 int port, ttl;
01271
01272 if (reply->transports[0].destination) {
01273 in.s_addr = htonl(reply->transports[0].destination);
01274 port = reply->transports[0].port_min;
01275 ttl = reply->transports[0].ttl;
01276 } else {
01277 in = rtsp_st->sdp_ip;
01278 port = rtsp_st->sdp_port;
01279 ttl = rtsp_st->sdp_ttl;
01280 }
01281 ff_url_join(url, sizeof(url), "rtp", NULL, inet_ntoa(in),
01282 port, "?ttl=%d", ttl);
01283 if (url_open(&rtsp_st->rtp_handle, url, URL_RDWR) < 0) {
01284 err = AVERROR_INVALIDDATA;
01285 goto fail;
01286 }
01287 break;
01288 }
01289 }
01290
01291 if ((err = rtsp_open_transport_ctx(s, rtsp_st)))
01292 goto fail;
01293 }
01294
01295 if (reply->timeout > 0)
01296 rt->timeout = reply->timeout;
01297
01298 if (rt->server_type == RTSP_SERVER_REAL)
01299 rt->need_subscription = 1;
01300
01301 return 0;
01302
01303 fail:
01304 for (i = 0; i < rt->nb_rtsp_streams; i++) {
01305 if (rt->rtsp_streams[i]->rtp_handle) {
01306 url_close(rt->rtsp_streams[i]->rtp_handle);
01307 rt->rtsp_streams[i]->rtp_handle = NULL;
01308 }
01309 }
01310 return err;
01311 }
01312
01313 static int rtsp_read_play(AVFormatContext *s)
01314 {
01315 RTSPState *rt = s->priv_data;
01316 RTSPMessageHeader reply1, *reply = &reply1;
01317 char cmd[1024];
01318
01319 av_log(s, AV_LOG_DEBUG, "hello state=%d\n", rt->state);
01320
01321 if (!(rt->server_type == RTSP_SERVER_REAL && rt->need_subscription)) {
01322 if (rt->state == RTSP_STATE_PAUSED) {
01323 cmd[0] = 0;
01324 } else {
01325 snprintf(cmd, sizeof(cmd),
01326 "Range: npt=%0.3f-\r\n",
01327 (double)rt->seek_timestamp / AV_TIME_BASE);
01328 }
01329 ff_rtsp_send_cmd(s, "PLAY", rt->control_uri, cmd, reply, NULL);
01330 if (reply->status_code != RTSP_STATUS_OK) {
01331 return -1;
01332 }
01333 }
01334 rt->state = RTSP_STATE_STREAMING;
01335 return 0;
01336 }
01337
01338 static int rtsp_setup_input_streams(AVFormatContext *s, RTSPMessageHeader *reply)
01339 {
01340 RTSPState *rt = s->priv_data;
01341 char cmd[1024];
01342 unsigned char *content = NULL;
01343 int ret;
01344
01345
01346 snprintf(cmd, sizeof(cmd),
01347 "Accept: application/sdp\r\n");
01348 if (rt->server_type == RTSP_SERVER_REAL) {
01353 av_strlcat(cmd,
01354 "Require: com.real.retain-entity-for-setup\r\n",
01355 sizeof(cmd));
01356 }
01357 ff_rtsp_send_cmd(s, "DESCRIBE", rt->control_uri, cmd, reply, &content);
01358 if (!content)
01359 return AVERROR_INVALIDDATA;
01360 if (reply->status_code != RTSP_STATUS_OK) {
01361 av_freep(&content);
01362 return AVERROR_INVALIDDATA;
01363 }
01364
01365
01366 ret = sdp_parse(s, (const char *)content);
01367 av_freep(&content);
01368 if (ret < 0)
01369 return AVERROR_INVALIDDATA;
01370
01371 return 0;
01372 }
01373
01374 static int rtsp_setup_output_streams(AVFormatContext *s, const char *addr)
01375 {
01376 RTSPState *rt = s->priv_data;
01377 RTSPMessageHeader reply1, *reply = &reply1;
01378 int i;
01379 char *sdp;
01380 AVFormatContext sdp_ctx, *ctx_array[1];
01381
01382 rt->start_time = av_gettime();
01383
01384
01385 sdp = av_mallocz(8192);
01386 if (sdp == NULL)
01387 return AVERROR(ENOMEM);
01388
01389
01390
01391
01392
01393
01394
01395
01396
01397
01398
01399
01400 sdp_ctx = *s;
01401 ff_url_join(sdp_ctx.filename, sizeof(sdp_ctx.filename),
01402 "rtsp", NULL, addr, -1, NULL);
01403 ctx_array[0] = &sdp_ctx;
01404 if (avf_sdp_create(ctx_array, 1, sdp, 8192)) {
01405 av_free(sdp);
01406 return AVERROR_INVALIDDATA;
01407 }
01408 av_log(s, AV_LOG_INFO, "SDP:\n%s\n", sdp);
01409 ff_rtsp_send_cmd_with_content(s, "ANNOUNCE", rt->control_uri,
01410 "Content-Type: application/sdp\r\n",
01411 reply, NULL, sdp, strlen(sdp));
01412 av_free(sdp);
01413 if (reply->status_code != RTSP_STATUS_OK)
01414 return AVERROR_INVALIDDATA;
01415
01416
01417 for (i = 0; i < s->nb_streams; i++) {
01418 RTSPStream *rtsp_st;
01419 AVStream *st = s->streams[i];
01420
01421 rtsp_st = av_mallocz(sizeof(RTSPStream));
01422 if (!rtsp_st)
01423 return AVERROR(ENOMEM);
01424 dynarray_add(&rt->rtsp_streams, &rt->nb_rtsp_streams, rtsp_st);
01425
01426 st->priv_data = rtsp_st;
01427 rtsp_st->stream_index = i;
01428
01429 av_strlcpy(rtsp_st->control_url, rt->control_uri, sizeof(rtsp_st->control_url));
01430
01431 av_strlcatf(rtsp_st->control_url, sizeof(rtsp_st->control_url),
01432 "/streamid=%d", i);
01433 }
01434
01435 return 0;
01436 }
01437
01438 int ff_rtsp_connect(AVFormatContext *s)
01439 {
01440 RTSPState *rt = s->priv_data;
01441 char host[1024], path[1024], tcpname[1024], cmd[2048], auth[128];
01442 char *option_list, *option, *filename;
01443 URLContext *rtsp_hd;
01444 int port, err, tcp_fd;
01445 RTSPMessageHeader reply1 = {}, *reply = &reply1;
01446 int lower_transport_mask = 0;
01447 char real_challenge[64];
01448 struct sockaddr_storage peer;
01449 socklen_t peer_len = sizeof(peer);
01450
01451 if (!ff_network_init())
01452 return AVERROR(EIO);
01453 redirect:
01454
01455 ff_url_split(NULL, 0, auth, sizeof(auth),
01456 host, sizeof(host), &port, path, sizeof(path), s->filename);
01457 if (*auth) {
01458 av_strlcpy(rt->auth, auth, sizeof(rt->auth));
01459 }
01460 if (port < 0)
01461 port = RTSP_DEFAULT_PORT;
01462
01463
01464 option_list = strrchr(path, '?');
01465 if (option_list) {
01466
01467
01468 filename = option_list;
01469 while (option_list) {
01470
01471 option = ++option_list;
01472 option_list = strchr(option_list, '&');
01473 if (option_list)
01474 *option_list = 0;
01475
01476
01477 if (!strcmp(option, "udp")) {
01478 lower_transport_mask |= (1<< RTSP_LOWER_TRANSPORT_UDP);
01479 } else if (!strcmp(option, "multicast")) {
01480 lower_transport_mask |= (1<< RTSP_LOWER_TRANSPORT_UDP_MULTICAST);
01481 } else if (!strcmp(option, "tcp")) {
01482 lower_transport_mask |= (1<< RTSP_LOWER_TRANSPORT_TCP);
01483 } else {
01484
01485
01486 int len = strlen(option);
01487 memmove(++filename, option, len);
01488 filename += len;
01489 if (option_list) *filename = '&';
01490 }
01491 }
01492 *filename = 0;
01493 }
01494
01495 if (!lower_transport_mask)
01496 lower_transport_mask = (1 << RTSP_LOWER_TRANSPORT_NB) - 1;
01497
01498 if (s->oformat) {
01499
01500 lower_transport_mask &= (1 << RTSP_LOWER_TRANSPORT_UDP) |
01501 (1 << RTSP_LOWER_TRANSPORT_TCP);
01502 if (!lower_transport_mask) {
01503 av_log(s, AV_LOG_ERROR, "Unsupported lower transport method, "
01504 "only UDP and TCP are supported for output.\n");
01505 err = AVERROR(EINVAL);
01506 goto fail;
01507 }
01508 }
01509
01510
01511
01512
01513 ff_url_join(rt->control_uri, sizeof(rt->control_uri), "rtsp", NULL,
01514 host, port, "%s", path);
01515
01516
01517 ff_url_join(tcpname, sizeof(tcpname), "tcp", NULL, host, port, NULL);
01518 if (url_open(&rtsp_hd, tcpname, URL_RDWR) < 0) {
01519 err = AVERROR(EIO);
01520 goto fail;
01521 }
01522 rt->rtsp_hd = rtsp_hd;
01523 rt->seq = 0;
01524
01525 tcp_fd = url_get_file_handle(rtsp_hd);
01526 if (!getpeername(tcp_fd, (struct sockaddr*) &peer, &peer_len)) {
01527 getnameinfo((struct sockaddr*) &peer, peer_len, host, sizeof(host),
01528 NULL, 0, NI_NUMERICHOST);
01529 }
01530
01531
01532
01533 for (rt->server_type = RTSP_SERVER_RTP;;) {
01534 cmd[0] = 0;
01535 if (rt->server_type == RTSP_SERVER_REAL)
01536 av_strlcat(cmd,
01546 "ClientChallenge: 9e26d33f2984236010ef6253fb1887f7\r\n"
01547 "PlayerStarttime: [28/03/2003:22:50:23 00:00]\r\n"
01548 "CompanyID: KnKV4M4I/B2FjJ1TToLycw==\r\n"
01549 "GUID: 00000000-0000-0000-0000-000000000000\r\n",
01550 sizeof(cmd));
01551 ff_rtsp_send_cmd(s, "OPTIONS", rt->control_uri, cmd, reply, NULL);
01552 if (reply->status_code != RTSP_STATUS_OK) {
01553 err = AVERROR_INVALIDDATA;
01554 goto fail;
01555 }
01556
01557
01558 if (rt->server_type != RTSP_SERVER_REAL && reply->real_challenge[0]) {
01559 rt->server_type = RTSP_SERVER_REAL;
01560 continue;
01561 } else if (!strncasecmp(reply->server, "WMServer/", 9)) {
01562 rt->server_type = RTSP_SERVER_WMS;
01563 } else if (rt->server_type == RTSP_SERVER_REAL)
01564 strcpy(real_challenge, reply->real_challenge);
01565 break;
01566 }
01567
01568 if (s->iformat)
01569 err = rtsp_setup_input_streams(s, reply);
01570 else
01571 err = rtsp_setup_output_streams(s, host);
01572 if (err)
01573 goto fail;
01574
01575 do {
01576 int lower_transport = ff_log2_tab[lower_transport_mask &
01577 ~(lower_transport_mask - 1)];
01578
01579 err = make_setup_request(s, host, port, lower_transport,
01580 rt->server_type == RTSP_SERVER_REAL ?
01581 real_challenge : NULL);
01582 if (err < 0)
01583 goto fail;
01584 lower_transport_mask &= ~(1 << lower_transport);
01585 if (lower_transport_mask == 0 && err == 1) {
01586 err = FF_NETERROR(EPROTONOSUPPORT);
01587 goto fail;
01588 }
01589 } while (err);
01590
01591 rt->state = RTSP_STATE_IDLE;
01592 rt->seek_timestamp = 0;
01593 return 0;
01594 fail:
01595 ff_rtsp_close_streams(s);
01596 url_close(rt->rtsp_hd);
01597 if (reply->status_code >=300 && reply->status_code < 400 && s->iformat) {
01598 av_strlcpy(s->filename, reply->location, sizeof(s->filename));
01599 av_log(s, AV_LOG_INFO, "Status %d: Redirecting to %s\n",
01600 reply->status_code,
01601 s->filename);
01602 goto redirect;
01603 }
01604 ff_network_close();
01605 return err;
01606 }
01607 #endif
01608
01609 #if CONFIG_RTSP_DEMUXER
01610 static int rtsp_read_header(AVFormatContext *s,
01611 AVFormatParameters *ap)
01612 {
01613 RTSPState *rt = s->priv_data;
01614 int ret;
01615
01616 ret = ff_rtsp_connect(s);
01617 if (ret)
01618 return ret;
01619
01620 if (ap->initial_pause) {
01621
01622 } else {
01623 if (rtsp_read_play(s) < 0) {
01624 ff_rtsp_close_streams(s);
01625 url_close(rt->rtsp_hd);
01626 return AVERROR_INVALIDDATA;
01627 }
01628 }
01629
01630 return 0;
01631 }
01632
01633 static int udp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st,
01634 uint8_t *buf, int buf_size)
01635 {
01636 RTSPState *rt = s->priv_data;
01637 RTSPStream *rtsp_st;
01638 fd_set rfds;
01639 int fd, fd_max, n, i, ret, tcp_fd, timeout_cnt = 0;
01640 struct timeval tv;
01641
01642 for (;;) {
01643 if (url_interrupt_cb())
01644 return AVERROR(EINTR);
01645 FD_ZERO(&rfds);
01646 if (rt->rtsp_hd) {
01647 tcp_fd = fd_max = url_get_file_handle(rt->rtsp_hd);
01648 FD_SET(tcp_fd, &rfds);
01649 } else {
01650 fd_max = 0;
01651 tcp_fd = -1;
01652 }
01653 for (i = 0; i < rt->nb_rtsp_streams; i++) {
01654 rtsp_st = rt->rtsp_streams[i];
01655 if (rtsp_st->rtp_handle) {
01656
01657
01658 fd = url_get_file_handle(rtsp_st->rtp_handle);
01659 if (fd > fd_max)
01660 fd_max = fd;
01661 FD_SET(fd, &rfds);
01662 }
01663 }
01664 tv.tv_sec = 0;
01665 tv.tv_usec = SELECT_TIMEOUT_MS * 1000;
01666 n = select(fd_max + 1, &rfds, NULL, NULL, &tv);
01667 if (n > 0) {
01668 timeout_cnt = 0;
01669 for (i = 0; i < rt->nb_rtsp_streams; i++) {
01670 rtsp_st = rt->rtsp_streams[i];
01671 if (rtsp_st->rtp_handle) {
01672 fd = url_get_file_handle(rtsp_st->rtp_handle);
01673 if (FD_ISSET(fd, &rfds)) {
01674 ret = url_read(rtsp_st->rtp_handle, buf, buf_size);
01675 if (ret > 0) {
01676 *prtsp_st = rtsp_st;
01677 return ret;
01678 }
01679 }
01680 }
01681 }
01682 #if CONFIG_RTSP_DEMUXER
01683 if (tcp_fd != -1 && FD_ISSET(tcp_fd, &rfds)) {
01684 RTSPMessageHeader reply;
01685
01686 ret = ff_rtsp_read_reply(s, &reply, NULL, 0);
01687 if (ret < 0)
01688 return ret;
01689
01690 if (rt->state != RTSP_STATE_STREAMING)
01691 return 0;
01692 }
01693 #endif
01694 } else if (n == 0 && ++timeout_cnt >= MAX_TIMEOUTS) {
01695 return FF_NETERROR(ETIMEDOUT);
01696 } else if (n < 0 && errno != EINTR)
01697 return AVERROR(errno);
01698 }
01699 }
01700
01701 static int tcp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st,
01702 uint8_t *buf, int buf_size)
01703 {
01704 RTSPState *rt = s->priv_data;
01705 int id, len, i, ret;
01706 RTSPStream *rtsp_st;
01707
01708 #ifdef DEBUG_RTP_TCP
01709 dprintf(s, "tcp_read_packet:\n");
01710 #endif
01711 redo:
01712 for (;;) {
01713 RTSPMessageHeader reply;
01714
01715 ret = ff_rtsp_read_reply(s, &reply, NULL, 1);
01716 if (ret == -1)
01717 return -1;
01718 if (ret == 1)
01719 break;
01720
01721 if (rt->state != RTSP_STATE_STREAMING)
01722 return 0;
01723 }
01724 ret = url_read_complete(rt->rtsp_hd, buf, 3);
01725 if (ret != 3)
01726 return -1;
01727 id = buf[0];
01728 len = AV_RB16(buf + 1);
01729 #ifdef DEBUG_RTP_TCP
01730 dprintf(s, "id=%d len=%d\n", id, len);
01731 #endif
01732 if (len > buf_size || len < 12)
01733 goto redo;
01734
01735 ret = url_read_complete(rt->rtsp_hd, buf, len);
01736 if (ret != len)
01737 return -1;
01738 if (rt->transport == RTSP_TRANSPORT_RDT &&
01739 ff_rdt_parse_header(buf, len, &id, NULL, NULL, NULL, NULL) < 0)
01740 return -1;
01741
01742
01743 for (i = 0; i < rt->nb_rtsp_streams; i++) {
01744 rtsp_st = rt->rtsp_streams[i];
01745 if (id >= rtsp_st->interleaved_min &&
01746 id <= rtsp_st->interleaved_max)
01747 goto found;
01748 }
01749 goto redo;
01750 found:
01751 *prtsp_st = rtsp_st;
01752 return len;
01753 }
01754
01755 static int rtsp_fetch_packet(AVFormatContext *s, AVPacket *pkt)
01756 {
01757 RTSPState *rt = s->priv_data;
01758 int ret, len;
01759 uint8_t buf[10 * RTP_MAX_PACKET_LENGTH];
01760 RTSPStream *rtsp_st;
01761
01762
01763 if (rt->cur_transport_priv) {
01764 if (rt->transport == RTSP_TRANSPORT_RDT) {
01765 ret = ff_rdt_parse_packet(rt->cur_transport_priv, pkt, NULL, 0);
01766 } else
01767 ret = rtp_parse_packet(rt->cur_transport_priv, pkt, NULL, 0);
01768 if (ret == 0) {
01769 rt->cur_transport_priv = NULL;
01770 return 0;
01771 } else if (ret == 1) {
01772 return 0;
01773 } else
01774 rt->cur_transport_priv = NULL;
01775 }
01776
01777
01778 redo:
01779 switch(rt->lower_transport) {
01780 default:
01781 #if CONFIG_RTSP_DEMUXER
01782 case RTSP_LOWER_TRANSPORT_TCP:
01783 len = tcp_read_packet(s, &rtsp_st, buf, sizeof(buf));
01784 break;
01785 #endif
01786 case RTSP_LOWER_TRANSPORT_UDP:
01787 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
01788 len = udp_read_packet(s, &rtsp_st, buf, sizeof(buf));
01789 if (len >=0 && rtsp_st->transport_priv && rt->transport == RTSP_TRANSPORT_RTP)
01790 rtp_check_and_send_back_rr(rtsp_st->transport_priv, len);
01791 break;
01792 }
01793 if (len < 0)
01794 return len;
01795 if (len == 0)
01796 return AVERROR_EOF;
01797 if (rt->transport == RTSP_TRANSPORT_RDT) {
01798 ret = ff_rdt_parse_packet(rtsp_st->transport_priv, pkt, buf, len);
01799 } else
01800 ret = rtp_parse_packet(rtsp_st->transport_priv, pkt, buf, len);
01801 if (ret < 0)
01802 goto redo;
01803 if (ret == 1)
01804
01805 rt->cur_transport_priv = rtsp_st->transport_priv;
01806
01807 return ret;
01808 }
01809
01810 static int rtsp_read_packet(AVFormatContext *s, AVPacket *pkt)
01811 {
01812 RTSPState *rt = s->priv_data;
01813 int ret;
01814 RTSPMessageHeader reply1, *reply = &reply1;
01815 char cmd[1024];
01816
01817 if (rt->server_type == RTSP_SERVER_REAL) {
01818 int i;
01819 enum AVDiscard cache[MAX_STREAMS];
01820
01821 for (i = 0; i < s->nb_streams; i++)
01822 cache[i] = s->streams[i]->discard;
01823
01824 if (!rt->need_subscription) {
01825 if (memcmp (cache, rt->real_setup_cache,
01826 sizeof(enum AVDiscard) * s->nb_streams)) {
01827 snprintf(cmd, sizeof(cmd),
01828 "Unsubscribe: %s\r\n",
01829 rt->last_subscription);
01830 ff_rtsp_send_cmd(s, "SET_PARAMETER", rt->control_uri,
01831 cmd, reply, NULL);
01832 if (reply->status_code != RTSP_STATUS_OK)
01833 return AVERROR_INVALIDDATA;
01834 rt->need_subscription = 1;
01835 }
01836 }
01837
01838 if (rt->need_subscription) {
01839 int r, rule_nr, first = 1;
01840
01841 memcpy(rt->real_setup_cache, cache,
01842 sizeof(enum AVDiscard) * s->nb_streams);
01843 rt->last_subscription[0] = 0;
01844
01845 snprintf(cmd, sizeof(cmd),
01846 "Subscribe: ");
01847 for (i = 0; i < rt->nb_rtsp_streams; i++) {
01848 rule_nr = 0;
01849 for (r = 0; r < s->nb_streams; r++) {
01850 if (s->streams[r]->priv_data == rt->rtsp_streams[i]) {
01851 if (s->streams[r]->discard != AVDISCARD_ALL) {
01852 if (!first)
01853 av_strlcat(rt->last_subscription, ",",
01854 sizeof(rt->last_subscription));
01855 ff_rdt_subscribe_rule(
01856 rt->last_subscription,
01857 sizeof(rt->last_subscription), i, rule_nr);
01858 first = 0;
01859 }
01860 rule_nr++;
01861 }
01862 }
01863 }
01864 av_strlcatf(cmd, sizeof(cmd), "%s\r\n", rt->last_subscription);
01865 ff_rtsp_send_cmd(s, "SET_PARAMETER", rt->control_uri,
01866 cmd, reply, NULL);
01867 if (reply->status_code != RTSP_STATUS_OK)
01868 return AVERROR_INVALIDDATA;
01869 rt->need_subscription = 0;
01870
01871 if (rt->state == RTSP_STATE_STREAMING)
01872 rtsp_read_play (s);
01873 }
01874 }
01875
01876 ret = rtsp_fetch_packet(s, pkt);
01877 if (ret < 0)
01878 return ret;
01879
01880
01881 if ((rt->server_type == RTSP_SERVER_WMS ||
01882 rt->server_type == RTSP_SERVER_REAL) &&
01883 (av_gettime() - rt->last_cmd_time) / 1000000 >= rt->timeout / 2) {
01884 if (rt->server_type == RTSP_SERVER_WMS) {
01885 ff_rtsp_send_cmd_async(s, "GET_PARAMETER", rt->control_uri, NULL);
01886 } else {
01887 ff_rtsp_send_cmd_async(s, "OPTIONS", "*", NULL);
01888 }
01889 }
01890
01891 return 0;
01892 }
01893
01894
01895 static int rtsp_read_pause(AVFormatContext *s)
01896 {
01897 RTSPState *rt = s->priv_data;
01898 RTSPMessageHeader reply1, *reply = &reply1;
01899
01900 if (rt->state != RTSP_STATE_STREAMING)
01901 return 0;
01902 else if (!(rt->server_type == RTSP_SERVER_REAL && rt->need_subscription)) {
01903 ff_rtsp_send_cmd(s, "PAUSE", rt->control_uri, NULL, reply, NULL);
01904 if (reply->status_code != RTSP_STATUS_OK) {
01905 return -1;
01906 }
01907 }
01908 rt->state = RTSP_STATE_PAUSED;
01909 return 0;
01910 }
01911
01912 static int rtsp_read_seek(AVFormatContext *s, int stream_index,
01913 int64_t timestamp, int flags)
01914 {
01915 RTSPState *rt = s->priv_data;
01916
01917 rt->seek_timestamp = av_rescale_q(timestamp,
01918 s->streams[stream_index]->time_base,
01919 AV_TIME_BASE_Q);
01920 switch(rt->state) {
01921 default:
01922 case RTSP_STATE_IDLE:
01923 break;
01924 case RTSP_STATE_STREAMING:
01925 if (rtsp_read_pause(s) != 0)
01926 return -1;
01927 rt->state = RTSP_STATE_SEEKING;
01928 if (rtsp_read_play(s) != 0)
01929 return -1;
01930 break;
01931 case RTSP_STATE_PAUSED:
01932 rt->state = RTSP_STATE_IDLE;
01933 break;
01934 }
01935 return 0;
01936 }
01937
01938 static int rtsp_read_close(AVFormatContext *s)
01939 {
01940 RTSPState *rt = s->priv_data;
01941
01942 #if 0
01943
01944 if (rt->lower_transport == RTSP_LOWER_TRANSPORT_TCP) {
01945 url_fclose(&rt->rtsp_gb);
01946 }
01947 #endif
01948 ff_rtsp_send_cmd_async(s, "TEARDOWN", rt->control_uri, NULL);
01949
01950 ff_rtsp_close_streams(s);
01951 url_close(rt->rtsp_hd);
01952 ff_network_close();
01953 return 0;
01954 }
01955
01956 AVInputFormat rtsp_demuxer = {
01957 "rtsp",
01958 NULL_IF_CONFIG_SMALL("RTSP input format"),
01959 sizeof(RTSPState),
01960 rtsp_probe,
01961 rtsp_read_header,
01962 rtsp_read_packet,
01963 rtsp_read_close,
01964 rtsp_read_seek,
01965 .flags = AVFMT_NOFILE,
01966 .read_play = rtsp_read_play,
01967 .read_pause = rtsp_read_pause,
01968 };
01969 #endif
01970
01971 static int sdp_probe(AVProbeData *p1)
01972 {
01973 const char *p = p1->buf, *p_end = p1->buf + p1->buf_size;
01974
01975
01976 while (p < p_end && *p != '\0') {
01977 if (p + sizeof("c=IN IP4") - 1 < p_end &&
01978 av_strstart(p, "c=IN IP4", NULL))
01979 return AVPROBE_SCORE_MAX / 2;
01980
01981 while (p < p_end - 1 && *p != '\n') p++;
01982 if (++p >= p_end)
01983 break;
01984 if (*p == '\r')
01985 p++;
01986 }
01987 return 0;
01988 }
01989
01990 #define SDP_MAX_SIZE 8192
01991
01992 static int sdp_read_header(AVFormatContext *s, AVFormatParameters *ap)
01993 {
01994 RTSPState *rt = s->priv_data;
01995 RTSPStream *rtsp_st;
01996 int size, i, err;
01997 char *content;
01998 char url[1024];
01999
02000 if (!ff_network_init())
02001 return AVERROR(EIO);
02002
02003
02004
02005 content = av_malloc(SDP_MAX_SIZE);
02006 size = get_buffer(s->pb, content, SDP_MAX_SIZE - 1);
02007 if (size <= 0) {
02008 av_free(content);
02009 return AVERROR_INVALIDDATA;
02010 }
02011 content[size] ='\0';
02012
02013 sdp_parse(s, content);
02014 av_free(content);
02015
02016
02017 for (i = 0; i < rt->nb_rtsp_streams; i++) {
02018 rtsp_st = rt->rtsp_streams[i];
02019
02020 ff_url_join(url, sizeof(url), "rtp", NULL,
02021 inet_ntoa(rtsp_st->sdp_ip), rtsp_st->sdp_port,
02022 "?localport=%d&ttl=%d", rtsp_st->sdp_port,
02023 rtsp_st->sdp_ttl);
02024 if (url_open(&rtsp_st->rtp_handle, url, URL_RDWR) < 0) {
02025 err = AVERROR_INVALIDDATA;
02026 goto fail;
02027 }
02028 if ((err = rtsp_open_transport_ctx(s, rtsp_st)))
02029 goto fail;
02030 }
02031 return 0;
02032 fail:
02033 ff_rtsp_close_streams(s);
02034 ff_network_close();
02035 return err;
02036 }
02037
02038 static int sdp_read_close(AVFormatContext *s)
02039 {
02040 ff_rtsp_close_streams(s);
02041 ff_network_close();
02042 return 0;
02043 }
02044
02045 AVInputFormat sdp_demuxer = {
02046 "sdp",
02047 NULL_IF_CONFIG_SMALL("SDP"),
02048 sizeof(RTSPState),
02049 sdp_probe,
02050 sdp_read_header,
02051 rtsp_fetch_packet,
02052 sdp_read_close,
02053 };