Ticket #1215: pulse.patch

File pulse.patch, 16.8 kB (added by martin, 10 months ago)

patch with initial implemantation

  • configure.ac

    old new  
    46464646   fi]) 
    46474647 
    46484648dnl 
     4649dnl  Pulseaudio module 
     4650dnl 
     4651AC_ARG_ENABLE(pulse, 
     4652  [  --enable-pulse          Pulseaudio support (default disabled)], 
     4653  [ 
     4654    if test "x$enable_pulse" != "xno"; then 
     4655    PKG_CHECK_MODULES([PULSEAUDIO], [libpulse], [ 
     4656       VLC_ADD_PLUGINS([pulse]) 
     4657       VLC_ADD_CFLAGS([pulse],[`pkg-config --cflags libpulse`]) 
     4658       VLC_ADD_LIBS([pulse],[`pkg-config --libs libpulse`]) 
     4659     ], [ 
     4660    AS_IF([test "${enable_pulse}" = "yes"],[ 
     4661        AC_MSG_WARN( libpulse not found)  
     4662       ]) 
     4663    ]) 
     4664   fi]) 
     4665 
     4666dnl 
    46494667dnl  Portaudio module 
    46504668dnl 
    46514669AC_ARG_ENABLE(portaudio, 
  • modules/audio_output/Modules.am

    old new  
    1010SOURCES_portaudio = portaudio.c 
    1111SOURCES_auhal = auhal.c 
    1212SOURCES_jack = jack.c 
     13SOURCES_pulse = pulse.c 
  • modules/audio_output/pulse.c

    old new  
     1/***************************************************************************** 
     2 * pulse.c : Pulseaudio output plugin for vlc 
     3 ***************************************************************************** 
     4 * Copyright (C) 2000-2002 the VideoLAN team 
     5 * 
     6 * Authors: Martin Hamrle <hamrle@post.cz> 
     7 * 
     8 * This program is free software; you can redistribute it and/or modify 
     9 * it under the terms of the GNU General Public License as published by 
     10 * the Free Software Foundation; either version 2 of the License, or 
     11 * (at your option) any later version. 
     12 * 
     13 * This program is distributed in the hope that it will be useful, 
     14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
     15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     16 * GNU General Public License for more details. 
     17 * 
     18 * You should have received a copy of the GNU General Public License 
     19 * along with this program; if not, write to the Free Software 
     20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. 
     21 *****************************************************************************/ 
     22 
     23/***************************************************************************** 
     24 * Preamble 
     25 *****************************************************************************/ 
     26#ifdef HAVE_CONFIG_H 
     27# include "config.h" 
     28#endif 
     29 
     30#include <vlc/vlc.h> 
     31#include <vlc_aout.h> 
     32 
     33#include <pulse/pulseaudio.h> 
     34 
     35/***************************************************************************** 
     36 * aout_sys_t: Pulseaudio output method descriptor 
     37 ***************************************************************************** 
     38 * This structure is part of the audio output thread descriptor. 
     39 * It describes the specific properties of an audio device. 
     40 *****************************************************************************/ 
     41struct aout_sys_t 
     42{ 
     43    /** PulseAudio playback stream object */ 
     44    struct pa_stream *stream; 
     45 
     46    /** PulseAudio connection context */ 
     47    struct pa_context *context; 
     48 
     49    /** Main event loop object */ 
     50    struct pa_threaded_mainloop *mainloop; 
     51 
     52    int started; 
     53    size_t buffer_size; 
     54    mtime_t start_date; 
     55}; 
     56 
     57#define PULSE_CLIENT_NAME "vlc" 
     58 
     59#define CHECK_DEAD_GOTO(label) do { \ 
     60if (!p_sys->context || pa_context_get_state(p_sys->context) != PA_CONTEXT_READY || \ 
     61    !p_sys->stream || pa_stream_get_state(p_sys->stream) != PA_STREAM_READY) { \ 
     62        msg_Err(p_aout, "Connection died: %s\n", p_sys->context ? pa_strerror(pa_context_errno(p_sys->context)) : "NULL"); \ 
     63        goto label; \ 
     64    }  \ 
     65} while(0); 
     66/***************************************************************************** 
     67 * Local prototypes 
     68 *****************************************************************************/ 
     69static int  Open        ( vlc_object_t * ); 
     70static void Close       ( vlc_object_t * ); 
     71static void Play        ( aout_instance_t * ); 
     72 
     73static void context_state_cb(pa_context *c, void *userdata); 
     74static void stream_state_cb(pa_stream *s, void * userdata); 
     75static void stream_request_cb(pa_stream *s, size_t length, void *userdata); 
     76static void stream_latency_update_cb(pa_stream *s, void *userdata); 
     77static void success_cb(pa_stream *s, int success, void *userdata); 
     78static void uninit(aout_instance_t *p_aout); 
     79/***************************************************************************** 
     80 * Module descriptor 
     81 *****************************************************************************/ 
     82vlc_module_begin(); 
     83    set_shortname( "pulse" ); 
     84    set_description( _("Pulseaudio audio output") ); 
     85    set_capability( "audio output", 40 ); 
     86    set_category( CAT_AUDIO ); 
     87    set_subcategory( SUBCAT_AUDIO_AOUT ); 
     88    add_shortcut( "pulse" ); 
     89    set_callbacks( Open, Close ); 
     90vlc_module_end(); 
     91 
     92/***************************************************************************** 
     93 * Open: open the audio device 
     94 *****************************************************************************/ 
     95static int Open ( vlc_object_t *p_this ) 
     96{ 
     97    aout_instance_t *p_aout = (aout_instance_t *)p_this; 
     98    struct aout_sys_t * p_sys; 
     99    struct pa_sample_spec ss; 
     100    const struct pa_buffer_attr *buffer_attr; 
     101    struct pa_buffer_attr a; 
     102    struct pa_channel_map map; 
     103         
     104    /* Allocate structures */ 
     105    p_aout->output.p_sys = p_sys = malloc( sizeof( aout_sys_t ) ); 
     106    if( p_sys == NULL ) 
     107    { 
     108        msg_Err( p_aout, "out of memory" ); 
     109        return VLC_ENOMEM; 
     110    } 
     111    p_sys->started = 0; 
     112    p_sys->stream = NULL; 
     113    p_sys->mainloop = NULL; 
     114    p_sys->context = NULL; 
     115 
     116    msg_Dbg(p_aout, "Pulse start initialization\n"); 
     117 
     118    ss.rate = p_aout->output.output.i_rate; 
     119    ss.channels = 2; 
     120 
     121    ss.format = PA_SAMPLE_S16LE; 
     122    p_aout->output.output.i_physical_channels = 
     123            AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT; 
     124    p_aout->output.output.i_format = AOUT_FMT_S16_NE; 
     125 
     126    if (!pa_sample_spec_valid(&ss)) { 
     127        msg_Err(p_aout,"Invalid sample spec\n"); 
     128        goto fail; 
     129    } 
     130     
     131    a.maxlength = pa_bytes_per_second(&ss)/4/pa_frame_size(&ss); 
     132    a.tlength = a.maxlength*9/10; 
     133    a.prebuf = a.tlength/2; 
     134    a.minreq = a.tlength/10; 
     135 
     136    a.maxlength *= pa_frame_size(&ss); 
     137    a.tlength *= pa_frame_size(&ss); 
     138    a.prebuf *= pa_frame_size(&ss); 
     139    a.minreq *= pa_frame_size(&ss); 
     140 
     141    p_sys->buffer_size = a.minreq; 
     142 
     143    pa_channel_map_init_stereo(&map); 
     144 
     145 
     146    if (!(p_sys->mainloop = pa_threaded_mainloop_new())) { 
     147        msg_Err(p_aout, "Failed to allocate main loop\n"); 
     148        goto fail; 
     149    } 
     150 
     151    if (!(p_sys->context = pa_context_new(pa_threaded_mainloop_get_api(p_sys->mainloop), PULSE_CLIENT_NAME))) { 
     152        msg_Err(p_aout, "Failed to allocate context\n"); 
     153        goto fail; 
     154    } 
     155 
     156    pa_context_set_state_callback(p_sys->context, context_state_cb, p_aout); 
     157 
     158    msg_Dbg(p_aout, "Pulse before context connect\n"); 
     159 
     160    if (pa_context_connect(p_sys->context, NULL, 0, NULL) < 0) { 
     161        msg_Err(p_aout, "Failed to connect to server: %s\n", pa_strerror(pa_context_errno(p_sys->context))); 
     162        goto fail; 
     163    } 
     164 
     165    msg_Dbg(p_aout, "Pulse after context connect\n"); 
     166 
     167    pa_threaded_mainloop_lock(p_sys->mainloop); 
     168     
     169    if (pa_threaded_mainloop_start(p_sys->mainloop) < 0) { 
     170        msg_Err(p_aout, "Failed to start main loop\n"); 
     171        goto unlock_and_fail; 
     172    } 
     173 
     174    msg_Dbg(p_aout, "Pulse mainloop started\n"); 
     175 
     176    /* Wait until the context is ready */ 
     177    pa_threaded_mainloop_wait(p_sys->mainloop); 
     178 
     179    if (pa_context_get_state(p_sys->context) != PA_CONTEXT_READY) { 
     180        msg_Err(p_aout, "Failed to connect to server: %s\n", pa_strerror(pa_context_errno(p_sys->context))); 
     181        goto unlock_and_fail; 
     182    } 
     183 
     184    if (!(p_sys->stream = pa_stream_new(p_sys->context, "audio stream", &ss, &map))) { 
     185        msg_Err(p_aout, "Failed to create stream: %s\n", pa_strerror(pa_context_errno(p_sys->context))); 
     186        goto unlock_and_fail; 
     187    } 
     188 
     189    msg_Dbg(p_aout, "Pulse after new stream\n"); 
     190 
     191    pa_stream_set_state_callback(p_sys->stream, stream_state_cb, p_aout); 
     192    pa_stream_set_write_callback(p_sys->stream, stream_request_cb, p_aout); 
     193    pa_stream_set_latency_update_callback(p_sys->stream, stream_latency_update_cb, p_aout); 
     194 
     195    if (pa_stream_connect_playback(p_sys->stream, NULL, &a, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL) < 0) { 
     196        msg_Err(p_aout, "Failed to connect stream: %s\n", pa_strerror(pa_context_errno(p_sys->context))); 
     197        goto unlock_and_fail; 
     198    } 
     199 
     200    msg_Dbg(p_aout, "Pulse stream connect\n"); 
     201 
     202    /* Wait until the stream is ready */ 
     203    pa_threaded_mainloop_wait(p_sys->mainloop); 
     204 
     205    msg_Dbg(p_aout, "Pulse stream connected\n"); 
     206 
     207    if (pa_stream_get_state(p_sys->stream) != PA_STREAM_READY) { 
     208        msg_Err(p_aout, "Failed to connect to server: %s\n", pa_strerror(pa_context_errno(p_sys->context))); 
     209        goto unlock_and_fail; 
     210    } 
     211 
     212 
     213    msg_Dbg(p_aout, "Pulse after stream get status\n"); 
     214 
     215    pa_threaded_mainloop_unlock(p_sys->mainloop); 
     216     
     217    buffer_attr = pa_stream_get_buffer_attr(p_sys->stream); 
     218    p_aout->output.i_nb_samples = buffer_attr->minreq / pa_frame_size(&ss); 
     219    p_aout->output.pf_play = Play; 
     220    msg_Dbg(p_aout, "Pulse initialized successfully\n"); 
     221    { 
     222            char cmt[PA_CHANNEL_MAP_SNPRINT_MAX], sst[PA_SAMPLE_SPEC_SNPRINT_MAX]; 
     223 
     224            msg_Dbg(p_aout, "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u\n", buffer_attr->maxlength, buffer_attr->tlength, buffer_attr->prebuf, buffer_attr->minreq); 
     225            msg_Dbg(p_aout, "Using sample spec '%s', channel map '%s'.\n", 
     226                            pa_sample_spec_snprint(sst, sizeof(sst), pa_stream_get_sample_spec(p_sys->stream)), 
     227                            pa_channel_map_snprint(cmt, sizeof(cmt), pa_stream_get_channel_map(p_sys->stream))); 
     228 
     229            msg_Dbg(p_aout, "Connected to device %s (%u, %ssuspended).\n", 
     230                        pa_stream_get_device_name(p_sys->stream), 
     231                        pa_stream_get_device_index(p_sys->stream), 
     232                        pa_stream_is_suspended(p_sys->stream) ? "" : "not "); 
     233    } 
     234                     
     235    return VLC_SUCCESS; 
     236 
     237unlock_and_fail: 
     238    msg_Dbg(p_aout, "Pulse initialization unlock and fail\n"); 
     239 
     240    if (p_sys->mainloop) 
     241        pa_threaded_mainloop_unlock(p_sys->mainloop); 
     242fail: 
     243    msg_Err(p_aout, "Pulse initialization failed\n"); 
     244    uninit(p_aout); 
     245    return VLC_EGENERIC; 
     246} 
     247 
     248/***************************************************************************** 
     249 * Play: play a sound samples buffer 
     250 *****************************************************************************/ 
     251static void Play( aout_instance_t * p_aout ) 
     252{ 
     253    struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys; 
     254 
     255    pa_operation *o; 
     256 
     257    if(!p_sys->started){ 
     258            msg_Dbg(p_aout, "Pulse stream started\n"); 
     259            p_sys->start_date = 
     260                    aout_FifoFirstDate( p_aout, &p_aout->output.fifo ); 
     261            p_sys->started = 1; 
     262 
     263             
     264            pa_threaded_mainloop_lock(p_sys->mainloop); 
     265            if((o = pa_stream_flush(p_sys->stream, success_cb, p_aout))){ 
     266                pa_operation_unref(o); 
     267            } 
     268            pa_threaded_mainloop_unlock(p_sys->mainloop); 
     269 
     270            pa_threaded_mainloop_signal(p_sys->mainloop, 0); 
     271    } 
     272} 
     273 
     274/***************************************************************************** 
     275 * Close: close the audio device 
     276 *****************************************************************************/ 
     277static void Close ( vlc_object_t *p_this ) 
     278{ 
     279    aout_instance_t *p_aout = (aout_instance_t *)p_this; 
     280    struct aout_sys_t * p_sys = p_aout->output.p_sys; 
     281 
     282    msg_Dbg(p_aout, "Pulse Close\n"); 
     283 
     284    if(p_sys->stream){ 
     285            pa_operation *o; 
     286            pa_threaded_mainloop_lock(p_sys->mainloop); 
     287            pa_stream_set_write_callback(p_sys->stream, NULL, NULL); 
     288 
     289            if((o = pa_stream_drain(p_sys->stream, success_cb, p_aout))){ 
     290                    while (pa_operation_get_state(o) != PA_OPERATION_DONE) { 
     291                            CHECK_DEAD_GOTO(fail); 
     292                            pa_threaded_mainloop_wait(p_sys->mainloop); 
     293                    } 
     294 
     295                fail: 
     296 
     297                    pa_operation_unref(o); 
     298            } 
     299 
     300            pa_threaded_mainloop_unlock(p_sys->mainloop); 
     301    } 
     302    uninit(p_aout); 
     303} 
     304 
     305static void uninit(aout_instance_t *p_aout){ 
     306    struct aout_sys_t * p_sys = p_aout->output.p_sys; 
     307 
     308    if (p_sys->mainloop) 
     309            pa_threaded_mainloop_stop(p_sys->mainloop); 
     310     
     311    if (p_sys->stream) { 
     312        pa_stream_disconnect(p_sys->stream); 
     313        pa_stream_unref(p_sys->stream); 
     314        p_sys->stream = NULL; 
     315    } 
     316 
     317    if (p_sys->context) { 
     318        pa_context_disconnect(p_sys->context); 
     319        pa_context_unref(p_sys->context); 
     320        p_sys->context = NULL; 
     321    } 
     322 
     323    if (p_sys->mainloop) { 
     324        pa_threaded_mainloop_free(p_sys->mainloop); 
     325        p_sys->mainloop = NULL; 
     326    } 
     327 
     328    free(p_sys); 
     329    p_aout->output.p_sys = NULL; 
     330} 
     331 
     332static void context_state_cb(pa_context *c, void *userdata) { 
     333    aout_instance_t *p_aout = (aout_instance_t *)userdata; 
     334    struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys; 
     335 
     336    assert(c); 
     337 
     338    msg_Dbg(p_aout, "Pulse context state changed\n"); 
     339 
     340    switch (pa_context_get_state(c)) { 
     341        case PA_CONTEXT_READY: 
     342        case PA_CONTEXT_TERMINATED: 
     343        case PA_CONTEXT_FAILED: 
     344                msg_Dbg(p_aout, "Pulse context state changed signal\n"); 
     345            pa_threaded_mainloop_signal(p_sys->mainloop, 0); 
     346            break; 
     347 
     348        case PA_CONTEXT_UNCONNECTED: 
     349        case PA_CONTEXT_CONNECTING: 
     350        case PA_CONTEXT_AUTHORIZING: 
     351        case PA_CONTEXT_SETTING_NAME: 
     352            msg_Dbg(p_aout, "Pulse context state changed no signal\n"); 
     353            break; 
     354    } 
     355} 
     356 
     357static void stream_state_cb(pa_stream *s, void * userdata) { 
     358    aout_instance_t *p_aout = (aout_instance_t *)userdata; 
     359    struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys; 
     360 
     361    assert(s); 
     362   
     363    msg_Dbg(p_aout, "Pulse stream state changed\n"); 
     364 
     365    switch (pa_stream_get_state(s)) { 
     366 
     367        case PA_STREAM_READY: 
     368        case PA_STREAM_FAILED: 
     369        case PA_STREAM_TERMINATED: 
     370            pa_threaded_mainloop_signal(p_sys->mainloop, 0); 
     371            break; 
     372 
     373        case PA_STREAM_UNCONNECTED: 
     374        case PA_STREAM_CREATING: 
     375            break; 
     376    } 
     377} 
     378 
     379static void stream_request_cb(pa_stream *s, size_t length, void *userdata) { 
     380    aout_instance_t *p_aout = (aout_instance_t *)userdata; 
     381    struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys; 
     382    mtime_t next_date;  
     383 
     384    assert(s); 
     385    assert(p_sys); 
     386 
     387    size_t buffer_size = p_sys->buffer_size; 
     388 
     389    msg_Dbg(p_aout, "Pulse stream request %d\n", length); 
     390 
     391    do{ 
     392            aout_buffer_t *   p_buffer = NULL; 
     393            if(p_sys->started){ 
     394                    pa_usec_t latency; 
     395                    int negative; 
     396                    if(pa_stream_get_latency(p_sys->stream, &latency, &negative)<0){ 
     397                            if (pa_context_errno(p_sys->context) != PA_ERR_NODATA) { 
     398                                    msg_Err(p_aout, "pa_stream_get_latency() failed: %s\n", pa_strerror(pa_context_errno(p_sys->context))); 
     399                            } 
     400                            latency = 0; 
     401 
     402                    } 
     403                    msg_Dbg(p_aout, "Pulse stream request latency="I64Fd"\n", latency); 
     404                    next_date = mdate() + latency; 
     405 
     406 
     407                    if(p_sys->start_date < next_date + AOUT_PTS_TOLERANCE ){ 
     408    /* 
     409                    vlc_mutex_lock( &p_aout->output_fifo_lock ); 
     410                    p_buffer = aout_FifoPop( p_aout, &p_aout->output.fifo ); 
     411                    vlc_mutex_unlock( &p_aout->output_fifo_lock ); 
     412    */               
     413                            p_buffer = aout_OutputNextBuffer( p_aout, next_date, 0); 
     414                    } 
     415            } 
     416 
     417            if ( p_buffer != NULL ) 
     418            { 
     419                    msg_Dbg(p_aout, "Pulse stream request write buffer %d\n", p_buffer->i_nb_bytes); 
     420                    pa_stream_write(p_sys->stream, p_buffer->p_buffer, p_buffer->i_nb_bytes, NULL, 0, PA_SEEK_RELATIVE); 
     421                    length -= p_buffer->i_nb_bytes; 
     422                    aout_BufferFree( p_buffer ); 
     423            } 
     424            else 
     425            { 
     426                    msg_Dbg(p_aout, "Pulse stream request write zeroes\n"); 
     427                    void *data = pa_xmalloc(buffer_size); 
     428                    bzero(data, buffer_size); 
     429                    pa_stream_write(p_sys->stream, data, buffer_size, pa_xfree, 0, PA_SEEK_RELATIVE); 
     430                    length -= buffer_size; 
     431            } 
     432    }while(length > buffer_size); 
     433 
     434    pa_threaded_mainloop_signal(p_sys->mainloop, 0); 
     435} 
     436 
     437static void stream_latency_update_cb(pa_stream *s, void *userdata) { 
     438    aout_instance_t *p_aout = (aout_instance_t *)userdata; 
     439    struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys; 
     440 
     441    assert(s); 
     442     
     443    msg_Dbg(p_aout, "Pulse stream latency update\n"); 
     444 
     445    pa_threaded_mainloop_signal(p_sys->mainloop, 0); 
     446} 
     447 
     448static void success_cb(pa_stream *s, int success, void *userdata) { 
     449    aout_instance_t *p_aout = (aout_instance_t *)userdata; 
     450    struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys; 
     451    assert(s); 
     452 
     453    pa_threaded_mainloop_signal(p_sys->mainloop, 0); 
     454} 
     455