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_interrupt(RTSPContext *c, const char *url, RTSPMessageHeader *h, int pause_only) { RTSPContext *rtp_c; rtp_c = find_rtp_session_with_url(url, h->session_id); if (!rtp_c) { rtsp_reply_error(c, RTSP_STATUS_SESSION); return; } if (pause_only) { if (rtp_c->state != RTPSTATE_SEND_DATA) { rtsp_reply_error(c, RTSP_STATUS_STATE); return; } rtp_c->state = HTTPSTATE_READY; c->ntp_start_time = 0.0; } rtsp_reply_header(c, RTSP_STATUS_OK); /* session ID */ avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id); avio_printf(c->pb, "\r\n"); if (!pause_only) close_connection(rtp_c); }
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_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_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_play(RTSPContext *c, const char *url, RTSPMessageHeader *h) { RTSPContext *rtp_c; rtp_c = find_rtp_session_with_url(url, h->session_id); if (!rtp_c) { printf("can't find rtp session %s with url %s \n", h->session_id, url); rtsp_reply_error(c, RTSP_STATUS_SESSION); return; } rtp_c->start_time = cur_time; // set start time // pos * 188이 offset! int64_t last_offset = rtp_c->cur_offset; printf("cmd play! cur_offset = %lld\n", rtp_c->cur_offset); if (c->ntp_start_time != 0.0) { // set start time for seek! printf("find start time by ntp time!!! seek request! seek time = %lfs \n", c->ntp_start_time); rtp_c->ntp_start_time = c->ntp_start_time; rtp_c->cur_idx = get_closest_iframe_by_time(rtp_c->stream->first_idx, rtp_c->ntp_start_time); if (rtp_c->cur_idx != NULL) rtp_c->cur_offset = rtp_c->cur_idx->pos * 188; // play time이 바뀐 경우, buffer도 비워줘야 함. if (rtp_c->buffer_ptr >= rtp_c->buffer_end) { printf("no need to reset rtp_c->buffer ptr \n"); } else { printf("need to reset rtp_c->buffer ptr \n"); rtp_c->buffer_ptr = rtp_c->buffer_end; av_free(c->pb_buffer); } } else { rtp_c->cur_idx = NULL; } printf("cmd play! after find iframe cur_offset = %lld\n", rtp_c->cur_offset); // reset served bytes for restart from pause state rtp_c->served_bytes = 0; rtp_c->state = RTPSTATE_SEND_DATA; /* now everything is OK, so we can send the connection parameters */ rtsp_reply_header(c, RTSP_STATUS_OK); /* session ID */ avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id); if (rtp_c->cur_idx != NULL) { avio_printf(c->pb, "Range: npt=%.3lf-\r\n", get_iIndex_PCR(rtp_c->cur_idx)); printf("range response! Range: npt=%.3lf-\n", get_iIndex_PCR(rtp_c->cur_idx)); avio_printf(c->pb, "RTP-Info: seq=%d\r\n", rtp_c->cur_seq); printf("RTP info: seq=%d\n", rtp_c->cur_seq); } avio_printf(c->pb, "\r\n"); }
static void rtsp_cmd_describe(RTSPContext *c, const char *url) { iStream *stream; char path1[1024]; const char *path; uint8_t *content; int content_length = 0; socklen_t len; struct sockaddr_in my_addr; /* find which URL is asked */ av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url); int l = strlen(path1); if (path1[l-1] == '/') { path1[l-1] = '\0'; } path = path1; if (*path == '/') path++; for(stream = first_stream; stream != NULL; stream = stream->next) { if (!strcmp(path, stream->name)) { goto found; } } /* no stream found */ rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */ return; found: /* prepare the media description in SDP format */ content = av_mallocz(1024); /* get the host IP */ len = sizeof(my_addr); getsockname(c->fd, (struct sockaddr *)&my_addr, &len); rtsp_reply_header(c, RTSP_STATUS_OK); avio_printf(c->pb, "Content-Base: %s/\r\n", url); avio_printf(c->pb, "Content-Type: application/sdp\r\n"); int added_length = 0; // 여기에 sdp정보 추가 해야 함. content_length = generate_sdp_context(stream, &content); avio_printf(c->pb, "Content-Length: %d\r\n", content_length + added_length); avio_printf(c->pb, "\r\n"); // content added avio_write(c->pb, content, content_length + added_length); av_free(content); }
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_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; }
void* rtspserver(void *arg) { #ifdef WIN32 SOCKET s = *((SOCKET*) arg); int sinlen = sizeof(struct sockaddr_in); #else int s = *((int*) arg); socklen_t sinlen = sizeof(struct sockaddr_in); #endif const char *p; char buf[8192]; char cmd[32], url[1024], protocol[32]; int rlen; struct sockaddr_in sin; RTSPContext ctx; RTSPMessageHeader header1, *header = &header1; //int thread_ret; // image info //int iwidth = video_source_maxwidth(0); //int iheight = video_source_maxheight(0); // rtspconf = rtspconf_global(); sinlen = sizeof(sin); getpeername(s, (struct sockaddr*) &sin, &sinlen); // bzero(&ctx, sizeof(ctx)); if(per_client_init(&ctx) < 0) { ga_error("server initialization failed.\n"); return NULL; } bcopy(&sin, &ctx.client, sizeof(ctx.client)); ctx.state = SERVER_STATE_IDLE; // XXX: hasVideo is used to sync audio/video // This value is increased by 1 for each captured frame until it is gerater than zero // when this value is greater than zero, audio encoding then starts ... //ctx.hasVideo = -(rtspconf->video_fps>>1); // for slow encoders? ctx.hasVideo = 0; // with 'zerolatency' pthread_mutex_init(&ctx.rtsp_writer_mutex, NULL); // ga_error("[tid %ld] client connected from %s:%d\n", ga_gettid(), inet_ntoa(sin.sin_addr), htons(sin.sin_port)); // ctx.fd = s; // do { int i, fdmax, active; fd_set rfds; struct timeval to; FD_ZERO(&rfds); FD_SET(ctx.fd, &rfds); fdmax = ctx.fd; #ifdef HOLE_PUNCHING for(i = 0; i < 2*ctx.streamCount; i++) { FD_SET(ctx.rtpSocket[i], &rfds); if(ctx.rtpSocket[i] > fdmax) fdmax = ctx.rtpSocket[i]; } #endif to.tv_sec = 0; to.tv_usec = 500000; if((active = select(fdmax+1, &rfds, NULL, NULL, &to)) < 0) { ga_error("select() failed: %s\n", strerror(errno)); goto quit; } if(active == 0) { // try again! continue; } #ifdef HOLE_PUNCHING for(i = 0; i < 2*ctx.streamCount; i++) { struct sockaddr_in xsin; #ifdef WIN32 int xsinlen = sizeof(xsin); #else socklen_t xsinlen = sizeof(xsin); #endif if(FD_ISSET(ctx.rtpSocket[i], &rfds) == 0) continue; recvfrom(ctx.rtpSocket[i], buf, sizeof(buf), 0, (struct sockaddr*) &xsin, &xsinlen); if(ctx.rtpPortChecked[i] != 0) continue; // XXX: port should not flip-flop, so check only once if(xsin.sin_addr.s_addr != ctx.client.sin_addr.s_addr) { ga_error("RTP: client address mismatched? %u.%u.%u.%u != %u.%u.%u.%u\n", NIPQUAD(ctx.client.sin_addr.s_addr), NIPQUAD(xsin.sin_addr.s_addr)); continue; } if(xsin.sin_port != ctx.rtpPeerPort[i]) { ga_error("RTP: client port reconfigured: %u -> %u\n", (unsigned int) ntohs(ctx.rtpPeerPort[i]), (unsigned int) ntohs(xsin.sin_port)); ctx.rtpPeerPort[i] = xsin.sin_port; } else { ga_error("RTP: client is not under an NAT, port %d confirmed\n", (int) ntohs(ctx.rtpPeerPort[i])); } ctx.rtpPortChecked[i] = 1; } // is RTSP connection? if(FD_ISSET(ctx.fd, &rfds) == 0) continue; #endif // read commands if((rlen = rtsp_getnext(&ctx, buf, sizeof(buf))) < 0) { goto quit; } // Interleaved binary data? if(buf[0] == '$') { handle_rtcp(&ctx, buf, rlen); continue; } // REQUEST line ga_error("%s", buf); p = buf; get_word(cmd, sizeof(cmd), &p); get_word(url, sizeof(url), &p); get_word(protocol, sizeof(protocol), &p); // check protocol if(strcmp(protocol, "RTSP/1.0") != 0) { rtsp_reply_error(&ctx, RTSP_STATUS_VERSION); goto quit; } // read headers bzero(header, sizeof(*header)); do { int myseq = -1; char mysession[sizeof(header->session_id)] = ""; if((rlen = rtsp_getnext(&ctx, buf, sizeof(buf))) < 0) goto quit; if(buf[0]=='\n' || (buf[0]=='\r' && buf[1]=='\n')) break; #if 0 ga_error("HEADER: %s", buf); #endif // Special handling to CSeq & Session header // ff_rtsp_parse_line cannot handle CSeq & Session properly on Windows // any more? if(strncasecmp("CSeq: ", buf, 6) == 0) { myseq = strtol(buf+6, NULL, 10); } if(strncasecmp("Session: ", buf, 9) == 0) { strcpy(mysession, buf+9); } // ff_rtsp_parse_line(header, buf, NULL, NULL); // if(myseq > 0 && header->seq <= 0) { ga_error("WARNING: CSeq fixes applied (%d->%d).\n", header->seq, myseq); header->seq = myseq; } if(mysession[0] != '\0' && header->session_id[0]=='\0') { unsigned i; for(i = 0; i < sizeof(header->session_id)-1; i++) { if(mysession[i] == '\0' || isspace(mysession[i]) || mysession[i] == ';') break; header->session_id[i] = mysession[i]; } header->session_id[i+1] = '\0'; ga_error("WARNING: Session fixes applied (%s)\n", header->session_id); } } while(1); // special handle to session_id if(header->session_id != NULL) { char *p = header->session_id; while(*p != '\0') { if(*p == '\r' || *p == '\n') { *p = '\0'; break; } p++; } } // handle commands ctx.seq = header->seq; if (!strcmp(cmd, "DESCRIBE")) rtsp_cmd_describe(&ctx, url); else if (!strcmp(cmd, "OPTIONS")) rtsp_cmd_options(&ctx, url); else if (!strcmp(cmd, "SETUP")) rtsp_cmd_setup(&ctx, url, header); else if (!strcmp(cmd, "PLAY")) rtsp_cmd_play(&ctx, url, header); else if (!strcmp(cmd, "PAUSE")) rtsp_cmd_pause(&ctx, url, header); else if (!strcmp(cmd, "TEARDOWN")) rtsp_cmd_teardown(&ctx, url, header, 1); else rtsp_reply_error(&ctx, RTSP_STATUS_METHOD); if(ctx.state == SERVER_STATE_TEARDOWN) { break; } } while(1); quit: ctx.state = SERVER_STATE_TEARDOWN; // close(ctx.fd); // 2014-05-20: support only share-encoder model ff_server_unregister_client(&ctx); // per_client_deinit(&ctx); //ga_error("RTSP client thread terminated (%d/%d clients left).\n", // video_source_client_count(), audio_source_client_count()); ga_error("RTSP client thread terminated.\n"); // return NULL; }
void* rtspserver(void *arg) { #ifdef WIN32 SOCKET s = *((SOCKET*) arg); int sinlen = sizeof(struct sockaddr_in); #else int s = *((int*) arg); socklen_t sinlen = sizeof(struct sockaddr_in); #endif const char *p; char buf[8192]; char cmd[32], url[1024], protocol[32]; int rlen; struct sockaddr_in sin; RTSPContext ctx; RTSPMessageHeader header1, *header = &header1; int thread_ret; // image info int iwidth = video_source_width(0); int iheight = video_source_height(0); // rtspconf = rtspconf_global(); sinlen = sizeof(sin); getpeername(s, (struct sockaddr*) &sin, &sinlen); // bzero(&ctx, sizeof(ctx)); if(per_client_init(&ctx) < 0) { ga_error("server initialization failed.\n"); return NULL; } ctx.state = SERVER_STATE_IDLE; // XXX: hasVideo is used to sync audio/video // This value is increased by 1 for each captured frame until it is gerater than zero // when this value is greater than zero, audio encoding then starts ... //ctx.hasVideo = -(rtspconf->video_fps>>1); // for slow encoders? ctx.hasVideo = 0; // with 'zerolatency' pthread_mutex_init(&ctx.rtsp_writer_mutex, NULL); #if 0 ctx.audioparam.channels = rtspconf->audio_channels; ctx.audioparam.samplerate = rtspconf->audio_samplerate; if(rtspconf->audio_device_format == AV_SAMPLE_FMT_S16) { #ifdef WIN32 #else ctx.audioparam.format = SND_PCM_FORMAT_S16_LE; #endif ctx.audioparam.bits_per_sample = 16; } // ga_error("INFO: image: %dx%d; audio: %d ch 16-bit pcm @ %dHz\n", iwidth, iheight, ctx.audioparam.channels, ctx.audioparam.samplerate); #endif // #if 0 #ifdef WIN32 if(ga_wasapi_init(&ctx.audioparam) < 0) { ga_error("cannot init wasapi.\n"); return NULL; } #else if((ctx.audioparam.handle = ga_alsa_init(&ctx.audioparam.sndlog)) == NULL) { ga_error("cannot init alsa.\n"); return NULL; } if(ga_alsa_set_param(&ctx.audioparam) < 0) { ga_error("cannot set alsa parameter\n"); return NULL; } #endif #endif // ga_error("[tid %ld] client connected from %s:%d\n", ga_gettid(), inet_ntoa(sin.sin_addr), htons(sin.sin_port)); // ctx.fd = s; // do { fd_set rfds; FD_ZERO(&rfds); FD_SET(ctx.fd, &rfds); if(select(ctx.fd+1, &rfds, NULL, NULL, NULL) <=0) { ga_error("select() failed: %s\n", strerror(errno)); goto quit; } // read commands if((rlen = rtsp_getnext(&ctx, buf, sizeof(buf))) < 0) { goto quit; } // Interleaved binary data? if(buf[0] == '$') { handle_rtcp(&ctx, buf, rlen); continue; } // REQUEST line ga_error("%s", buf); p = buf; get_word(cmd, sizeof(cmd), &p); get_word(url, sizeof(url), &p); get_word(protocol, sizeof(protocol), &p); // check protocol if(strcmp(protocol, "RTSP/1.0") != 0) { rtsp_reply_error(&ctx, RTSP_STATUS_VERSION); goto quit; } // read headers bzero(header, sizeof(*header)); do { int myseq = -1; char mysession[sizeof(header->session_id)] = ""; if((rlen = rtsp_getnext(&ctx, buf, sizeof(buf))) < 0) goto quit; if(buf[0]=='\n' || (buf[0]=='\r' && buf[1]=='\n')) break; #if 0 ga_error("HEADER: %s", buf); #endif // Special handling to CSeq & Session header // ff_rtsp_parse_line cannot handle CSeq & Session properly on Windows // any more? if(strncasecmp("CSeq: ", buf, 6) == 0) { myseq = strtol(buf+6, NULL, 10); } if(strncasecmp("Session: ", buf, 9) == 0) { strcpy(mysession, buf+9); } // ff_rtsp_parse_line(header, buf, NULL, NULL); // if(myseq > 0 && header->seq <= 0) { ga_error("WARNING: CSeq fixes applied (%d->%d).\n", header->seq, myseq); header->seq = myseq; } if(mysession[0] != '\0' && header->session_id[0]=='\0') { unsigned i; for(i = 0; i < sizeof(header->session_id)-1; i++) { if(mysession[i] == '\0' || isspace(mysession[i]) || mysession[i] == ';') break; header->session_id[i] = mysession[i]; } header->session_id[i+1] = '\0'; ga_error("WARNING: Session fixes applied (%s)\n", header->session_id); } } while(1); // special handle to session_id if(header->session_id != NULL) { char *p = header->session_id; while(*p != '\0') { if(*p == '\r' || *p == '\n') { *p = '\0'; break; } p++; } } // handle commands ctx.seq = header->seq; if (!strcmp(cmd, "DESCRIBE")) rtsp_cmd_describe(&ctx, url); else if (!strcmp(cmd, "OPTIONS")) rtsp_cmd_options(&ctx, url); else if (!strcmp(cmd, "SETUP")) rtsp_cmd_setup(&ctx, url, header); else if (!strcmp(cmd, "PLAY")) rtsp_cmd_play(&ctx, url, header); else if (!strcmp(cmd, "PAUSE")) rtsp_cmd_pause(&ctx, url, header); else if (!strcmp(cmd, "TEARDOWN")) rtsp_cmd_teardown(&ctx, url, header); else rtsp_reply_error(&ctx, RTSP_STATUS_METHOD); if(ctx.state == SERVER_STATE_TEARDOWN) { break; } } while(1); quit: ctx.state = SERVER_STATE_TEARDOWN; // close(ctx.fd); #ifdef SHARE_ENCODER encoder_unregister_client(&ctx); #else ga_error("connection closed, checking for worker threads...\n"); #if 0 // if(ctx.vthreadId != 0) { video_source_notify_one(ctx.vthreadId); } #endif pthread_join(ctx.vthread, (void**) &thread_ret); #ifdef ENABLE_AUDIO pthread_join(ctx.athread, (void**) &thread_ret); #endif /* ENABLE_AUDIO */ #endif /* SHARE_ENCODER */ // per_client_deinit(&ctx); //ga_error("RTSP client thread terminated (%d/%d clients left).\n", // video_source_client_count(), audio_source_client_count()); ga_error("RTSP client thread terminated.\n"); // return NULL; }
static int rtsp_parse_request(RTSPContext *c) { const char *p, *p1, *p2; char cmd[32]; char url[1024]; char protocol[32]; char line[1024]; int len; RTSPMessageHeader header1 = { 0 }, *header = &header1; c->buffer_ptr[0] = '\0'; p = c->buffer; get_word(cmd, sizeof(cmd), &p); get_word(url, sizeof(url), &p); get_word(protocol, sizeof(protocol), &p); av_strlcpy(c->method, cmd, sizeof(c->method)); av_strlcpy(c->url, url, sizeof(c->url)); av_strlcpy(c->protocol, protocol, sizeof(c->protocol)); if (avio_open_dyn_buf(&c->pb) < 0) { // XXX: cannot do more c->pb = NULL;// safety return -1; } printf("cmd = %s \n", cmd); /* check version name */ if (strcmp(protocol, "RTSP/1.0") != 0) { rtsp_reply_error(c, RTSP_STATUS_VERSION); goto the_end; } /* parse each header line */ /* skip to next line */ while (*p != '\n' && *p != '\0') p++; if (*p == '\n') p++; while (*p != '\0') { p1 = memchr(p, '\n', (char *)c->buffer_ptr - p); if (!p1) break; p2 = p1; if (p2 > p && p2[-1] == '\r') p2--; /* skip empty line */ if (p2 == p) break; len = p2 - p; if (len > sizeof(line) - 1) len = sizeof(line) - 1; memcpy(line, p, len); line[len] = '\0'; //printf("rstp_parse_line = %s\n", line); // range parsing! if (!strncmp(line, "Range: npt=", strlen("Range: npt="))) { printf("range info contained! line = %s \n", line); char *end_pos = strrchr(line, '-'); char *start_pos = strrchr(line, '='); char *start_npt = malloc(end_pos - start_pos - 1); strncpy(start_npt, start_pos+1, end_pos - start_pos - 1); //printf("range start npt = %s\n", start_npt); double start_time = 0.0; sscanf(start_npt, "%lf", &start_time); //printf("range start time = %lfs\n", start_time); c->ntp_start_time = start_time; } // for trick if (!strncmp(line, "Scale:", strlen("Scale:"))) { printf("scale info contained! line = %s \n", line); char *end_pos = strrchr(line, '\n'); char *start_pos = strrchr(line, ' '); char *scale = malloc(end_pos - start_pos - 1); strncpy(scale, start_pos+1, end_pos - start_pos - 1); printf("scale = %s\n", scale); double speed = 0.0; sscanf(scale, "%lf", &speed); } ff_rtsp_parse_line(header, line, NULL, NULL); p = p1 + 1; } /* handle sequence number */ c->seq = header->seq; if (!strcmp(cmd, "DESCRIBE")) { printf("DESCRIBE!!!\n"); rtsp_cmd_describe(c, url); } else if (!strcmp(cmd, "OPTIONS")) { printf("Options! \n"); rtsp_cmd_options(c, url); } else if (!strcmp(cmd, "SETUP")) { printf("Setup!!!\n"); rtsp_cmd_setup(c, url, header); } else if (!strcmp(cmd, "PLAY")) { printf("Play!!! \n"); rtsp_cmd_play(c, url, header); } else if (!strcmp(cmd, "PAUSE")) { printf("PAUSE!!!\n"); rtsp_cmd_interrupt(c, url, header, 1); } else if (!strcmp(cmd, "TEARDOWN")) { rtsp_cmd_interrupt(c, url, header, 0); printf("TEARDOWN!!!\n"); } else { rtsp_reply_error(c, RTSP_STATUS_METHOD); printf("Error!!"); } the_end: // write rstp reply packet! text len = avio_close_dyn_buf(c->pb, &c->pb_buffer); c->pb = NULL; /* safety */ if (len < 0) { /* XXX: cannot do more */ printf("error! len < 0 when avio close dyn buf"); return -1; } c->buffer_ptr = c->pb_buffer; c->buffer_end = c->pb_buffer + len; c->state = RTSPSTATE_SEND_REPLY; return 0; }
static void rtsp_cmd_setup(RTSPContext *c, const char *url, RTSPMessageHeader *h) { iStream *stream; int stream_index, rtp_port, rtcp_port; char buf[1024]; char path1[1024]; char *path; RTSPContext *rtp_c; RTSPTransportField *th; struct sockaddr_in dest_addr; RTSPActionServerSetup setup; /* find which URL is asked */ av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url); int len = strlen(path1); if (path1[len-1] == '/') { path1[len-1] = '\0'; } // / 뒤에꺼 없애자! char *removed_path = strrchr(path1, '/'); if (removed_path != NULL && removed_path != path1) { path = path1; path[removed_path-path1] = '\0'; } else { path = path1; } if (*path == '/') path++; printf("rtsp setup! find stream name = %s\n", path); /* now check each stream */ for(stream = first_stream; stream != NULL; stream = stream->next) { /* accept aggregate filenames only if single stream */ if (!strcmp(path, stream->name)) { goto found; } } /* no stream found */ rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */ return; found: /* generate session id if needed */ if (h->session_id[0] == '\0') { unsigned random0 = av_lfg_get(&random_state); unsigned random1 = av_lfg_get(&random_state); snprintf(h->session_id, sizeof(h->session_id), "%08x%08x", random0, random1); } /* find RTP session, and create it if none found */ rtp_c = find_rtp_session(h->session_id); if (!rtp_c) { rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id); if (!rtp_c) { rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH); return; } } /* test if stream is OK (test needed because several SETUP needs to be done for a given file) */ if (rtp_c->stream != stream) { rtsp_reply_error(c, RTSP_STATUS_SERVICE); return; } // udp 로 전송 가능한지 체크. th = find_transport(h); if (!th) { printf("can't do udp transport \n"); rtsp_reply_error(c, RTSP_STATUS_INTERNAL); return; } /* setup default options */ setup.transport_option[0] = '\0'; dest_addr = rtp_c->from_addr; dest_addr.sin_port = htons(th->client_port_min); // setup stream // open new rtp/udp socket! if (rtp_new_stream(rtp_c, &dest_addr) < 0) { rtsp_reply_error(c, RTSP_STATUS_TRANSPORT); return; } rtp_c->to_addr = dest_addr; /* now everything is OK, so we can send the connection parameters */ rtsp_reply_header(c, RTSP_STATUS_OK); /* session ID */ avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id); // rtcp 도 열어야되나? -_-; rtp_port = rtp_c->udp_port; rtcp_port = rtp_port+1; // 실제로 만들지는 않음.. avio_printf(c->pb, "Transport: RTP/AVP/UDP;unicast;" "client_port=%d-%d;server_port=%d-%d", th->client_port_min, th->client_port_max, rtp_port, rtcp_port); if (setup.transport_option[0] != '\0') avio_printf(c->pb, ";%s", setup.transport_option); avio_printf(c->pb, "\r\n"); avio_printf(c->pb, "\r\n"); }