blob: e1845600183bab772acaece9d998585b6c75e22b
1 | #include <errno.h> |
2 | #include <pthread.h> |
3 | #include <stdint.h> |
4 | #include <sys/time.h> |
5 | #include <stdlib.h> |
6 | #include <sys/stat.h> |
7 | #include <fcntl.h> |
8 | #include <stdio.h> |
9 | #include <stdarg.h> |
10 | #include <string.h> |
11 | #include <android/log.h> |
12 | #include <cutils/properties.h> |
13 | //get android media stream volume |
14 | #define CODE_CALC_VOLUME |
15 | #ifdef CODE_CALC_VOLUME |
16 | #include <media/AudioSystem.h> |
17 | #define EXTERN_TAG extern "C" |
18 | namespace android |
19 | { |
20 | #else |
21 | #define EXTERN_TAG |
22 | #endif |
23 | #define LOG_TAG "wfd-output" |
24 | #define adec_print(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) |
25 | |
26 | // default using tinyalsa |
27 | #include <tinyalsa/asoundlib.h> |
28 | |
29 | |
30 | |
31 | //"/sys/class/switch/hdmi/state" |
32 | #define HDMI_SWITCH_STATE_PATH "/sys/class/amhdmitx/amhdmitx0/hpd_state" |
33 | |
34 | // audio PCM output configuration |
35 | #define WFD_PERIOD_SIZE 1024 |
36 | #define WFD_PERIOD_NUM 4 |
37 | static struct pcm_config wfd_config_out; |
38 | static struct pcm *wfd_pcm; |
39 | static char cache_buffer_bytes[64]; |
40 | static int cached_len=0; |
41 | static const char *const SOUND_CARDS_PATH = "/proc/asound/cards"; |
42 | static int tv_mode = 0; |
43 | static int getprop_bool(const char * path) |
44 | { |
45 | char buf[PROPERTY_VALUE_MAX]; |
46 | int ret = -1; |
47 | ret = property_get(path, buf, NULL); |
48 | if (ret > 0) { |
49 | if (strcasecmp(buf,"true") == 0 || strcmp(buf,"1") == 0) { |
50 | return 1; |
51 | } |
52 | } |
53 | return 0; |
54 | } |
55 | static int get_hdmi_switch_state() |
56 | { |
57 | return 0; |
58 | #if 0 |
59 | int state = 0; |
60 | int fd = -1; |
61 | char bcmd[16] = {0}; |
62 | fd = open(HDMI_SWITCH_STATE_PATH, O_RDONLY); |
63 | if (fd >= 0) { |
64 | read(fd, bcmd, sizeof(bcmd)); |
65 | state = strtol(bcmd, NULL, 10); |
66 | close(fd); |
67 | }else { |
68 | adec_print("unable to open file %s,err: %s", HDMI_SWITCH_STATE_PATH, strerror(errno)); |
69 | } |
70 | return state; |
71 | #endif |
72 | } |
73 | #ifdef CODE_CALC_VOLUME |
74 | // save the last volume in case get vol failure, which cause the volume value large gap |
75 | static float last_vol = 1.0; |
76 | static float get_android_stream_volume() |
77 | { |
78 | float vol = last_vol; |
79 | #if defined(ANDROID_VERSION_JBMR2_UP) |
80 | unsigned int sr = 0; |
81 | #else |
82 | int sr = 0; |
83 | #endif |
84 | #if ANDROID_PLATFORM_SDK_VERSION >= 21 |
85 | audio_stream_type_t media_type = AUDIO_STREAM_SYSTEM; |
86 | #else |
87 | audio_stream_type_t media_type = AUDIO_STREAM_MUSIC; |
88 | #endif |
89 | |
90 | AudioSystem::getOutputSamplingRate(&sr,AUDIO_STREAM_MUSIC); |
91 | if(sr > 0){ |
92 | audio_io_handle_t handle = -1; |
93 | handle = AudioSystem::getOutput(AUDIO_STREAM_MUSIC, |
94 | 48000, |
95 | AUDIO_FORMAT_PCM_16_BIT, |
96 | AUDIO_CHANNEL_OUT_STEREO, |
97 | #if defined(_VERSION_ICS) |
98 | AUDIO_POLICY_OUTPUT_FLAG_INDIRECT |
99 | #else //JB... |
100 | AUDIO_OUTPUT_FLAG_PRIMARY |
101 | #endif |
102 | ); |
103 | if(handle > 0){ |
104 | if(AudioSystem::getStreamVolume(media_type,&vol,handle) == NO_ERROR){ |
105 | last_vol = vol; |
106 | // adec_print("stream volume %f \n",vol); |
107 | } |
108 | else |
109 | adec_print("get stream volume failed\n"); |
110 | } |
111 | else |
112 | adec_print("get output handle failed\n"); |
113 | } |
114 | return vol; |
115 | |
116 | } |
117 | static void apply_stream_volume(float vol,char *buf,int size) |
118 | { |
119 | int i; |
120 | short *sample = (short*)buf; |
121 | for(i = 0;i < size/sizeof(short);i++) |
122 | sample[i] = vol*sample[i]; |
123 | } |
124 | #endif |
125 | static int get_aml_card(){ |
126 | int card = -1, err = 0; |
127 | int fd = -1; |
128 | unsigned fileSize = 512; |
129 | char *read_buf = NULL, *pd = NULL; |
130 | fd = open(SOUND_CARDS_PATH, O_RDONLY); |
131 | if (fd < 0) { |
132 | adec_print("ERROR: failed to open config file %s error: %d\n", SOUND_CARDS_PATH, errno); |
133 | close(fd); |
134 | return -EINVAL; |
135 | } |
136 | |
137 | read_buf = (char *)malloc(fileSize); |
138 | if (!read_buf) { |
139 | adec_print("Failed to malloc read_buf"); |
140 | close(fd); |
141 | return -ENOMEM; |
142 | } |
143 | memset(read_buf, 0x0, fileSize); |
144 | err = read(fd, read_buf, fileSize); |
145 | if (fd < 0) { |
146 | adec_print("ERROR: failed to read config file %s error: %d\n", SOUND_CARDS_PATH, errno); |
147 | free(read_buf); |
148 | close(fd); |
149 | return -EINVAL; |
150 | } |
151 | pd = strstr(read_buf, "AML"); |
152 | card = *(pd - 3) - '0'; |
153 | |
154 | OUT: |
155 | free(read_buf); |
156 | close(fd); |
157 | return card; |
158 | } |
159 | static int get_spdif_port() { |
160 | return 0; |
161 | #if 0 |
162 | int port = -1, err = 0; |
163 | int fd = -1; |
164 | unsigned fileSize = 512; |
165 | char *read_buf = NULL, *pd = NULL; |
166 | static const char *const SOUND_PCM_PATH = "/proc/asound/pcm"; |
167 | fd = open(SOUND_PCM_PATH, O_RDONLY); |
168 | if (fd < 0) { |
169 | adec_print("ERROR: failed to open config file %s error: %d\n", SOUND_PCM_PATH, errno); |
170 | close(fd); |
171 | return -EINVAL; |
172 | } |
173 | |
174 | read_buf = (char *)malloc(fileSize); |
175 | if (!read_buf) { |
176 | adec_print("Failed to malloc read_buf"); |
177 | close(fd); |
178 | return -ENOMEM; |
179 | } |
180 | memset(read_buf, 0x0, fileSize); |
181 | err = read(fd, read_buf, fileSize); |
182 | if (fd < 0) { |
183 | adec_print("ERROR: failed to read config file %s error: %d\n", SOUND_PCM_PATH, errno); |
184 | free(read_buf); |
185 | close(fd); |
186 | return -EINVAL; |
187 | } |
188 | pd = strstr(read_buf, "SPDIF"); |
189 | if(!pd) |
190 | goto OUT; |
191 | adec_print("%s \n",pd ); |
192 | |
193 | port = *(pd -3) - '0'; |
194 | adec_print("%s \n",(pd -3) ); |
195 | |
196 | OUT: |
197 | free(read_buf); |
198 | close(fd); |
199 | return port; |
200 | #endif |
201 | } |
202 | |
203 | EXTERN_TAG int pcm_output_init(int sr,int ch) |
204 | { |
205 | int card = 0; |
206 | int device = 2; |
207 | cached_len = 0; |
208 | tv_mode = getprop_bool("ro.platform.has.tvuimode"); |
209 | wfd_config_out.channels = 2; |
210 | wfd_config_out.rate = 48000; |
211 | wfd_config_out.period_size = WFD_PERIOD_SIZE; |
212 | wfd_config_out.period_count = WFD_PERIOD_NUM; |
213 | wfd_config_out.format = PCM_FORMAT_S16_LE; |
214 | wfd_config_out.start_threshold = WFD_PERIOD_SIZE; |
215 | wfd_config_out.avail_min = 0;//SHORT_PERIOD_SIZE; |
216 | card = get_aml_card(); |
217 | if(card < 0) |
218 | { |
219 | card = 0; |
220 | adec_print("get aml card fail, use default \n"); |
221 | } |
222 | // if hdmi state on |
223 | if(get_hdmi_switch_state()) |
224 | device = get_spdif_port(); |
225 | else |
226 | device = 0; //i2s output for analog output |
227 | if(device < 0) |
228 | { |
229 | device = 0; |
230 | adec_print("get aml card device fail, use default \n"); |
231 | } |
232 | device = 0; |
233 | adec_print("open output device card %d, device %d \n",card,device); |
234 | if(sr < 32000|| sr > 48000 || ch != 2){ |
235 | adec_print("wfd output: not right parameter sr %d,ch %d \n",sr,ch); |
236 | return -1; |
237 | } |
238 | wfd_config_out.rate = sr; |
239 | wfd_config_out.channels = ch; |
240 | if (tv_mode) { |
241 | wfd_config_out.channels = 8; |
242 | wfd_config_out.format = PCM_FORMAT_S32_LE; |
243 | wfd_config_out.period_size = WFD_PERIOD_SIZE; |
244 | wfd_config_out.period_count = WFD_PERIOD_NUM; |
245 | wfd_config_out.start_threshold = WFD_PERIOD_SIZE; |
246 | } |
247 | wfd_pcm = pcm_open(card, device, PCM_OUT /*| PCM_MMAP | PCM_NOIRQ*/, &wfd_config_out); |
248 | if (!pcm_is_ready(wfd_pcm)) { |
249 | adec_print("wfd cannot open pcm_out driver: %s", pcm_get_error(wfd_pcm)); |
250 | pcm_close(wfd_pcm); |
251 | return -1; |
252 | } |
253 | adec_print("pcm_output_init done wfd : %p,\n",wfd_pcm); |
254 | return 0; |
255 | |
256 | } |
257 | |
258 | EXTERN_TAG int pcm_output_write(char *buf,unsigned size) |
259 | { |
260 | |
261 | int ret = 0; |
262 | char *data, *data_dst; |
263 | char *data_src; |
264 | char outbuf[8192]; |
265 | int total_len,ouput_len; |
266 | #ifdef CODE_CALC_VOLUME |
267 | float vol = get_android_stream_volume(); |
268 | apply_stream_volume(vol,buf,size); |
269 | #endif |
270 | if(size < 64) |
271 | return 0; |
272 | if(size > sizeof(outbuf)){ |
273 | adec_print("write size tooo big %d \n",size); |
274 | } |
275 | total_len = size + cached_len; |
276 | |
277 | //adec_print("total_len(%d) = + cached_len111(%d)", size, cached_len); |
278 | |
279 | data_src = (char *)cache_buffer_bytes; |
280 | data_dst = (char *)outbuf; |
281 | |
282 | |
283 | |
284 | /*write_back data from cached_buffer*/ |
285 | if(cached_len){ |
286 | memcpy((void *)data_dst, (void *)data_src, cached_len); |
287 | data_dst += cached_len; |
288 | } |
289 | ouput_len = total_len &(~0x3f); |
290 | data = (char*)buf; |
291 | |
292 | memcpy((void *)data_dst, (void *)data, ouput_len-cached_len); |
293 | data += (ouput_len-cached_len); |
294 | cached_len = total_len & 0x3f; |
295 | data_src = (char *)cache_buffer_bytes; |
296 | |
297 | /*save data to cached_buffer*/ |
298 | if(cached_len){ |
299 | memcpy((void *)data_src, (void *)data, cached_len); |
300 | } |
301 | char *write_buf = outbuf; |
302 | int *tmp_buffer = NULL; |
303 | if (tv_mode) { |
304 | tmp_buffer = (int*)malloc(ouput_len*8); |
305 | if (tmp_buffer == NULL) { |
306 | ALOGE("malloc tmp_buffer failed\n"); |
307 | return -1; |
308 | } |
309 | int i; |
310 | int out_frames = ouput_len/4; |
311 | short *in_buffer = (short*)outbuf; |
312 | for (i = 0; i < out_frames; i ++) { |
313 | tmp_buffer[8*i] = ((int)(in_buffer[2*i])) << 16; |
314 | tmp_buffer[8*i + 1] = ((int)(in_buffer[2*i + 1])) << 16; |
315 | tmp_buffer[8*i + 2] = ((int)(in_buffer[2*i])) << 16; |
316 | tmp_buffer[8*i + 3] = ((int)(in_buffer[2*i + 1])) << 16; |
317 | tmp_buffer[8*i + 4] = 0; |
318 | tmp_buffer[8*i + 5] = 0; |
319 | tmp_buffer[8*i + 6] = 0; |
320 | tmp_buffer[8*i + 7] = 0; |
321 | } |
322 | write_buf = (char*)tmp_buffer; |
323 | ouput_len = ouput_len*8; |
324 | } |
325 | ret = pcm_write(wfd_pcm,write_buf,ouput_len); |
326 | if(ret < 0 ){ |
327 | adec_print("pcm_output_write failed ? \n"); |
328 | } |
329 | if (tmp_buffer) { |
330 | free(tmp_buffer); |
331 | } |
332 | //adec_print("write size %d ,ret %d \n",size,ret); |
333 | return ret; |
334 | } |
335 | EXTERN_TAG int pcm_output_uninit() |
336 | { |
337 | if(wfd_pcm) |
338 | pcm_close(wfd_pcm); |
339 | wfd_pcm = NULL; |
340 | adec_print("pcm_output_uninit done \n"); |
341 | return 0; |
342 | } |
343 | EXTERN_TAG int pcm_output_latency() |
344 | { |
345 | #if 1 |
346 | struct timespec tstamp; |
347 | unsigned int avail = 0; |
348 | int ret; |
349 | ret = pcm_get_htimestamp(wfd_pcm,&avail, &tstamp); |
350 | //adec_print("pcm_get_latency ret %d,latency %d \n",ret,avail*1000/48000); |
351 | if(ret) |
352 | return ret; |
353 | else |
354 | return avail*1000/wfd_config_out.rate; |
355 | #else |
356 | return pcm_hw_lantency(wfd_pcm); |
357 | #endif |
358 | } |
359 | #ifdef CODE_CALC_VOLUME |
360 | } |
361 | #endif |
362 |