sgimb.c 16.5 KB
Newer Older
hartman's avatar
hartman committed
1
2
3
/*****************************************************************************
 * sgimb.c: a meta demux to parse sgimb referrer files
 *****************************************************************************
4
 * Copyright (C) 2004 the VideoLAN team
hartman's avatar
hartman committed
5
 * $Id$
hartman's avatar
hartman committed
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 *
 * Authors: Derk-Jan Hartman <hartman at videolan dot org>
 *
 * 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.
hartman's avatar
hartman committed
22
23
24
25
26
27
28
29
 *****************************************************************************/

/*****************************************************************************
 * This is a metademux for the Kasenna MediaBase metafile format.
 * Kasenna MediaBase first returns this file when you are trying to access
 * their MPEG streams (MIME: application/x-sgimb). Very few applications
 * understand this format and the format is not really documented on the net.
 * Following a typical MediaBase file. Notice the sgi prefix of all the elements.
30
 * This stems from the fact that the MediaBase servers were first introduced by SGI?????.
hartman's avatar
hartman committed
31
32
 *
 * sgiNameServerHost=host.name.tld
33
 *     Obvious: the host hosting this stream
hartman's avatar
hartman committed
34
 * Stream="xdma://host.name.tld/demo/a_very_cool.mpg"
35
 *     Not always present. xdma can be read as RTSP.
hartman's avatar
hartman committed
36
 * sgiMovieName=/demo/a_very_cool.mpg
37
38
39
40
41
42
43
 *     The path to the asset
 * sgiAuxState=1|2
 *     AuxState=2 is always Video On Demand (so not Scheduled)
 *     Not present with Live streams
 * sgiLiveFeed=True|False
 *     Denounces if the stream is live or from assets (Canned?)
 *     Live appears as a little sattelite dish in the web interface of Kasenna
hartman's avatar
hartman committed
44
 * sgiFormatName=PARTNER_41_MPEG-4
45
46
47
48
49
50
51
52
53
 *     The type of stream. One of:
 *       PARTNER_41_MPEG-4 (RTSP MPEG-4 fully compliant)
 *       MPEG1-Audio       (MP3 Audio streams in MPEG TS)
 *       MPEG-1            (MPEG 1 A/V in MPEG TS)
 *       MPEG-2            (MPEG 2 A/V in MPEG TS)
 * sgiWidth=720
 *     The width of the to be received stream. Only present if stream is not Live.
 * sgiHeight=576
 *     The height of the to be received stream. Only present if stream is not Live.
hartman's avatar
hartman committed
54
 * sgiBitrate=1630208
55
 *     The bitrate of the to be received stream. Only present if stream is not Live.
hartman's avatar
hartman committed
56
 * sgiDuration=378345000
57
 *     The duration of the to be received stream. Only present if stream is not Live.
hartman's avatar
hartman committed
58
59
60
61
 * sgiQTFileBegin
 * rtsptext
 * rtsp://host.name.tld/demo/a_very_cool.mpg
 * sgiQTFileEnd
62
 *     Sometimes present. QT will recognize this as a RTSP reference file, if present.
hartman's avatar
hartman committed
63
 * sgiApplicationName=MediaBaseURL
64
 *     Beats me !! :)
hartman's avatar
hartman committed
65
 * sgiElapsedTime=0
66
67
68
69
70
71
72
73
74
 *     Time passed since the asset was started (resets for repeating non live assets?)
 * sgiMulticastAddress=233.81.233.15
 *     The multicast IP used for the Multicast feed.
 *     Also defines if a stream is multicast or not. (blue dot in kasenna web interface)
 * sgiMulticastPort=1234
 *     The multicast port for the same Multicast feed.
 * sgiPacketSize=16384
 *     The packetsize of the UDP frames that Kasenna sends. They should have used a default
 *     that is a multiple of 188 (TS frame size). Most networks don't support more than 1500 anyways.
trax's avatar
trax committed
75
 *     Also, when you lose a frame of this size, imagecorruption is more likely then with smaller
76
 *     frames.
hartman's avatar
hartman committed
77
 * sgiServerVersion=6.1.2
78
 *     Version of the server
hartman's avatar
hartman committed
79
 * sgiRtspPort=554
80
 *     TCP port used for RTSP communication
hartman's avatar
hartman committed
81
 * AutoStart=True
82
83
84
85
86
87
88
 *     Start playing automatically
 * DeliveryService=cds
 *     Simulcasted (scheduled unicast) content. (Green dot in Kasenna web interface) 
 * sgiShowingName=A nice name that everyone likes
 *     A human readible descriptive title for this stream.
 * sgiSid=2311
 *     Looks like this is the ID of the scheduled asset?
hartman's avatar
hartman committed
89
 * sgiUserAccount=pid=1724&time=1078527309&displayText=You%20are%20logged%20as%20guest&
90
 *     User Authentication. Above is a default guest entry. Not required for RTSP communication.
hartman's avatar
hartman committed
91
 * sgiUserPassword=
92
 *     Password :)
hartman's avatar
hartman committed
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
 *
 *****************************************************************************/


/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <stdlib.h>                                      /* malloc(), free() */

#include <vlc/vlc.h>
#include <vlc/input.h>
#include <vlc_playlist.h>

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
static int  Activate  ( vlc_object_t * );
static void Deactivate( vlc_object_t * );

vlc_module_begin();
zorglub's avatar
zorglub committed
113
    set_description( _("Kasenna MediaBase parser") );
zorglub's avatar
zorglub committed
114
115
    set_category( CAT_INPUT );
    set_subcategory( SUBCAT_INPUT_DEMUX );
hartman's avatar
hartman committed
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
    set_capability( "demux2", 170 );
    set_callbacks( Activate, Deactivate );
    add_shortcut( "sgimb" );
vlc_module_end();

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
#define MAX_LINE 1024

struct demux_sys_t
{
    char        *psz_uri;       /* Stream= or sgiQTFileBegin rtsp link */
    char        *psz_server;    /* sgiNameServerHost= */
    char        *psz_location;  /* sgiMovieName= */
    char        *psz_name;      /* sgiShowingName= */
    char        *psz_user;      /* sgiUserAccount= */
    char        *psz_password;  /* sgiUserPassword= */
hartman's avatar
hartman committed
134
135
136
    char        *psz_mcast_ip;  /* sgiMulticastAddress= */
    int         i_mcast_port;   /* sgiMulticastPort= */
    int         i_packet_size;  /* sgiPacketSize= */
hartman's avatar
hartman committed
137
138
139
    mtime_t     i_duration;     /* sgiDuration= */
    int         i_port;         /* sgiRtspPort= */
    int         i_sid;          /* sgiSid= */
140
    vlc_bool_t  b_concert;      /* DeliveryService=cds */
141
    vlc_bool_t  b_rtsp_kasenna; /* kasenna style RTSP */
hartman's avatar
hartman committed
142
143
144
145
146
147
148
149
150
151
152
153
};

static int Demux ( demux_t *p_demux );
static int Control( demux_t *p_demux, int i_query, va_list args );

/*****************************************************************************
 * Activate: initializes m3u demux structures
 *****************************************************************************/
static int Activate( vlc_object_t * p_this )
{
    demux_t *p_demux = (demux_t *)p_this;
    demux_sys_t *p_sys;
154
155
    byte_t *p_peek;
    int i_size;
hartman's avatar
hartman committed
156
157

    /* Lets check the content to see if this is a sgi mediabase file */
158
    i_size = stream_Peek( p_demux->s, &p_peek, MAX_LINE );
hartman's avatar
hartman committed
159
    i_size -= sizeof("sgiNameServerHost=") - 1;
160
161
    if ( i_size > 0 )
    {
162
163
        unsigned int i_len = sizeof("sgiNameServerHost=") - 1;
        while ( i_size && strncasecmp( (char *)p_peek, "sgiNameServerHost=", i_len ) )
hartman's avatar
hartman committed
164
165
166
167
        {
            p_peek++;
            i_size--;
        }
168
        if ( !strncasecmp( (char *)p_peek, "sgiNameServerHost=", i_len ) )
hartman's avatar
hartman committed
169
        {
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
            p_demux->pf_demux = Demux;
            p_demux->pf_control = Control;

            p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) );
            p_sys->psz_uri = NULL;
            p_sys->psz_server = NULL;
            p_sys->psz_location = NULL;
            p_sys->psz_name = NULL;
            p_sys->psz_user = NULL;
            p_sys->psz_password = NULL;
            p_sys->psz_mcast_ip = NULL;
            p_sys->i_mcast_port = 0;
            p_sys->i_packet_size = 0;
            p_sys->i_duration = 0;
            p_sys->i_port = 0;
185
            p_sys->i_sid = 0;
186
            p_sys->b_rtsp_kasenna = VLC_FALSE;
187
            p_sys->b_concert = VLC_FALSE;
188

hartman's avatar
hartman committed
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
            return VLC_SUCCESS;
        }
    }
    return VLC_EGENERIC;
}

/*****************************************************************************
 * Deactivate: frees unused data
 *****************************************************************************/
static void Deactivate( vlc_object_t *p_this )
{
    demux_t *p_demux = (demux_t*)p_this;
    demux_sys_t *p_sys = p_demux->p_sys;
    if( p_sys->psz_uri )
        free( p_sys->psz_uri );
    if( p_sys->psz_server )
        free( p_sys->psz_server );
    if( p_sys->psz_location )
        free( p_sys->psz_location );
    if( p_sys->psz_name )
        free( p_sys->psz_name );
    if( p_sys->psz_user )
        free( p_sys->psz_user );
    if( p_sys->psz_password )
        free( p_sys->psz_password );
hartman's avatar
hartman committed
214
215
    if( p_sys->psz_mcast_ip )
        free( p_sys->psz_mcast_ip );
hartman's avatar
hartman committed
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
    free( p_demux->p_sys );
    return;
}

static int ParseLine ( demux_t *p_demux, char *psz_line )
{
    char        *psz_bol;
    demux_sys_t *p_sys = p_demux->p_sys;

    psz_bol = psz_line;

    /* Remove unnecessary tabs or spaces at the beginning of line */
    while( *psz_bol == ' ' || *psz_bol == '\t' ||
           *psz_bol == '\n' || *psz_bol == '\r' )
    {
        psz_bol++;
    }

    if( !strncasecmp( psz_bol, "rtsp://", sizeof("rtsp://") - 1 ) )
    {
        /* We found the link, it was inside a sgiQTFileBegin */
        p_sys->psz_uri = strdup( psz_bol );
    }
    else if( !strncasecmp( psz_bol, "Stream=\"", sizeof("Stream=\"") - 1 ) )
    {
        psz_bol += sizeof("Stream=\"") - 1;
        if ( !psz_bol )
            return 0;
        strrchr( psz_bol, '"' )[0] = '\0';
        /* We cheat around xdma. for some reason xdma links work different then rtsp */
        if( !strncasecmp( psz_bol, "xdma://", sizeof("xdma://") - 1 ) )
        {
            psz_bol[0] = 'r';
            psz_bol[1] = 't';
            psz_bol[2] = 's';
            psz_bol[3] = 'p';
        }
        p_sys->psz_uri = strdup( psz_bol );
    }
    else if( !strncasecmp( psz_bol, "sgiNameServerHost=", sizeof("sgiNameServerHost=") - 1 ) )
    {
        psz_bol += sizeof("sgiNameServerHost=") - 1;
        p_sys->psz_server = strdup( psz_bol );
    }
    else if( !strncasecmp( psz_bol, "sgiMovieName=", sizeof("sgiMovieName=") - 1 ) )
    {
        psz_bol += sizeof("sgiMovieName=") - 1;
        p_sys->psz_location = strdup( psz_bol );
    }
    else if( !strncasecmp( psz_bol, "sgiUserAccount=", sizeof("sgiUserAccount=") - 1 ) )
    {
        psz_bol += sizeof("sgiUserAccount=") - 1;
        p_sys->psz_user = strdup( psz_bol );
    }
    else if( !strncasecmp( psz_bol, "sgiUserPassword=", sizeof("sgiUserPassword=") - 1 ) )
    {
        psz_bol += sizeof("sgiUserPassword=") - 1;
        p_sys->psz_password = strdup( psz_bol );
    }
    else if( !strncasecmp( psz_bol, "sgiShowingName=", sizeof("sgiShowingName=") - 1 ) )
    {
        psz_bol += sizeof("sgiShowingName=") - 1;
        p_sys->psz_name = strdup( psz_bol );
    }
280
281
282
    else if( !strncasecmp( psz_bol, "sgiFormatName=", sizeof("sgiFormatName=") - 1 ) )
    {
        psz_bol += sizeof("sgiFormatName=") - 1;
283
        if( strcasestr( psz_bol, "MPEG-4") == NULL ) /*not mpeg4 found in string */
284
285
            p_sys->b_rtsp_kasenna = VLC_TRUE;
    }
hartman's avatar
hartman committed
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
    else if( !strncasecmp( psz_bol, "sgiMulticastAddress=", sizeof("sgiMulticastAddress=") - 1 ) )
    {
        psz_bol += sizeof("sgiMulticastAddress=") - 1;
        p_sys->psz_mcast_ip = strdup( psz_bol );
    }
    else if( !strncasecmp( psz_bol, "sgiMulticastPort=", sizeof("sgiMulticastPort=") - 1 ) )
    {
        psz_bol += sizeof("sgiMulticastPort=") - 1;
        p_sys->i_mcast_port = (int) strtol( psz_bol, NULL, 0 );
    }
    else if( !strncasecmp( psz_bol, "sgiPacketSize=", sizeof("sgiPacketSize=") - 1 ) )
    {
        psz_bol += sizeof("sgiPacketSize=") - 1;
        p_sys->i_packet_size = (int) strtol( psz_bol, NULL, 0 );
    }
hartman's avatar
hartman committed
301
302
303
304
305
306
307
308
309
    else if( !strncasecmp( psz_bol, "sgiDuration=", sizeof("sgiDuration=") - 1 ) )
    {
        psz_bol += sizeof("sgiDuration=") - 1;
        p_sys->i_duration = (mtime_t) strtol( psz_bol, NULL, 0 );
    }
    else if( !strncasecmp( psz_bol, "sgiRtspPort=", sizeof("sgiRtspPort=") - 1 ) )
    {
        psz_bol += sizeof("sgiRtspPort=") - 1;
        p_sys->i_port = (int) strtol( psz_bol, NULL, 0 );
310
311
312
313
314
315
316
317
318
319
320
    }
    else if( !strncasecmp( psz_bol, "sgiSid=", sizeof("sgiSid=") - 1 ) )
    {
        psz_bol += sizeof("sgiSid=") - 1;
        p_sys->i_sid = (int) strtol( psz_bol, NULL, 0 );
    }
    else if( !strncasecmp( psz_bol, "DeliveryService=cds", sizeof("DeliveryService=cds") - 1 ) )
    {
        p_sys->b_concert = VLC_TRUE;
    }
    else
hartman's avatar
hartman committed
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
    {
        /* This line isn't really important */
        return 0;
    }
    return VLC_SUCCESS;
}

/*****************************************************************************
 * Demux: reads and demuxes data packets
 *****************************************************************************
 * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
 *****************************************************************************/
static int Demux ( demux_t *p_demux )
{
    demux_sys_t     *p_sys = p_demux->p_sys;
    playlist_t      *p_playlist;
hartman's avatar
hartman committed
337
    playlist_item_t *p_item;
hartman's avatar
hartman committed
338
339
340
341
342
343
344
345
346
347
    char            *psz_line;

    p_playlist = (playlist_t *) vlc_object_find( p_demux, VLC_OBJECT_PLAYLIST,
                                                 FIND_ANYWHERE );
    if( !p_playlist )
    {
        msg_Err( p_demux, "can't find playlist" );
        return -1;
    }

348
349
350
    p_item = playlist_LockItemGetByInput( p_playlist,
                        ((input_thread_t *)p_demux->p_parent)->input.p_item );
    playlist_ItemToNode( p_playlist, p_item );
hartman's avatar
hartman committed
351
352
353
354
355
356

    while( ( psz_line = stream_ReadLine( p_demux->s ) ) )
    {
        ParseLine( p_demux, psz_line );
        if( psz_line ) free( psz_line );
    }
hartman's avatar
hartman committed
357
358
359

    if( p_sys->psz_mcast_ip )
    {
360
361
        /* Definetly schedules multicast session */
        /* We don't care if it's live or not */
hartman's avatar
hartman committed
362
363
        char *temp;

364
        asprintf( &temp, "udp://@" "%s:%i", p_sys->psz_mcast_ip, p_sys->i_mcast_port );
hartman's avatar
hartman committed
365
366
367
368
        if( p_sys->psz_uri ) free( p_sys->psz_uri );
        p_sys->psz_uri = strdup( temp );
        free( temp );
    }
369
370

    if( p_sys->psz_uri == NULL )
hartman's avatar
hartman committed
371
372
373
374
    {
        if( p_sys->psz_server && p_sys->psz_location )
        {
            char *temp;
375

376
            asprintf( &temp, "rtsp://" "%s:%i%s",
hartman's avatar
hartman committed
377
                     p_sys->psz_server, p_sys->i_port > 0 ? p_sys->i_port : 554, p_sys->psz_location );
378

hartman's avatar
hartman committed
379
380
381
382
            p_sys->psz_uri = strdup( temp );
            free( temp );
        }
    }
hartman's avatar
hartman committed
383

384
385
386
387
388
389
390
391
392
393
394
    if( p_sys->b_concert )
    {
        /* It's definetly a simulcasted scheduled stream */
        /* We don't care if it's live or not */
        char *temp;

        if( p_sys->psz_uri == NULL )
        {
            msg_Err( p_demux, "no URI was found" );
            return -1;
        }
395

396
397
398
399
400
401
402
403
        asprintf( &temp, "%s%%3FMeDiAbAsEshowingId=%d%%26MeDiAbAsEconcert%%3FMeDiAbAsE",
                p_sys->psz_uri, p_sys->i_sid );

        free( p_sys->psz_uri );
        p_sys->psz_uri = strdup( temp );
        free( temp );
    }

404
405
    msg_Err( p_playlist, "SGIMB playlist handling is broken" );
#if 0
406
    p_child = playlist_ItemNew( p_playlist, p_sys->psz_uri,
hartman's avatar
hartman committed
407
408
                      p_sys->psz_name ? p_sys->psz_name : p_sys->psz_uri );

409
    if( !p_child || !p_child->input.psz_uri )
hartman's avatar
hartman committed
410
411
412
413
414
415
416
    {
        msg_Err( p_demux, "A valid playlistitem could not be created" );
        return VLC_EGENERIC;
    }

    if( p_sys->i_packet_size && p_sys->psz_mcast_ip )
    {
417
        char *psz_option;
418
        p_sys->i_packet_size += 1000;
419
        asprintf( &psz_option, "mtu=%i", p_sys->i_packet_size );
420
        playlist_ItemAddOption( p_child, psz_option );
hartman's avatar
hartman committed
421
422
        free( psz_option );
    }
423
424
425
    if( !p_sys->psz_mcast_ip )
    {
        char *psz_option;
426
427
428
        asprintf( &psz_option, "rtsp-caching=5000" );
        playlist_ItemAddOption( p_child, psz_option );
        free( psz_option );
429
430
    }
    if( !p_sys->psz_mcast_ip && p_sys->b_rtsp_kasenna )
431
432
433
    {
        char *psz_option;
        asprintf( &psz_option, "rtsp-kasenna" );
434
        playlist_ItemAddOption( p_child, psz_option );
435
436
        free( psz_option );
    }
hartman's avatar
hartman committed
437

438
439
440
441
442
443
    playlist_ItemSetDuration( p_child, p_sys->i_duration );
    playlist_NodeAddItem( p_playlist, p_child, p_item->pp_parents[0]->i_view, p_item, PLAYLIST_APPEND, PLAYLIST_END );
    playlist_CopyParents( p_item, p_child );
    playlist_Control( p_playlist, PLAYLIST_VIEWPLAY,
                           p_playlist->status.i_view,
                           p_playlist->status.p_item, NULL );
444
#endif
hartman's avatar
hartman committed
445
446
447
448
449
450
451
452
453
    vlc_object_release( p_playlist );
    return VLC_SUCCESS;
}

static int Control( demux_t *p_demux, int i_query, va_list args )
{
    return VLC_EGENERIC;
}