smem.c 15 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/*****************************************************************************
 * smem.c: stream output to memory buffer module
 *****************************************************************************
 * Copyright (C) 2009 the VideoLAN team
 * $Id$
 *
 * Authors: Christophe Courtaut <christophe.courtaut@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

/*****************************************************************************
 * How to use it
 *****************************************************************************
 *
 * You should use this module in combination with the transcode module, to get
 * raw datas from it. This module does not make any conversion at all, so you
 * need to use the transcode module for this purpose.
 *
 * For example, you can use smem as it :
 * --sout="#transcode{vcodec=RV24,acodec=s16l}:smem{smem-options}"
 *
ivoire's avatar
ivoire committed
35
 * Into each lock function (audio and video), you will have all the information
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
 * you need to allocate a buffer, so that this module will copy data in it.
 *
 * the video-data and audio-data pointers will be passed to lock/unlock function
 *
 ******************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_sout.h>
#include <vlc_block.h>
#include <vlc_codec.h>

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/

#define T_VIDEO_PRERENDER_CALLBACK N_( "Video prerender callback" )
#define LT_VIDEO_PRERENDER_CALLBACK N_( "Address of the video prerender callback function" \
                                "this function will set the buffer where render will be done" )

#define T_AUDIO_PRERENDER_CALLBACK N_( "Audio prerender callback" )
#define LT_AUDIO_PRERENDER_CALLBACK N_( "Address of the audio prerender callback function." \
                                        "this function will set the buffer where render will be done" )

#define T_VIDEO_POSTRENDER_CALLBACK N_( "Video postrender callback" )
#define LT_VIDEO_POSTRENDER_CALLBACK N_( "Address of the video postrender callback function." \
                                        "this function will be called when the render is into the buffer" )

#define T_AUDIO_POSTRENDER_CALLBACK N_( "Audio postrender callback" )
#define LT_AUDIO_POSTRENDER_CALLBACK N_( "Address of the audio postrender callback function." \
                                        "this function will be called when the render is into the buffer" )

#define T_VIDEO_DATA N_( "Video Callback data" )
#define LT_VIDEO_DATA N_( "Data for the video callback function." )

#define T_AUDIO_DATA N_( "Audio callback data" )
#define LT_AUDIO_DATA N_( "Data for the audio callback function." )

#define T_TIME_SYNC N_( "Time Synchronized output" )
#define LT_TIME_SYNC N_( "Time Synchronisation option for output. " \
                        "If true, stream will render as usual, else " \
                        "it will be rendered as fast as possible.")

static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );

#define SOUT_CFG_PREFIX "sout-smem-"
#define SOUT_PREFIX_VIDEO SOUT_CFG_PREFIX"video-"
#define SOUT_PREFIX_AUDIO SOUT_CFG_PREFIX"audio-"

vlc_module_begin ()
95
    set_shortname( N_("Smem"))
96
    set_description( N_("Stream output to memory buffer") )
97
    set_capability( "sout stream", 0 )
98
99
100
101
    add_shortcut( "smem" )
    set_category( CAT_SOUT )
    set_subcategory( SUBCAT_SOUT_STREAM )
    add_string( SOUT_PREFIX_VIDEO "prerender-callback", "0", NULL, T_VIDEO_PRERENDER_CALLBACK, LT_VIDEO_PRERENDER_CALLBACK, true )
102
        change_volatile()
103
    add_string( SOUT_PREFIX_AUDIO "prerender-callback", "0", NULL, T_AUDIO_PRERENDER_CALLBACK, LT_AUDIO_PRERENDER_CALLBACK, true )
104
        change_volatile()
105
    add_string( SOUT_PREFIX_VIDEO "postrender-callback", "0", NULL, T_VIDEO_POSTRENDER_CALLBACK, LT_VIDEO_POSTRENDER_CALLBACK, true )
106
        change_volatile()
107
    add_string( SOUT_PREFIX_AUDIO "postrender-callback", "0", NULL, T_AUDIO_POSTRENDER_CALLBACK, LT_AUDIO_POSTRENDER_CALLBACK, true )
108
        change_volatile()
109
    add_string( SOUT_PREFIX_VIDEO "data", "0", NULL, T_VIDEO_DATA, LT_VIDEO_DATA, true )
110
        change_volatile()
111
    add_string( SOUT_PREFIX_AUDIO "data", "0", NULL, T_AUDIO_DATA, LT_VIDEO_DATA, true )
112
113
114
        change_volatile()
    add_bool( SOUT_CFG_PREFIX "time-sync", true, NULL, T_TIME_SYNC, LT_TIME_SYNC, true )
        change_private()
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
    set_callbacks( Open, Close )
vlc_module_end ()


/*****************************************************************************
 * Exported prototypes
 *****************************************************************************/
static const char *const ppsz_sout_options[] = {
    "video-prerender-callback", "audio-prerender-callback",
    "video-postrender-callback", "audio-postrender-callback", "video-data", "audio-data", "time-sync", NULL
};

static sout_stream_id_t *Add ( sout_stream_t *, es_format_t * );
static int               Del ( sout_stream_t *, sout_stream_id_t * );
static int               Send( sout_stream_t *, sout_stream_id_t *, block_t* );

static sout_stream_id_t *AddVideo( sout_stream_t *p_stream, es_format_t *p_fmt );
static sout_stream_id_t *AddAudio( sout_stream_t *p_stream, es_format_t *p_fmt );

static int SendVideo( sout_stream_t *p_stream, sout_stream_id_t *id,
                      block_t *p_buffer );
static int SendAudio( sout_stream_t *p_stream, sout_stream_id_t *id,
                      block_t *p_buffer );

struct sout_stream_id_t
{
    es_format_t* format;
142
    void *p_data;
143
144
145
146
147
};

struct sout_stream_sys_t
{
    vlc_mutex_t *p_lock;
148
149
    void ( *pf_video_prerender_callback ) ( void* p_video_data, uint8_t** pp_pixel_buffer , int size );
    void ( *pf_audio_prerender_callback ) ( void* p_audio_data, uint8_t** pp_pcm_buffer , unsigned int size );
150
151
    void ( *pf_video_postrender_callback ) ( void* p_video_data, uint8_t* p_pixel_buffer, int width, int height, int pixel_pitch, int size, mtime_t pts );
    void ( *pf_audio_postrender_callback ) ( void* p_audio_data, uint8_t* p_pcm_buffer, unsigned int channels, unsigned int rate, unsigned int nb_samples, unsigned int bits_per_sample, unsigned int size, mtime_t pts );
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
    bool time_sync;
};

/*****************************************************************************
 * Open:
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
{
    char* psz_tmp;
    sout_stream_t *p_stream = (sout_stream_t*)p_this;
    sout_stream_sys_t *p_sys;

    p_sys = calloc( 1, sizeof( sout_stream_sys_t ) );
    if( !p_sys )
        return VLC_ENOMEM;
    p_stream->p_sys = p_sys;

169
170
171
    config_ChainParse( p_stream, SOUT_CFG_PREFIX, ppsz_sout_options,
                       p_stream->p_cfg );

172
    p_sys->time_sync = var_GetBool( p_stream, SOUT_CFG_PREFIX "time-sync" );
173

174
    psz_tmp = var_GetString( p_stream, SOUT_PREFIX_VIDEO "prerender-callback" );
175
176
177
    p_sys->pf_video_prerender_callback = (void (*) (void *, uint8_t**, int))(intptr_t)atoll( psz_tmp );
    free( psz_tmp );

178
    psz_tmp = var_GetString( p_stream, SOUT_PREFIX_AUDIO "prerender-callback" );
179
180
181
    p_sys->pf_audio_prerender_callback = (void (*) (void* , uint8_t**, unsigned int))(intptr_t)atoll( psz_tmp );
    free( psz_tmp );

182
    psz_tmp = var_GetString( p_stream, SOUT_PREFIX_VIDEO "postrender-callback" );
183
    p_sys->pf_video_postrender_callback = (void (*) (void*, uint8_t*, int, int, int, int, mtime_t))(intptr_t)atoll( psz_tmp );
184
185
    free( psz_tmp );

186
    psz_tmp = var_GetString( p_stream, SOUT_PREFIX_AUDIO "postrender-callback" );
187
    p_sys->pf_audio_postrender_callback = (void (*) (void*, uint8_t*, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, mtime_t))(intptr_t)atoll( psz_tmp );
188
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
214
215
216
217
218
219
220
221
222
223
224
225
    free( psz_tmp );

    /* Setting stream out module callbacks */
    p_stream->pf_add    = Add;
    p_stream->pf_del    = Del;
    p_stream->pf_send   = Send;

    /* Does the module need out_pace_control? */
    if ( p_sys->time_sync )
        p_stream->p_sout->i_out_pace_nocontrol++;

    return VLC_SUCCESS;
}

/*****************************************************************************
 * Close:
 *****************************************************************************/
static void Close( vlc_object_t * p_this )
{
    sout_stream_t *p_stream = (sout_stream_t*)p_this;
    if ( p_stream->p_sys->time_sync )
        p_stream->p_sout->i_out_pace_nocontrol--;
    free( p_stream->p_sys );
}

static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
{
    sout_stream_id_t *id = NULL;

    if ( p_fmt->i_cat == VIDEO_ES )
        id = AddVideo( p_stream, p_fmt );
    else if ( p_fmt->i_cat == AUDIO_ES )
        id = AddAudio( p_stream, p_fmt );
    return id;
}

static sout_stream_id_t *AddVideo( sout_stream_t *p_stream, es_format_t *p_fmt )
{
226
    char* psz_tmp;
227
228
229
230
231
232
    sout_stream_id_t    *id;
    int i_bits_per_pixel;

    switch( p_fmt->i_codec )
    {
        case VLC_CODEC_RGB32:
233
        case VLC_CODEC_RGBA:
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
            i_bits_per_pixel = 32;
            break;
        case VLC_CODEC_I444:
        case VLC_CODEC_RGB24:
            i_bits_per_pixel = 24;
            break;
        case VLC_CODEC_RGB16:
        case VLC_CODEC_RGB15:
        case VLC_CODEC_RGB8:
        case VLC_CODEC_I422:
            i_bits_per_pixel = 16;
            break;
        case VLC_CODEC_YV12:
        case VLC_CODEC_I420:
            i_bits_per_pixel = 12;
            break;
        case VLC_CODEC_RGBP:
            i_bits_per_pixel = 8;
            break;
        default:
254
255
256
            i_bits_per_pixel = 0;
            msg_Dbg( p_stream, "non raw video format detected (%4.4s), buffers will contain compressed video", (char *)&p_fmt->i_codec );
            break;
257
258
259
260
261
262
    }

    id = calloc( 1, sizeof( sout_stream_id_t ) );
    if( !id )
        return NULL;

263
    psz_tmp = var_GetString( p_stream, SOUT_PREFIX_VIDEO "data" );
264
    id->p_data = (void *)( intptr_t )atoll( psz_tmp );
265
266
    free( psz_tmp );

267
268
269
270
271
272
273
    id->format = p_fmt;
    id->format->video.i_bits_per_pixel = i_bits_per_pixel;
    return id;
}

static sout_stream_id_t *AddAudio( sout_stream_t *p_stream, es_format_t *p_fmt )
{
274
    char* psz_tmp;
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
    sout_stream_id_t* id;
    int i_bits_per_sample;

    switch( p_fmt->i_codec )
    {
    case VLC_CODEC_U8:
    case VLC_CODEC_S8:
        i_bits_per_sample = 8;
        break;
    case VLC_CODEC_U16L:
    case VLC_CODEC_S16L:
    case VLC_CODEC_U16B:
    case VLC_CODEC_S16B:
        i_bits_per_sample =  16;
        break;
    case VLC_CODEC_U24L:
    case VLC_CODEC_S24L:
    case VLC_CODEC_U24B:
    case VLC_CODEC_S24B:
        i_bits_per_sample = 24;
        break;
    case VLC_CODEC_S32L:
    case VLC_CODEC_S32B:
    case VLC_CODEC_FL32:
    case VLC_CODEC_FI32:
        i_bits_per_sample = 32;
        break;
    case VLC_CODEC_FL64:
        i_bits_per_sample = 64;
        break;
    default:
        msg_Err( p_stream, "Smem does only support raw audio format" );
        return NULL;
    }

    id = calloc( 1, sizeof( sout_stream_id_t ) );
    if( !id )
        return NULL;

314
    psz_tmp = var_GetString( p_stream, SOUT_PREFIX_AUDIO "data" );
315
    id->p_data = (void *)( intptr_t )atoll( psz_tmp );
316
317
    free( psz_tmp );

318
319
320
    id->format = p_fmt;
    id->format->audio.i_bitspersample = i_bits_per_sample;
    return id;
321
322
323
324
325
}

static int Del( sout_stream_t *p_stream, sout_stream_id_t *id )
{
    VLC_UNUSED( p_stream );
326
    free( id );
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
    return VLC_SUCCESS;
}

static int Send( sout_stream_t *p_stream, sout_stream_id_t *id,
                 block_t *p_buffer )
{
    if ( id->format->i_cat == VIDEO_ES )
        return SendVideo( p_stream, id, p_buffer );
    else if ( id->format->i_cat == AUDIO_ES )
        return SendAudio( p_stream, id, p_buffer );
    return VLC_SUCCESS;
}

static int SendVideo( sout_stream_t *p_stream, sout_stream_id_t *id,
                      block_t *p_buffer )
{
    sout_stream_sys_t *p_sys = p_stream->p_sys;
    int i_line, i_line_size, i_size, i_pixel_pitch;
    uint8_t* p_pixels;

347
348
349
350
351
352
353
354
355
356
357
    if( id->format->video.i_bits_per_pixel > 0 )
    {
        i_line = id->format->video.i_height;
        i_pixel_pitch = id->format->video.i_bits_per_pixel / 8;
        i_line_size = i_pixel_pitch * id->format->video.i_width;
        i_size = i_line * i_line_size;
    }
    else
    {
        i_size = p_buffer->i_buffer;
    }
358
    /* Calling the prerender callback to get user buffer */
359
    p_sys->pf_video_prerender_callback( id->p_data, &p_pixels , i_size );
360
    /* Copying data into user buffer */
361
362
363
364
365
366
367
368
369
    if( id->format->video.i_bits_per_pixel > 0 )
    {
        for ( int line = 0; line < i_line; line++, p_pixels += i_line_size )
            vlc_memcpy( p_pixels, p_buffer->p_buffer + i_line_size * line , i_line_size );
    }
    else
    {
        vlc_memcpy( p_pixels, p_buffer->p_buffer, i_size );
    }
370
    /* Calling the postrender callback to tell the user his buffer is ready */
371
    p_sys->pf_video_postrender_callback( id->p_data, p_pixels,
372
373
                                         id->format->video.i_width, id->format->video.i_height,
                                         id->format->video.i_bits_per_pixel, i_size, p_buffer->i_pts );
374
    block_ChainRelease( p_buffer );
375
376
377
378
379
380
    return VLC_SUCCESS;
}

static int SendAudio( sout_stream_t *p_stream, sout_stream_id_t *id,
                      block_t *p_buffer )
{
381
382
383
384
385
386
387
388
    sout_stream_sys_t *p_sys = p_stream->p_sys;
    int i_size;
    uint8_t* p_pcm_buffer;
    int i_samples = 0;

    i_size = p_buffer->i_buffer;
    i_samples = i_size / ( ( id->format->audio.i_bitspersample / 8 ) * id->format->audio.i_channels );
    /* Calling the prerender callback to get user buffer */
389
    p_sys->pf_audio_prerender_callback( id->p_data, &p_pcm_buffer, i_size );
390
391
392
    /* Copying data into user buffer */
    vlc_memcpy( p_pcm_buffer, p_buffer->p_buffer, i_size );
    /* Calling the postrender callback to tell the user his buffer is ready */
393
    p_sys->pf_audio_postrender_callback( id->p_data, p_pcm_buffer,
394
                                         id->format->audio.i_channels, id->format->audio.i_rate, i_samples,
395
396
                                         id->format->audio.i_bitspersample, i_size, p_buffer->i_pts );
    block_ChainRelease( p_buffer );
397
398
    return VLC_SUCCESS;
}
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
399