mmstu.c 45.4 KB
Newer Older
1 2 3
/*****************************************************************************
 * mms.c: MMS access plug-in
 *****************************************************************************
4
 * Copyright (C) 2001, 2002 the VideoLAN team
5
 * $Id$
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
 *
 * 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
dionoea's avatar
dionoea committed
21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 23 24 25 26 27
 *****************************************************************************/


/*****************************************************************************
 * Preamble
 *****************************************************************************/
28
#include <vlc/vlc.h>
zorglub's avatar
zorglub committed
29 30
#include <vlc_access.h>

31
#include <string.h>
32
#include <errno.h>
33 34 35 36

#ifdef HAVE_UNISTD_H
#   include <unistd.h>
#endif
37 38 39 40 41 42
#ifdef HAVE_FCNTL_H
#   include <fcntl.h>
#endif
#ifdef HAVE_SYS_TIME_H
#   include <sys/time.h>
#endif
zorglub's avatar
zorglub committed
43
#ifdef HAVE_SYS_TYPES_H
44 45 46 47 48
#   include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
#   include <sys/stat.h>
#endif
49

zorglub's avatar
zorglub committed
50
#include <vlc_network.h>
51
#include "vlc_url.h"
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
#include "asf.h"
#include "buffer.h"

#include "mms.h"
#include "mmstu.h"

#undef MMS_DEBUG

/****************************************************************************
 * NOTES:
 *  MMSProtocole documentation found at http://get.to/sdp
 ****************************************************************************/

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
68 69
int  E_( MMSTUOpen )  ( access_t * );
void E_( MMSTUClose ) ( access_t * );
70 71


72 73 74
static int Read( access_t *, uint8_t *, int );
static int Seek( access_t *, int64_t );
static int Control( access_t *, int, va_list );
75

76 77 78 79
static int  MMSOpen ( access_t *, vlc_url_t *, int );
static int  MMSStart( access_t *, uint32_t );
static int  MMSStop ( access_t * );
static void MMSClose( access_t * );
80 81


82 83
static int  mms_CommandRead( access_t *p_access, int i_command1, int i_command2 );
static int  mms_CommandSend( access_t *, int, uint32_t, uint32_t, uint8_t *, int );
84

85
static int  mms_HeaderMediaRead( access_t *, int );
86

87
static int  mms_ReceivePacket( access_t * );
88 89


90
int  E_(MMSTUOpen)( access_t *p_access )
91
{
Laurent Aimar's avatar
Laurent Aimar committed
92 93 94
    access_sys_t   *p_sys;
    int             i_proto;
    int             i_status;
95

96 97 98 99 100 101 102 103 104 105 106 107
    /* Set up p_access */
    p_access->pf_read = Read;
    p_access->pf_block = NULL;
    p_access->pf_control = Control;
    p_access->pf_seek = Seek;
    p_access->info.i_update = 0;
    p_access->info.i_size = 0;
    p_access->info.i_pos = 0;
    p_access->info.b_eof = VLC_FALSE;
    p_access->info.i_title = 0;
    p_access->info.i_seekpoint = 0;
    p_access->p_sys = p_sys = malloc( sizeof( access_sys_t ) );
108 109 110
    memset( p_sys, 0, sizeof( access_sys_t ) );

    /* *** Parse URL and get server addr/port and path *** */
111 112
    vlc_UrlParse( &p_sys->url, p_access->psz_path, 0 );
    if( p_sys->url.psz_host == NULL || *p_sys->url.psz_host == '\0' )
113
    {
114 115
        msg_Err( p_access, "invalid server name" );
        vlc_UrlClean( &p_sys->url );
116
        free( p_sys );
Laurent Aimar's avatar
Laurent Aimar committed
117
        return VLC_EGENERIC;
118
    }
119
    if( p_sys->url.i_port <= 0 )
120
    {
121
        p_sys->url.i_port = 1755;
122 123 124
    }

    /* *** connect to this server *** */
Laurent Aimar's avatar
Laurent Aimar committed
125
    /* look at  requested protocol (udp/tcp) */
126
    i_proto = MMS_PROTO_AUTO;
127
    if( *p_access->psz_access )
128
    {
129
        if( !strncmp( p_access->psz_access, "mmsu", 4 ) )
130 131 132
        {
            i_proto = MMS_PROTO_UDP;
        }
133
        else if( !strncmp( p_access->psz_access, "mmst", 4 ) )
134 135 136 137
        {
            i_proto = MMS_PROTO_TCP;
        }
    }
Laurent Aimar's avatar
Laurent Aimar committed
138 139

    /* connect */
140
    if( i_proto == MMS_PROTO_AUTO )
141 142 143 144
    {   /* first try with TCP and then UDP*/
        if( ( i_status = MMSOpen( p_access, &p_sys->url, MMS_PROTO_TCP ) ) )
        {
            i_status = MMSOpen( p_access, &p_sys->url, MMS_PROTO_UDP );
145 146 147 148
        }
    }
    else
    {
149
        i_status = MMSOpen( p_access, &p_sys->url, i_proto );
150 151
    }

Laurent Aimar's avatar
Laurent Aimar committed
152
    if( i_status )
153
    {
154 155
        msg_Err( p_access, "cannot connect to server" );
        vlc_UrlClean( &p_sys->url );
156
        free( p_sys );
Laurent Aimar's avatar
Laurent Aimar committed
157
        return VLC_EGENERIC;
158 159
    }

160
    msg_Dbg( p_access, "connected to %s:%d", p_sys->url.psz_host, p_sys->url.i_port );
161 162 163 164 165 166
    /*
     * i_flags_broadcast
     *  yy xx ?? ??
     *  broadcast    yy=0x02, xx= 0x00
     *  pre-recorded yy=0x01, xx= 0x80 if video, 0x00 no video
     */
167 168 169 170
    if( p_sys->i_packet_count <= 0 && p_sys->asfh.i_data_packets_count > 0 )
    {
        p_sys->i_packet_count = p_sys->asfh.i_data_packets_count;
    }
171 172
    if( p_sys->i_packet_count <= 0 || ( p_sys->i_flags_broadcast >> 24 ) == 0x02 )
    {
173
        p_sys->b_seekable = VLC_FALSE;
174 175 176
    }
    else
    {
177 178
        p_sys->b_seekable = VLC_TRUE;
        p_access->info.i_size =
179 180
            (uint64_t)p_sys->i_header +
            (uint64_t)p_sys->i_packet_count * (uint64_t)p_sys->i_packet_length;
181 182 183
    }

    /* *** Start stream *** */
184
    if( MMSStart( p_access, 0xffffffff ) < 0 )
185
    {
186
        msg_Err( p_access, "cannot start stream" );
187
        E_(MMSTUClose) ( p_access );
Laurent Aimar's avatar
Laurent Aimar committed
188
        return VLC_EGENERIC;
189
    }
Laurent Aimar's avatar
Laurent Aimar committed
190
    return VLC_SUCCESS;
191 192 193 194 195
}

/*****************************************************************************
 * Close: free unused data structures
 *****************************************************************************/
196
void E_(MMSTUClose)( access_t *p_access )
197
{
198
    access_sys_t *p_sys = p_access->p_sys;
199 200

    /* close connection with server */
201
    MMSClose( p_access );
202 203

    /* free memory */
204
    vlc_UrlClean( &p_sys->url );
205 206 207 208

    free( p_sys );
}

209 210 211 212 213 214 215 216 217
/*****************************************************************************
 * Control:
 *****************************************************************************/
static int Control( access_t *p_access, int i_query, va_list args )
{
    access_sys_t *p_sys = p_access->p_sys;
    vlc_bool_t   *pb_bool;
    int          *pi_int;
    int64_t      *pi_64;
218
    int           i_int;
219 220 221 222 223 224 225 226 227
    vlc_value_t  val;

    switch( i_query )
    {
        /* */
        case ACCESS_CAN_SEEK:
            pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
            *pb_bool = p_sys->b_seekable;
            break;
228

229 230
        case ACCESS_CAN_FASTSEEK:
        case ACCESS_CAN_PAUSE:
231 232 233 234
            pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
            *pb_bool = VLC_FALSE;
            break;

235 236
        case ACCESS_CAN_CONTROL_PACE:
            pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
237 238 239

#if 0       /* Disable for now until we have a clock synchro algo
             * which works with something else than MPEG over UDP */
240
            *pb_bool = VLC_FALSE;
241 242
#endif
            *pb_bool = VLC_TRUE;
243 244 245 246 247 248 249 250 251 252 253
            break;

        /* */
        case ACCESS_GET_MTU:
            pi_int = (int*)va_arg( args, int * );
            *pi_int = 3 * p_sys->i_packet_length;
            break;

        case ACCESS_GET_PTS_DELAY:
            pi_64 = (int64_t*)va_arg( args, int64_t * );
            var_Get( p_access, "mms-caching", &val );
254
            *pi_64 = (int64_t)var_GetInteger( p_access, "mms-caching" ) * I64C(1000);
255 256
            break;

257 258
        case ACCESS_GET_PRIVATE_ID_STATE:
            i_int = (int)va_arg( args, int );
259
            pb_bool = (vlc_bool_t *)va_arg( args, vlc_bool_t * );
260 261 262 263 264 265

            if( i_int < 0 || i_int > 127 )
                return VLC_EGENERIC;
            *pb_bool =  p_sys->asfh.stream[i_int].i_selected ? VLC_TRUE : VLC_FALSE;
            break;

266 267 268 269 270
        /* */
        case ACCESS_SET_PAUSE_STATE:
        case ACCESS_GET_TITLE_INFO:
        case ACCESS_SET_TITLE:
        case ACCESS_SET_SEEKPOINT:
271
        case ACCESS_SET_PRIVATE_ID_STATE:
272 273
            return VLC_EGENERIC;

274

275
        default:
276
            msg_Warn( p_access, "unimplemented query in control" );
277 278 279 280 281 282
            return VLC_EGENERIC;

    }
    return VLC_SUCCESS;
}

283 284 285
/*****************************************************************************
 * Seek: try to go at the right place
 *****************************************************************************/
286
static int Seek( access_t * p_access, int64_t i_pos )
287
{
288
    access_sys_t *p_sys = p_access->p_sys;
289 290
    uint32_t    i_packet;
    uint32_t    i_offset;
291
    var_buffer_t buffer;
292 293

    if( i_pos < 0 )
294
        return VLC_EGENERIC;
295 296 297 298

    if( i_pos < p_sys->i_header)
    {

299
        if( p_access->info.i_pos < p_sys->i_header )
300 301 302
        {
            /* no need to restart stream, it was already one
             * or no stream was yet read */
303 304
            p_access->info.i_pos = i_pos;
            return VLC_SUCCESS;
305 306 307 308 309 310 311 312 313 314 315 316
        }
        else
        {
            i_packet = 0xffffffff;
            i_offset = 0;
        }
    }
    else
    {
        i_packet = ( i_pos - p_sys->i_header ) / p_sys->i_packet_length;
        i_offset = ( i_pos - p_sys->i_header ) % p_sys->i_packet_length;
    }
317
    msg_Dbg( p_access, "seeking to "I64Fd " (packet:%d)", i_pos, i_packet );
318

319 320
    MMSStop( p_access );
    msg_Dbg( p_access, "stream stopped (seek)" );
321 322 323 324 325 326 327 328 329 330 331 332

    /* *** restart stream *** */
    var_buffer_initwrite( &buffer, 0 );
    var_buffer_add64( &buffer, 0 ); /* seek point in second */
    var_buffer_add32( &buffer, 0xffffffff );
    var_buffer_add32( &buffer, i_packet ); // begin from start
    var_buffer_add8( &buffer, 0xff ); // stream time limit
    var_buffer_add8( &buffer, 0xff ); //  on 3bytes ...
    var_buffer_add8( &buffer, 0xff ); //
    var_buffer_add8( &buffer, 0x00 ); // don't use limit
    var_buffer_add32( &buffer, p_sys->i_media_packet_id_type );

333
    mms_CommandSend( p_access, 0x07, p_sys->i_command_level, 0x0001ffff,
334 335 336 337 338
                     buffer.p_data, buffer.i_data );

    var_buffer_free( &buffer );


339
    while( !p_access->b_die )
340
    {
341
        mms_HeaderMediaRead( p_access, MMS_PACKET_CMD );
342 343
        if( p_sys->i_command == 0x1e )
        {
344
            msg_Dbg( p_access, "received 0x1e (seek)" );
345 346 347 348
            break;
        }
    }

349
    while( !p_access->b_die )
350
    {
351
        mms_HeaderMediaRead( p_access, MMS_PACKET_CMD );
352 353
        if( p_sys->i_command == 0x05 )
        {
354
            msg_Dbg( p_access, "received 0x05 (seek)" );
355 356 357 358 359
            break;
        }
    }

    /* get a packet */
360 361
    mms_HeaderMediaRead( p_access, MMS_PACKET_MEDIA );
    msg_Dbg( p_access, "Streaming restarted" );
362 363

    p_sys->i_media_used += i_offset;
364 365 366 367
    p_access->info.i_pos = i_pos;
    p_access->info.b_eof = VLC_FALSE;

    return VLC_SUCCESS;
368 369
}

370 371 372
/*****************************************************************************
 * Read:
 *****************************************************************************/
373
static int Read( access_t *p_access, uint8_t *p_buffer, int i_len )
374
{
375
    access_sys_t *p_sys = p_access->p_sys;
376 377 378 379 380 381
    size_t      i_data;
    size_t      i_copy;

    i_data = 0;

    /* *** now send data if needed *** */
382
    while( i_data < (size_t)i_len )
383
    {
384
        if( p_access->info.i_pos < p_sys->i_header )
385
        {
386 387
            i_copy = __MIN( i_len, p_sys->i_header - p_access->info.i_pos );
            memcpy( &p_buffer[i_data], &p_sys->p_header[p_access->info.i_pos], i_copy );
388
            i_data += i_copy;
389
            p_access->info.i_pos += i_copy;
390 391
        }
        else if( p_sys->i_media_used < p_sys->i_media )
392 393 394
        {
            i_copy = __MIN( i_len - i_data ,
                            p_sys->i_media - p_sys->i_media_used );
395
            memcpy( &p_buffer[i_data], &p_sys->p_media[p_sys->i_media_used], i_copy );
396 397
            i_data += i_copy;
            p_sys->i_media_used += i_copy;
398
            p_access->info.i_pos += i_copy;
399 400 401 402 403 404
        }
        else if( p_sys->p_media != NULL &&
                 p_sys->i_media_used < p_sys->i_packet_length )
        {
            i_copy = __MIN( i_len - i_data,
                            p_sys->i_packet_length - p_sys->i_media_used);
405
            memset( &p_buffer[i_data], 0, i_copy );
406 407 408

            i_data += i_copy;
            p_sys->i_media_used += i_copy;
409
            p_access->info.i_pos += i_copy;
410
        }
411 412
        else if( p_access->info.b_eof ||
                 mms_HeaderMediaRead( p_access, MMS_PACKET_MEDIA ) < 0 )
413
        {
414
            break;
415 416 417
        }
    }

418
    return i_data;
419 420 421 422 423
}

/****************************************************************************
 * MMSOpen : Open a connection with the server over mmst or mmsu
 ****************************************************************************/
424
static int MMSOpen( access_t  *p_access, vlc_url_t *p_url, int  i_proto )
425
{
426 427
    access_sys_t *p_sys = p_access->p_sys;
    int           b_udp = ( i_proto == MMS_PROTO_UDP ) ? 1 : 0;
428 429 430 431 432 433 434 435 436 437 438 439 440 441

    var_buffer_t buffer;
    char         tmp[4096];
    uint16_t     *p;
    int          i_server_version;
    int          i_tool_version;
    int          i_update_player_url;
    int          i_encryption_type;
    int          i;
    int          i_streams;
    int          i_first;


    /* *** Open a TCP connection with server *** */
442
    msg_Dbg( p_access, "waiting for connection..." );
443
    p_sys->i_handle_tcp = net_ConnectTCP( p_access, p_url->psz_host, p_url->i_port );
444
    if( p_sys->i_handle_tcp < 0 )
445
    {
446
        msg_Err( p_access, "failed to open a connection (tcp)" );
Laurent Aimar's avatar
Laurent Aimar committed
447
        return VLC_EGENERIC;
448
    }
449
    msg_Dbg( p_access,
450 451 452 453 454 455 456
             "connection(tcp) with \"%s:%d\" successful",
             p_url->psz_host,
             p_url->i_port );

    /* *** Bind port if UDP protocol is selected *** */
    if( b_udp )
    {
457 458
        if( net_GetSockAddress( p_sys->i_handle_tcp, p_sys->sz_bind_addr,
                                NULL ) )
459
        {
460
            net_Close( p_sys->i_handle_tcp );
Laurent Aimar's avatar
Laurent Aimar committed
461
            return VLC_EGENERIC;
462
        }
463

464
        p_sys->i_handle_udp = net_ListenUDP1( (vlc_object_t *)p_access, p_sys->sz_bind_addr,
465
                                              7000 );
466
        if( p_sys->i_handle_udp < 0 )
467
        {
468 469
            msg_Err( p_access, "failed to open a connection (udp)" );
            net_Close( p_sys->i_handle_tcp );
Laurent Aimar's avatar
Laurent Aimar committed
470
            return VLC_EGENERIC;
471
        }
472
        msg_Dbg( p_access,
473
                 "connection(udp) at \"%s:%d\" successful",
474
                 p_sys->sz_bind_addr, 7000 );
475 476 477 478
    }

    /* *** Init context for mms prototcol *** */
    E_( GenerateGuid )( &p_sys->guid );    /* used to identify client by server */
479
    msg_Dbg( p_access,
480 481 482 483 484 485 486 487 488 489 490 491 492 493
             "generated guid: "GUID_FMT,
             GUID_PRINT( p_sys->guid ) );
    p_sys->i_command_level = 1;          /* updated after 0x1A command */
    p_sys->i_seq_num = 0;
    p_sys->i_media_packet_id_type  = 0x04;
    p_sys->i_header_packet_id_type = 0x02;
    p_sys->i_proto = i_proto;
    p_sys->i_packet_seq_num = 0;
    p_sys->p_header = NULL;
    p_sys->i_header = 0;
    p_sys->p_media = NULL;
    p_sys->i_media = 0;
    p_sys->i_media_used = 0;

494
    p_access->info.i_pos = 0;
495 496 497 498
    p_sys->i_buffer_tcp = 0;
    p_sys->i_buffer_udp = 0;
    p_sys->p_cmd = NULL;
    p_sys->i_cmd = 0;
499
    p_access->info.b_eof = 0;
500 501 502 503 504 505 506 507 508 509 510

    /* *** send command 1 : connection request *** */
    var_buffer_initwrite( &buffer, 0 );
    var_buffer_add16( &buffer, 0x001c );
    var_buffer_add16( &buffer, 0x0003 );
    sprintf( tmp,
             "NSPlayer/7.0.0.1956; {"GUID_FMT"}; Host: %s",
             GUID_PRINT( p_sys->guid ),
             p_url->psz_host );
    var_buffer_addUTF16( &buffer, tmp );

511
    mms_CommandSend( p_access,
512 513 514 515 516 517
                     0x01,          /* connexion request */
                     0x00000000,    /* flags, FIXME */
                     0x0004000b,    /* ???? */
                     buffer.p_data,
                     buffer.i_data );

518
    if( mms_CommandRead( p_access, 0x01, 0 ) < 0 )
519 520
    {
        var_buffer_free( &buffer );
521
        MMSClose( p_access );
Laurent Aimar's avatar
Laurent Aimar committed
522
        return VLC_EGENERIC;
523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538
    }

    i_server_version = GetDWLE( p_sys->p_cmd + MMS_CMD_HEADERSIZE + 32 );
    i_tool_version = GetDWLE( p_sys->p_cmd + MMS_CMD_HEADERSIZE + 36 );
    i_update_player_url = GetDWLE( p_sys->p_cmd + MMS_CMD_HEADERSIZE + 40 );
    i_encryption_type = GetDWLE( p_sys->p_cmd + MMS_CMD_HEADERSIZE + 44 );
    p = (uint16_t*)( p_sys->p_cmd + MMS_CMD_HEADERSIZE + 48 );
#define GETUTF16( psz, size ) \
    { \
        int i; \
        psz = malloc( size + 1); \
        for( i = 0; i < size; i++ ) \
        { \
            psz[i] = p[i]; \
        } \
        psz[size] = '\0'; \
539
        p += ( size ); \
540 541 542 543 544 545
    }
    GETUTF16( p_sys->psz_server_version, i_server_version );
    GETUTF16( p_sys->psz_tool_version, i_tool_version );
    GETUTF16( p_sys->psz_update_player_url, i_update_player_url );
    GETUTF16( p_sys->psz_encryption_type, i_encryption_type );
#undef GETUTF16
546
    msg_Dbg( p_access,
547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563
             "0x01 --> server_version:\"%s\" tool_version:\"%s\" update_player_url:\"%s\" encryption_type:\"%s\"",
             p_sys->psz_server_version,
             p_sys->psz_tool_version,
             p_sys->psz_update_player_url,
             p_sys->psz_encryption_type );

    /* *** should make an 18 command to make data timing *** */

    /* *** send command 2 : transport protocol selection *** */
    var_buffer_reinitwrite( &buffer, 0 );
    var_buffer_add32( &buffer, 0x00000000 );
    var_buffer_add32( &buffer, 0x000a0000 );
    var_buffer_add32( &buffer, 0x00000002 );
    if( b_udp )
    {
        sprintf( tmp,
                 "\\\\%s\\UDP\\%d",
564
                 p_sys->sz_bind_addr,
565 566 567 568 569 570 571 572 573
                 7000 ); // FIXME
    }
    else
    {
        sprintf( tmp, "\\\\192.168.0.1\\TCP\\1242"  );
    }
    var_buffer_addUTF16( &buffer, tmp );
    var_buffer_add16( &buffer, '0' );

574
    mms_CommandSend( p_access,
575 576 577 578 579 580 581
                     0x02,          /* connexion request */
                     0x00000000,    /* flags, FIXME */
                     0xffffffff,    /* ???? */
                     buffer.p_data,
                     buffer.i_data );

    /* *** response from server, should be 0x02 or 0x03 *** */
582
    mms_CommandRead( p_access, 0x02, 0x03 );
583 584
    if( p_sys->i_command == 0x03 )
    {
585
        msg_Err( p_access,
586 587
                 "%s protocol selection failed", b_udp ? "UDP" : "TCP" );
        var_buffer_free( &buffer );
588
        MMSClose( p_access );
Laurent Aimar's avatar
Laurent Aimar committed
589
        return VLC_EGENERIC;
590 591 592
    }
    else if( p_sys->i_command != 0x02 )
    {
593
        msg_Warn( p_access, "received command isn't 0x02 in reponse to 0x02" );
594 595 596 597 598 599 600
    }

    /* *** send command 5 : media file name/path requested *** */
    var_buffer_reinitwrite( &buffer, 0 );
    var_buffer_add64( &buffer, 0 );
    var_buffer_addUTF16( &buffer, p_url->psz_path );

601
    mms_CommandSend( p_access,
602 603 604 605 606 607 608
                     0x05,
                     p_sys->i_command_level,
                     0xffffffff,
                     buffer.p_data,
                     buffer.i_data );

    /* *** wait for reponse *** */
609
    mms_CommandRead( p_access, 0x1a, 0x06 );
610 611 612 613

    /* test if server send 0x1A answer */
    if( p_sys->i_command == 0x1A )
    {
614
        msg_Err( p_access, "id/password requested (not yet supported)" );
615 616
        /*  FIXME */
        var_buffer_free( &buffer );
617
        MMSClose( p_access );
Laurent Aimar's avatar
Laurent Aimar committed
618
        return VLC_EGENERIC;
619 620 621
    }
    if( p_sys->i_command != 0x06 )
    {
622
        msg_Err( p_access,
623 624 625
                 "unknown answer (0x%x instead of 0x06)",
                 p_sys->i_command );
        var_buffer_free( &buffer );
626
        MMSClose( p_access );
627 628 629 630 631 632 633
        return( -1 );
    }

    /*  1 for file ok, 2 for authen ok */
    switch( GetDWLE( p_sys->p_cmd + MMS_CMD_HEADERSIZE ) )
    {
        case 0x0001:
634
            msg_Dbg( p_access, "media file name/path accepted" );
635 636
            break;
        case 0x0002:
637
            msg_Dbg( p_access, "authentication accepted" );
638 639 640
            break;
        case -1:
        default:
641
        msg_Err( p_access, "error while asking for file %d",
642 643
                 GetDWLE( p_sys->p_cmd + MMS_CMD_HEADERSIZE ) );
        var_buffer_free( &buffer );
644
        MMSClose( p_access );
Laurent Aimar's avatar
Laurent Aimar committed
645
        return VLC_EGENERIC;
646 647 648 649 650 651 652 653 654 655 656 657 658 659 660
    }

    p_sys->i_flags_broadcast =
        GetDWLE( p_sys->p_cmd + MMS_CMD_HEADERSIZE + 12 );
    p_sys->i_media_length =
        GetDWLE( p_sys->p_cmd + MMS_CMD_HEADERSIZE + 24 );
    p_sys->i_packet_length =
        GetDWLE( p_sys->p_cmd + MMS_CMD_HEADERSIZE + 44 );
    p_sys->i_packet_count =
        GetDWLE( p_sys->p_cmd + MMS_CMD_HEADERSIZE + 48 );
    p_sys->i_max_bit_rate =
        GetDWLE( p_sys->p_cmd + MMS_CMD_HEADERSIZE + 56 );
    p_sys->i_header_size =
        GetDWLE( p_sys->p_cmd + MMS_CMD_HEADERSIZE + 60 );

661
    msg_Dbg( p_access,
662
             "answer 0x06 flags:0x%8.8x media_length:%us packet_length:%ul packet_count:%u max_bit_rate:%d header_size:%d",
663 664
             p_sys->i_flags_broadcast,
             p_sys->i_media_length,
665
             p_sys->i_packet_length,
666 667 668 669 670 671 672 673 674 675 676 677 678
             p_sys->i_packet_count,
             p_sys->i_max_bit_rate,
             p_sys->i_header_size );

    /* *** send command 15 *** */

    var_buffer_reinitwrite( &buffer, 0 );
    var_buffer_add32( &buffer, 0 );
    var_buffer_add32( &buffer, 0x8000 );
    var_buffer_add32( &buffer, 0xffffffff );
    var_buffer_add32( &buffer, 0x00 );
    var_buffer_add32( &buffer, 0x00 );
    var_buffer_add32( &buffer, 0x00 );
Sam Hocevar's avatar
Sam Hocevar committed
679
    var_buffer_add64( &buffer, (((uint64_t)0x40ac2000)<<32) );
680
    var_buffer_add32( &buffer, p_sys->i_header_packet_id_type );
681
    var_buffer_add32( &buffer, 0x00 );
682
    mms_CommandSend( p_access, 0x15, p_sys->i_command_level, 0x00,
683 684 685
                     buffer.p_data, buffer.i_data );

    /* *** wait for reponse *** */
686 687
    /* Commented out because it fails on some stream (no 0x11 answer) */
#if 0
688
    mms_CommandRead( p_access, 0x11, 0 );
689 690 691

    if( p_sys->i_command != 0x11 )
    {
692
        msg_Err( p_access,
693 694 695
                 "unknown answer (0x%x instead of 0x11)",
                 p_sys->i_command );
        var_buffer_free( &buffer );
696
        MMSClose( p_access );
697 698
        return( -1 );
    }
699 700
#endif

701 702
    /* *** now read header packet *** */
    /* XXX could be split over multiples packets */
703
    msg_Dbg( p_access, "reading header" );
704 705
    for( ;; )
    {
706
        if( mms_HeaderMediaRead( p_access, MMS_PACKET_HEADER ) < 0 )
707
        {
708
            msg_Err( p_access, "cannot receive header" );
709
            var_buffer_free( &buffer );
710
            MMSClose( p_access );
Laurent Aimar's avatar
Laurent Aimar committed
711
            return VLC_EGENERIC;
712 713 714
        }
        if( p_sys->i_header >= p_sys->i_header_size )
        {
715
            msg_Dbg( p_access,
716 717 718 719
                     "header complete(%d)",
                     p_sys->i_header );
            break;
        }
720
        msg_Dbg( p_access,
721 722 723 724 725 726 727 728 729 730 731 732 733
                 "header incomplete (%d/%d), reading more",
                 p_sys->i_header,
                 p_sys->i_header_size );
    }

    /* *** parse header and get stream and their id *** */
    /* get all streams properties,
     *
     * TODO : stream bitrates properties(optional)
     *        and bitrate mutual exclusion(optional) */
    E_( asf_HeaderParse )( &p_sys->asfh,
                           p_sys->p_header, p_sys->i_header );
    E_( asf_StreamSelect)( &p_sys->asfh,
734 735 736 737
                           var_CreateGetInteger( p_access, "mms-maxbitrate" ),
                           var_CreateGetInteger( p_access, "mms-all" ),
                           var_CreateGetInteger( p_access, "audio" ),
                           var_CreateGetInteger( p_access, "video" ) );
738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762

    /* *** now select stream we want to receive *** */
    /* TODO take care of stream bitrate TODO */
    i_streams = 0;
    i_first = -1;
    var_buffer_reinitwrite( &buffer, 0 );
    /* for now, select first audio and video stream */
    for( i = 1; i < 128; i++ )
    {

        if( p_sys->asfh.stream[i].i_cat != ASF_STREAM_UNKNOWN )
        {
            i_streams++;
            if( i_first != -1 )
            {
                var_buffer_add16( &buffer, 0xffff );
                var_buffer_add16( &buffer, i );
            }
            else
            {
                i_first = i;
            }
            if( p_sys->asfh.stream[i].i_selected )
            {
                var_buffer_add16( &buffer, 0x0000 );
763
                msg_Info( p_access,
764 765 766 767 768 769 770 771 772
                          "selecting stream[0x%x] %s (%d kb/s)",
                          i,
                          ( p_sys->asfh.stream[i].i_cat == ASF_STREAM_AUDIO  ) ?
                                                  "audio" : "video" ,
                          p_sys->asfh.stream[i].i_bitrate / 1024);
            }
            else
            {
                var_buffer_add16( &buffer, 0x0002 );
773
                msg_Info( p_access,
774 775 776 777 778 779 780 781 782 783 784 785
                          "ignoring stream[0x%x] %s (%d kb/s)",
                          i,
                          ( p_sys->asfh.stream[i].i_cat == ASF_STREAM_AUDIO  ) ?
                                    "audio" : "video" ,
                          p_sys->asfh.stream[i].i_bitrate / 1024);

            }
        }
    }

    if( i_streams == 0 )
    {
786
        msg_Err( p_access, "cannot find any stream" );
787
        var_buffer_free( &buffer );
788
        MMSClose( p_access );
Laurent Aimar's avatar
Laurent Aimar committed
789
        return VLC_EGENERIC;
790
    }
791
    mms_CommandSend( p_access, 0x33,
792 793 794 795
                     i_streams,
                     0xffff | ( i_first << 16 ),
                     buffer.p_data, buffer.i_data );

796
    mms_CommandRead( p_access, 0x21, 0 );
797 798
    if( p_sys->i_command != 0x21 )
    {
799
        msg_Err( p_access,
800 801 802
                 "unknown answer (0x%x instead of 0x21)",
                 p_sys->i_command );
        var_buffer_free( &buffer );
803
        MMSClose( p_access );
Laurent Aimar's avatar
Laurent Aimar committed
804
        return VLC_EGENERIC;
805 806 807 808 809
    }


    var_buffer_free( &buffer );

810
    msg_Info( p_access, "connection successful" );
811

Laurent Aimar's avatar
Laurent Aimar committed
812
    return VLC_SUCCESS;
813 814 815 816 817
}

/****************************************************************************
 * MMSStart : Start streaming
 ****************************************************************************/
818
static int MMSStart( access_t  *p_access, uint32_t i_packet )
819
{
820
    access_sys_t        *p_sys = p_access->p_sys;
821 822 823 824 825 826 827 828 829 830 831 832 833
    var_buffer_t    buffer;

    /* *** start stream from packet 0 *** */
    var_buffer_initwrite( &buffer, 0 );
    var_buffer_add64( &buffer, 0 ); /* seek point in second */
    var_buffer_add32( &buffer, 0xffffffff );
    var_buffer_add32( &buffer, i_packet ); // begin from start
    var_buffer_add8( &buffer, 0xff ); // stream time limit
    var_buffer_add8( &buffer, 0xff ); //  on 3bytes ...
    var_buffer_add8( &buffer, 0xff ); //
    var_buffer_add8( &buffer, 0x00 ); // don't use limit
    var_buffer_add32( &buffer, p_sys->i_media_packet_id_type );

834
    mms_CommandSend( p_access, 0x07, p_sys->i_command_level, 0x0001ffff,
835 836 837 838
                     buffer.p_data, buffer.i_data );

    var_buffer_free( &buffer );

839
    mms_CommandRead( p_access, 0x05, 0 );
840 841 842

    if( p_sys->i_command != 0x05 )
    {
843
        msg_Err( p_access,
844 845 846 847 848 849 850
                 "unknown answer (0x%x instead of 0x05)",
                 p_sys->i_command );
        return( -1 );
    }
    else
    {
        /* get a packet */
851
        mms_HeaderMediaRead( p_access, MMS_PACKET_MEDIA );
852
        msg_Dbg( p_access, "streaming started" );
853 854 855 856 857 858 859
        return( 0 );
    }
}

/****************************************************************************
 * MMSStop : Stop streaming
 ****************************************************************************/
860
static int MMSStop( access_t  *p_access )
861
{
862
    access_sys_t *p_sys = p_access->p_sys;
863 864

    /* *** stop stream but keep connection alive *** */
865
    mms_CommandSend( p_access,
866 867 868 869 870 871 872 873 874 875
                     0x09,
                     p_sys->i_command_level,
                     0x001fffff,
                     NULL, 0 );
    return( 0 );
}

/****************************************************************************
 * MMSClose : Close streaming and connection
 ****************************************************************************/
876
static void MMSClose( access_t  *p_access )
877
{
878
    access_sys_t        *p_sys = p_access->p_sys;
879

880
    msg_Dbg( p_access, "Connection closed" );
881 882

    /* *** tell server that we will disconnect *** */
883
    mms_CommandSend( p_access,
884 885 886 887 888
                     0x0d,
                     p_sys->i_command_level,
                     0x00000001,
                     NULL, 0 );

Laurent Aimar's avatar
Laurent Aimar committed
889
    /* *** close sockets *** */
890
    net_Close( p_sys->i_handle_tcp );
891 892
    if( p_sys->i_proto == MMS_PROTO_UDP )
    {
893
        net_Close( p_sys->i_handle_udp );
894 895
    }

zorglub's avatar
zorglub committed
896 897 898 899 900 901 902 903
    FREENULL( p_sys->p_cmd );
    FREENULL( p_sys->p_media );
    FREENULL( p_sys->p_header );

    FREENULL( p_sys->psz_server_version );
    FREENULL( p_sys->psz_tool_version );
    FREENULL( p_sys->psz_update_player_url );
    FREENULL( p_sys->psz_encryption_type );
904 905 906 907 908 909 910
}

/****************************************************************************
 *
 * MMS specific functions
 *
 ****************************************************************************/
911 912 913
static int mms_CommandSend( access_t *p_access, int i_command,
                            uint32_t i_prefix1, uint32_t i_prefix2,
                            uint8_t *p_data, int i_data_old )
914 915
{
    var_buffer_t buffer;
916 917
    access_sys_t *p_sys = p_access->p_sys;
    int i_data_by8, i_ret;
hartman's avatar
hartman committed
918
    int i_data = i_data_old;
919

hartman's avatar
hartman committed
920 921
    while( i_data & 0x7 ) i_data++;
    i_data_by8 = i_data >> 3;
922

hartman's avatar
hartman committed
923
    /* first init buffer */
924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941
    var_buffer_initwrite( &buffer, 0 );

    var_buffer_add32( &buffer, 0x00000001 );    /* start sequence */
    var_buffer_add32( &buffer, 0xB00BFACE );
    /* size after protocol type */
    var_buffer_add32( &buffer, i_data + MMS_CMD_HEADERSIZE -