00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include <stdlib.h>
00028
00029 #include <vlc/vlc.h>
00030 #include <vlc/input.h>
00031 #include "network.h"
00032
00033 #include <iostream>
00034
00035 #if defined( WIN32 )
00036 # include <winsock2.h>
00037 #endif
00038
00039 #include "BasicUsageEnvironment.hh"
00040 #include "GroupsockHelper.hh"
00041 #include "liveMedia.hh"
00042
00043 extern "C" {
00044 #include "../access/mms/asf.h"
00045 }
00046
00047 #if (LIVEMEDIA_LIBRARY_VERSION_INT < 1089936000)
00048 #define RECLAIM_ENV(env) delete (env)
00049 #else
00050 #define RECLAIM_ENV(env) (env)->reclaim()
00051 #endif
00052
00053 using namespace std;
00054
00055
00056
00057
00058 static int Open ( vlc_object_t * );
00059 static void Close( vlc_object_t * );
00060
00061 #define CACHING_TEXT N_("Caching value (ms)")
00062 #define CACHING_LONGTEXT N_( \
00063 "Allows you to modify the default caching value for RTSP streams. This " \
00064 "value should be set in millisecond units." )
00065
00066 #define KASENNA_TEXT N_( "Kasenna RTSP dialect")
00067 #define KASENNA_LONGTEXT N_( "Kasenna server speak an old and unstandard " \
00068 "dialect of RTSP. When you set this parameter, VLC will try this dialect "\
00069 "for communication. In this mode you cannot talk to normal RTSP servers." )
00070
00071 vlc_module_begin();
00072 set_description( _("RTP/RTSP/SDP demuxer (using Live.com)" ) );
00073 set_capability( "demux2", 50 );
00074 set_shortname( "RTP/RTSP");
00075 set_callbacks( Open, Close );
00076 add_shortcut( "live" );
00077 set_category( CAT_INPUT );
00078 set_subcategory( SUBCAT_INPUT_DEMUX );
00079
00080 add_submodule();
00081 set_description( _("RTSP/RTP access and demux") );
00082 add_shortcut( "rtsp" );
00083 add_shortcut( "sdp" );
00084 set_capability( "access_demux", 0 );
00085 set_callbacks( Open, Close );
00086 add_bool( "rtsp-tcp", 0, NULL,
00087 N_("Use RTP over RTSP (TCP)"),
00088 N_("Use RTP over RTSP (TCP)"), VLC_TRUE );
00089 add_integer( "rtsp-caching", 4 * DEFAULT_PTS_DELAY / 1000, NULL,
00090 CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
00091 add_bool( "rtsp-kasenna", VLC_FALSE, NULL, KASENNA_TEXT,
00092 KASENNA_LONGTEXT, VLC_TRUE );
00093 vlc_module_end();
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107 typedef struct
00108 {
00109 demux_t *p_demux;
00110
00111 vlc_bool_t b_quicktime;
00112 vlc_bool_t b_muxed;
00113 vlc_bool_t b_asf;
00114
00115 es_format_t fmt;
00116 es_out_id_t *p_es;
00117
00118 stream_t *p_out_muxed;
00119
00120 RTPSource *rtpSource;
00121 FramedSource *readSource;
00122 vlc_bool_t b_rtcp_sync;
00123
00124 uint8_t *p_buffer;
00125 unsigned int i_buffer;
00126
00127 char waiting;
00128
00129 mtime_t i_pts;
00130
00131 } live_track_t;
00132
00133 struct demux_sys_t
00134 {
00135 char *p_sdp;
00136 char *psz_path;
00137
00138 MediaSession *ms;
00139 TaskScheduler *scheduler;
00140 UsageEnvironment *env ;
00141 RTSPClient *rtsp;
00142
00143
00144 int i_track;
00145 live_track_t **track;
00146 mtime_t i_pcr;
00147 mtime_t i_pcr_start;
00148 mtime_t i_pcr_previous;
00149 mtime_t i_pcr_repeatdate;
00150 int i_pcr_repeats;
00151
00152
00153 asf_header_t asfh;
00154 stream_t *p_out_asf;
00155
00156
00157 mtime_t i_length;
00158 mtime_t i_start;
00159
00160
00161 vlc_bool_t b_multicast;
00162 vlc_bool_t b_no_data;
00163 int i_no_data_ti;
00164
00165 char event;
00166 };
00167
00168 static int Demux ( demux_t * );
00169 static int Control( demux_t *, int, va_list );
00170
00171 static int ParseASF( demux_t * );
00172
00173 static int RollOverTcp( demux_t * );
00174
00175 static void StreamRead( void *, unsigned int, unsigned int,
00176 struct timeval, unsigned int );
00177 static void StreamClose( void * );
00178 static void TaskInterrupt( void * );
00179
00180 #if LIVEMEDIA_LIBRARY_VERSION_INT >= 1117756800
00181 static unsigned char* parseH264ConfigStr( char const* configStr,
00182 unsigned int& configSize );
00183 #endif
00184
00185
00186
00187
00188 static int Open ( vlc_object_t *p_this )
00189 {
00190 demux_t *p_demux = (demux_t*)p_this;
00191 demux_sys_t *p_sys;
00192
00193 MediaSubsessionIterator *iter;
00194 MediaSubsession *sub;
00195
00196 vlc_bool_t b_rtsp_tcp;
00197 uint8_t *p_peek;
00198
00199 int i_sdp;
00200 int i_sdp_max;
00201 uint8_t *p_sdp;
00202
00203 if( p_demux->s )
00204 {
00205
00206
00207 if( stream_Peek( p_demux->s, &p_peek, 7 ) < 7 ) return VLC_EGENERIC;
00208
00209 if( memcmp( (char*)p_peek, "v=0\r\n", 5 ) &&
00210 memcmp( (char*)p_peek, "v=0\n", 4 ) &&
00211 ( p_peek[0] < 'a' || p_peek[0] > 'z' || p_peek[1] != '=' ) )
00212 {
00213 return VLC_EGENERIC;
00214 }
00215 }
00216 else
00217 {
00218 var_Create( p_demux, "rtsp-caching", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
00219 }
00220
00221 p_demux->pf_demux = Demux;
00222 p_demux->pf_control= Control;
00223 p_demux->p_sys = p_sys = (demux_sys_t*)malloc( sizeof( demux_sys_t ) );
00224 p_sys->p_sdp = NULL;
00225 p_sys->scheduler = NULL;
00226 p_sys->env = NULL;
00227 p_sys->ms = NULL;
00228 p_sys->rtsp = NULL;
00229 p_sys->i_track = 0;
00230 p_sys->track = NULL;
00231 p_sys->i_pcr = 0;
00232 p_sys->i_pcr_start = 0;
00233 p_sys->i_pcr_previous = 0;
00234 p_sys->i_pcr_repeatdate = 0;
00235 p_sys->i_pcr_repeats = 0;
00236 p_sys->i_length = 0;
00237 p_sys->i_start = 0;
00238 p_sys->p_out_asf = NULL;
00239 p_sys->b_no_data = VLC_TRUE;
00240 p_sys->i_no_data_ti = 0;
00241 p_sys->b_multicast = VLC_FALSE;
00242 p_sys->psz_path = strdup(p_demux->psz_path);
00243
00244
00245 if( ( p_sys->scheduler = BasicTaskScheduler::createNew() ) == NULL )
00246 {
00247 msg_Err( p_demux, "BasicTaskScheduler::createNew failed" );
00248 goto error;
00249 }
00250 if( !( p_sys->env = BasicUsageEnvironment::createNew(*p_sys->scheduler) ) )
00251 {
00252 msg_Err( p_demux, "BasicUsageEnvironment::createNew failed" );
00253 goto error;
00254 }
00255
00256 if( strcasecmp( p_demux->psz_access, "sdp" ) )
00257 {
00258 char *p = p_sys->psz_path;
00259 while( (p = strchr( p, ' ' )) != NULL ) *p = '+';
00260 }
00261
00262 if( p_demux->s == NULL && !strcasecmp( p_demux->psz_access, "rtsp" ) )
00263 {
00264 char *psz_url;
00265 char *psz_options;
00266
00267 if( ( p_sys->rtsp = RTSPClient::createNew(*p_sys->env, 1,
00268 "VLC Media Player" ) ) == NULL )
00269 {
00270 msg_Err( p_demux, "RTSPClient::createNew failed (%s)",
00271 p_sys->env->getResultMsg() );
00272 goto error;
00273 }
00274 psz_url = (char*)malloc( strlen( p_sys->psz_path ) + 8 );
00275 sprintf( psz_url, "rtsp://%s", p_sys->psz_path );
00276
00277 psz_options = p_sys->rtsp->sendOptionsCmd( psz_url );
00278 if( psz_options ) delete [] psz_options;
00279
00280 p_sdp = (uint8_t*)p_sys->rtsp->describeURL( psz_url,
00281 NULL, var_CreateGetBool( p_demux, "rtsp-kasenna" ) ) ;
00282 if( p_sdp == NULL )
00283 {
00284 msg_Err( p_demux, "describeURL failed (%s)",
00285 p_sys->env->getResultMsg() );
00286 free( psz_url );
00287 goto error;
00288 }
00289 free( psz_url );
00290
00291
00292 p_sys->p_sdp = strdup( (char*)p_sdp );
00293 delete[] p_sdp;
00294 msg_Dbg( p_demux, "sdp=%s\n", p_sys->p_sdp );
00295 }
00296 else if( p_demux->s == NULL && !strcasecmp( p_demux->psz_access, "sdp" ) )
00297 {
00298 p_sys->p_sdp = strdup( p_sys->psz_path );
00299 }
00300 else
00301 {
00302
00303 i_sdp = 0;
00304 i_sdp_max = 1000;
00305 p_sdp = (uint8_t*)malloc( i_sdp_max );
00306 for( ;; )
00307 {
00308 int i_read = stream_Read( p_demux->s, &p_sdp[i_sdp],
00309 i_sdp_max - i_sdp - 1 );
00310
00311 if( i_read < 0 )
00312 {
00313 msg_Err( p_demux, "failed to read SDP" );
00314 free( p_sys );
00315 return VLC_EGENERIC;
00316 }
00317
00318 i_sdp += i_read;
00319
00320 if( i_read < i_sdp_max - i_sdp - 1 )
00321 {
00322 p_sdp[i_sdp] = '\0';
00323 break;
00324 }
00325
00326 i_sdp_max += 1000;
00327 p_sdp = (uint8_t*)realloc( p_sdp, i_sdp_max );
00328 }
00329 p_sys->p_sdp = (char*)p_sdp;
00330
00331 msg_Dbg( p_demux, "sdp=%s\n", p_sys->p_sdp );
00332 }
00333 if( !( p_sys->ms = MediaSession::createNew( *p_sys->env, p_sys->p_sdp ) ) )
00334 {
00335 msg_Err( p_demux, "MediaSession::createNew failed" );
00336 goto error;
00337 }
00338
00339 b_rtsp_tcp = var_CreateGetBool( p_demux, "rtsp-tcp" );
00340
00341
00342 iter = new MediaSubsessionIterator( *p_sys->ms );
00343 while( ( sub = iter->next() ) != NULL )
00344 {
00345 unsigned int i_buffer = 0;
00346 Boolean bInit;
00347
00348
00349 if( !strcmp( sub->mediumName(), "audio" ) )
00350 i_buffer = 100000;
00351 else if( !strcmp( sub->mediumName(), "video" ) )
00352 i_buffer = 2000000;
00353 else
00354 continue;
00355
00356 if( !strcmp( sub->codecName(), "X-ASF-PF" ) )
00357 bInit = sub->initiate( 4 );
00358 else
00359 bInit = sub->initiate();
00360
00361 if( !bInit )
00362 {
00363 msg_Warn( p_demux, "RTP subsession '%s/%s' failed (%s)",
00364 sub->mediumName(), sub->codecName(),
00365 p_sys->env->getResultMsg() );
00366 }
00367 else
00368 {
00369 if( sub->rtpSource() )
00370 {
00371 int fd = sub->rtpSource()->RTPgs()->socketNum();
00372
00373 increaseReceiveBufferTo( *p_sys->env, fd, i_buffer );
00374 }
00375
00376 msg_Dbg( p_demux, "RTP subsession '%s/%s'", sub->mediumName(),
00377 sub->codecName() );
00378
00379
00380 if( p_sys->rtsp )
00381 {
00382 p_sys->rtsp->setupMediaSubsession( *sub, False,
00383 b_rtsp_tcp ? True : False );
00384 }
00385 if( !p_sys->b_multicast )
00386 {
00387
00388 p_sys->b_multicast = IsMulticastAddress( sub->connectionEndpointAddress() );
00389 }
00390 }
00391 }
00392
00393 if( p_sys->rtsp )
00394 {
00395
00396 if( !p_sys->rtsp->playMediaSession( *p_sys->ms ) )
00397 {
00398 msg_Err( p_demux, "PLAY failed %s", p_sys->env->getResultMsg() );
00399 delete iter;
00400 goto error;
00401 }
00402 }
00403
00404
00405 iter->reset();
00406 while( ( sub = iter->next() ) != NULL )
00407 {
00408 live_track_t *tk;
00409
00410 if( sub->readSource() == NULL ) continue;
00411
00412 tk = (live_track_t*)malloc( sizeof( live_track_t ) );
00413 tk->p_demux = p_demux;
00414 tk->waiting = 0;
00415 tk->i_pts = 0;
00416 tk->b_quicktime = VLC_FALSE;
00417 tk->b_muxed = VLC_FALSE;
00418 tk->b_asf = VLC_FALSE;
00419 tk->b_rtcp_sync = VLC_FALSE;
00420 tk->p_out_muxed = NULL;
00421 tk->p_es = NULL;
00422 tk->i_buffer = 65536;
00423 tk->p_buffer = (uint8_t *)malloc( 65536 );
00424
00425
00426 if( !strcmp( sub->mediumName(), "audio" ) )
00427 {
00428 es_format_Init( &tk->fmt, AUDIO_ES, VLC_FOURCC('u','n','d','f') );
00429 tk->fmt.audio.i_channels = sub->numChannels();
00430 tk->fmt.audio.i_rate = sub->rtpTimestampFrequency();
00431
00432 if( !strcmp( sub->codecName(), "MPA" ) ||
00433 !strcmp( sub->codecName(), "MPA-ROBUST" ) ||
00434 !strcmp( sub->codecName(), "X-MP3-DRAFT-00" ) )
00435 {
00436 tk->fmt.i_codec = VLC_FOURCC( 'm', 'p', 'g', 'a' );
00437 tk->fmt.audio.i_rate = 0;
00438 }
00439 else if( !strcmp( sub->codecName(), "AC3" ) )
00440 {
00441 tk->fmt.i_codec = VLC_FOURCC( 'a', '5', '2', ' ' );
00442 tk->fmt.audio.i_rate = 0;
00443 }
00444 else if( !strcmp( sub->codecName(), "L16" ) )
00445 {
00446 tk->fmt.i_codec = VLC_FOURCC( 't', 'w', 'o', 's' );
00447 tk->fmt.audio.i_bitspersample = 16;
00448 }
00449 else if( !strcmp( sub->codecName(), "L8" ) )
00450 {
00451 tk->fmt.i_codec = VLC_FOURCC( 'a', 'r', 'a', 'w' );
00452 tk->fmt.audio.i_bitspersample = 8;
00453 }
00454 else if( !strcmp( sub->codecName(), "PCMU" ) )
00455 {
00456 tk->fmt.i_codec = VLC_FOURCC( 'u', 'l', 'a', 'w' );
00457 }
00458 else if( !strcmp( sub->codecName(), "PCMA" ) )
00459 {
00460 tk->fmt.i_codec = VLC_FOURCC( 'a', 'l', 'a', 'w' );
00461 }
00462 else if( !strcmp( sub->codecName(), "AMR" ) )
00463 {
00464 tk->fmt.i_codec = VLC_FOURCC( 's', 'a', 'm', 'r' );
00465 }
00466 else if( !strcmp( sub->codecName(), "AMR-WB" ) )
00467 {
00468 tk->fmt.i_codec = VLC_FOURCC( 's', 'a', 'w', 'b' );
00469 }
00470 else if( !strcmp( sub->codecName(), "MP4A-LATM" ) )
00471 {
00472 unsigned int i_extra;
00473 uint8_t *p_extra;
00474
00475 tk->fmt.i_codec = VLC_FOURCC( 'm', 'p', '4', 'a' );
00476
00477 if( ( p_extra = parseStreamMuxConfigStr( sub->fmtp_config(),
00478 i_extra ) ) )
00479 {
00480 tk->fmt.i_extra = i_extra;
00481 tk->fmt.p_extra = malloc( i_extra );
00482 memcpy( tk->fmt.p_extra, p_extra, i_extra );
00483 delete[] p_extra;
00484 }
00485 }
00486 else if( !strcmp( sub->codecName(), "MPEG4-GENERIC" ) )
00487 {
00488 unsigned int i_extra;
00489 uint8_t *p_extra;
00490
00491 tk->fmt.i_codec = VLC_FOURCC( 'm', 'p', '4', 'a' );
00492
00493 if( ( p_extra = parseGeneralConfigStr( sub->fmtp_config(),
00494 i_extra ) ) )
00495 {
00496 tk->fmt.i_extra = i_extra;
00497 tk->fmt.p_extra = malloc( i_extra );
00498 memcpy( tk->fmt.p_extra, p_extra, i_extra );
00499 delete[] p_extra;
00500 }
00501 }
00502 else if( !strcmp( sub->codecName(), "X-ASF-PF" ) )
00503 {
00504 tk->b_asf = VLC_TRUE;
00505 if( p_sys->p_out_asf == NULL )
00506 p_sys->p_out_asf = stream_DemuxNew( p_demux, "asf",
00507 p_demux->out );
00508 }
00509 }
00510 else if( !strcmp( sub->mediumName(), "video" ) )
00511 {
00512 es_format_Init( &tk->fmt, VIDEO_ES, VLC_FOURCC('u','n','d','f') );
00513 if( !strcmp( sub->codecName(), "MPV" ) )
00514 {
00515 tk->fmt.i_codec = VLC_FOURCC( 'm', 'p', 'g', 'v' );
00516 }
00517 else if( !strcmp( sub->codecName(), "H263" ) ||
00518 !strcmp( sub->codecName(), "H263-1998" ) ||
00519 !strcmp( sub->codecName(), "H263-2000" ) )
00520 {
00521 tk->fmt.i_codec = VLC_FOURCC( 'H', '2', '6', '3' );
00522 }
00523 else if( !strcmp( sub->codecName(), "H261" ) )
00524 {
00525 tk->fmt.i_codec = VLC_FOURCC( 'H', '2', '6', '1' );
00526 }
00527 else if( !strcmp( sub->codecName(), "H264" ) )
00528 {
00529 #if LIVEMEDIA_LIBRARY_VERSION_INT >= 1117756800
00530 unsigned int i_extra = 0;
00531 uint8_t *p_extra = NULL;
00532 #endif
00533 tk->fmt.i_codec = VLC_FOURCC( 'H', '2', '6', '4' );
00534 tk->fmt.b_packetized = VLC_FALSE;
00535
00536
00537 #if LIVEMEDIA_LIBRARY_VERSION_INT >= 1117756800
00538 if((p_extra=parseH264ConfigStr( sub->fmtp_spropparametersets(),
00539 i_extra ) ) )
00540 {
00541 tk->fmt.i_extra = i_extra;
00542 tk->fmt.p_extra = malloc( i_extra );
00543 memcpy( tk->fmt.p_extra, p_extra, i_extra );
00544
00545 delete[] p_extra;
00546 }
00547 #endif
00548 }
00549 else if( !strcmp( sub->codecName(), "JPEG" ) )
00550 {
00551 tk->fmt.i_codec = VLC_FOURCC( 'M', 'J', 'P', 'G' );
00552 }
00553 else if( !strcmp( sub->codecName(), "MP4V-ES" ) )
00554 {
00555 unsigned int i_extra;
00556 uint8_t *p_extra;
00557
00558 tk->fmt.i_codec = VLC_FOURCC( 'm', 'p', '4', 'v' );
00559
00560 if( ( p_extra = parseGeneralConfigStr( sub->fmtp_config(),
00561 i_extra ) ) )
00562 {
00563 tk->fmt.i_extra = i_extra;
00564 tk->fmt.p_extra = malloc( i_extra );
00565 memcpy( tk->fmt.p_extra, p_extra, i_extra );
00566 delete[] p_extra;
00567 }
00568 }
00569 else if( !strcmp( sub->codecName(), "X-QT" ) ||
00570 !strcmp( sub->codecName(), "X-QUICKTIME" ) ||
00571 !strcmp( sub->codecName(), "X-QDM" ) ||
00572 !strcmp( sub->codecName(), "X-SV3V-ES" ) ||
00573 !strcmp( sub->codecName(), "X-SORENSONVIDEO" ) )
00574 {
00575 tk->b_quicktime = VLC_TRUE;
00576 }
00577 else if( !strcmp( sub->codecName(), "MP2T" ) )
00578 {
00579 tk->b_muxed = VLC_TRUE;
00580 tk->p_out_muxed = stream_DemuxNew( p_demux, "ts", p_demux->out );
00581 }
00582 else if( !strcmp( sub->codecName(), "MP2P" ) ||
00583 !strcmp( sub->codecName(), "MP1S" ) )
00584 {
00585 tk->b_muxed = VLC_TRUE;
00586 tk->p_out_muxed = stream_DemuxNew( p_demux, "ps",
00587 p_demux->out );
00588 }
00589 else if( !strcmp( sub->codecName(), "X-ASF-PF" ) )
00590 {
00591 tk->b_asf = VLC_TRUE;
00592 if( p_sys->p_out_asf == NULL )
00593 p_sys->p_out_asf = stream_DemuxNew( p_demux, "asf",
00594 p_demux->out );;
00595 }
00596 }
00597
00598 if( tk->fmt.i_codec != VLC_FOURCC( 'u', 'n', 'd', 'f' ) )
00599 {
00600 tk->p_es = es_out_Add( p_demux->out, &tk->fmt );
00601 }
00602
00603 if( sub->rtcpInstance() != NULL )
00604 {
00605 sub->rtcpInstance()->setByeHandler( StreamClose, tk );
00606 }
00607
00608 if( tk->p_es || tk->b_quicktime || tk->b_muxed || tk->b_asf )
00609 {
00610 tk->readSource = sub->readSource();
00611 tk->rtpSource = sub->rtpSource();
00612
00613
00614 p_sys->track = (live_track_t**)realloc( p_sys->track, sizeof( live_track_t ) * ( p_sys->i_track + 1 ) );
00615 p_sys->track[p_sys->i_track++] = tk;
00616 }
00617 else
00618 {
00619 free( tk );
00620 }
00621 }
00622
00623 delete iter;
00624
00625 if( p_sys->p_out_asf && ParseASF( p_demux ) )
00626 {
00627 msg_Err( p_demux, "cannot find a usable asf header" );
00628
00629 goto error;
00630 }
00631
00632 p_sys->i_length = (mtime_t)(p_sys->ms->playEndTime() * 1000000.0);
00633 if( p_sys->i_length < 0 )
00634 {
00635 p_sys->i_length = 0;
00636 }
00637 else if( p_sys->i_length > 0 )
00638 {
00639
00640
00641 }
00642
00643 if( p_sys->i_track <= 0 )
00644 {
00645 msg_Err( p_demux, "no codec supported, aborting" );
00646 goto error;
00647 }
00648
00649 return VLC_SUCCESS;
00650
00651 error:
00652 if( p_sys->p_out_asf ) stream_DemuxDelete( p_sys->p_out_asf );
00653 if( p_sys->ms ) Medium::close( p_sys->ms );
00654 if( p_sys->rtsp ) Medium::close( p_sys->rtsp );
00655 if( p_sys->env ) RECLAIM_ENV(p_sys->env);
00656 if( p_sys->scheduler ) delete p_sys->scheduler;
00657 if( p_sys->p_sdp ) free( p_sys->p_sdp );
00658 if( p_sys->psz_path ) free( p_sys->psz_path );
00659
00660 free( p_sys );
00661 return VLC_EGENERIC;
00662 }
00663
00664
00665
00666
00667 static void Close( vlc_object_t *p_this )
00668 {
00669 demux_t *p_demux = (demux_t*)p_this;
00670 demux_sys_t *p_sys = p_demux->p_sys;
00671 int i;
00672
00673 for( i = 0; i < p_sys->i_track; i++ )
00674 {
00675 live_track_t *tk = p_sys->track[i];
00676
00677 if( tk->b_muxed ) stream_DemuxDelete( tk->p_out_muxed );
00678 free( tk->p_buffer );
00679 free( tk );
00680 }
00681
00682 if( p_sys->i_track ) free( p_sys->track );
00683 if( p_sys->p_out_asf ) stream_DemuxDelete( p_sys->p_out_asf );
00684
00685 if( p_sys->rtsp && p_sys->ms )
00686 {
00687
00688 p_sys->rtsp->teardownMediaSession( *p_sys->ms );
00689 }
00690
00691 Medium::close( p_sys->ms );
00692
00693 if( p_sys->rtsp ) Medium::close( p_sys->rtsp );
00694 if( p_sys->env ) RECLAIM_ENV(p_sys->env);
00695 if( p_sys->scheduler ) delete p_sys->scheduler;
00696 if( p_sys->p_sdp ) free( p_sys->p_sdp );
00697 if( p_sys->psz_path ) free( p_sys->psz_path );
00698 free( p_sys );
00699 }
00700
00701
00702
00703
00704 static int Demux( demux_t *p_demux )
00705 {
00706 demux_sys_t *p_sys = p_demux->p_sys;
00707 TaskToken task;
00708
00709 vlc_bool_t b_send_pcr = VLC_TRUE;
00710 mtime_t i_pcr = 0;
00711 int i;
00712
00713 for( i = 0; i < p_sys->i_track; i++ )
00714 {
00715 live_track_t *tk = p_sys->track[i];
00716
00717 if( tk->b_asf || tk->b_muxed )
00718 b_send_pcr = VLC_FALSE;
00719
00720 if( i_pcr == 0 )
00721 {
00722 i_pcr = tk->i_pts;
00723 }
00724 else if( tk->i_pts != 0 && i_pcr > tk->i_pts )
00725 {
00726 i_pcr = tk->i_pts ;
00727 }
00728 }
00729 if( i_pcr != p_sys->i_pcr && i_pcr > 0 )
00730 {
00731 p_sys->i_pcr = i_pcr;
00732
00733 if( b_send_pcr )
00734 es_out_Control( p_demux->out, ES_OUT_SET_PCR, i_pcr );
00735 if( p_sys->i_pcr_start <= 0 || p_sys->i_pcr_start > i_pcr ||
00736 ( p_sys->i_length > 0 && i_pcr - p_sys->i_pcr_start > p_sys->i_length ) )
00737 {
00738 p_sys->i_pcr_start = i_pcr;
00739 }
00740 }
00741
00742
00743
00744 if( i_pcr > 0 && p_sys->i_pcr == p_sys->i_pcr_previous )
00745 {
00746 if( p_sys->i_pcr_repeats == 0 )
00747 p_sys->i_pcr_repeatdate = mdate();
00748 p_sys->i_pcr_repeats++;
00749 }
00750 else
00751 {
00752 p_sys->i_pcr_previous = p_sys->i_pcr;
00753 p_sys->i_pcr_repeatdate = 0;
00754 p_sys->i_pcr_repeats = 0;
00755 }
00756
00757 if( p_sys->i_pcr_repeats > 5 && mdate() > p_sys->i_pcr_repeatdate + 1000000 )
00758 {
00759
00760 msg_Dbg( p_demux, "suspect EOF due to end of VoD session" );
00761 return 0;
00762 }
00763
00764
00765 p_sys->event = 0;
00766 for( i = 0; i < p_sys->i_track; i++ )
00767 {
00768 live_track_t *tk = p_sys->track[i];
00769
00770 if( tk->waiting == 0 )
00771 {
00772 tk->waiting = 1;
00773 tk->readSource->getNextFrame( tk->p_buffer, tk->i_buffer,
00774 StreamRead, tk, StreamClose, tk );
00775 }
00776 }
00777
00778 task = p_sys->scheduler->scheduleDelayedTask( 300000, TaskInterrupt, p_demux );
00779
00780
00781 p_sys->scheduler->doEventLoop( &p_sys->event );
00782
00783
00784 p_sys->scheduler->unscheduleDelayedTask( task );
00785
00786
00787 for( i = 0; i < p_sys->i_track; i++ )
00788 {
00789 live_track_t *tk = p_sys->track[i];
00790
00791 if( !tk->b_muxed && !tk->b_rtcp_sync &&
00792 tk->rtpSource && tk->rtpSource->hasBeenSynchronizedUsingRTCP() )
00793 {
00794 msg_Dbg( p_demux, "tk->rtpSource->hasBeenSynchronizedUsingRTCP()" );
00795
00796 es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
00797 tk->b_rtcp_sync = VLC_TRUE;
00798
00799
00800 tk->i_pts = 0;
00801 p_sys->i_pcr_start = 0;
00802 p_sys->i_pcr = 0;
00803 i_pcr = 0;
00804 }
00805 }
00806
00807 if( p_sys->b_multicast && p_sys->b_no_data && p_sys->i_no_data_ti > 120 )
00808 {
00809
00810
00811
00812
00813 }
00814 else if( !p_sys->b_multicast && p_sys->b_no_data && p_sys->i_no_data_ti > 34 )
00815 {
00816 vlc_bool_t b_rtsp_tcp = var_GetBool( p_demux, "rtsp-tcp" );
00817
00818 if( !b_rtsp_tcp && p_sys->rtsp && p_sys->ms )
00819 {
00820 msg_Warn( p_demux, "no data received in 10s. Switching to TCP" );
00821 if( RollOverTcp( p_demux ) )
00822 {
00823 msg_Err( p_demux, "TCP rollover failed, aborting" );
00824 return 0;
00825 }
00826 var_SetBool( p_demux, "rtsp-tcp", VLC_TRUE );
00827 }
00828 else if( p_sys->i_no_data_ti > 34 )
00829 {
00830 msg_Err( p_demux, "no data received in 10s, aborting" );
00831 return 0;
00832 }
00833 }
00834 else if( !p_sys->b_multicast && p_sys->b_no_data&& p_sys->i_no_data_ti > 34 )
00835 {
00836
00837 msg_Warn( p_demux, "no data received in 10s, eof ?" );
00838 return 0;
00839 }
00840
00841 return p_demux->b_error ? 0 : 1;
00842 }
00843
00844
00845
00846
00847 static int Control( demux_t *p_demux, int i_query, va_list args )
00848 {
00849 demux_sys_t *p_sys = p_demux->p_sys;
00850 int64_t *pi64;
00851 double *pf, f;
00852 vlc_bool_t *pb, b_bool;
00853
00854 switch( i_query )
00855 {
00856 case DEMUX_GET_TIME:
00857 pi64 = (int64_t*)va_arg( args, int64_t * );
00858 *pi64 = p_sys->i_pcr - p_sys->i_pcr_start + p_sys->i_start;
00859 return VLC_SUCCESS;
00860
00861 case DEMUX_GET_LENGTH:
00862 pi64 = (int64_t*)va_arg( args, int64_t * );
00863 *pi64 = p_sys->i_length;
00864 return VLC_SUCCESS;
00865
00866 case DEMUX_GET_POSITION:
00867 pf = (double*)va_arg( args, double* );
00868 if( p_sys->i_length > 0 )
00869 {
00870 *pf = (double)( p_sys->i_pcr - p_sys->i_pcr_start +
00871 p_sys->i_start ) / (double)(p_sys->i_length);
00872 }
00873 else
00874 {
00875 *pf = 0;
00876 }
00877 return VLC_SUCCESS;
00878
00879 case DEMUX_SET_POSITION:
00880 {
00881 float time;
00882
00883 f = (double)va_arg( args, double );
00884 time = f * (double)p_sys->i_length / 1000000.0;
00885
00886 if( p_sys->rtsp && p_sys->i_length > 0 )
00887 {
00888 int i;
00889
00890 if( !p_sys->rtsp->playMediaSession( *p_sys->ms, time ) )
00891 {
00892 msg_Err( p_demux, "PLAY failed %s", p_sys->env->getResultMsg() );
00893 return VLC_EGENERIC;
00894 }
00895 p_sys->i_start = (mtime_t)(f * (double)p_sys->i_length);
00896 p_sys->i_pcr_start = 0;
00897 p_sys->i_pcr = 0;
00898
00899 #if 0
00900 for( i = 0; i < p_sys->i_track; i++ )
00901 {
00902 p_sys->track[i]->i_pts = 0;
00903 }
00904 #endif
00905 return VLC_SUCCESS;
00906 }
00907 return VLC_SUCCESS;
00908 }
00909
00910
00911 case DEMUX_CAN_PAUSE:
00912 pb = (vlc_bool_t*)va_arg( args, vlc_bool_t * );
00913 if( p_sys->rtsp && p_sys->i_length )
00914
00915 *pb = VLC_TRUE;
00916 else
00917 *pb = VLC_FALSE;
00918 return VLC_SUCCESS;
00919
00920 case DEMUX_CAN_CONTROL_PACE:
00921 pb = (vlc_bool_t*)va_arg( args, vlc_bool_t * );
00922
00923 #if 0
00924
00925 *pb = VLC_FALSE;
00926 #else
00927 *pb = VLC_TRUE;
00928 #endif
00929 return VLC_SUCCESS;
00930
00931 case DEMUX_SET_PAUSE_STATE:
00932 double d_npt;
00933
00934 d_npt = ( (double)( p_sys->i_pcr - p_sys->i_pcr_start +
00935 p_sys->i_start ) ) / 1000000.00;
00936
00937 b_bool = (vlc_bool_t)va_arg( args, vlc_bool_t );
00938 if( p_sys->rtsp == NULL )
00939 return VLC_EGENERIC;
00940
00941 if( ( b_bool && !p_sys->rtsp->pauseMediaSession( *p_sys->ms ) ) ||
00942 ( !b_bool && !p_sys->rtsp->playMediaSession( *p_sys->ms,
00943 d_npt > 0 ? d_npt : -1 ) ) )
00944 {
00945 msg_Err( p_demux, "PLAY or PAUSE failed %s", p_sys->env->getResultMsg() );
00946 return VLC_EGENERIC;
00947 }
00948 #if 0
00949
00950 for( i = 0; i < p_sys->i_track; i++ )
00951 {
00952 p_sys->track[i]->i_pts = 0;
00953 }
00954 p_sys->i_pcr_start = 0;
00955 p_sys->i_pcr = 0;
00956 #endif
00957 return VLC_SUCCESS;
00958
00959 case DEMUX_GET_TITLE_INFO:
00960 case DEMUX_SET_TITLE:
00961 case DEMUX_SET_SEEKPOINT:
00962 return VLC_EGENERIC;
00963
00964 case DEMUX_GET_PTS_DELAY:
00965 pi64 = (int64_t*)va_arg( args, int64_t * );
00966 *pi64 = (int64_t)var_GetInteger( p_demux, "rtsp-caching" ) * 1000;
00967 return VLC_SUCCESS;
00968
00969 default:
00970 return VLC_EGENERIC;
00971 }
00972 }
00973
00974
00975
00976
00977
00978 static int RollOverTcp( demux_t *p_demux )
00979 {
00980 demux_sys_t *p_sys = p_demux->p_sys;
00981 MediaSubsessionIterator *iter;
00982 MediaSubsession *sub;
00983 char *psz_url;
00984 char *psz_options;
00985 uint8_t *p_sdp;
00986 int i_tk;
00987
00988
00989 p_sys->rtsp->teardownMediaSession( *p_sys->ms );
00990
00991 Medium::close( p_sys->ms );
00992 Medium::close( p_sys->rtsp );
00993
00994 p_sys->ms = NULL;
00995 p_sys->rtsp = NULL;
00996
00997
00998 if( ( p_sys->rtsp = RTSPClient::createNew(*p_sys->env, 1,
00999 "VLC Media Player" ) ) == NULL )
01000 {
01001 msg_Err( p_demux, "RTSPClient::createNew failed (%s)",
01002 p_sys->env->getResultMsg() );
01003 return VLC_EGENERIC;
01004 }
01005
01006 asprintf( &psz_url, "rtsp://%s", p_sys->psz_path );
01007
01008 if( ( psz_options = p_sys->rtsp->sendOptionsCmd( psz_url ) ) )
01009 delete [] psz_options;
01010
01011 p_sdp = (uint8_t*)p_sys->rtsp->describeURL( psz_url,
01012 NULL, var_CreateGetBool( p_demux, "rtsp-kasenna" ) );
01013 free( psz_url );
01014 if( p_sdp == NULL )
01015 {
01016 msg_Err( p_demux, "describeURL failed (%s)",
01017 p_sys->env->getResultMsg() );
01018 return VLC_EGENERIC;
01019 }
01020
01021
01022 p_sys->p_sdp = strdup( (char*)p_sdp );
01023 delete[] p_sdp;
01024
01025 if( !( p_sys->ms = MediaSession::createNew( *p_sys->env, p_sys->p_sdp ) ) )
01026 {
01027 msg_Err( p_demux, "MediaSession::createNew failed" );
01028 return VLC_EGENERIC;
01029 }
01030
01031
01032 iter = new MediaSubsessionIterator( *p_sys->ms );
01033 while( ( sub = iter->next() ) != NULL )
01034 {
01035 Boolean bInit;
01036
01037 if( !strcmp( sub->codecName(), "X-ASF-PF" ) )
01038 bInit = sub->initiate( 4 );
01039 else
01040 bInit = sub->initiate();
01041
01042 if( !bInit )
01043 {
01044 msg_Warn( p_demux, "RTP subsession '%s/%s' failed (%s)",
01045 sub->mediumName(), sub->codecName(),
01046 p_sys->env->getResultMsg() );
01047 continue;
01048 }
01049 msg_Dbg( p_demux, "RTP subsession '%s/%s'", sub->mediumName(),
01050 sub->codecName() );
01051
01052
01053 p_sys->rtsp->setupMediaSubsession( *sub, False, True );
01054 }
01055
01056
01057 if( !p_sys->rtsp->playMediaSession( *p_sys->ms ) )
01058 {
01059 msg_Err( p_demux, "PLAY failed %s", p_sys->env->getResultMsg() );
01060 return VLC_EGENERIC;
01061 }
01062
01063
01064 iter->reset();
01065 i_tk = 0;
01066 while( ( sub = iter->next() ) != NULL )
01067 {
01068 live_track_t *tk;
01069
01070 if( sub->readSource() == NULL )
01071 continue;
01072 if( i_tk >= p_sys->i_track )
01073 {
01074 msg_Err( p_demux, "WTF !" );
01075 break;
01076 }
01077
01078 tk = p_sys->track[i_tk];
01079
01080
01081 tk->waiting = 0;
01082 tk->i_pts = 0;
01083 tk->b_rtcp_sync = VLC_FALSE;
01084
01085 if( sub->rtcpInstance() != NULL )
01086 sub->rtcpInstance()->setByeHandler( StreamClose, tk );
01087
01088 tk->readSource = sub->readSource();
01089 tk->rtpSource = sub->rtpSource();
01090
01091 i_tk++;
01092 }
01093
01094 delete iter;
01095
01096 return VLC_SUCCESS;
01097 }
01098
01099
01100
01101
01102
01103 static void StreamRead( void *p_private, unsigned int i_size,
01104 unsigned int i_truncated_bytes, struct timeval pts,
01105 unsigned int duration )
01106 {
01107 live_track_t *tk = (live_track_t*)p_private;
01108 demux_t *p_demux = tk->p_demux;
01109 demux_sys_t *p_sys = p_demux->p_sys;
01110 block_t *p_block;
01111
01112 mtime_t i_pts = (uint64_t)pts.tv_sec * UI64C(1000000) +
01113 (uint64_t)pts.tv_usec;
01114
01115
01116 i_pts &= UI64C(0x00ffffffffffffff);
01117
01118 if( tk->b_quicktime && tk->p_es == NULL )
01119 {
01120 QuickTimeGenericRTPSource *qtRTPSource =
01121 (QuickTimeGenericRTPSource*)tk->rtpSource;
01122 QuickTimeGenericRTPSource::QTState &qtState = qtRTPSource->qtState;
01123 uint8_t *sdAtom = (uint8_t*)&qtState.sdAtom[4];
01124
01125 if( qtState.sdAtomSize < 16 + 32 )
01126 {
01127
01128 p_sys->event = 0xff;
01129 tk->waiting = 0;
01130 return;
01131 }
01132 tk->fmt.i_codec = VLC_FOURCC(sdAtom[0],sdAtom[1],sdAtom[2],sdAtom[3]);
01133 tk->fmt.video.i_width = (sdAtom[28] << 8) | sdAtom[29];
01134 tk->fmt.video.i_height = (sdAtom[30] << 8) | sdAtom[31];
01135
01136 tk->fmt.i_extra = qtState.sdAtomSize - 16;
01137 tk->fmt.p_extra = malloc( tk->fmt.i_extra );
01138 memcpy( tk->fmt.p_extra, &sdAtom[12], tk->fmt.i_extra );
01139
01140 tk->p_es = es_out_Add( p_demux->out, &tk->fmt );
01141 }
01142
01143 #if 0
01144 fprintf( stderr, "StreamRead size=%d pts=%lld\n",
01145 i_size,
01146 pts.tv_sec * 1000000LL + pts.tv_usec );
01147 #endif
01148
01149
01150
01151 if( i_truncated_bytes > 0 && tk->i_buffer < 2000000 )
01152 {
01153 void *p_tmp;
01154 msg_Dbg( p_demux, "lost %d bytes", i_truncated_bytes );
01155 msg_Dbg( p_demux, "increasing buffer size to %d", tk->i_buffer * 2 );
01156 tk->i_buffer *= 2;
01157 p_tmp = realloc( tk->p_buffer, tk->i_buffer );
01158 if (p_tmp == NULL)
01159 {
01160 msg_Warn( p_demux, "realloc failed" );
01161 }
01162 else
01163 {
01164 tk->p_buffer = (uint8_t*)p_tmp;
01165 }
01166 }
01167 if( i_size > tk->i_buffer )
01168 {
01169 msg_Warn( p_demux, "buffer overflow" );
01170 }
01171
01172 if( tk->fmt.i_codec == VLC_FOURCC('H','2','6','1') )
01173 {
01174 #if LIVEMEDIA_LIBRARY_VERSION_INT >= 1081468800
01175 H261VideoRTPSource *h261Source = (H261VideoRTPSource*)tk->rtpSource;
01176 uint32_t header = h261Source->lastSpecialHeader();
01177 #else
01178 uint32_t header = 0;
01179 msg_Warn( p_demux, "need livemedia library >= \"2004.04.09\"" );
01180 #endif
01181 p_block = block_New( p_demux, i_size + 4 );
01182 memcpy( p_block->p_buffer, &header, 4 );
01183 memcpy( p_block->p_buffer + 4, tk->p_buffer, i_size );
01184
01185 if( tk->rtpSource->curPacketMarkerBit() )
01186 p_block->i_flags |= BLOCK_FLAG_END_OF_FRAME;
01187 }
01188 else if( tk->fmt.i_codec == VLC_FOURCC('H','2','6','4') )
01189 {
01190 if( (tk->p_buffer[0] & 0x1f) >= 24 )
01191 msg_Warn( p_demux, "unsupported NAL type for H264" );
01192
01193
01194 p_block = block_New( p_demux, i_size + 4 );
01195 p_block->p_buffer[0] = 0x00;
01196 p_block->p_buffer[1] = 0x00;
01197 p_block->p_buffer[2] = 0x00;
01198 p_block->p_buffer[3] = 0x01;
01199 memcpy( &p_block->p_buffer[4], tk->p_buffer, i_size );
01200 }
01201 else if( tk->b_asf )
01202 {
01203 int i_copy = __MIN( p_sys->asfh.i_min_data_packet_size, (int)i_size );
01204 p_block = block_New( p_demux, p_sys->asfh.i_min_data_packet_size );
01205
01206 memcpy( p_block->p_buffer, tk->p_buffer, i_copy );
01207 }
01208 else
01209 {
01210 p_block = block_New( p_demux, i_size );
01211 memcpy( p_block->p_buffer, tk->p_buffer, i_size );
01212 }
01213
01214
01215
01216 if( i_pts != tk->i_pts && !tk->b_muxed )
01217 {
01218 p_block->i_dts = ( tk->fmt.i_cat == VIDEO_ES ) ? 0 : i_pts;
01219 p_block->i_pts = i_pts;
01220 }
01221
01222 if( tk->b_muxed )
01223 {
01224 stream_DemuxSend( tk->p_out_muxed, p_block );
01225 }
01226 else if( tk->b_asf )
01227 {
01228 stream_DemuxSend( p_sys->p_out_asf, p_block );
01229 }
01230 else
01231 {
01232 es_out_Send( p_demux->out, tk->p_es, p_block );
01233 }
01234
01235
01236 p_sys->event = 0xff;
01237
01238
01239 tk->waiting = 0;
01240 p_demux->p_sys->b_no_data = VLC_FALSE;
01241 p_demux->p_sys->i_no_data_ti = 0;
01242
01243 if( i_pts > 0 && !tk->b_muxed )
01244 {
01245 tk->i_pts = i_pts;
01246 }
01247 }
01248
01249
01250
01251
01252 static void StreamClose( void *p_private )
01253 {
01254 live_track_t *tk = (live_track_t*)p_private;
01255 demux_t *p_demux = tk->p_demux;
01256 demux_sys_t *p_sys = p_demux->p_sys;
01257
01258 msg_Dbg( p_demux, "StreamClose" );
01259
01260 p_sys->event = 0xff;
01261 p_demux->b_error = VLC_TRUE;
01262 }
01263
01264
01265
01266
01267
01268 static void TaskInterrupt( void *p_private )
01269 {
01270 demux_t *p_demux = (demux_t*)p_private;
01271
01272 p_demux->p_sys->i_no_data_ti++;
01273
01274
01275 p_demux->p_sys->event = 0xff;
01276 }
01277
01278
01279
01280
01281 static int b64_decode( char *dest, char *src );
01282
01283 static int ParseASF( demux_t *p_demux )
01284 {
01285 demux_sys_t *p_sys = p_demux->p_sys;
01286
01287 const char *psz_marker = "a=pgmpu:data:application/vnd.ms.wms-hdr.asfv1;base64,";
01288 char *psz_asf = strcasestr( p_sys->p_sdp, psz_marker );
01289 char *psz_end;
01290 block_t *p_header;
01291
01292
01293 if( psz_asf == NULL )
01294 return VLC_EGENERIC;
01295
01296 psz_asf += strlen( psz_marker );
01297 psz_asf = strdup( psz_asf );
01298 psz_end = strchr( psz_asf, '\n' );
01299
01300 while( psz_end > psz_asf && ( *psz_end == '\n' || *psz_end == '\r' ) )
01301 *psz_end-- = '\0';
01302
01303 if( psz_asf >= psz_end )
01304 {
01305 free( psz_asf );
01306 return VLC_EGENERIC;
01307 }
01308
01309
01310 p_header = block_New( p_demux, psz_end - psz_asf );
01311 p_header->i_buffer = b64_decode( (char*)p_header->p_buffer, psz_asf );
01312
01313 if( p_header->i_buffer <= 0 )
01314 {
01315 free( psz_asf );
01316 return VLC_EGENERIC;
01317 }
01318
01319
01320 E_(asf_HeaderParse)( &p_sys->asfh, p_header->p_buffer, p_header->i_buffer );
01321
01322
01323 stream_DemuxSend( p_sys->p_out_asf, p_header );
01324
01325 free( psz_asf );
01326 return VLC_SUCCESS;
01327 }
01328
01329 #if LIVEMEDIA_LIBRARY_VERSION_INT >= 1117756800
01330 static unsigned char* parseH264ConfigStr( char const* configStr,
01331 unsigned int& configSize )
01332 {
01333 char *dup, *psz;
01334
01335 if( configSize )
01336 configSize = 0;
01337
01338 if( configStr == NULL || *configStr == '\0' )
01339 return NULL;
01340
01341 psz = dup = strdup( configStr );
01342
01343 unsigned char *cfg = new unsigned char[5 * strlen(psz)];
01344 for( ;; )
01345 {
01346 char *p = strchr( psz, ',' );
01347 if( p )
01348 *p++ = '\0';
01349
01350 cfg[configSize++] = 0x00;
01351 cfg[configSize++] = 0x00;
01352 cfg[configSize++] = 0x00;
01353 cfg[configSize++] = 0x01;
01354 configSize += b64_decode( (char*)&cfg[configSize], psz );
01355
01356 if( p == NULL )
01357 break;
01358 psz = p;
01359 }
01360
01361 if( dup ) free( dup );
01362 return cfg;
01363 }
01364 #endif
01365
01366
01367 static int b64_decode( char *dest, char *src )
01368 {
01369 const char *dest_start = dest;
01370 int i_level;
01371 int last = 0;
01372 int b64[256] = {
01373 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
01374 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
01375 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,
01376 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,
01377 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
01378 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,
01379 -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
01380 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,
01381 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
01382 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
01383 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
01384 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
01385 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
01386 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
01387 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
01388 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
01389 };
01390
01391 for( i_level = 0; *src != '\0'; src++ )
01392 {
01393 int c;
01394
01395 c = b64[(unsigned int)*src];
01396 if( c == -1 )
01397 {
01398 continue;
01399 }
01400
01401 switch( i_level )
01402 {
01403 case 0:
01404 i_level++;
01405 break;
01406 case 1:
01407 *dest++ = ( last << 2 ) | ( ( c >> 4)&0x03 );
01408 i_level++;
01409 break;
01410 case 2:
01411 *dest++ = ( ( last << 4 )&0xf0 ) | ( ( c >> 2 )&0x0f );
01412 i_level++;
01413 break;
01414 case 3:
01415 *dest++ = ( ( last &0x03 ) << 6 ) | c;
01416 i_level = 0;
01417 }
01418 last = c;
01419 }
01420
01421 *dest = '\0';
01422
01423 return dest - dest_start;
01424 }