static void rtsp_cmd_pause(RTSPContext *ctx, const char *url, RTSPMessageHeader *h) { char path[4096]; // av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path, sizeof(path), url); if(strncmp(path, rtspconf->object, strlen(rtspconf->object)) != 0) { rtsp_reply_error(ctx, RTSP_STATUS_SESSION); return; } if(strcmp(ctx->session_id, h->session_id) != 0) { rtsp_reply_error(ctx, RTSP_STATUS_SESSION); return; } // if(ctx->state != SERVER_STATE_PLAYING) { rtsp_reply_error(ctx, RTSP_STATUS_STATE); return; } // ctx->state = SERVER_STATE_PAUSE; rtsp_reply_header(ctx, RTSP_STATUS_OK); rtsp_printf(ctx, "Session: %s\r\n", ctx->session_id); rtsp_printf(ctx, "\r\n"); return; }
static void rtsp_cmd_describe(RTSPContext *ctx, const char *url) { struct sockaddr_in myaddr; #ifdef WIN32 int addrlen; #else socklen_t addrlen; #endif char path[4096]; char content[4096]; int content_length; // av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path, sizeof(path), url); if(strcmp(path, rtspconf->object) != 0) { rtsp_reply_error(ctx, RTSP_STATUS_SERVICE); return; } // addrlen = sizeof(myaddr); getsockname(ctx->fd, (struct sockaddr*) &myaddr, &addrlen); content_length = prepare_sdp_description(ctx, content, sizeof(content)); if(content_length < 0) { rtsp_reply_error(ctx, RTSP_STATUS_INTERNAL); return; } // state does not change rtsp_reply_header(ctx, RTSP_STATUS_OK); rtsp_printf(ctx, "Content-Base: %s/\r\n", url); rtsp_printf(ctx, "Content-Type: application/sdp\r\n"); rtsp_printf(ctx, "Content-Length: %d\r\n", content_length); rtsp_printf(ctx, "\r\n"); rtsp_write(ctx, content, content_length); return; }
static void rtsp_cmd_play(RTSPContext *ctx, const char *url, RTSPMessageHeader *h) { char path[4096]; // av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path, sizeof(path), url); if(strncmp(path, rtspconf->object, strlen(rtspconf->object)) != 0) { rtsp_reply_error(ctx, RTSP_STATUS_SESSION); return; } if(strcmp(ctx->session_id, h->session_id) != 0) { rtsp_reply_error(ctx, RTSP_STATUS_SESSION); return; } // if(ctx->state != SERVER_STATE_READY && ctx->state != SERVER_STATE_PAUSE) { rtsp_reply_error(ctx, RTSP_STATUS_STATE); return; } // 2014-05-20: support only shared-encoder model if(ff_server_register_client(ctx) < 0) { ga_error("cannot register encoder client.\n"); rtsp_reply_error(ctx, RTSP_STATUS_INTERNAL); return; } // ctx->state = SERVER_STATE_PLAYING; rtsp_reply_header(ctx, RTSP_STATUS_OK); rtsp_printf(ctx, "Session: %s\r\n", ctx->session_id); rtsp_printf(ctx, "\r\n"); return; }
static void rtsp_reply_header(RTSPContext *c, enum RTSPStatusCode error_number) { const char *str; time_t ti; struct tm rtm; char buf2[32]; switch(error_number) { case RTSP_STATUS_OK: str = "OK"; break; case RTSP_STATUS_METHOD: str = "Method Not Allowed"; break; case RTSP_STATUS_BANDWIDTH: str = "Not Enough Bandwidth"; break; case RTSP_STATUS_SESSION: str = "Session Not Found"; break; case RTSP_STATUS_STATE: str = "Method Not Valid in This State"; break; case RTSP_STATUS_AGGREGATE: str = "Aggregate operation not allowed"; break; case RTSP_STATUS_ONLY_AGGREGATE: str = "Only aggregate operation allowed"; break; case RTSP_STATUS_TRANSPORT: str = "Unsupported transport"; break; case RTSP_STATUS_INTERNAL: str = "Internal Server Error"; break; case RTSP_STATUS_SERVICE: str = "Service Unavailable"; break; case RTSP_STATUS_VERSION: str = "RTSP Version not supported"; break; default: str = "Unknown Error"; break; } rtsp_printf(c, "RTSP/1.0 %d %s\r\n", error_number, str); rtsp_printf(c, "CSeq: %d\r\n", c->seq); /* output GMT time */ ti = time(NULL); #ifdef MSYS gmtime_s(&rtm, &ti); #else gmtime_r(&ti, &rtm); #endif strftime(buf2, sizeof(buf2), "%a, %d %b %Y %H:%M:%S", &rtm); rtsp_printf(c, "Date: %s GMT\r\n", buf2); // return; }
static void rtsp_cmd_options(RTSPContext *c, const char *url) { // state does not change rtsp_printf(c, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK"); rtsp_printf(c, "CSeq: %d\r\n", c->seq); //rtsp_printf(c, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE"); rtsp_printf(c, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY"); rtsp_printf(c, "\r\n"); return; }
static void rtsp_cmd_play(RTSPContext *ctx, const char *url, RTSPMessageHeader *h) { char path[4096]; // av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path, sizeof(path), url); if(strncmp(path, rtspconf->object, strlen(rtspconf->object)) != 0) { rtsp_reply_error(ctx, RTSP_STATUS_SESSION); return; } if(strcmp(ctx->session_id, h->session_id) != 0) { rtsp_reply_error(ctx, RTSP_STATUS_SESSION); return; } // if(ctx->state != SERVER_STATE_READY && ctx->state != SERVER_STATE_PAUSE) { rtsp_reply_error(ctx, RTSP_STATUS_STATE); return; } // create threads #ifndef SHARE_ENCODER if(pthread_create(&ctx->vthread, NULL, vencoder_thread, ctx) != 0) { ga_error("cannot create video thread\n"); rtsp_reply_error(ctx, RTSP_STATUS_INTERNAL); return; } #ifdef ENABLE_AUDIO if(pthread_create(&ctx->athread, NULL, aencoder_thread, ctx) != 0) { ga_error("cannot create audio thread\n"); rtsp_reply_error(ctx, RTSP_STATUS_INTERNAL); return; } #endif /* ENABLE_AUDIO */ #else if(encoder_register_client(ctx) < 0) { ga_error("cannot register encoder client.\n"); rtsp_reply_error(ctx, RTSP_STATUS_INTERNAL); return; } #endif /* SHARE_ENCODER */ // ctx->state = SERVER_STATE_PLAYING; rtsp_reply_header(ctx, RTSP_STATUS_OK); rtsp_printf(ctx, "Session: %s\r\n", ctx->session_id); rtsp_printf(ctx, "\r\n"); return; }
static void rtsp_cmd_teardown(RTSPContext *ctx, const char *url, RTSPMessageHeader *h, int bruteforce) { char path[4096]; // av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path, sizeof(path), url); if(strncmp(path, rtspconf->object, strlen(rtspconf->object)) != 0) { rtsp_reply_error(ctx, RTSP_STATUS_SESSION); return; } if(strcmp(ctx->session_id, h->session_id) != 0) { rtsp_reply_error(ctx, RTSP_STATUS_SESSION); return; } // ctx->state = SERVER_STATE_TEARDOWN; if(bruteforce != 0) return; // XXX: well, gently response rtsp_reply_header(ctx, RTSP_STATUS_OK); rtsp_printf(ctx, "Session: %s\r\n", ctx->session_id); rtsp_printf(ctx, "\r\n"); return; }
static void rtsp_cmd_setup(RTSPContext *ctx, const char *url, RTSPMessageHeader *h) { int i; RTSPTransportField *th; struct sockaddr_in destaddr, myaddr; #ifdef WIN32 int destaddrlen, myaddrlen; #else socklen_t destaddrlen, myaddrlen; #endif char path[4096]; char channelname[VIDEO_SOURCE_CHANNEL_MAX+1][RTSP_STREAM_FORMAT_MAXLEN]; int baselen = strlen(rtspconf->object); int streamid; int rtp_port, rtcp_port; enum RTSPStatusCode errcode; // av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path, sizeof(path), url); for(i = 0; i < VIDEO_SOURCE_CHANNEL_MAX+1; i++) { snprintf(channelname[i], RTSP_STREAM_FORMAT_MAXLEN, RTSP_STREAM_FORMAT, i); } // if(strncmp(path, rtspconf->object, baselen) != 0) { ga_error("invalid object (path=%s)\n", path); rtsp_reply_error(ctx, RTSP_STATUS_AGGREGATE); return; } for(i = 0; i < VIDEO_SOURCE_CHANNEL_MAX+1; i++) { if(strcmp(path+baselen+1, channelname[i]) == 0) { streamid = i; break; } } if(i == VIDEO_SOURCE_CHANNEL_MAX+1) { // not found ga_error("invalid service (path=%s)\n", path); rtsp_reply_error(ctx, RTSP_STATUS_SERVICE); return; } // if(ctx->state != SERVER_STATE_IDLE && ctx->state != SERVER_STATE_READY) { rtsp_reply_error(ctx, RTSP_STATUS_STATE); return; } // create session id? if(ctx->session_id == NULL) { if(h->session_id[0] == '\0') { snprintf(h->session_id, sizeof(h->session_id), "%04x%04x", rand()%0x0ffff, rand()%0x0ffff); ctx->session_id = strdup(h->session_id); ga_error("New session created (id = %s)\n", ctx->session_id); } } // session id must match -- we have only one session if(ctx->session_id == NULL || strcmp(ctx->session_id, h->session_id) != 0) { ga_error("Bad session id %s != %s\n", h->session_id, ctx->session_id); errcode = RTSP_STATUS_SESSION; goto error_setup; } // find supported transport if((th = find_transport(h, RTSP_LOWER_TRANSPORT_UDP)) == NULL) { th = find_transport(h, RTSP_LOWER_TRANSPORT_TCP); } if(th == NULL) { ga_error("Cannot find transport\n"); errcode = RTSP_STATUS_TRANSPORT; goto error_setup; } // destaddrlen = sizeof(destaddr); bzero(&destaddr, destaddrlen); if(getpeername(ctx->fd, (struct sockaddr*) &destaddr, &destaddrlen) < 0) { ga_error("Cannot get peername\n"); errcode = RTSP_STATUS_INTERNAL; goto error_setup; } destaddr.sin_port = htons(th->client_port_min); // myaddrlen = sizeof(myaddr); bzero(&myaddr, myaddrlen); if(getsockname(ctx->fd, (struct sockaddr*) &myaddr, &myaddrlen) < 0) { ga_error("Cannot get sockname\n"); errcode = RTSP_STATUS_INTERNAL; goto error_setup; } // ctx->lower_transport[streamid] = th->lower_transport; if(rtp_new_av_stream(ctx, &destaddr, streamid, streamid == video_source_channels()/*rtspconf->audio_id*/ ? rtspconf->audio_encoder_codec->id : rtspconf->video_encoder_codec->id) < 0) { ga_error("Create AV stream %d failed.\n", streamid); errcode = RTSP_STATUS_TRANSPORT; goto error_setup; } // ctx->state = SERVER_STATE_READY; rtsp_reply_header(ctx, RTSP_STATUS_OK); rtsp_printf(ctx, "Session: %s\r\n", ctx->session_id); switch(th->lower_transport) { case RTSP_LOWER_TRANSPORT_UDP: #ifdef HOLE_PUNCHING rtp_port = ctx->rtpLocalPort[streamid*2]; rtcp_port = ctx->rtpLocalPort[streamid*2+1]; ctx->rtpPeerPort[streamid*2] = htons(th->client_port_min); ctx->rtpPeerPort[streamid*2+1] = htons(th->client_port_max); #else rtp_port = ff_rtp_get_local_rtp_port((URLContext*) ctx->fmtctx[streamid]->pb->opaque); rtcp_port = ff_rtp_get_local_rtcp_port((URLContext*) ctx->fmtctx[streamid]->pb->opaque); #endif ga_error("RTP/UDP: streamid=%d; client=%d-%d; server=%d-%d\n", streamid, th->client_port_min, th->client_port_max, rtp_port, rtcp_port); rtsp_printf(ctx, "Transport: RTP/AVP/UDP;unicast;client_port=%d-%d;server_port=%d-%d\r\n", th->client_port_min, th->client_port_max, rtp_port, rtcp_port); break; case RTSP_LOWER_TRANSPORT_TCP: ga_error("RTP/TCP: interleaved=%d-%d\n", streamid*2, streamid*2+1); rtsp_printf(ctx, "Transport: RTP/AVP/TCP;unicast;interleaved=%d-%d\r\n", streamid*2, streamid*2+1, streamid*2); break; default: // should not happen break; } rtsp_printf(ctx, "\r\n"); return; error_setup: if(ctx->session_id != NULL) { free(ctx->session_id); ctx->session_id = NULL; } if(ctx->encoder[streamid] != NULL) { ctx->encoder[streamid] = NULL; } if(ctx->stream[streamid] != NULL) { ctx->stream[streamid] = NULL; } if(ctx->fmtctx[streamid] != NULL) { avformat_free_context(ctx->fmtctx[streamid]); ctx->fmtctx[streamid] = NULL; } rtsp_reply_error(ctx, errcode); return; }
static void rtsp_reply_error(RTSPContext *c, enum RTSPStatusCode error_number) { rtsp_reply_header(c, error_number); rtsp_printf(c, "\r\n"); }