📄 rtsp.c
字号:
"port-audio=%i}", ip, i_port_video, i_port_audio ); } vod_MediaControl( p_vod, p_media, psz_session, VOD_MEDIA_PLAY, psz_output ); free( psz_output ); break; } case HTTPD_MSG_DESCRIBE: { char *psz_sdp = SDPGenerate( p_media, cl ); if( psz_sdp != NULL ) { answer->i_status = 200; answer->psz_status = strdup( "OK" ); httpd_MsgAdd( answer, "Content-type", "%s", "application/sdp" ); answer->p_body = (uint8_t *)psz_sdp; answer->i_body = strlen( psz_sdp ); } else { answer->i_status = 500; answer->psz_status = strdup( "Internal server error" ); answer->p_body = NULL; answer->i_body = 0; } break; } case HTTPD_MSG_PAUSE: psz_session = httpd_MsgGet( query, "Session" ); msg_Dbg( p_vod, "HTTPD_MSG_PAUSE for session: %s", psz_session ); p_rtsp = RtspClientGet( p_media, psz_session ); if( !p_rtsp ) break; vod_MediaControl( p_vod, p_media, psz_session, VOD_MEDIA_PAUSE ); p_rtsp->b_paused = VLC_TRUE; answer->i_status = 200; answer->psz_status = strdup( "OK" ); answer->i_body = 0; answer->p_body = NULL; break; case HTTPD_MSG_TEARDOWN: /* for now only multicast so easy again */ answer->i_status = 200; answer->psz_status = strdup( "OK" ); answer->i_body = 0; answer->p_body = NULL; psz_session = httpd_MsgGet( query, "Session" ); msg_Dbg( p_vod, "HTTPD_MSG_TEARDOWN for session: %s", psz_session); p_rtsp = RtspClientGet( p_media, psz_session ); if( !p_rtsp ) break; vod_MediaControl( p_vod, p_media, psz_session, VOD_MEDIA_STOP ); RtspClientDel( p_media, p_rtsp ); break; default: return VLC_EGENERIC; } httpd_MsgAdd( answer, "Server", "VLC Server" ); httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body ); psz_cseq = httpd_MsgGet( query, "Cseq" ); psz_cseq ? i_cseq = atoi( psz_cseq ) : 0; httpd_MsgAdd( answer, "Cseq", "%d", i_cseq ); httpd_MsgAdd( answer, "Cache-Control", "%s", "no-cache" ); if( psz_session ) { httpd_MsgAdd( answer, "Session", "%s;timeout=5", psz_session ); } return VLC_SUCCESS;}static int RtspCallbackES( httpd_callback_sys_t *p_args, httpd_client_t *cl, httpd_message_t *answer, httpd_message_t *query ){ media_es_t *p_es = (media_es_t*)p_args; vod_media_t *p_media = p_es->p_media; vod_t *p_vod = p_media->p_vod; rtsp_client_t *p_rtsp = NULL; char *psz_transport = NULL; char *psz_playnow = NULL; /* support option: x-playNow */ char *psz_session = NULL; char *psz_position = NULL; char *psz_cseq = NULL; int i_cseq = 0; int i; if( answer == NULL || query == NULL ) return VLC_SUCCESS; msg_Dbg( p_vod, "RtspCallback query: type=%d", query->i_type ); answer->i_proto = HTTPD_PROTO_RTSP; answer->i_version = query->i_version; answer->i_type = HTTPD_MSG_ANSWER; answer->i_body = 0; answer->p_body = NULL; switch( query->i_type ) { case HTTPD_MSG_SETUP: psz_playnow = httpd_MsgGet( query, "x-playNow" ); psz_transport = httpd_MsgGet( query, "Transport" ); msg_Dbg( p_vod, "HTTPD_MSG_SETUP: transport=%s", psz_transport ); if( strstr( psz_transport, "unicast" ) && strstr( psz_transport, "client_port=" ) ) { rtsp_client_t *p_rtsp; rtsp_client_es_t *p_rtsp_es; char ip[NI_MAXNUMERICHOST]; int i_port = atoi( strstr( psz_transport, "client_port=" ) + strlen("client_port=") ); if( httpd_ClientIP( cl, ip ) == NULL ) { answer->i_status = 500; answer->psz_status = strdup( "Internal server error" ); answer->i_body = 0; answer->p_body = NULL; break; } msg_Dbg( p_vod, "HTTPD_MSG_SETUP: unicast ip=%s port=%d", ip, i_port ); psz_session = httpd_MsgGet( query, "Session" ); if( !psz_session || !*psz_session ) { if( ( p_vod->p_sys->i_throttle_users > 0 ) && ( p_vod->p_sys->i_connections >= p_vod->p_sys->i_throttle_users ) ) { answer->i_status = 503; answer->psz_status = strdup( "Too many connections" ); answer->i_body = 0; answer->p_body = NULL; break; } asprintf( &psz_session, "%d", rand() ); p_rtsp = RtspClientNew( p_media, psz_session ); } else { p_rtsp = RtspClientGet( p_media, psz_session ); if( !p_rtsp ) { answer->i_status = 454; answer->psz_status = strdup( "Unknown session id" ); answer->i_body = 0; answer->p_body = NULL; break; } } p_rtsp_es = malloc( sizeof(rtsp_client_es_t) ); p_rtsp_es->i_port = i_port; p_rtsp_es->psz_ip = strdup( ip ); p_rtsp_es->p_media_es = p_es; TAB_APPEND( p_rtsp->i_es, p_rtsp->es, p_rtsp_es ); answer->i_status = 200; answer->psz_status = strdup( "OK" ); answer->i_body = 0; answer->p_body = NULL; if( p_media->b_raw ) { if( strstr( psz_transport, "MP2T/H2221/UDP" ) ) { httpd_MsgAdd( answer, "Transport", "MP2T/H2221/UDP;client_port=%d-%d", i_port, i_port + 1 ); } else if( strstr( psz_transport, "RAW/RAW/UDP" ) ) { httpd_MsgAdd( answer, "Transport", "RAW/RAW/UDP;client_port=%d-%d", i_port, i_port + 1 ); } } else { httpd_MsgAdd( answer, "Transport", "RTP/AVP/UDP;client_port=%d-%d", i_port, i_port + 1 ); } } else /* TODO strstr( psz_transport, "interleaved" ) ) */ { answer->i_status = 461; answer->psz_status = strdup( "Unsupported Transport" ); answer->i_body = 0; answer->p_body = NULL; } /* Intentional fall-through on x-playNow option in RTSP request */ if( !psz_playnow ) break; case HTTPD_MSG_PLAY: /* This is kind of a kludge. Should we only support Aggregate * Operations ? */ psz_session = httpd_MsgGet( query, "Session" ); msg_Dbg( p_vod, "HTTPD_MSG_PLAY for session: %s", psz_session ); p_rtsp = RtspClientGet( p_media, psz_session ); psz_position = httpd_MsgGet( query, "Range" ); if( psz_position ) psz_position = strstr( psz_position, "npt=" ); if( psz_position ) { double f_pos; char *end; msg_Dbg( p_vod, "seeking request: %s", psz_position ); psz_position += 4; /* FIXME: npt= is not necessarily formatted as a float */ f_pos = us_strtod( psz_position, &end ); if( end > psz_position ) { f_pos /= ((double)(p_media->i_length))/1000 /1000 / 100; vod_MediaControl( p_vod, p_media, psz_session, VOD_MEDIA_SEEK, f_pos ); } } if( !psz_playnow ) { answer->i_status = 200; answer->psz_status = strdup( "OK" ); answer->i_body = 0; answer->p_body = NULL; } break; case HTTPD_MSG_TEARDOWN: answer->i_status = 200; answer->psz_status = strdup( "OK" ); answer->i_body = 0; answer->p_body = NULL; psz_session = httpd_MsgGet( query, "Session" ); msg_Dbg( p_vod, "HTTPD_MSG_TEARDOWN for session: %s", psz_session); p_rtsp = RtspClientGet( p_media, psz_session ); if( !p_rtsp ) break; for( i = 0; i < p_rtsp->i_es; i++ ) { if( p_rtsp->es[i]->p_media_es == p_es ) { if( p_rtsp->es[i]->psz_ip ) free( p_rtsp->es[i]->psz_ip ); TAB_REMOVE( p_rtsp->i_es, p_rtsp->es, p_rtsp->es[i] ); break; } } if( !p_rtsp->i_es ) { vod_MediaControl( p_vod, p_media, psz_session, VOD_MEDIA_STOP ); RtspClientDel( p_media, p_rtsp ); } break; case HTTPD_MSG_PAUSE: /* This is kind of a kludge. Should we only support Aggregate * Operations ? */ psz_session = httpd_MsgGet( query, "Session" ); msg_Dbg( p_vod, "HTTPD_MSG_PAUSE for session: %s", psz_session ); p_rtsp = RtspClientGet( p_media, psz_session ); if( !p_rtsp ) break; vod_MediaControl( p_vod, p_media, psz_session, VOD_MEDIA_PAUSE ); p_rtsp->b_paused = VLC_TRUE; answer->i_status = 200; answer->psz_status = strdup( "OK" ); answer->i_body = 0; answer->p_body = NULL; break; default: return VLC_EGENERIC; break; } httpd_MsgAdd( answer, "Server", "VLC Server" ); httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body ); psz_cseq = httpd_MsgGet( query, "Cseq" ); if (psz_cseq) i_cseq = atoi( psz_cseq ); else i_cseq = 0; httpd_MsgAdd( answer, "Cseq", "%d", i_cseq ); httpd_MsgAdd( answer, "Cache-Control", "%s", "no-cache" ); if( psz_session ) httpd_MsgAdd( answer, "Session", "%s"/*;timeout=5*/, psz_session ); return VLC_SUCCESS;}/***************************************************************************** * SDPGenerate: TODO * FIXME: need to be moved to a common place ? *****************************************************************************/static char *SDPGenerate( const vod_media_t *p_media, httpd_client_t *cl ){ int i, i_size; char *p, *psz_sdp, ip[NI_MAXNUMERICHOST], ipv; const char *psz_control; if( httpd_ServerIP( cl, ip ) == NULL ) return NULL; p = strchr( ip, '%' ); if( p != NULL ) *p = '\0'; /* remove scope if present */ ipv = ( strchr( ip, ':' ) != NULL ) ? '6' : '4'; /* Calculate size */ i_size = sizeof( "v=0\r\n" ) + sizeof( "o=- * * IN IP4 \r\n" ) + 10 + NI_MAXNUMERICHOST + sizeof( "s=*\r\n" ) + strlen( p_media->psz_session_name ) + sizeof( "i=*\r\n" ) + strlen( p_media->psz_session_description ) + sizeof( "u=*\r\n" ) + strlen( p_media->psz_session_url ) + sizeof( "e=*\r\n" ) + strlen( p_media->psz_session_email ) + sizeof( "c=IN IP4 0.0.0.0\r\n" ) + 20 + 10 + sizeof( "t=0 0\r\n" ) + /* FIXME */ sizeof( "a=tool:"PACKAGE_STRING"\r\n" ) + sizeof( "a=range:npt=0-1000000000.000\r\n" ); psz_control = (ipv == '6') ? p_media->psz_rtsp_control_v6 : p_media->psz_rtsp_control_v4; for( i = 0; i < p_media->i_es; i++ ) { media_es_t *p_es = p_media->es[i]; i_size += sizeof( "m=**d*o * RTP/AVP *\r\n" ) + 19; if( p_es->psz_rtpmap ) { i_size += sizeof( "a=rtpmap:* *\r\n" ) + strlen( p_es->psz_rtpmap ) + 9; } if( p_es->psz_fmtp ) { i_size += sizeof( "a=fmtp:* *\r\n" ) + strlen( p_es->psz_fmtp ) + 9; } } i_size += (strlen( psz_control ) + strlen( ip ) + 9) * p_media->i_es; p = psz_sdp = malloc( i_size ); p += sprintf( p, "v=0\r\n" ); p += sprintf( p, "o=- "I64Fd" %d IN IP%c %s\r\n", p_media->i_sdp_id, p_media->i_sdp_version, ipv, ip ); if( *p_media->psz_session_name ) p += sprintf( p, "s=%s\r\n", p_media->psz_session_name ); if( *p_media->psz_session_description ) p += sprintf( p, "i=%s\r\n", p_media->psz_session_description ); if( *p_media->psz_session_url ) p += sprintf( p, "u=%s\r\n", p_media->psz_session_url ); if( *p_media->psz_session_email ) p += sprintf( p, "e=%s\r\n", p_media->psz_session_email ); p += sprintf( p, "c=IN IP%c %s\r\n", ipv, ipv == '6' ? "::" : "0.0.0.0" ); p += sprintf( p, "t=0 0\r\n" ); /* FIXME */ p += sprintf( p, "a=tool:"PACKAGE_STRING"\r\n" ); if( p_media->i_length > 0 ) { lldiv_t d = lldiv( p_media->i_length / 1000, 1000 ); p += sprintf( p, "a=range:npt=0-"I64Fd".%03u\r\n", d.quot, (unsigned)d.rem ); } for( i = 0; i < p_media->i_es; i++ ) { media_es_t *p_es = p_media->es[i]; if( p_es->fmt.i_cat == AUDIO_ES ) { p += sprintf( p, "m=audio %d RTP/AVP %d\r\n", p_es->i_port, p_es->i_payload_type ); } else if( p_es->fmt.i_cat == VIDEO_ES ) { p += sprintf( p, "m=video %d RTP/AVP %d\r\n", p_es->i_port, p_es->i_payload_type ); } else { continue; } if( p_es->psz_rtpmap ) { p += sprintf( p, "a=rtpmap:%d %s\r\n", p_es->i_payload_type, p_es->psz_rtpmap ); } if( p_es->psz_fmtp ) { p += sprintf( p, "a=fmtp:%d %s\r\n", p_es->i_payload_type, p_es->psz_fmtp ); } p += sprintf( p, psz_control, ip, i ); } return psz_sdp;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -