📄 rtsp.c
字号:
/*********************************************************************** * rtsp.c: for downloading via rtsp (Real Time Streaming Protocol) *********************************************************************** * Copyright (C) 2007 metro <me_t_ro@yahoo.com> * * This file is part of msdl, media stream downloader * * This file is based on rtsp implementation of * mplayer and xine, and includes code from both projects. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. * ***********************************************************************//* * Copyright notice of MPlayer project * which some part of msdl is based on. * (from MPlayer-1.0rc2/stream/realrtsp/.c) *//* * This file was ported to MPlayer from xine CVS rtsp.c,v 1.9 2003/04/10 02:30:48 *//* * Copyright (C) 2000-2002 the xine project * * This file is part of xine, a free video player. * * xine is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * xine is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * * * a minimalistic implementation of rtsp protocol, * *not* RFC 2326 compilant yet. * * 2006, Benjamin Zores and Vincent Mussard * fixed a lot of RFC compliance issues. *//* * Copyright notice of xine-lib project * which some part of msdl is based on. * (from xine-lib-1.1.14/src/input/librtsp/rtsp.c) *//* * Copyright (C) 2000-2003 the xine project * * This file is part of xine, a free video player. * * xine is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * xine is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * a minimalistic implementation of rtsp protocol, * *not* RFC 2326 compilant yet. */#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <ctype.h>#include <sys/types.h>#include <sys/socket.h>#include "msdl.h"#include "msdllib.h"#include "display.h"#include "network.h"#include "rtsp.h"#include "real.h"#include "rmff.h"#include "asf.h"#include "wmserver.h"const char rtsp_protocol_version[] = "RTSP/1.0";/* defined in real.c */extern const char rtsp_protocol_version[];extern const char real_clientid[];extern const char real_companyid[];extern const char real_useragent[];extern const char real_clientchallenge[];extern const char real_playerstarttime[];extern const char real_transport[];static struct rtsp_ctrl_t *new_rtsp_ctrl_t(void);static void free_rtsp_ctrl_t(struct rtsp_ctrl_t *ctrlt);static int rtsp_recv_header_get(struct stream_t *stream, struct rtsp_header_t *rtsp_hdr);int rtsp_parse_response(struct rtsp_ctrl_t *rtsp_ctrl, struct rtsp_header_t *rtsp_header);/* * read rtsp stream. filles buffer, and buffer size is 'size' * * read cached data from stream->netsock->buffer * check for to see if network access is necessary * get chunk(media packet) from network. * * return value: bytes written to buffer. -1 if error. * 0 if buffering */int rtsp_streaming_read(struct stream_t *stream, uint8_t *buffer, size_t buffer_size){ struct stream_ctrl_t *stream_ctrl = stream->stream_ctrl; size_t pos = 0; // how many bytes are in 'buffer' int ret = 0; if(buffer_size == 0) { display(MSDL_ERR,"buffer_size must be bigger than 0\n"); return -1; } if(stream_ctrl->write_data_len) { /* there are waiting data to be written */ if(buffer_size <= stream_ctrl->write_data_len) { /* buffer_size < stream_ctrl->write_data_len... fill buffer, and return. */ memcpy(buffer,stream_ctrl->write_buffer + stream_ctrl->write_pos, buffer_size); stream_ctrl->write_data_len -= buffer_size; stream_ctrl->write_pos += buffer_size; return buffer_size; } else { /* stream_ctrl->write_data_len < buffer_size, have to read from network. */ memcpy(buffer,stream_ctrl->write_buffer + stream_ctrl->write_pos, stream_ctrl->write_data_len); pos = stream_ctrl->write_data_len; /* and stream_ctrl->write_buffer is empty. */ stream_ctrl->write_data_len = 0; stream_ctrl->write_pos = 0; } } stream_ctrl->write_data_len = 0; stream_ctrl->write_pos = 0; /* still have to get data from network. */ if(stream_ctrl->status != STREAMING_FINISHED) { /* not finished */ ret = stream_ctrl->rtsp_ctrl->get_media_packet(stream,buffer + pos, buffer_size - pos); } if(stream_ctrl->status == STREAMING_RESUME_BUFFERING) { return 0; } else if(ret >= 0) { return (ret + pos); } else { return -1; /* error */ }}/* * find Content-Length field in rtsp_hdr, and read (and trash) following data * return value: bytes received and discarded */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); read_data(stream,ignorebuf,body_left); free(ignorebuf); } } return body_left;}int is_rtsp_response_ok(int status_code){ return ((200 <= status_code) && (status_code <= 299)) ? 1 : 0;}/* * receive rtsp header but ignore its message, etc.. * this function is purely for making code more readable. * * return value: what rtsp_recv_header returned */int rtsp_recv_header_ignore_message(struct stream_t *stream){ struct rtsp_header_t *rtsp_hdr = NULL; int ret = 0; rtsp_hdr = new_rtsp_header(); ret = rtsp_recv_header(stream,rtsp_hdr); /* if content-length field, get all following data */ rtsp_ignore_data_after_header(stream,rtsp_hdr); free_rtsp_header(rtsp_hdr); return ret;}/* * alloc new rtsp_header_t and set standard fields for send. * this function is purely for making code more readable. * * return value: what rtsp_recv_header returned (status code: success -1: failure) */struct rtsp_header_t *new_rtsp_header_with_standard_fields(struct rtsp_ctrl_t *rtsp_ctrl){ struct rtsp_header_t *rtsp_hdr; rtsp_hdr = new_rtsp_header(); rtsp_set_standard_fields(rtsp_ctrl,rtsp_hdr); return rtsp_hdr;} /* * send rtsp_request and free it. * this function is purely for making code more readable. * * return value: what rtsp_send request returned. (status code: success -1: failure) */int rtsp_send_request_and_free(struct stream_t *stream,struct rtsp_header_t *rtsp_hdr){ int ret; ret = rtsp_send_request(stream,rtsp_hdr); free_rtsp_header(rtsp_hdr); return ret;}/* * make range string */char *rtsp_make_range_from_timestamp(uint32_t range_ts){ char *rangestr = (char *)xmalloc(256); snprintf(rangestr,255,"%d.000-",(range_ts / 1000)); return rangestr;}/* * 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 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);}/* * 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 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);}/* * * str: [ npt-hh ":" npt-mm ":" ] npt-ss [ "." *DIGIT ] | [00h][00m][00s] ["." *DIGIT] * * return value: NULL: really inacceptable, such as "0.0a1" * npt-time: rtsp npt valid string (malloc) */char *rtsp_time_to_npt_time(const char *str,char **reason_ret){ enum { SEC = 0, MIN, HOUR, DAY, /* joke */ YEAR, /* joke */ NUM_COLS, }; enum { NOT_SEPARATED = 0, COLON_SEPARATED, CHAR_SEPARATED, }; int i = 0; int j = 0; /* only to slide sep_time */ int len = strlen(str); int sep_time_filled[NUM_COLS] = {0}; uint64_t sep_time[NUM_COLS] = {0}; /* separated time */ int separated_way = NOT_SEPARATED; int met_colon = 0; uint64_t value = 0; int npt_acceptable = 1; char *str_after_dot = "\0"; /* points to empty string by default. includes '.' itself */ char *npt_time = NULL; char *reason = NULL; if(!strcmp(str,"now")) { npt_time = strdup("now"); reason = ""; if(reason_ret) { *reason_ret = reason; } return npt_time; } value = 0; met_colon = 0; npt_acceptable = 1; for(i = 0 ; (i < len) && npt_acceptable ; i++) { if('0' <= str[i] && str[i] <= '9') { value *= 10; value += str[i] - '0'; } else if(str[i] == '.') { char *dot_pos = (char *)str + i; if((i == 0) || (i == len - 1)) { /* dot was at the beginning or end of string */ reason = "\'.\' at the beginning or end of string"; npt_acceptable = 0; break; } i++; for(; i < len ; i++) { if(('0' <= str[i]) && (str[i] <= '9')) { continue; } else { reason = "invalid char after \'.\'"; npt_acceptable = 0; break; } } /* string after dot was valid */ if(npt_acceptable) { str_after_dot = dot_pos; } } else if(str[i] == ':') { /* colon can be a separator */ if(separated_way == CHAR_SEPARATED) { reason = "confusing time separator"; npt_acceptable = 0; break; } separated_way = COLON_SEPARATED; if(met_colon < NUM_COLS - 1) { /* separator = column = 1 */ for(j = NUM_COLS - 1; j ; j--) { sep_time[j] = sep_time[j - 1]; /* slide */ } sep_time[0] = value; met_colon++; value = 0; /* reset value */ continue; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -