static void *rtsp_conversation_thread_func(void *pconn) { // SIGUSR1 is used to interrupt this thread if blocked for read sigset_t set; sigemptyset(&set); sigaddset(&set, SIGUSR1); pthread_sigmask(SIG_UNBLOCK, &set, NULL); rtsp_conn_info *conn = pconn; rtsp_message *req, *resp; char *hdr, *auth_nonce = NULL; while ((req = rtsp_read_request(conn->fd))) { resp = msg_init(); resp->respcode = 400; apple_challenge(conn->fd, req, resp); hdr = msg_get_header(req, "CSeq"); if (hdr) msg_add_header(resp, "CSeq", hdr); msg_add_header(resp, "Audio-Jack-Status", "connected; type=analog"); if (rtsp_auth(&auth_nonce, req, resp)) goto respond; struct method_handler *mh; for (mh=method_handlers; mh->method; mh++) { if (!strcmp(mh->method, req->method)) { mh->handler(conn, req, resp); break; } } respond: msg_write_response(conn->fd, resp); msg_free(req); msg_free(resp); } debug(1, "closing RTSP connection\n"); if (conn->fd > 0) close(conn->fd); if (rtsp_playing()) { rtp_shutdown(); debug(1, "stop player\n"); player_stop(); debug(1, "player stoped\n"); please_shutdown = 0; pthread_mutex_unlock(&playing_mutex); } if (auth_nonce) free(auth_nonce); conn->running = 0; debug(2, "terminating RTSP thread\n"); return NULL; }
static int rtsp_read_options(AVFormatContext *s) { RTSPState *rt = s->priv_data; RTSPMessageHeader request = { 0 }; int ret = 0; /* Parsing headers */ ret = rtsp_read_request(s, &request, "OPTIONS"); if (ret) return ret; rt->seq++; /* Send Reply */ rtsp_send_reply(s, RTSP_STATUS_OK, "Public: ANNOUNCE, PAUSE, SETUP, TEARDOWN, RECORD\r\n", request.seq); return 0; }
static int rtsp_read_record(AVFormatContext *s) { RTSPState *rt = s->priv_data; RTSPMessageHeader request = { 0 }; int ret = 0; char responseheaders[1024]; ret = rtsp_read_request(s, &request, "RECORD"); if (ret) return ret; ret = check_sessionid(s, &request); if (ret) return ret; rt->seq++; snprintf(responseheaders, sizeof(responseheaders), "Session: %s\r\n", rt->session_id); rtsp_send_reply(s, RTSP_STATUS_OK, responseheaders, request.seq); rt->state = RTSP_STATE_STREAMING; return 0; }
int ff_rtsp_parse_streaming_commands(AVFormatContext *s) { RTSPState *rt = s->priv_data; unsigned char rbuf[4096]; unsigned char method[10]; char uri[500]; int ret; int rbuflen = 0; RTSPMessageHeader request = { 0 }; enum RTSPMethod methodcode; ret = read_line(s, rbuf, sizeof(rbuf), &rbuflen); if (ret < 0) return ret; ret = parse_command_line(s, rbuf, rbuflen, uri, sizeof(uri), method, sizeof(method), &methodcode); if (ret) { av_log(s, AV_LOG_ERROR, "RTSP: Unexpected Command\n"); return ret; } ret = rtsp_read_request(s, &request, method); if (ret) return ret; rt->seq++; if (methodcode == PAUSE) { rt->state = RTSP_STATE_PAUSED; ret = rtsp_send_reply(s, RTSP_STATUS_OK, NULL , request.seq); // TODO: Missing date header in response } else if (methodcode == OPTIONS) { ret = rtsp_send_reply(s, RTSP_STATUS_OK, "Public: ANNOUNCE, PAUSE, SETUP, TEARDOWN, " "RECORD\r\n", request.seq); } else if (methodcode == TEARDOWN) { rt->state = RTSP_STATE_IDLE; ret = rtsp_send_reply(s, RTSP_STATUS_OK, NULL , request.seq); return 0; } return ret; }
static int rtsp_read_announce(AVFormatContext *s) { RTSPState *rt = s->priv_data; RTSPMessageHeader request = { 0 }; char sdp[4096]; int ret; ret = rtsp_read_request(s, &request, "ANNOUNCE"); if (ret) return ret; rt->seq++; if (strcmp(request.content_type, "application/sdp")) { av_log(s, AV_LOG_ERROR, "Unexpected content type %s\n", request.content_type); rtsp_send_reply(s, RTSP_STATUS_SERVICE, NULL, request.seq); return AVERROR_OPTION_NOT_FOUND; } if (request.content_length && request.content_length < sizeof(sdp) - 1) { /* Read SDP */ if (ffurl_read_complete(rt->rtsp_hd, sdp, request.content_length) < request.content_length) { av_log(s, AV_LOG_ERROR, "Unable to get complete SDP Description in ANNOUNCE\n"); rtsp_send_reply(s, RTSP_STATUS_INTERNAL, NULL, request.seq); return AVERROR(EIO); } sdp[request.content_length] = '\0'; av_log(s, AV_LOG_VERBOSE, "SDP: %s\n", sdp); ret = ff_sdp_parse(s, sdp); if (ret) return ret; rtsp_send_reply(s, RTSP_STATUS_OK, NULL, request.seq); return 0; } av_log(s, AV_LOG_ERROR, "Content-Length header value exceeds sdp allocated buffer (4KB)\n"); rtsp_send_reply(s, RTSP_STATUS_INTERNAL, "Content-Length exceeds buffer size", request.seq); return AVERROR(EIO); }
static int rtsp_read_setup(AVFormatContext *s, char* host, char *controlurl) { RTSPState *rt = s->priv_data; RTSPMessageHeader request = { 0 }; int ret = 0; char url[1024]; RTSPStream *rtsp_st; char responseheaders[1024]; int localport = -1; int transportidx = 0; int streamid = 0; ret = rtsp_read_request(s, &request, "SETUP"); if (ret) return ret; rt->seq++; if (!request.nb_transports) { av_log(s, AV_LOG_ERROR, "No transport defined in SETUP\n"); return AVERROR_INVALIDDATA; } for (transportidx = 0; transportidx < request.nb_transports; transportidx++) { if (!request.transports[transportidx].mode_record || (request.transports[transportidx].lower_transport != RTSP_LOWER_TRANSPORT_UDP && request.transports[transportidx].lower_transport != RTSP_LOWER_TRANSPORT_TCP)) { av_log(s, AV_LOG_ERROR, "mode=record/receive not set or transport" " protocol not supported (yet)\n"); return AVERROR_INVALIDDATA; } } if (request.nb_transports > 1) av_log(s, AV_LOG_WARNING, "More than one transport not supported, " "using first of all\n"); for (streamid = 0; streamid < rt->nb_rtsp_streams; streamid++) { if (!strcmp(rt->rtsp_streams[streamid]->control_url, controlurl)) break; } if (streamid == rt->nb_rtsp_streams) { av_log(s, AV_LOG_ERROR, "Unable to find requested track\n"); return AVERROR_STREAM_NOT_FOUND; } rtsp_st = rt->rtsp_streams[streamid]; localport = rt->rtp_port_min; if (request.transports[0].lower_transport == RTSP_LOWER_TRANSPORT_TCP) { rt->lower_transport = RTSP_LOWER_TRANSPORT_TCP; if ((ret = ff_rtsp_open_transport_ctx(s, rtsp_st))) { rtsp_send_reply(s, RTSP_STATUS_TRANSPORT, NULL, request.seq); return ret; } rtsp_st->interleaved_min = request.transports[0].interleaved_min; rtsp_st->interleaved_max = request.transports[0].interleaved_max; snprintf(responseheaders, sizeof(responseheaders), "Transport: " "RTP/AVP/TCP;unicast;mode=receive;interleaved=%d-%d" "\r\n", request.transports[0].interleaved_min, request.transports[0].interleaved_max); } else { do { ff_url_join(url, sizeof(url), "rtp", NULL, host, localport, NULL); av_dlog(s, "Opening: %s", url); ret = ffurl_open(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ_WRITE, &s->interrupt_callback, NULL); if (ret) localport += 2; } while (ret || localport > rt->rtp_port_max); if (localport > rt->rtp_port_max) { rtsp_send_reply(s, RTSP_STATUS_TRANSPORT, NULL, request.seq); return ret; } av_dlog(s, "Listening on: %d", ff_rtp_get_local_rtp_port(rtsp_st->rtp_handle)); if ((ret = ff_rtsp_open_transport_ctx(s, rtsp_st))) { rtsp_send_reply(s, RTSP_STATUS_TRANSPORT, NULL, request.seq); return ret; } localport = ff_rtp_get_local_rtp_port(rtsp_st->rtp_handle); snprintf(responseheaders, sizeof(responseheaders), "Transport: " "RTP/AVP/UDP;unicast;mode=receive;source=%s;" "client_port=%d-%d;server_port=%d-%d\r\n", host, request.transports[0].client_port_min, request.transports[0].client_port_max, localport, localport + 1); } /* Establish sessionid if not previously set */ /* Put this in a function? */ /* RFC 2326: session id must be at least 8 digits */ while (strlen(rt->session_id) < 8) av_strlcatf(rt->session_id, 512, "%u", av_get_random_seed()); av_strlcatf(responseheaders, sizeof(responseheaders), "Session: %s\r\n", rt->session_id); /* Send Reply */ rtsp_send_reply(s, RTSP_STATUS_OK, responseheaders, request.seq); rt->state = RTSP_STATE_PAUSED; return 0; }