📄 a2dpd.c
字号:
} // Wait must take place after sending a packet // This way, you will allow the plugin to send it's data // And you will collect the new data // Time reference floating because of 44100/1000 error in integer calculation if((delay = a2dp_timer_sleep(&TimerInfos, A2DPTIMERPREDELAY))>0) { usleep(delay); } // Display daemon state if enabled if(TimerInfos.display>0) { if(g_brereadconfig) { char addr[20]; char plug[20]; int bredirect = read_config_int(g_srcfilename, "a2dpd", "enableredirectalsa", REDIRECT_A2DP); read_config_string(g_srcfilename, "a2dpd", "address", addr, sizeof(addr), ""); read_config_string(g_srcfilename, "a2dpd", "alsaoutput", plug, sizeof(plug), ""); if((strcmp(addr, lpDevice->addr) != 0) || (strcmp(plug, lpDevice->plug) != 0) || (bredirect != lpDevice->bredirect)) { DBG("File change detected"); // Force destroy, device will be recreated upon audio incoming strcpy(lpDevice->addr, addr); strcpy(lpDevice->plug, plug); lpDevice->bredirect = bredirect; a2dp_set_dst_addr(a2dp, addr); destroy_count=lpDevice->a2dp_timeout+1; } } if(a2dp_get_user_flags(a2dp) & A2DPD_FLAGS_DISPLAYSTATE) { struct timeval now; gettimeofday(&now, NULL); timersub(&now, &hdr.packet_date, &now); DBG("[%d,%d|%d,%d] %s %s clients=%d freq=%d latency=%d", lpDevice->mixer.volume_speaker_left, lpDevice->mixer.volume_speaker_right, lpDevice->mixer.volume_micro_left, lpDevice->mixer.volume_micro_right, (state_current==DEVICE_STATE_SOUND)?"playing":"silent", a2dp_is_connected(a2dp)?"connected":"disconnected", lpDevice->nb_clients, TimerInfos.display, (int)now.tv_usec); } } // Force destroy if(bError || (g_autoconnect && destroy_count>lpDevice->a2dp_timeout)){ if(a2dp_is_connecting(a2dp)) { destroy_count = -1; a2dp_state_disconnect(a2dp); } if(sco_is_connecting(sco)) { destroy_count = -1; sco_state_disconnect(sco); } } // Remember state state_previous = state_current; if(a2dp_is_connected(a2dp) || sco_is_connected(sco)) { a2dpd_dbus_state = CONNECTED; a2dpd_signal_state(CONNECTED, lpDevice->addr); } else if(a2dp_is_connecting(a2dp) || sco_is_connecting(sco)) { a2dpd_dbus_state = CONNECTING; a2dpd_signal_state(CONNECTING, lpDevice->addr); } else { a2dpd_dbus_state = DISCONNECTED; a2dpd_signal_state(DISCONNECTED, ""); } } safefree(pcm_buffer); // Sleep a little bit before retrying if (!bSigINTReceived) { DBG("Sleeping"); sleep(1); } // Free A2DP alsa_destroy(&alsa); a2dp_destroy(&a2dp); close_socket(&avdtpfd); } DBG("Thread finished"); return 0;}// This function handle the bluetooth connectionvoid * avrcp_listener(void *param){ // As long as daemon is running while (!bSigINTReceived) { int sockfd = a2dp_make_listen_socket(23); if (sockfd >= 0) { DBG("avrcp: Accepting incoming connection"); while (!bSigINTReceived) { // Wait for incoming connections char szRemote[64]; uint16_t iMTU = 0; if(poll_accept(sockfd, 1000)) { int new_fd = a2dp_wait_connection(sockfd, szRemote, sizeof(szRemote), &iMTU); if (new_fd > 0) { DBG("socket %d: Connection from %s, mtu=%d", new_fd, szRemote, iMTU); // Loop and manage what the client sends int iReceived = 0; do { iReceived = 0; errno = 0; if(poll_accept(new_fd, 1000)) { iReceived = a2dp_handle_avrcp_message(new_fd); } } while (!bSigINTReceived && (iReceived > 0 || errno == EAGAIN)); DBG("socket %d: timed out", new_fd); close_socket(&new_fd); } else if (errno != EAGAIN) { DBG("a2dp_wait_connection failed"); break; } } else { if(errno==EINTR) { DBG("Signal received in poll"); } } } close_socket(&sockfd); } // Sleep a little bit if we must retry sleep(bSigINTReceived ? 0 : 1); } DBG("Thread finished"); return 0;}// server processing loopvoid main_loop(char *addr){ DBG(""); while (!bSigINTReceived) { // Master socket int sockfd = make_server_socket(); if (sockfd > 0) { LPBTA2DPPERDEVICEDATA lpDevice = bta2dpdevicenew(addr); // Set pthread stack size to decrease unused memory usage pthread_attr_t tattr; pthread_t havrcp; size_t size = PTHREAD_STACK_MIN; int ret = pthread_attr_init(&tattr); ret = pthread_attr_setstacksize(&tattr, size); pthread_create(&lpDevice->thread, &tattr, a2dp_handler, lpDevice); pthread_create(&havrcp, &tattr, avrcp_listener, lpDevice); while (!bSigINTReceived) { int new_fd = -1; if(g_stdin) { // Are there some data on stdin if(poll_accept(fileno(stdin), 0)) { DBG("Data on stdin"); char c [512]; fgets(c, sizeof(c), stdin); if(c[0]) { // write valid commands to ctl_socket write(lpDevice->ctl_socket[WRITE_SIDE], &c, sizeof(c)); DBG("Sent %c %d", c[0], c[0]); } } } if(poll_accept(sockfd, 0)) { new_fd = accept_socket(sockfd); // Handle connection if it is not the final dummy client if (!bSigINTReceived && new_fd > 0) { // Add client to device table pthread_mutex_lock(&lpDevice->mutex); DBG("New client %d", new_fd); int client_index = get_index_for_client(lpDevice); DBG("Got index %d", client_index); if(client_index<MAXCLIENTSPERDEVICE) { lpDevice->clients[client_index].socket = new_fd; } else { DBG("Too many clients"); close_socket(&new_fd); } pthread_mutex_unlock(&lpDevice->mutex); } else { close_socket(&new_fd); } } // Let this thread handle everything stream socket related poll_incoming_clients_sockets(lpDevice, 10000); } close_server_socket(&sockfd); // ask avdtp and avrcp thread to stop DBG("Killing"); pthread_kill(havrcp, SIGUSR1); pthread_kill(lpDevice->thread, SIGUSR1); DBG("joining"); pthread_join(lpDevice->thread, NULL); pthread_join(havrcp, NULL); DBG("continuing"); // Free informations on the device bta2dpdevicefree(lpDevice); pthread_attr_destroy(&tattr); } else { DBG("Cannot get the socket"); } sleep(bSigINTReceived?0:1); }}static struct option main_lopts[] = { { "nofork", 0, 0, 'n' }, { "daemon", 0, 0, 'd' }, { "verbose", 0, 0, 'v' }, { "silent", 0, 0, 's' }, { "kill", 0, 0, 'k' }, { "realtime", 0, 0, 'r' }, { "file", 1, 0, 'f' }, { "address", 1, 0, 'a' }, { "logfile", 1, 0, 'l' }, { "configread", 0, 0, 'c' }, { "autoconnect", 0, 0, 'o' }, { "debug", 0, 0, 'g' }, { "sessionbus", 0, 0, 'b' }, { "help", 0, 0, 'h' }, { 0, 0, 0, 0 }};static char main_sopts[] = "ndvskrf:a:l:cogbh";char* opts_comment[] = { "Do not fork", "Fork and run as daemon", "Use stdout instead of /dev/null", "Do not redirect output", "Kill currently running daemon", "Set realtime priority, use with care", "Read config from file <param>", "Connect to bluetooth address <param>", "Redirect output in file <param>", "Read config file periodically", "Automatically connect to headset", "Display debug traces", "Use session bus", // keep last "Display usage", NULL};void usage(int argc, char** argv){ int i = 0; printf("%s", argv[0]); while(opts_comment[i]) { printf("\t--%-15s -%c\t%s\t%s\r\n", main_lopts[i].name, main_lopts[i].val, main_lopts[i].has_arg?"<param>":"", opts_comment[i]); i++; }}// main functionint main(int argc, char *argv[]){ int opt = 0; struct timespec timer_resolution = { 0, 0 }; char address[256] = ""; char *addr = &address[0]; struct sched_param schedparam = { sched_get_priority_max(SCHED_FIFO) }; int res = 0, bFork = 0, bVerbose = 1, bKill = 0, bRealtime = 0, bSessionBus = -1; get_config_filename(g_srcfilename, sizeof(g_srcfilename)); // parse arguments on cmdline while ((opt=getopt_long(argc, argv, main_sopts, main_lopts, NULL)) != -1) { switch(opt) { case 'n': bFork = 0; break; case 'd': bFork = 1; break; case 'v': bVerbose = 1; break; case 's': bVerbose = 0; break; case 'k': bKill = 1; break; case 'r': bRealtime = 1; break; case 'f': strncpy(g_srcfilename, optarg, sizeof(g_srcfilename)-1); g_srcfilename[sizeof(g_srcfilename)-1] = 0; DBG("Config file %s", g_srcfilename); break; case 'a': strncpy(address, optarg, sizeof(address)-1); address[sizeof(address)-1] = 0; break; case 'l': strncpy(g_sOutputFilename, optarg, sizeof(g_sOutputFilename)-1); g_sOutputFilename[sizeof(g_sOutputFilename)-1] = 0; break; case 'c': g_brereadconfig = 1; break; case 'o': g_autoconnect = 1; break; case 'g': g_bdebug = 1; break; case 'b': bSessionBus = 1; break; case 'h': default: usage(argc, argv); exit(0); break; } } // Read config values from config file if(address[0]=='\0') read_config_string(g_srcfilename, "a2dpd", "address", address, sizeof(address), ""); if(g_sCmdPlay[0]=='\0') read_config_string(g_srcfilename, "a2dpd", "cmdplay", g_sCmdPlay, sizeof(g_sCmdPlay), ""); if(g_sCmdPause[0]=='\0') read_config_string(g_srcfilename, "a2dpd", "cmdpause", g_sCmdPause, sizeof(g_sCmdPause), ""); if(g_sCmdStop[0]=='\0') read_config_string(g_srcfilename, "a2dpd", "cmdstop", g_sCmdStop, sizeof(g_sCmdStop), ""); if(g_sCmdPrev[0]=='\0') read_config_string(g_srcfilename, "a2dpd", "cmdprev", g_sCmdPrev, sizeof(g_sCmdPrev), ""); if(g_sCmdNext[0]=='\0') read_config_string(g_srcfilename, "a2dpd", "cmdnext", g_sCmdNext, sizeof(g_sCmdNext), ""); if(g_sCmdNew[0]=='\0') read_config_string(g_srcfilename, "a2dpd", "cmdnew", g_sCmdNew, sizeof(g_sCmdNew), ""); if(g_sOutputFilename[0]=='\0') read_config_string(g_srcfilename, "a2dpd", "logfile", g_sOutputFilename, sizeof(g_sOutputFilename), "/dev/null"); if(g_brereadconfig<0) g_brereadconfig = read_config_int(g_srcfilename, "a2dpd", "enablerereadconfig", 1); if(g_breversestereo<0) g_breversestereo = read_config_int(g_srcfilename, "a2dpd", "enablereversestereo", 0); if(g_autoconnect<0) g_autoconnect = read_config_int(g_srcfilename, "a2dpd", "enableautoconnect", 1); if(g_bdebug<0) g_bdebug = read_config_int(g_srcfilename, "a2dpd", "enabledebug", 1); if(g_stdin<0) g_stdin = read_config_int(g_srcfilename, "a2dpd", "enablestdin", 0); if(bSessionBus<0) bSessionBus = read_config_int(g_srcfilename, "a2dpd", "sessionbus", 0); clock_getres(CLOCK_REALTIME, &timer_resolution); ignore_child_processes_return_values(); // Redirect outputs (if a file is specified, redirect to it and not stdout) if(strcmp(g_sOutputFilename, "/dev/null") != 0) bVerbose = 0; make_daemon_process(bFork, bVerbose, g_sOutputFilename); // Lockfile must be acquired after daemonisation if(lockfile(bKill)<0) RETURNERROR("Lockfile acquisition failed"); init_uinput(); DBG("%s addr=%s timer=%d us [%s %s]", argv[0], addr, (int) (timer_resolution.tv_nsec / 1000), __DATE__, __TIME__); // If we can be realtime it will be better if(bRealtime) { DBG("Setting realtime priority to process, use with care"); // After some trouble while coding, a2dpd started spining 100%cpu // In realtime, this led me with the only option of rebooting my PC res = sched_setscheduler(0, SCHED_FIFO, &schedparam); if(res != 0) DBG("Setscheduler failed"); } // set up the handler signal(SIGINT, sigint_handler); signal(SIGTERM, sigint_handler); signal(SIGUSR1, SIG_IGN); // global initialisations a2dpd_signal_init(bSessionBus); a2dp_init(); // Run main loop main_loop(addr); // global termination a2dp_exit(); a2dpd_signal_kill(); kill_uinput(); DBG("Terminated succesfully"); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -