/* * process rtsp header coming among rtsp real media packet * * return value: status code ... normal proceed case * 0 ... EOF packet (not possible) * -1 ... error */ static int real_process_header(struct stream_t *stream) { 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; int status_code = 0; int bodylen = 0; uint8_t *body = NULL; char *field = NULL; int got_cseq = 0; rtsp_hdr = new_rtsp_header(); status_code = rtsp_recv_header(stream,rtsp_hdr); if(status_code < 0) { goto failed; } /* get content length data */ if((field = rtsp_get_field(rtsp_hdr,"Content-length")) != NULL) { while(*field == ' ') field++; bodylen = atoi(field); body = (uint8_t *)xmalloc(bodylen); bodylen = read_data(stream,body,bodylen); } /* get Cseq, for sending OK back */ if((field = rtsp_get_field(rtsp_hdr,"Cseq")) != NULL) { while(*field == ' ') field++; got_cseq = atoi(field); } if(!strncmp(rtsp_hdr->protocol,"RTSP",4)) { /* RTSP/1.0 200 OK , ignore this */ /* somehow RTSP message reply can be found among rdt messages. send nothing. */ if(!got_cseq == rtsp_ctrl->cseq - 1) { display(MSDL_DBG,"CSeq mismatch: expected %d, got %d", rtsp_ctrl->cseq - 1,got_cseq); } /* DO NOT send RTSP OK */ } else { /* not RTSP, whatever it is, send OK would be just fine */ rtsp_200ok(stream,got_cseq,rtsp_ctrl->session); } if(rtsp_hdr) free_rtsp_header(rtsp_hdr); if(body) free(body); return status_code; failed: if(body) free(body); if(rtsp_hdr) free_rtsp_header(rtsp_hdr); return -1; }
/* * find Content-Length field in rtsp_hdr, and read (and trash) following data * return value: bytes received and discarded * -1 ... failed */ int rtsp_ignore_data_after_header(struct stream_t *stream,struct rtsp_header_t *rtsp_hdr) { char *field = NULL; int body_left = 0; if((field = rtsp_get_field(rtsp_hdr,"Content-Length")) != NULL) { uint8_t *ignorebuf = NULL; while(*field == ' ') field++; body_left = atoi(field); if(body_left) { ignorebuf = xmalloc(body_left); if(read_data(stream,ignorebuf,body_left) <= 0) { free(ignorebuf); goto failed; } free(ignorebuf); } } return body_left; failed: return -1; }
/* * parse rtsp response * return value: 1 ... success * -1 ... parse error */ int rtsp_parse_response(struct rtsp_ctrl_t *rtsp_ctrl, struct rtsp_header_t *rtsp_hdr) { char *field; int answer_cseq; if((field = rtsp_get_field(rtsp_hdr,"Alert")) != NULL) { display(MSDL_ERR,"*** Alert ***\n%s\n",field); } if((field = rtsp_get_field(rtsp_hdr,"Cseq")) != NULL) { sscanf(field,"%d",&answer_cseq); if(answer_cseq != rtsp_ctrl->cseq - 1) { /* -1 because cseq is incremented in build_request */ display(MSDL_DBG,"warning: Cseq mismatch, got %u, expected %u\n", answer_cseq,rtsp_ctrl->cseq - 1); } } if((field = rtsp_get_field(rtsp_hdr,"Server")) != NULL) { while(*field == ' ') field++; if(rtsp_ctrl->server) { /* already has it */ if(strcmp(rtsp_ctrl->server,field)) { /* different server */ display(MSDL_DBG,"warning: got different server\n" " old: %s\n" " new: %s\n",rtsp_ctrl->server,field); free(rtsp_ctrl->server); rtsp_ctrl->server = strdup(field); } } else { /* not set yet */ rtsp_ctrl->server = strdup(field); } } if((field = rtsp_get_field(rtsp_hdr,"Session")) != NULL) { while(*field == ' ') field++; if(rtsp_ctrl->session) { if(strcmp(rtsp_ctrl->session,field)) { /* different session */ display(MSDL_DBG,"warning: got different session\n" " old: [%s]\n" " new: [%s]\n",rtsp_ctrl->session,field); free(rtsp_ctrl->session); rtsp_ctrl->session = strdup(field); } } else { rtsp_ctrl->session = strdup(field); } } /* real only, ignore in other protocol... */ if((field = rtsp_get_field(rtsp_hdr,"RealChallenge1")) != NULL) { while(*field == ' ') field++; if(rtsp_ctrl->challenge) { if(strcmp(rtsp_ctrl->challenge,field)) { /* different server */ display(MSDL_DBG,"warning: got different client challenge\n" " old: %s\n" " new: %s\n",rtsp_ctrl->challenge,field); free(rtsp_ctrl->challenge); rtsp_ctrl->challenge = strdup(field); } } else { rtsp_ctrl->challenge = strdup(field); } } return 1; }
/* * start rtsp streaming. * * return value : negative or 0 ... error * 1 ... success */ int rtsp_streaming_start(struct stream_t *stream) { struct rtsp_header_t *rtsp_answer = NULL; int header_size; char *answer = NULL; /* temporary pointer */ char *server = NULL; char *redirected = NULL; stream->stream_ctrl->status = STREAMING_HANDSHAKING; if(stream->dlopts->dl_protocol == RTSP_WMS) { server = strdup("WMServer"); } else { /* still don't know which rtsp protocol, or REAL specified (need challenge) */ stream->netsock->sock = rtsp_connect(stream); if(stream->netsock->sock < 0) { display(MSDL_ERR,"cannot establish rtsp connection\n"); goto failed; } real_rtsp_options(stream,&rtsp_answer); if(rtsp_answer == NULL) { display(MSDL_ERR,"rtsp connection failed\n"); goto failed; } /* get server name */ if((answer = rtsp_get_field(rtsp_answer,"Server")) != NULL) { while(*answer == ' ') answer++; /* skip space */ server = strdup(answer); } else { if((rtsp_get_field(rtsp_answer,"RealChallenge1")) != NULL) { server = strdup("Real"); } else { server = strdup("Unknown Server"); } } } /* real or helix server (supported) */ if(strstr(server,"Real") || strstr(server,"Helix")) { /* real-rtsp */ struct rmff_header_t *rmff_header = NULL; int ret = 0; /* connection is already setup, by real_rtsp_options called above */ /* this is the function to do almost all setup process */ ret = real_setup_and_get_header(stream,&rmff_header); if((!rmff_header) || (ret <= 0)) { /* NOT OK */ if((answer = rtsp_get_field(rtsp_answer,"Location")) != NULL) { /* redirected */ while(*answer == ' ') answer++; redirected = strdup(answer); display(MSDL_NOR,"redirected to %s\n",redirected); /* TODO redirection support and I have never seen example... */ goto failed; } else { display(MSDL_ERR,"real/helix connection not established\n"); goto failed; } } /* OK, connection established */ header_size = rmff_dump_header(rmff_header, stream->stream_ctrl->write_buffer); stream->stream_ctrl->write_data_len = header_size; display(MSDL_VER,"rmff_header_size = %d\n",header_size); /* All Green */ stream->stream_ctrl->rtsp_ctrl->rmff_header = rmff_header; stream->stream_ctrl->file_size = rmff_header->data->size; stream->stream_ctrl->total_packets = rmff_header->data->num_packets; stream->stream_ctrl->rtsp_ctrl->get_media_packet = real_rdt_get_media_packet; /* to distinguish which rtsp */ stream->stream_ctrl->rtsp_ctrl->rtsp_protocol = RTSP_REAL_PROTOCOL; /* for msdl function to know protocol */ stream->stream_ctrl->protocol = RTSP_REAL; } else if(strstr(server,"WMServer")) { /* try rtsp-wms */ struct asf_headerinfo_t *asf_headerinfo = NULL; int ret = 0; /* * Clean up first. */ if(stream->netsock->sock) { close(stream->netsock->sock); } /* free rtsp_ctrl_t */ free_rtsp_ctrl_t(stream->stream_ctrl->rtsp_ctrl); stream->stream_ctrl->rtsp_ctrl = new_rtsp_ctrl_t(); /* free serverinfo_t */ free_serverinfo_t(stream->serverinfo); stream->serverinfo = new_serverinfo_t(); /* DO NOT MESS WITH * stream_t->netsock, * stream_t->url, * stream_t->dlopts, * stream_t->resumeinfo, * these are still valid (or just nothing (resumeinfo)) */ /* re-establish rtsp connection for WMServer */ stream->netsock->sock = rtsp_connect(stream); if(stream->netsock->sock < 0) { /* couldn't connect for some reason. (rtsp port probably closed) */ display(MSDL_ERR,"rtsp-wms connection not established\n"); display(MSDL_ERR,"server probably does not accept rtsp\n" "retry using mmst protocol\n"); stream->stream_ctrl->status = STREAMING_OTHER_PROTOCOL; stream->stream_ctrl->retry_protocol = MMST; goto failed; } ret = wmserver_setup_and_get_header(stream,&asf_headerinfo); if(ret == 0) { /* retry */ /* probably non-wmserver */ display(MSDL_ERR,"server probably does not accept rtsp\n" "retry using mmst protocol\n",server); stream->stream_ctrl->status = STREAMING_OTHER_PROTOCOL; stream->stream_ctrl->retry_protocol = MMST; goto failed; } else if((!asf_headerinfo) || (ret < 0)) { /* no retry */ display(MSDL_ERR,"rtsp setup failed\n"); goto failed; } /* All Green */ stream->stream_ctrl->rtsp_ctrl->asf_headerinfo = asf_headerinfo; /* set infomation */ stream->stream_ctrl->packet_length = asf_headerinfo->fileh->max_packet_size; stream->stream_ctrl->file_size = asf_headerinfo->fileh->file_size; stream->stream_ctrl->total_packets = asf_headerinfo->fileh->num_packets; stream->stream_ctrl->rtsp_ctrl->get_media_packet = wmserver_rtp_get_media_packet; /* distinguish which rtsp */ stream->stream_ctrl->rtsp_ctrl->rtsp_protocol = RTSP_WMS_PROTOCOL; /* for msdl function to know protocol */ stream->stream_ctrl->protocol = RTSP_WMS; } else { /* unsupported servers */ display(MSDL_ERR,"server type [%s] not supported\n",server); stream->stream_ctrl->rtsp_ctrl->rtsp_protocol = RTSP_UNKNOWN_PROTOCOL; goto failed; } if(server) free(server); if(rtsp_answer) free_rtsp_header(rtsp_answer); if(redirected) free(redirected); stream->stream_ctrl->status = STREAMING_DOWNLOADING; return 1; /* success */ failed: if(server) free(server); if(rtsp_answer) free_rtsp_header(rtsp_answer); if(redirected) free(redirected); 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; }