pulse.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:17k
源码类别:

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * pulse.c : Pulseaudio output plugin for vlc
  3.  *****************************************************************************
  4.  * Copyright (C) 2008 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.  * Preamble
  24.  *****************************************************************************/
  25. #ifdef HAVE_CONFIG_H
  26. # include "config.h"
  27. #endif
  28. #include <vlc_common.h>
  29. #include <vlc_plugin.h>
  30. #include <vlc_aout.h>
  31. #include <pulse/pulseaudio.h>
  32. #include <assert.h>
  33. /*****************************************************************************
  34.  * aout_sys_t: Pulseaudio output method descriptor
  35.  *****************************************************************************
  36.  * This structure is part of the audio output thread descriptor.
  37.  * It describes the specific properties of an audio device.
  38.  *****************************************************************************/
  39. struct aout_sys_t
  40. {
  41.     /** PulseAudio playback stream object */
  42.     struct pa_stream *stream;
  43.     /** PulseAudio connection context */
  44.     struct pa_context *context;
  45.     /** Main event loop object */
  46.     struct pa_threaded_mainloop *mainloop;
  47.     int started;
  48.     size_t buffer_size;
  49.     mtime_t start_date;
  50. };
  51. #define    PULSE_CLIENT_NAME N_("VLC media player")
  52. #if 0
  53. #define PULSE_DEBUG( ...) 
  54.     msg_Dbg( p_aout, __VA_ARGS__ )
  55. #else
  56. #define PULSE_DEBUG( ...) 
  57.     (void) 0
  58. #endif
  59. #define CHECK_DEAD_GOTO(label) do { 
  60. if (!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", p_sys->context ? pa_strerror(pa_context_errno(p_sys->context)) : "NULL"); 
  63.         goto label; 
  64.     }  
  65. } while(0);
  66. /*****************************************************************************
  67.  * Local prototypes
  68.  *****************************************************************************/
  69. static int  Open        ( vlc_object_t * );
  70. static void Close       ( vlc_object_t * );
  71. static void Play        ( aout_instance_t * );
  72. static void context_state_cb(pa_context *c, void *userdata);
  73. static void stream_state_cb(pa_stream *s, void * userdata);
  74. static void stream_request_cb(pa_stream *s, size_t length, void *userdata);
  75. static void stream_latency_update_cb(pa_stream *s, void *userdata);
  76. static void success_cb(pa_stream *s, int sucess, void *userdata);
  77. static void uninit(aout_instance_t *p_aout);
  78. /*****************************************************************************
  79.  * Module descriptor
  80.  *****************************************************************************/
  81. vlc_module_begin ()
  82.     set_shortname( "Pulse Audio" )
  83.     set_description( N_("Pulseaudio audio output") )
  84.     set_capability( "audio output", 160 )
  85.     set_category( CAT_AUDIO )
  86.     set_subcategory( SUBCAT_AUDIO_AOUT )
  87.     add_shortcut( "pulseaudio" )
  88.     add_shortcut( "pa" )
  89.     set_callbacks( Open, Close )
  90.     linked_with_a_crap_library_which_uses_atexit()
  91. vlc_module_end ()
  92. /*****************************************************************************
  93.  * Open: open the audio device
  94.  *****************************************************************************/
  95. static 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.     /* Allocate structures */
  104.     p_aout->output.p_sys = p_sys = calloc( 1, sizeof( aout_sys_t ) );
  105.     if( p_sys == NULL )
  106.         return VLC_ENOMEM;
  107.     PULSE_DEBUG( "Pulse start initialization");
  108.     ss.channels = aout_FormatNbChannels( &p_aout->output.output ); /* Get the input stream channel count */
  109.     /* Setup the pulse audio stream based on the input stream count */
  110.     switch(ss.channels)
  111.     {
  112.         case 8:
  113.             p_aout->output.output.i_physical_channels
  114.                 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
  115.                 | AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT
  116.                 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
  117.                 | AOUT_CHAN_LFE;
  118.             break;
  119.         case 6:
  120.             p_aout->output.output.i_physical_channels
  121.                 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
  122.                 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
  123.                 | AOUT_CHAN_LFE;
  124.             break;
  125.         case 4:
  126.             p_aout->output.output.i_physical_channels
  127.                 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
  128.                 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
  129.             break;
  130.         case 2:
  131.             p_aout->output.output.i_physical_channels
  132.                 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
  133.             break;
  134.         case 1:
  135.             p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
  136.             break;
  137.         default:
  138.             msg_Err(p_aout,"Invalid number of channels");
  139.         goto fail;
  140.     }
  141.     /* Add a quick command line info message */
  142.     msg_Info(p_aout, "No. of Audio Channels: %d", ss.channels);
  143.     ss.rate = p_aout->output.output.i_rate;
  144.     ss.format = PA_SAMPLE_FLOAT32NE;
  145.     p_aout->output.output.i_format = VLC_FOURCC('f','l','3','2');
  146.     if (!pa_sample_spec_valid(&ss)) {
  147.         msg_Err(p_aout,"Invalid sample spec");
  148.         goto fail;
  149.     }
  150.     /* Reduce overall latency to 200mS to reduce audible clicks
  151.      * Also pulse minreq and internal buffers are now 20mS which reduces resampling
  152.      */
  153.     a.tlength = pa_bytes_per_second(&ss)/5;
  154.     a.maxlength = a.tlength * 2;
  155.     a.prebuf = a.tlength / 2;
  156.     a.minreq = a.tlength / 10;
  157.     /* Buffer size is 20mS */
  158.     p_sys->buffer_size = a.minreq;
  159.     /* Initialise the speaker map setup above */
  160.     pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_ALSA);
  161.     if (!(p_sys->mainloop = pa_threaded_mainloop_new())) {
  162.         msg_Err(p_aout, "Failed to allocate main loop");
  163.         goto fail;
  164.     }
  165.     if (!(p_sys->context = pa_context_new(pa_threaded_mainloop_get_api(p_sys->mainloop), _( PULSE_CLIENT_NAME )))) {
  166.         msg_Err(p_aout, "Failed to allocate context");
  167.         goto fail;
  168.     }
  169.     pa_context_set_state_callback(p_sys->context, context_state_cb, p_aout);
  170.     PULSE_DEBUG( "Pulse before context connect");
  171.     if (pa_context_connect(p_sys->context, NULL, 0, NULL) < 0) {
  172.         msg_Err(p_aout, "Failed to connect to server: %s", pa_strerror(pa_context_errno(p_sys->context)));
  173.         goto fail;
  174.     }
  175.     PULSE_DEBUG( "Pulse after context connect");
  176.     pa_threaded_mainloop_lock(p_sys->mainloop);
  177.     if (pa_threaded_mainloop_start(p_sys->mainloop) < 0) {
  178.         msg_Err(p_aout, "Failed to start main loop");
  179.         goto unlock_and_fail;
  180.     }
  181.     msg_Dbg(p_aout, "Pulse mainloop started");
  182.     /* Wait until the context is ready */
  183.     pa_threaded_mainloop_wait(p_sys->mainloop);
  184.     if (pa_context_get_state(p_sys->context) != PA_CONTEXT_READY) {
  185.         msg_Dbg(p_aout, "Failed to connect to server: %s", pa_strerror(pa_context_errno(p_sys->context)));
  186.         goto unlock_and_fail;
  187.     }
  188.     if (!(p_sys->stream = pa_stream_new(p_sys->context, "audio stream", &ss, &map))) {
  189.         msg_Err(p_aout, "Failed to create stream: %s", pa_strerror(pa_context_errno(p_sys->context)));
  190.         goto unlock_and_fail;
  191.     }
  192.     PULSE_DEBUG( "Pulse after new stream");
  193.     pa_stream_set_state_callback(p_sys->stream, stream_state_cb, p_aout);
  194.     pa_stream_set_write_callback(p_sys->stream, stream_request_cb, p_aout);
  195.     pa_stream_set_latency_update_callback(p_sys->stream, stream_latency_update_cb, p_aout);
  196.     if (pa_stream_connect_playback(p_sys->stream, NULL, &a, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_ADJUST_LATENCY, NULL, NULL) < 0) {
  197.         msg_Err(p_aout, "Failed to connect stream: %s", pa_strerror(pa_context_errno(p_sys->context)));
  198.         goto unlock_and_fail;
  199.     }
  200.      PULSE_DEBUG("Pulse stream connect");
  201.     /* Wait until the stream is ready */
  202.     pa_threaded_mainloop_wait(p_sys->mainloop);
  203.     msg_Dbg(p_aout,"Pulse stream connected");
  204.     if (pa_stream_get_state(p_sys->stream) != PA_STREAM_READY) {
  205.         msg_Err(p_aout, "Failed to connect to server: %s", pa_strerror(pa_context_errno(p_sys->context)));
  206.         goto unlock_and_fail;
  207.     }
  208.     PULSE_DEBUG("Pulse after stream get status");
  209.     pa_threaded_mainloop_unlock(p_sys->mainloop);
  210.     buffer_attr = pa_stream_get_buffer_attr(p_sys->stream);
  211.     p_aout->output.i_nb_samples = buffer_attr->minreq / pa_frame_size(&ss);
  212.     p_aout->output.pf_play = Play;
  213.     aout_VolumeSoftInit(p_aout);
  214.     msg_Dbg(p_aout, "Pulse initialized successfully");
  215.     {
  216.         char cmt[PA_CHANNEL_MAP_SNPRINT_MAX], sst[PA_SAMPLE_SPEC_SNPRINT_MAX];
  217.         msg_Dbg(p_aout, "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u", buffer_attr->maxlength, buffer_attr->tlength, buffer_attr->prebuf, buffer_attr->minreq);
  218.         msg_Dbg(p_aout, "Using sample spec '%s', channel map '%s'.",
  219.                 pa_sample_spec_snprint(sst, sizeof(sst), pa_stream_get_sample_spec(p_sys->stream)),
  220.                 pa_channel_map_snprint(cmt, sizeof(cmt), pa_stream_get_channel_map(p_sys->stream)));
  221.             msg_Dbg(p_aout, "Connected to device %s (%u, %ssuspended).",
  222.                         pa_stream_get_device_name(p_sys->stream),
  223.                         pa_stream_get_device_index(p_sys->stream),
  224.                         pa_stream_is_suspended(p_sys->stream) ? "" : "not ");
  225.     }
  226.     return VLC_SUCCESS;
  227. unlock_and_fail:
  228.     msg_Dbg(p_aout, "Pulse initialization unlock and fail");
  229.     if (p_sys->mainloop)
  230.         pa_threaded_mainloop_unlock(p_sys->mainloop);
  231. fail:
  232.     msg_Dbg(p_aout, "Pulse initialization failed");
  233.     uninit(p_aout);
  234.     return VLC_EGENERIC;
  235. }
  236. /*****************************************************************************
  237.  * Play: play a sound samples buffer
  238.  *****************************************************************************/
  239. static void Play( aout_instance_t * p_aout )
  240. {
  241.     struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys;
  242.     pa_operation *o;
  243.     if(!p_sys->started){
  244.         msg_Dbg(p_aout, "Pulse stream started");
  245.         p_sys->start_date =
  246.             aout_FifoFirstDate( p_aout, &p_aout->output.fifo );
  247.         p_sys->started = 1;
  248.         pa_threaded_mainloop_lock(p_sys->mainloop);
  249.         if((o = pa_stream_flush(p_sys->stream, success_cb, p_aout))){
  250.             pa_operation_unref(o);
  251.         }
  252.         pa_threaded_mainloop_unlock(p_sys->mainloop);
  253.         pa_threaded_mainloop_signal(p_sys->mainloop, 0);
  254.     }
  255. }
  256. /*****************************************************************************
  257.  * Close: close the audio device
  258.  *****************************************************************************/
  259. static void Close ( vlc_object_t *p_this )
  260. {
  261.     aout_instance_t *p_aout = (aout_instance_t *)p_this;
  262.     struct aout_sys_t * p_sys = p_aout->output.p_sys;
  263.     msg_Dbg(p_aout, "Pulse Close");
  264.     if(p_sys->stream){
  265.         pa_operation *o;
  266.         pa_threaded_mainloop_lock(p_sys->mainloop);
  267.         pa_stream_set_write_callback(p_sys->stream, NULL, NULL);
  268.         if((o = pa_stream_drain(p_sys->stream, success_cb, p_aout))){
  269.             while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
  270.                 CHECK_DEAD_GOTO(fail);
  271.                 pa_threaded_mainloop_wait(p_sys->mainloop);
  272.             }
  273.         fail:
  274.             pa_operation_unref(o);
  275.         }
  276.         pa_threaded_mainloop_unlock(p_sys->mainloop);
  277.     }
  278.     uninit(p_aout);
  279. }
  280. static void uninit(aout_instance_t *p_aout){
  281.     struct aout_sys_t * p_sys = p_aout->output.p_sys;
  282.     if (p_sys->mainloop)
  283.         pa_threaded_mainloop_stop(p_sys->mainloop);
  284.     if (p_sys->stream) {
  285.         pa_stream_disconnect(p_sys->stream);
  286.         pa_stream_unref(p_sys->stream);
  287.         p_sys->stream = NULL;
  288.     }
  289.     if (p_sys->context) {
  290.         pa_context_disconnect(p_sys->context);
  291.         pa_context_unref(p_sys->context);
  292.         p_sys->context = NULL;
  293.     }
  294.     if (p_sys->mainloop) {
  295.         pa_threaded_mainloop_free(p_sys->mainloop);
  296.         p_sys->mainloop = NULL;
  297.     }
  298.     free(p_sys);
  299.     p_aout->output.p_sys = NULL;
  300. }
  301. static void context_state_cb(pa_context *c, void *userdata) {
  302.     aout_instance_t *p_aout = (aout_instance_t *)userdata;
  303.     struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys;
  304.     assert(c);
  305.     PULSE_DEBUG( "Pulse context state changed");
  306.     switch (pa_context_get_state(c)) {
  307.         case PA_CONTEXT_READY:
  308.         case PA_CONTEXT_TERMINATED:
  309.         case PA_CONTEXT_FAILED:
  310.         PULSE_DEBUG( "Pulse context state changed signal");
  311.             pa_threaded_mainloop_signal(p_sys->mainloop, 0);
  312.             break;
  313.         case PA_CONTEXT_UNCONNECTED:
  314.         case PA_CONTEXT_CONNECTING:
  315.         case PA_CONTEXT_AUTHORIZING:
  316.         case PA_CONTEXT_SETTING_NAME:
  317.         PULSE_DEBUG( "Pulse context state changed no signal");
  318.             break;
  319.     }
  320. }
  321. static void stream_state_cb(pa_stream *s, void * userdata) {
  322.     aout_instance_t *p_aout = (aout_instance_t *)userdata;
  323.     struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys;
  324.     assert(s);
  325.     PULSE_DEBUG( "Pulse stream state changed");
  326.     switch (pa_stream_get_state(s)) {
  327.         case PA_STREAM_READY:
  328.         case PA_STREAM_FAILED:
  329.         case PA_STREAM_TERMINATED:
  330.             pa_threaded_mainloop_signal(p_sys->mainloop, 0);
  331.             break;
  332.         case PA_STREAM_UNCONNECTED:
  333.         case PA_STREAM_CREATING:
  334.             break;
  335.     }
  336. }
  337. static void stream_request_cb(pa_stream *s, size_t length, void *userdata) {
  338.     aout_instance_t *p_aout = (aout_instance_t *)userdata;
  339.     struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys;
  340.     mtime_t next_date;
  341.     assert(s);
  342.     assert(p_sys);
  343.     size_t buffer_size = p_sys->buffer_size;
  344.     PULSE_DEBUG( "Pulse stream request %d", length);
  345.     do{
  346.         aout_buffer_t *   p_buffer = NULL;
  347.         if(p_sys->started){
  348.             pa_usec_t latency;
  349.             int negative;
  350.             if(pa_stream_get_latency(p_sys->stream, &latency, &negative)<0){
  351.                 if (pa_context_errno(p_sys->context) != PA_ERR_NODATA) {
  352.                     msg_Err(p_aout, "pa_stream_get_latency() failed: %s", pa_strerror(pa_context_errno(p_sys->context)));
  353.                 }
  354.                 latency = 0;
  355.             }
  356.             PULSE_DEBUG( "Pulse stream request latency=%"PRId64"", latency);
  357.             next_date = mdate() + latency;
  358.             if(p_sys->start_date < next_date + AOUT_PTS_TOLERANCE ){
  359.                 p_buffer = aout_OutputNextBuffer( p_aout, next_date, 0);
  360.             }
  361.         }
  362.         if ( p_buffer != NULL )
  363.         {
  364.             PULSE_DEBUG( "Pulse stream request write buffer %d", p_buffer->i_nb_bytes);
  365.             pa_stream_write(p_sys->stream, p_buffer->p_buffer, p_buffer->i_nb_bytes, NULL, 0, PA_SEEK_RELATIVE);
  366.             length -= p_buffer->i_nb_bytes;
  367.             aout_BufferFree( p_buffer );
  368.         }
  369.         else
  370.         {
  371.             PULSE_DEBUG( "Pulse stream request write zeroes");
  372.             void *data = pa_xmalloc(buffer_size);
  373.             bzero(data, buffer_size);
  374.             pa_stream_write(p_sys->stream, data, buffer_size, pa_xfree, 0, PA_SEEK_RELATIVE);
  375.             length -= buffer_size;
  376.         }
  377.     }while(length > buffer_size);
  378.     pa_threaded_mainloop_signal(p_sys->mainloop, 0);
  379. }
  380. static void stream_latency_update_cb(pa_stream *s, void *userdata) {
  381.     aout_instance_t *p_aout = (aout_instance_t *)userdata;
  382.     struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys;
  383.     assert(s);
  384.     PULSE_DEBUG( "Pulse stream latency update");
  385.     pa_threaded_mainloop_signal(p_sys->mainloop, 0);
  386. }
  387. static void success_cb(pa_stream *s, int sucess, void *userdata)
  388. {
  389.     aout_instance_t *p_aout = (aout_instance_t *)userdata;
  390.     struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys;
  391.     VLC_UNUSED(sucess);
  392.     assert(s);
  393.     pa_threaded_mainloop_signal(p_sys->mainloop, 0);
  394. }
  395. #undef PULSE_DEBUG