1 /*
2 * Copyright (C) 2015-2020 Alibaba Group Holding Limited
3 */
4 #ifdef HAAS_AUDIO_DEMO
5 #include <posix/pthread.h>
6 #else
7 #include <pthread.h>
8 #endif
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <stdint.h>
12 #include <string.h>
13 #include <stdbool.h>
14 #include <aos/kernel.h>
15 #include <aos/list.h>
16 #include <errno.h>
17 #include "ulog/ulog.h"
18 #include "audio_rtos.h"
19
20 #define LOG_TAG "[pb_task]"
21 #define PB_HIGH_STACKSIZE 8192
22 #define PB_DEFAULT_PRIORITY 33
23 #define PB_TIMER_INTERVAL_MS 20 // minimum interval 20ms
24 #define PB_TIMER_MAX_IDLE_TIME_MS 2000 // max idle time 2s
25 #define PB_DATA_BUFFER_ENTRY_NUM_MAX 10
26
27 typedef struct {
28 struct dlist_s list;
29 char *data;
30 unsigned int size;
31 unsigned int wOffset;
32 void *dev;
33 } pb_data_buf_t;
34
35 static pthread_cond_t pb_cond;
36 static pthread_mutex_t pb_mutex;
37 static pthread_t pb_thread;
38 static bool bCreatePbThreadFlag = false;
39 static bool bPbStartFlag = false;
40 static aos_hdl_t flagMutex;
41 AOS_DLIST_HEAD(pb_data_list);
42
pb_thread_loop(void * arg)43 static void pb_thread_loop(void *arg)
44 {
45 pb_data_buf_t *data_buf = NULL;
46 native_pcm_device_t *pcm_dev = NULL;
47 dlist_t *temp = NULL;
48 unsigned int frame_size, period_bytes, left_bytes;
49 unsigned int sleepCntUs;
50 long long now_ts = 0;
51 static int totalIdleTime = 0;
52 bool copyStartFlag = false;
53 LOGD(LOG_TAG, "%s:%d, entry!! bPbStartFlag %d", __func__, __LINE__, bPbStartFlag);
54 while(1) {
55 now_ts = aos_calendar_localtime_get();
56 aos_mutex_lock(&flagMutex, AOS_WAIT_FOREVER);
57 copyStartFlag = bPbStartFlag;
58 aos_mutex_unlock(&flagMutex);
59 if(true == copyStartFlag) {
60 sleepCntUs = PB_TIMER_INTERVAL_MS * 1000;
61 if(dlist_empty(&pb_data_list)) {
62 totalIdleTime ++;
63 if(totalIdleTime > PB_TIMER_MAX_IDLE_TIME_MS / PB_TIMER_INTERVAL_MS) {
64 sleepCntUs = PB_TIMER_INTERVAL_MS * 100 * 1000;
65 }
66 LOGE(LOG_TAG, "%s:%d, data list empty, underflow!!", __func__, __LINE__);
67 } else {
68 data_buf = dlist_first_entry(&pb_data_list, pb_data_buf_t, list);
69 if(data_buf && data_buf->data && data_buf->dev) {
70 pcm_dev = (native_pcm_device_t *)data_buf->dev;
71 frame_size = pcm_dev->params.channels * pcm_dev->params.sample_bits / 8;
72 period_bytes = frame_size * PB_TIMER_INTERVAL_MS * pcm_dev->params.rate / 1000;
73 left_bytes = data_buf->size - data_buf->wOffset;
74 if(left_bytes > period_bytes) {
75 pcm_dev->ops->write(pcm_dev->hdl, data_buf->data + data_buf->wOffset, period_bytes);
76 data_buf->wOffset += period_bytes;
77 //LOGE(LOG_TAG, "%s:%d, fetch %d bytes from buffer", __func__, __LINE__, period_bytes);
78 } else {
79 pcm_dev->ops->write(pcm_dev->hdl, data_buf->data + data_buf->wOffset, left_bytes);
80 data_buf->wOffset += left_bytes;
81 sleepCntUs = left_bytes * PB_TIMER_INTERVAL_MS * 1000 / period_bytes;
82 //LOGE(LOG_TAG, "%s:%d, fetch %d bytes from buffer", __func__, __LINE__, left_bytes);
83
84 /* free data_buf */
85 dlist_del(&data_buf->list);
86 free(data_buf->data);
87 free(data_buf);
88 }
89 }
90 }
91 now_ts = (aos_calendar_localtime_get() - now_ts) * 1000;
92 if(sleepCntUs > now_ts) {
93 usleep(sleepCntUs - now_ts);
94 }
95 } else {
96 LOGD(LOG_TAG, "%s:%d, wait for pb_cond", __func__, __LINE__);
97 dlist_for_each_entry_safe(&pb_data_list, temp, data_buf, pb_data_buf_t, list) {
98 if(data_buf) {
99 dlist_del(&data_buf->list);
100 free(data_buf->data);
101 free(data_buf);
102 }
103 }
104 pthread_cond_wait(&pb_cond, &pb_mutex);
105 }
106 }
107
108 return 0;
109 }
110
create_pb_task(void)111 static int create_pb_task(void)
112 {
113 if(bCreatePbThreadFlag == true) {
114 LOGD(LOG_TAG, "%s:%d, pbthread is running, skip ...", __func__, __LINE__);
115 return -1;
116 }
117 LOGD(LOG_TAG, "%s:%d, -->>", __func__, __LINE__);
118 pthread_attr_t attr;
119 struct sched_param sched;
120
121 aos_mutex_new(&flagMutex);
122 pthread_cond_init(&pb_cond, NULL);
123 pthread_mutex_init(&pb_mutex, NULL);
124 pthread_attr_init(&attr);
125 pthread_attr_setstacksize(&attr, PB_HIGH_STACKSIZE);
126 sched.sched_priority = PB_DEFAULT_PRIORITY;
127 pthread_attr_setschedparam(&attr, &sched);
128 pthread_create(&pb_thread, &attr, pb_thread_loop, NULL);
129 pthread_setname_np(pb_thread, "pbthread");
130 pthread_attr_destroy(&attr);
131 bCreatePbThreadFlag = true;
132 return 0;
133 }
134
playback_task_start()135 void playback_task_start()
136 {
137 LOGE(LOG_TAG, "%s:%d", __func__, __LINE__);
138 aos_mutex_lock(&flagMutex, AOS_WAIT_FOREVER);
139 bPbStartFlag = true;
140 aos_mutex_unlock(&flagMutex);
141 create_pb_task();
142 pthread_cond_signal(&pb_cond);
143 }
144
playback_task_stop()145 void playback_task_stop()
146 {
147 LOGE(LOG_TAG, "%s:%d", __func__, __LINE__);
148 aos_mutex_lock(&flagMutex, AOS_WAIT_FOREVER);
149 bPbStartFlag = false;
150 aos_mutex_unlock(&flagMutex);
151 }
152
playback_task_drain()153 int playback_task_drain()
154 {
155 while(!dlist_empty(&pb_data_list)) {
156 aos_msleep(PB_TIMER_INTERVAL_MS);
157 }
158 return 0;
159 }
160
playback_push_data(void * dev,const char * buf,unsigned int len,int blockMode)161 int playback_push_data(void *dev, const char *buf, unsigned int len, int blockMode)
162 {
163 if(1 == blockMode) {
164 while(dlist_entry_number(&pb_data_list) >= PB_DATA_BUFFER_ENTRY_NUM_MAX) {
165 usleep(PB_TIMER_INTERVAL_MS * 1000);
166 }
167 }
168 if(dlist_entry_number(&pb_data_list) >= PB_DATA_BUFFER_ENTRY_NUM_MAX) {
169 return 0;
170 }
171 pb_data_buf_t *databuf = malloc(sizeof(pb_data_buf_t));
172
173 if(!databuf) {
174 LOGE(LOG_TAG, "%s:%d, malloc pb_data_buf_t failed", __func__, __LINE__);
175 return -1;
176 }
177 databuf->data = malloc(len);
178 if(!databuf->data) {
179 LOGE(LOG_TAG, "%s:%d, malloc %d bytes failed", __func__, __LINE__, len);
180 return -1;
181 }
182 memset(databuf->data, 0, len);
183 databuf->size = len;
184 memcpy(databuf->data, buf, len);
185 databuf->wOffset = 0;
186 databuf->dev = dev;
187 dlist_init(&databuf->list);
188 dlist_add_tail(&databuf->list, &pb_data_list);
189 //LOGE(LOG_TAG, "%s:%d: push %d bytes to buffer", __func__, __LINE__, len);
190 return len;
191 }