1 /*
2  * This file is part of MPlayer.
3  *
4  * MPlayer is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * MPlayer is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with MPlayer; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18 
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <sys/time.h>
22 #include <alloca.h>
23 #define ALSA_PCM_NEW_HW_PARAMS_API
24 #define ALSA_PCM_NEW_SW_PARAMS_API
25 #include <alsa/asoundlib.h>
26 
27 #include "config.h"
28 #include "audio_in.h"
29 #include "mp_msg.h"
30 #include "help_mp.h"
31 
ai_alsa_setup(audio_in_t * ai)32 int ai_alsa_setup(audio_in_t *ai)
33 {
34     snd_pcm_hw_params_t *params;
35     snd_pcm_sw_params_t *swparams;
36     snd_pcm_uframes_t buffer_size, period_size;
37     int err;
38     int dir;
39     unsigned int rate;
40 
41     snd_pcm_hw_params_alloca(&params);
42     snd_pcm_sw_params_alloca(&swparams);
43 
44     err = snd_pcm_hw_params_any(ai->alsa.handle, params);
45     if (err < 0) {
46 	mp_msg(MSGT_TV, MSGL_ERR, MSGTR_MPDEMUX_AIALSA_PcmBrokenConfig);
47 	return -1;
48     }
49 
50     err = snd_pcm_hw_params_set_access(ai->alsa.handle, params,
51 				       SND_PCM_ACCESS_RW_INTERLEAVED);
52     if (err < 0) {
53 	mp_msg(MSGT_TV, MSGL_ERR, MSGTR_MPDEMUX_AIALSA_UnavailableAccessType);
54 	return -1;
55     }
56 
57     err = snd_pcm_hw_params_set_format(ai->alsa.handle, params, SND_PCM_FORMAT_S16_LE);
58     if (err < 0) {
59 	mp_msg(MSGT_TV, MSGL_ERR, MSGTR_MPDEMUX_AIALSA_UnavailableSampleFmt);
60 	return -1;
61     }
62 
63     err = snd_pcm_hw_params_set_channels(ai->alsa.handle, params, ai->req_channels);
64     if (err < 0) {
65 	snd_pcm_hw_params_get_channels(params, &ai->channels);
66 	mp_msg(MSGT_TV, MSGL_ERR, MSGTR_MPDEMUX_AIALSA_UnavailableChanCount,
67 	       ai->channels);
68     } else {
69 	ai->channels = ai->req_channels;
70     }
71 
72     dir = 0;
73     rate = ai->req_samplerate;
74     err = snd_pcm_hw_params_set_rate_near(ai->alsa.handle, params, &rate, &dir);
75     if (err < 0) {
76 	mp_msg(MSGT_TV, MSGL_ERR, MSGTR_MPDEMUX_AIALSA_CannotSetSamplerate);
77     }
78     ai->samplerate = rate;
79 
80     dir = 0;
81     ai->alsa.buffer_time = 1000000;
82     err = snd_pcm_hw_params_set_buffer_time_near(ai->alsa.handle, params,
83 						 &ai->alsa.buffer_time, &dir);
84     if (err < 0) {
85 	mp_msg(MSGT_TV, MSGL_ERR, MSGTR_MPDEMUX_AIALSA_CannotSetBufferTime);
86     }
87 
88     dir = 0;
89     ai->alsa.period_time = ai->alsa.buffer_time / 4;
90     err = snd_pcm_hw_params_set_period_time_near(ai->alsa.handle, params,
91 						 &ai->alsa.period_time, &dir);
92     if (err < 0) {
93 	mp_msg(MSGT_TV, MSGL_ERR, MSGTR_MPDEMUX_AIALSA_CannotSetPeriodTime);
94     }
95 
96     err = snd_pcm_hw_params(ai->alsa.handle, params);
97     if (err < 0) {
98 	mp_msg(MSGT_TV, MSGL_ERR, MSGTR_MPDEMUX_AIALSA_CannotInstallHWParams, snd_strerror(err));
99 	snd_pcm_hw_params_dump(params, ai->alsa.log);
100 	return -1;
101     }
102 
103     dir = -1;
104     snd_pcm_hw_params_get_period_size(params, &period_size, &dir);
105     snd_pcm_hw_params_get_buffer_size(params, &buffer_size);
106     ai->alsa.chunk_size = period_size;
107     if (period_size == buffer_size) {
108 	mp_msg(MSGT_TV, MSGL_ERR, MSGTR_MPDEMUX_AIALSA_PeriodEqualsBufferSize, ai->alsa.chunk_size, (long)buffer_size);
109 	return -1;
110     }
111 
112     snd_pcm_sw_params_current(ai->alsa.handle, swparams);
113     err = snd_pcm_sw_params_set_sleep_min(ai->alsa.handle, swparams,0);
114     err = snd_pcm_sw_params_set_avail_min(ai->alsa.handle, swparams, ai->alsa.chunk_size);
115 
116     err = snd_pcm_sw_params_set_start_threshold(ai->alsa.handle, swparams, 0);
117     err = snd_pcm_sw_params_set_stop_threshold(ai->alsa.handle, swparams, buffer_size);
118 
119     if (snd_pcm_sw_params(ai->alsa.handle, swparams) < 0) {
120 	mp_msg(MSGT_TV, MSGL_ERR, MSGTR_MPDEMUX_AIALSA_CannotInstallSWParams);
121 	snd_pcm_sw_params_dump(swparams, ai->alsa.log);
122 	return -1;
123     }
124 
125     if (mp_msg_test(MSGT_TV, MSGL_V)) {
126 	snd_pcm_dump(ai->alsa.handle, ai->alsa.log);
127     }
128 
129     ai->alsa.bits_per_sample = snd_pcm_format_physical_width(SND_PCM_FORMAT_S16_LE);
130     ai->alsa.bits_per_frame = ai->alsa.bits_per_sample * ai->channels;
131     ai->blocksize = ai->alsa.chunk_size * ai->alsa.bits_per_frame / 8;
132     ai->samplesize = ai->alsa.bits_per_sample;
133     ai->bytes_per_sample = ai->alsa.bits_per_sample/8;
134 
135     return 0;
136 }
137 
ai_alsa_init(audio_in_t * ai)138 int ai_alsa_init(audio_in_t *ai)
139 {
140     int err;
141 
142     err = snd_pcm_open(&ai->alsa.handle, ai->alsa.device, SND_PCM_STREAM_CAPTURE, 0);
143     if (err < 0) {
144 	mp_msg(MSGT_TV, MSGL_ERR, MSGTR_MPDEMUX_AIALSA_ErrorOpeningAudio, snd_strerror(err));
145 	return -1;
146     }
147 
148     err = snd_output_stdio_attach(&ai->alsa.log, stderr, 0);
149 
150     if (err < 0) {
151 	return -1;
152     }
153 
154     err = ai_alsa_setup(ai);
155 
156     return err;
157 }
158 
159 #ifndef timersub
160 #define	timersub(a, b, result) \
161 do { \
162 	(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
163 	(result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
164 	if ((result)->tv_usec < 0) { \
165 		--(result)->tv_sec; \
166 		(result)->tv_usec += 1000000; \
167 	} \
168 } while (0)
169 #endif
170 
ai_alsa_xrun(audio_in_t * ai)171 int ai_alsa_xrun(audio_in_t *ai)
172 {
173     snd_pcm_status_t *status;
174     int res;
175 
176     snd_pcm_status_alloca(&status);
177     if ((res = snd_pcm_status(ai->alsa.handle, status))<0) {
178 	mp_msg(MSGT_TV, MSGL_ERR, MSGTR_MPDEMUX_AIALSA_AlsaStatusError, snd_strerror(res));
179 	return -1;
180     }
181     if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN) {
182 	struct timeval now, diff, tstamp;
183 	gettimeofday(&now, 0);
184 	snd_pcm_status_get_trigger_tstamp(status, &tstamp);
185 	timersub(&now, &tstamp, &diff);
186 	mp_msg(MSGT_TV, MSGL_ERR, MSGTR_MPDEMUX_AIALSA_AlsaXRUN,
187 	       diff.tv_sec * 1000 + diff.tv_usec / 1000.0);
188 	if (mp_msg_test(MSGT_TV, MSGL_V)) {
189 	    mp_msg(MSGT_TV, MSGL_ERR, "ALSA Status:\n");
190 	    snd_pcm_status_dump(status, ai->alsa.log);
191 	}
192 	if ((res = snd_pcm_prepare(ai->alsa.handle))<0) {
193 	    mp_msg(MSGT_TV, MSGL_ERR, MSGTR_MPDEMUX_AIALSA_AlsaXRUNPrepareError, snd_strerror(res));
194 	    return -1;
195 	}
196 	return 0;		/* ok, data should be accepted again */
197     }
198     mp_msg(MSGT_TV, MSGL_ERR, MSGTR_MPDEMUX_AIALSA_AlsaReadWriteError);
199     return -1;
200 }
201