summaryrefslogtreecommitdiff
path: root/amadec/adec-wfd-out.cpp (plain)
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"
18namespace 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
37static struct pcm_config wfd_config_out;
38static struct pcm *wfd_pcm;
39static char cache_buffer_bytes[64];
40static int cached_len=0;
41static const char *const SOUND_CARDS_PATH = "/proc/asound/cards";
42static int tv_mode = 0;
43static 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}
55static 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
75static float last_vol = 1.0;
76static 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}
117static 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
125static 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
154OUT:
155 free(read_buf);
156 close(fd);
157 return card;
158}
159static 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
196OUT:
197 free(read_buf);
198 close(fd);
199 return port;
200#endif
201}
202
203EXTERN_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
258EXTERN_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}
335EXTERN_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}
343EXTERN_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