/* * set Speed: field to rtsp_hdr. */ void rtsp_set_speed_field(struct rtsp_header_t *rtsp_hdr,const char *speedstr) { char *buffer = xmalloc(BUFSIZE_1K); /* speed specified */ if(speedstr) { char *reason = NULL; int guessed_speed = 0; if(speed_valid_and_guess(speedstr,&guessed_speed,&reason)) { snprintf(buffer,BUFSIZE_1K - 1,"Speed: %s",speedstr); } else { /* invlalid, use guessed value */ if(guessed_speed == 0) { guessed_speed = 1; /* default speed 1.000 */ } snprintf(buffer,BUFSIZE_1K - 1,"Speed: %d.000",guessed_speed); display(MSDL_ERR, "option \"-s %s\" is invalid as a rtsp speed request\n" ": %s\n" "send \"%s\" instead\n", speedstr,reason,buffer); } } else { /* set default speed */ strncpy(buffer,"Speed: 1.000",BUFSIZE_1K - 1); } rtsp_set_field(rtsp_hdr,buffer); free(buffer); }
/* * set fields which are common in all rtsp packet. * CSeq and Session field should be present in * every rtsp packet. */ int rtsp_set_standard_fields(struct rtsp_ctrl_t *rtsp_ctrl, struct rtsp_header_t *rtsp_hdr) { char buf[20]; snprintf(buf,20,"Cseq: %u",rtsp_ctrl->cseq); rtsp_set_field(rtsp_hdr,buf); if(rtsp_ctrl->session) { char *buf2; buf2 = (char *)xmalloc(strlen(rtsp_ctrl->session) + 15); sprintf(buf2,"Session: %s",rtsp_ctrl->session); rtsp_set_field(rtsp_hdr,buf2); free(buf2); } return 0; }
/* * send SET_PARAMETER, with stream choosing subscribe. * return value ... what rtsp_recv_header returned */ static int real_rtsp_set_parameter(struct stream_t *stream,char *subscribe) { struct rtsp_ctrl_t *rtsp_ctrl = stream->stream_ctrl->rtsp_ctrl; struct rtsp_header_t *rtsp_hdr = NULL; int ret = 0; rtsp_hdr = new_rtsp_header_with_standard_fields(rtsp_ctrl); rtsp_set_field(rtsp_hdr,subscribe); if(stream->dlopts->bandwidth) { /* when user specified bandwidth */ char *buffer = (char *)xmalloc(BUFSIZE_1K); snprintf(buffer,BUFSIZE_1K - 1, "SetDeliveryBandwidth: Bandwidth=%d;BackOff=0",stream->dlopts->bandwidth); rtsp_set_field(rtsp_hdr,buffer); free(buffer); } rtsp_request_set_parameter(rtsp_hdr,rtsp_ctrl->mrl); rtsp_send_request_and_free(stream,rtsp_hdr); ret = rtsp_recv_header_ignore_message(stream); return ret; }
/* * send OPTIONS request, this is used as very first trial to server * we will need rtsp_hdr later, so return that. * return value: -1: failure status_code: success, rtsp_hdr_ret(malloc) */ int real_rtsp_options(struct stream_t *stream,struct rtsp_header_t **rtsp_hdr_ret) { int ret = 0; struct rtsp_ctrl_t *rtsp_ctrl = stream->stream_ctrl->rtsp_ctrl; struct rtsp_header_t *rtsp_hdr = NULL; char *options_uri = NULL; int options_uri_len = 0; /* default is rtsp-real (becasue OPTIONS req is supported) */ rtsp_hdr = new_rtsp_header_with_standard_fields(rtsp_ctrl); rtsp_set_field(rtsp_hdr,real_useragent); rtsp_set_field(rtsp_hdr,"GUID: 00000000-0000-0000-0000-000000000000"); rtsp_set_field(rtsp_hdr,real_clientid); rtsp_set_field(rtsp_hdr,"Pragma: initiate-session"); rtsp_set_field(rtsp_hdr,"RegionData: 0"); rtsp_set_field(rtsp_hdr,real_clientchallenge); rtsp_set_field(rtsp_hdr,real_companyid); rtsp_set_field(rtsp_hdr,real_playerstarttime); options_uri_len = strlen(stream->serverinfo->host) + 20; options_uri = (char *)xmalloc(options_uri_len); snprintf(options_uri,options_uri_len,"rtsp://%s:%i", stream->serverinfo->host,stream->serverinfo->port); rtsp_request_options(rtsp_hdr,options_uri); rtsp_send_request_and_free(stream,rtsp_hdr); rtsp_hdr = new_rtsp_header(); ret = rtsp_recv_header(stream,rtsp_hdr); if(ret < 0) { goto failed; } *rtsp_hdr_ret = rtsp_hdr; if(options_uri) free(options_uri); return ret; failed: if(rtsp_hdr) free_rtsp_header(rtsp_hdr); if(options_uri) free(options_uri); *rtsp_hdr_ret = NULL; return -1; }
/* * send SETUP requst. 2 setups for multiple streams. * return value ... what rtsp_recv_header returned */ static int real_rtsp_setup(struct stream_t *stream,struct rmff_header_t *rmff_header) { struct rtsp_ctrl_t *rtsp_ctrl = stream->stream_ctrl->rtsp_ctrl; struct rtsp_header_t *rtsp_hdr = NULL; char *buffer = (char *)xmalloc(BUFSIZE_1K); int ret = 0; char challenge2[64]; char checksum[34]; rtsp_hdr = new_rtsp_header_with_standard_fields(rtsp_ctrl); real_calc_challenge2_and_checksum(rtsp_ctrl->challenge,challenge2,checksum); snprintf(buffer,BUFSIZE_1K - 1,"RealChallenge2: %s, sd=%s", challenge2,checksum); rtsp_set_field(rtsp_hdr,buffer); snprintf(buffer,BUFSIZE_1K - 1,"If-Match: %s",rtsp_ctrl->etag); rtsp_set_field(rtsp_hdr,buffer); rtsp_set_field(rtsp_hdr,"RDTFeatureLevel: 2"); rtsp_set_field(rtsp_hdr,real_transport); snprintf(buffer,BUFSIZE_1K - 1,"%s/streamid=0",rtsp_ctrl->mrl); rtsp_request_setup(rtsp_hdr,buffer); rtsp_send_request_and_free(stream,rtsp_hdr); /* receive message for SETUP */ ret = rtsp_recv_header_ignore_message(stream); /* send messages if multiple stream exists (max is 2 streams anyways...) */ if(rmff_header->prop->num_streams > 1) { rtsp_hdr = new_rtsp_header_with_standard_fields(rtsp_ctrl); rtsp_set_field(rtsp_hdr,real_transport); snprintf(buffer,BUFSIZE_1K - 1,"If-Match: %s",rtsp_ctrl->etag); rtsp_set_field(rtsp_hdr,buffer); snprintf(buffer,BUFSIZE_1K - 1,"%s/streamid=1",rtsp_ctrl->mrl); rtsp_request_setup(rtsp_hdr,buffer); rtsp_send_request_and_free(stream,rtsp_hdr); /* receive message for SETUP */ ret = rtsp_recv_header_ignore_message(stream); } free(buffer); return ret; }
/* * set Range: field to rtsp_hdr. */ void rtsp_set_range_field(struct rtsp_header_t *rtsp_hdr,const char *rangestr) { char *buffer = xmalloc(BUFSIZE_1K); /* range specified */ if(rangestr) { char *reason = NULL; if(rtsp_npt_range_valid(rangestr,&reason)) { /* valid */ snprintf(buffer,BUFSIZE_1K - 1,"Range: npt=%s",rangestr); } else { char *npt_range_str = NULL; npt_range_str = rtsp_range_to_npt_range(rangestr,&reason); if(npt_range_str) { /* acceptable */ snprintf(buffer,BUFSIZE_1K - 1,"Range: npt=%s",npt_range_str); display(MSDL_VER, "send \"%s\" for Range\n", npt_range_str); free(npt_range_str); } else { /* really inacceptable */ strncpy(buffer,"Range: npt=0.000-",BUFSIZE_1K - 1); display(MSDL_ERR, "option \"-r %s\" is invalid as a rtsp range request\n" ": %s\n" "send \"%s\" instead\n", rangestr,reason,buffer); } } } else { /* default range, from beginning */ strncpy(buffer,"Range: npt=0.000-",BUFSIZE_1K - 1); } rtsp_set_field(rtsp_hdr,buffer); free(buffer); }
/* * rtsp_response_parse : parse rtsp response header * make rtsp_hdr from raw data in rtsp_hdr->buffer * * return value : -1 ... ERROR * 0 ... already parsed. * 1 ... success. */ int rtsp_response_parse(struct rtsp_header_t *rtsp_hdr) { char *hdr_ptr, *ptr; char *field = NULL; int pos_hdr_sep, hdr_sep_len; size_t len; if(rtsp_hdr == NULL) return -1; if(rtsp_hdr->is_parsed) return 0; /* already parsed. */ /* get first word */ hdr_ptr = strstr(rtsp_hdr->buffer, " "); if(hdr_ptr == NULL) { display(MSDL_ERR,"Malformed answer : No %20 separator\n"); return -1; } len = hdr_ptr - rtsp_hdr->buffer; rtsp_hdr->protocol = xmalloc(len + 1); strncpy(rtsp_hdr->protocol,rtsp_hdr->buffer,len); rtsp_hdr->protocol[len] = '\0'; /* 1. set method */ rtsp_hdr->method = strdup(rtsp_hdr->protocol); if(!strncasecmp(rtsp_hdr->protocol,"RTSP",4)) { /* RTSP message*/ /* 2. get RTSP status code */ if(sscanf(++hdr_ptr,"%d",&(rtsp_hdr->status_code)) != 1) { display(MSDL_ERR,"Malformed answer : No rtsp status code!!\n"); return -1; } hdr_ptr += 4; /* "[0-9][0-9][0-9] " */ /* 3. get reason phrase */ ptr = strstr(hdr_ptr,"\n"); if(ptr == NULL) { display(MSDL_ERR,"Malformed answer : unable to get reason_phrase\n"); return -1; } len = ptr - hdr_ptr; // len of reason phrase. rtsp_hdr->reason_phrase = xmalloc(len + 1); strncpy(rtsp_hdr->reason_phrase,hdr_ptr,len); if(rtsp_hdr->reason_phrase[len - 1] == '\r') { // M$ style newline! len --; // we don't need \r. } rtsp_hdr->reason_phrase[len] = '\0'; // terminate string } else if(!strncasecmp(rtsp_hdr->protocol,"SET_PARAMETER",strlen("SET_PARAMETER"))) { rtsp_hdr->status_code = RTSP_STATUS_SET_PARAMETER; } else if(!strncasecmp(rtsp_hdr->protocol,"OPTIONS",strlen("OPTIONS"))) { rtsp_hdr->status_code = RTSP_STATUS_OPTIONS; } else if(!strncasecmp(rtsp_hdr->protocol,"ANNOUNCE",strlen("ANNOUNCE"))) { rtsp_hdr->status_code = RTSP_STATUS_ANNOUNCE; } else { /* NOT RTSP ---> abort */ display(MSDL_ERR,"Protocol not RTSP, server says '%s'",rtsp_hdr->protocol); return -1; } hdr_sep_len = 4; /* header separator length */ ptr = strstr(rtsp_hdr->buffer,"\r\n\r\n"); if(!ptr) { ptr = strstr(rtsp_hdr->buffer,"\n\n"); if(!ptr) { display(MSDL_ERR,"Header may be incomplete!\n"); return -1; } hdr_sep_len = 2; } pos_hdr_sep = ptr - rtsp_hdr->buffer; /* points to first line after method line. */ hdr_ptr = strstr(rtsp_hdr->buffer,"\n") + 1; /* parse of first line [DONE] */ /* get all fields. they are separated by '\n' */ do { ptr = hdr_ptr; while(*ptr != '\r' && *ptr != '\n') ptr++; len = ptr - hdr_ptr; if(len == 0) break; /* met \n\n --> end of header! */ field = (char *)xrealloc(field,len + 1); // +1 for '\0' strncpy(field,hdr_ptr,len); field[len] = '\0'; rtsp_set_field(rtsp_hdr,field); hdr_ptr = ptr + ((*ptr == '\r') ? 2 : 1); /* points to next line */ } while(hdr_ptr < (rtsp_hdr->buffer + pos_hdr_sep)); if(field) { free(field); } /* rtsp_hdr->buffer_len is length of header total data received */ if(pos_hdr_sep + hdr_sep_len < rtsp_hdr->buffer_len) { rtsp_hdr->body = rtsp_hdr->buffer + (pos_hdr_sep + hdr_sep_len); rtsp_hdr->body_len = rtsp_hdr->buffer_len - (pos_hdr_sep + hdr_sep_len); } /* mark parsed */ rtsp_hdr->is_parsed = 1; return 1; }
/* * send PLAY request * return value ... what rtsp_recv_header returned */ static int real_rtsp_play(struct stream_t *stream) { struct rtsp_ctrl_t *rtsp_ctrl = stream->stream_ctrl->rtsp_ctrl; struct rtsp_header_t *rtsp_hdr = NULL; char *field = NULL; int ret = 0; /* * Sending part */ rtsp_hdr = new_rtsp_header_with_standard_fields(rtsp_ctrl); if(stream->dlopts->resume_download) { real_prepare_resuming(stream); } if(stream->dlopts->bandwidth) { /* when user specified bandwidth */ char *buffer = (char *)xmalloc(BUFSIZE_1K); snprintf(buffer,BUFSIZE_1K - 1, "Bandwidth: %d",stream->dlopts->bandwidth); rtsp_set_field(rtsp_hdr,buffer); free(buffer); } rtsp_set_range_field(rtsp_hdr,stream->dlopts->range); rtsp_set_speed_field(rtsp_hdr,stream->dlopts->speed); rtsp_request_play(rtsp_hdr,rtsp_ctrl->mrl); rtsp_send_request_and_free(stream,rtsp_hdr); /* * Receiving part */ /* receive message for PLAY request */ rtsp_hdr = new_rtsp_header(); ret = rtsp_recv_header(stream,rtsp_hdr); if(!is_rtsp_response_ok(rtsp_hdr->status_code)) { display(MSDL_ERR,"PLAY request returned: %d (%s)\n", rtsp_hdr->status_code,rtsp_hdr->reason_phrase); field = rtsp_get_field(rtsp_hdr,"Alert"); if(field) { while(*field == ' ') field++; display(MSDL_ERR,"message from server --> %s\n",field); } free_rtsp_header(rtsp_hdr); goto failed; } /* display real speed (might differ from user requested) */ if((field = rtsp_get_field(rtsp_hdr,"Speed")) != NULL) { if(stream->dlopts->speed) { while(*field == ' ') field++; display(MSDL_NOR,"Speed: %s\n",field); } } if((field = rtsp_get_field(rtsp_hdr,"Range")) != NULL) { if(stream->dlopts->range) { while(*field == ' ') field++; display(MSDL_NOR,"Range: %s\n",field); } } /* skip content-length bytes from network */ rtsp_ignore_data_after_header(stream,rtsp_hdr); free_rtsp_header(rtsp_hdr); return ret; failed: return -1; }
/* * send DESCRIBE request * return value: -1: failure status_code: success */ static int real_rtsp_describe(struct stream_t *stream,char **description_ret) { struct stream_ctrl_t *stream_ctrl = stream->stream_ctrl; struct rtsp_ctrl_t *rtsp_ctrl = stream_ctrl->rtsp_ctrl; struct rtsp_header_t *rtsp_hdr = NULL; char *description = NULL; char *buffer = (char *)xmalloc(BUFSIZE_1K); char *field = NULL; int len = 0; int ret = 0; rtsp_hdr = new_rtsp_header_with_standard_fields(rtsp_ctrl); rtsp_set_field(rtsp_hdr, "Accept: application/sdp"); rtsp_set_field(rtsp_hdr, real_useragent); rtsp_set_field(rtsp_hdr, "Require: com.real.retain-entity-for-setup"); snprintf(buffer,BUFSIZE_1K - 1,"Bandwidth: %u",stream_ctrl->bandwidth); rtsp_set_field(rtsp_hdr,buffer); rtsp_set_field(rtsp_hdr, "Language: en-US"); rtsp_set_field(rtsp_hdr, "RegionData: 0"); rtsp_set_field(rtsp_hdr, real_clientid); rtsp_set_field(rtsp_hdr, "GUID: 00000000-0000-0000-0000-000000000000"); rtsp_set_field(rtsp_hdr, "SupportsMaximumASMBandwidth: 1"); rtsp_request_describe(rtsp_hdr,rtsp_ctrl->mrl); rtsp_send_request_and_free(stream,rtsp_hdr); rtsp_hdr = new_rtsp_header(); ret = rtsp_recv_header(stream,rtsp_hdr); if(ret < 0) { free_rtsp_header(rtsp_hdr); goto failed; } /* NOT OK */ if(!is_rtsp_response_ok(rtsp_hdr->status_code)) { display(MSDL_ERR,"DESCRIBE request returned: %d (%s)\n", rtsp_hdr->status_code,rtsp_hdr->reason_phrase); field = rtsp_get_field(rtsp_hdr,"Alert"); if(field) { while(*field == ' ') field++; display(MSDL_ERR,"message from server --> %s\n",field); } free_rtsp_header(rtsp_hdr); goto failed; } len = 0; /* Content-length must be present */ if((field = rtsp_get_field(rtsp_hdr,"Content-length")) != NULL) { while(*field == ' ') field++; len = atoi(field); } else { /* no Content-length */ display(MSDL_ERR,"warning: No Content-length in fields!!\n"); } if((field = rtsp_get_field(rtsp_hdr,"ETag")) != NULL) { while(*field == ' ') field++; rtsp_ctrl->etag = strdup(field); } else { display(MSDL_ERR,"warning: No ETag!!\n"); rtsp_ctrl->etag = NULL; } free_rtsp_header(rtsp_hdr); /* copy description (sdp) */ description = (char *)xmalloc(len + 1); if(read_data(stream,(uint8_t *)description,len) < 0) { goto failed; } description[len] = '\0'; display(MSDL_DBG,"==================\n%s\n=(%d bytes)========\n", description,(int)strlen(description)); if(buffer) free(buffer); *description_ret = description; return ret; failed: if(description) free(description); if(buffer) free(buffer); *description_ret = NULL; return -1; }