📄 pcm_a2dpd.c
字号:
DBG(""); a2dp_disconnect(a2dp); a2dp_free(a2dp); DBG("OK"); return 0;}static int a2dp_params(snd_pcm_ioplug_t * io, snd_pcm_hw_params_t * params){ snd_pcm_a2dp_t *a2dp = io->private_data; unsigned int period_bytes; a2dp->frame_bytes = (snd_pcm_format_physical_width(io->format) * io->channels) / 8; period_bytes = io->period_size * a2dp->frame_bytes; DBG("format %s rate %d channels %d", snd_pcm_format_name(io->format), io->rate, io->channels); return 0;}static int a2dp_prepare(snd_pcm_ioplug_t * io){ snd_pcm_a2dp_t *a2dp = io->private_data; float block = ((float)A2DPD_BLOCK_SIZE); a2dp->num = 0; a2dp->rate = io->rate; a2dp->channels = io->channels; if(a2dp->opened_for == A2DPD_PLUGIN_PCM_READ) { block = 48; // ALSA library is really picky on the fact num is not null. If it is, capture won't start a2dp->num = io->period_size; } a2dp->TimerInfos.fps = (float) ((((float)a2dp->rate) * ((float) a2dp->frame_bytes) / block) / 1.0); DBG("block %f, %f fps", block, a2dp->TimerInfos.fps); return 0;}// static int a2dp_drain(snd_pcm_ioplug_t * io)// {// snd_pcm_a2dp_t *a2dp = io->private_data;// DBG("a2dp %p", a2dp);// return 0;// }// static int a2dp_descriptors_count(snd_pcm_ioplug_t * io)// {// return 1;// }// static int a2dp_descriptors(snd_pcm_ioplug_t * io, struct pollfd *pfds, unsigned int space)// {// if (space < 1) {// DBG("Can't fill in descriptors");// SNDERR("Can't fill in descriptors");// return 0;// }// // Alsa does make sure writing now will not block// // So give him an always writable socket!// pfds[0].fd = fileno(stdout);// pfds[0].events = POLLOUT;// return 1;// }// static int a2dp_poll(snd_pcm_ioplug_t * io, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)// {// snd_pcm_a2dp_t *a2dp = io->private_data;// *revents = pfds[0].revents;// // if (a2dp->sk <= 0)// return 0;// // if (pfds[0].revents & POLLHUP) {// a2dp_disconnect(a2dp);// snd_pcm_ioplug_reinit_status(&a2dp->io);// }// // return 0;// }static snd_pcm_ioplug_callback_t a2dp_write_callback = { .close = a2dp_close, .start = a2dp_start, .stop = a2dp_stop, .prepare = a2dp_prepare, .pointer = a2dp_pointer, .transfer = a2dp_transfer, .hw_params = a2dp_params,// .drain = a2dp_drain,// .poll_descriptors_count = a2dp_descriptors_count,// .poll_descriptors = a2dp_descriptors,// .poll_revents = a2dp_poll,};static snd_pcm_ioplug_callback_t a2dp_read_callback = { .close = a2dp_close, .start = a2dp_start, .stop = a2dp_stop, .prepare = a2dp_prepare, .pointer = a2dp_pointer, .transfer = a2dp_read, .hw_params = a2dp_params,};// Alsa can convert about any format/channels/rate to any other rate// However, since we added some code in the daemon to convert, why not do it ourselves!!!// Moreover some player like aplay won't play a wav file if the device that do not natively support the requested format// If you want alsa to do the conversion, just remove the value you want to see convertedstatic int a2dp_constraint(snd_pcm_a2dp_t * a2dp){ snd_pcm_ioplug_t *io = &a2dp->io; snd_pcm_access_t access_list[] = { SND_PCM_ACCESS_RW_INTERLEAVED, SND_PCM_ACCESS_MMAP_INTERLEAVED }; unsigned int formats[] = { SND_PCM_FORMAT_U8, SND_PCM_FORMAT_S8, SND_PCM_FORMAT_S16_LE }; unsigned int channels[] = { 1, 2 }; unsigned int rates[] = { 8000, 11025, 22050, 32000, 44100, 48000 }; int formats_nb = ARRAY_SIZE(formats); int channels_nb = ARRAY_SIZE(channels); int rates_nb = ARRAY_SIZE(rates); int rate_daemon = 0; int rate_prefered = 0; int period_bytes = 8192; char srcfilename[512]; int err; if(a2dp->opened_for == A2DPD_PLUGIN_PCM_WRITE) { get_config_filename(srcfilename, sizeof(srcfilename)); // Default is same as the daemon rate_daemon = read_config_int(srcfilename, "a2dpd", "rate", A2DPD_FRAME_RATE); // If a value is specified, use it rate_prefered = read_config_int(srcfilename, "a2dpd", "plugin-rate", rate_daemon); // If a constraint is defined for the plugin use it if(a2dp->rateconstraint != 0) { rate_prefered = a2dp->rateconstraint; } // If this value is not 0, alsa will convert to plugin-rate if(rate_prefered != 0) { // use defaults settings the rate specified + 16 bits stereo rates[0] = rate_prefered; rates_nb = 1; formats[0] = SND_PCM_FORMAT_S16_LE; formats_nb = 1; channels[0] = 2; channels_nb = 1; period_bytes = 8192; DBG("%d", rate_prefered); } else { // If this value is 0, the daemon will do most conversions } DBG("A2DPD_PLUGIN_PCM_WRITE %d", rate_prefered); } else if(a2dp->opened_for == A2DPD_PLUGIN_PCM_READ) { // Capture is only available using SCO // 8000 hz, 16bits, Mono rates[0] = 8000; rates_nb = 1; formats[0] = SND_PCM_FORMAT_S16_LE; formats_nb = 1; channels[0] = 1; channels_nb = 1; period_bytes = 48; DBG("A2DPD_PLUGIN_PCM_READ %d", 8000); } err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS, ARRAY_SIZE(access_list), access_list); if (err < 0) return err; err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_FORMAT, formats_nb, formats); if (err < 0) return err; err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_CHANNELS, channels_nb, channels); if (err < 0) return err; err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_RATE, rates_nb, rates); if (err < 0) return err; err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, 8192, 8192); if (err < 0) return err; err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS, 2, 2); if (err < 0) return err; return 0;}SND_PCM_PLUGIN_DEFINE_FUNC(a2dpd){ snd_pcm_a2dp_t *a2dp = NULL; snd_config_iterator_t i, next; int err = 0; long rateconstraint = 0; long streamid = 0; long debug = 0; long starta2dpd = 0; DBG("Open mode is for %s", stream == SND_PCM_STREAM_PLAYBACK ? "Playback" : stream == SND_PCM_STREAM_CAPTURE ? "Capture" : "Undefined (capture and playback)"); snd_config_for_each(i, next, conf) { snd_config_t *n = snd_config_iterator_entry(i); const char *id; if (snd_config_get_id(n, &id) < 0) continue; // Alsa specific options if (!strcmp(id, "comment") || !strcmp(id, "type")) continue; // Ignore old options if (strstr("ipaddr bdaddr port src dst use_rfcomm", id)) continue; // rate of the plugin (overwrite plugin-rate in .a2dprc) if (!strcmp(id, "rateconstraint")) { if (snd_config_get_integer(n, &rateconstraint) < 0) { SNDERR("Invalid type for %s", id); return -EINVAL; } continue; } // Start a2dpd if (!strcmp(id, "starta2dpd")) { if (snd_config_get_integer(n, &starta2dpd) < 0) { SNDERR("Invalid type for %s", id); return -EINVAL; } continue; } // streamid (to be used later) if (!strcmp(id, "streamid")) { if (snd_config_get_integer(n, &streamid) < 0) { SNDERR("Invalid type for %s", id); return -EINVAL; } continue; } // debug if (!strcmp(id, "debug")) { if (snd_config_get_integer(n, &debug) < 0) { SNDERR("Invalid type for %s", id); return -EINVAL; } continue; } SNDERR("Unknown field %s", id); return -EINVAL; } g_bdebug = debug; a2dp = a2dp_alloc(); if (!a2dp) { SNDERR("Can't allocate plugin data"); return -ENOMEM; } // Notify plugin a2dp->rateconstraint = rateconstraint; a2dp->streamid = streamid; a2dp->io.version = SND_PCM_IOPLUG_VERSION; a2dp->io.name = "Bluetooth Advanced Audio Distribution"; a2dp->io.mmap_rw = 0; a2dp->io.poll_fd = 1; /* Do not use poll !! */ a2dp->io.poll_events = POLLOUT; /* Do not use poll !! */ a2dp->io.callback = (stream == SND_PCM_STREAM_PLAYBACK)?(&a2dp_write_callback):(&a2dp_read_callback); a2dp->io.private_data = a2dp; a2dp->opened_for = (stream == SND_PCM_STREAM_PLAYBACK)?(A2DPD_PLUGIN_PCM_WRITE):(A2DPD_PLUGIN_PCM_READ); // Startup A2DPD if(starta2dpd) { startup_a2dpd_upon_request(); } err = snd_pcm_ioplug_create(&a2dp->io, name, stream, mode); if (err < 0) goto error; err = a2dp_constraint(a2dp); if (err < 0) { snd_pcm_ioplug_delete(&a2dp->io); goto error; } *pcmp = a2dp->io.pcm; return 0; error: a2dp_disconnect(a2dp); a2dp_free(a2dp); return err;}SND_PCM_PLUGIN_SYMBOL(a2dpd);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -