pacat: Allow specifying non-PCM encoding for playback

This adds a --encoding argument to allow specifying a non-PCM format for
playback. This is expected to be useful for testing support for
compressed formats.
This commit is contained in:
Arun Raghavan 2022-01-12 13:22:33 -05:00
parent 47298b8de8
commit f9d139c3bc

View file

@ -100,6 +100,13 @@ static bool sample_spec_set = false;
static pa_channel_map channel_map; static pa_channel_map channel_map;
static bool channel_map_set = false; static bool channel_map_set = false;
/* If the encoding is set, we assume the pa_sample_spec will not be used, and
* that the pa_format_info will be. */
static pa_encoding_t encoding;
static bool encoding_set = false;
pa_format_info *formats[1] = { NULL, };
static sf_count_t (*readf_function)(SNDFILE *_sndfile, void *ptr, sf_count_t frames) = NULL; static sf_count_t (*readf_function)(SNDFILE *_sndfile, void *ptr, sf_count_t frames) = NULL;
static sf_count_t (*writef_function)(SNDFILE *_sndfile, const void *ptr, sf_count_t frames) = NULL; static sf_count_t (*writef_function)(SNDFILE *_sndfile, const void *ptr, sf_count_t frames) = NULL;
@ -319,7 +326,7 @@ static void stream_state_callback(pa_stream *s, void *userdata) {
if (verbose) { if (verbose) {
const pa_buffer_attr *a; const pa_buffer_attr *a;
char cmt[PA_CHANNEL_MAP_SNPRINT_MAX], sst[PA_SAMPLE_SPEC_SNPRINT_MAX]; char cmt[PA_CHANNEL_MAP_SNPRINT_MAX], sst[PA_SAMPLE_SPEC_SNPRINT_MAX], fst[PA_FORMAT_INFO_SNPRINT_MAX];
pa_log(_("Stream successfully created.")); pa_log(_("Stream successfully created."));
@ -335,9 +342,14 @@ static void stream_state_callback(pa_stream *s, void *userdata) {
} }
} }
pa_log(_("Using sample spec '%s', channel map '%s'."), if (!encoding_set) {
pa_sample_spec_snprint(sst, sizeof(sst), pa_stream_get_sample_spec(s)), pa_log(_("Using sample spec '%s', channel map '%s'."),
pa_channel_map_snprint(cmt, sizeof(cmt), pa_stream_get_channel_map(s))); pa_sample_spec_snprint(sst, sizeof(sst), pa_stream_get_sample_spec(s)),
pa_channel_map_snprint(cmt, sizeof(cmt), pa_stream_get_channel_map(s)));
} else {
pa_log(_("Using format '%s'."),
pa_format_info_snprint(fst, sizeof(fst), pa_stream_get_format_info(s)));
}
pa_log(_("Connected to device %s (index: %u, suspended: %s)."), pa_log(_("Connected to device %s (index: %u, suspended: %s)."),
pa_stream_get_device_name(s), pa_stream_get_device_name(s),
@ -449,7 +461,12 @@ static void context_state_callback(pa_context *c, void *userdata) {
if (verbose) if (verbose)
pa_log(_("Connection established.%s"), CLEAR_LINE); pa_log(_("Connection established.%s"), CLEAR_LINE);
if (!(stream = pa_stream_new_with_proplist(c, NULL, &sample_spec, &channel_map, proplist))) { if (!encoding_set)
stream = pa_stream_new_with_proplist(c, NULL, &sample_spec, &channel_map, proplist);
else
stream = pa_stream_new_extended(c, NULL, formats, 1, proplist);
if (!stream) {
pa_log(_("pa_stream_new() failed: %s"), pa_strerror(pa_context_errno(c))); pa_log(_("pa_stream_new() failed: %s"), pa_strerror(pa_context_errno(c)));
goto fail; goto fail;
} }
@ -692,6 +709,7 @@ static void help(const char *argv0) {
" --channels=CHANNELS The number of channels, 1 for mono, 2 for stereo\n" " --channels=CHANNELS The number of channels, 1 for mono, 2 for stereo\n"
" (defaults to 2)\n" " (defaults to 2)\n"
" --channel-map=CHANNELMAP Channel map to use instead of the default\n" " --channel-map=CHANNELMAP Channel map to use instead of the default\n"
" --encoding=ENCODING Encoding to use for non-PCM audio\n"
" --fix-format Take the sample format from the sink/source the stream is\n" " --fix-format Take the sample format from the sink/source the stream is\n"
" being connected to.\n" " being connected to.\n"
" --fix-rate Take the sampling rate from the sink/source the stream is\n" " --fix-rate Take the sampling rate from the sink/source the stream is\n"
@ -721,6 +739,7 @@ enum {
ARG_SAMPLEFORMAT, ARG_SAMPLEFORMAT,
ARG_CHANNELS, ARG_CHANNELS,
ARG_CHANNELMAP, ARG_CHANNELMAP,
ARG_ENCODING,
ARG_FIX_FORMAT, ARG_FIX_FORMAT,
ARG_FIX_RATE, ARG_FIX_RATE,
ARG_FIX_CHANNELS, ARG_FIX_CHANNELS,
@ -762,6 +781,7 @@ int main(int argc, char *argv[]) {
{"format", 1, NULL, ARG_SAMPLEFORMAT}, {"format", 1, NULL, ARG_SAMPLEFORMAT},
{"channels", 1, NULL, ARG_CHANNELS}, {"channels", 1, NULL, ARG_CHANNELS},
{"channel-map", 1, NULL, ARG_CHANNELMAP}, {"channel-map", 1, NULL, ARG_CHANNELMAP},
{"encoding", 1, NULL, ARG_ENCODING},
{"fix-format", 0, NULL, ARG_FIX_FORMAT}, {"fix-format", 0, NULL, ARG_FIX_FORMAT},
{"fix-rate", 0, NULL, ARG_FIX_RATE}, {"fix-rate", 0, NULL, ARG_FIX_RATE},
{"fix-channels", 0, NULL, ARG_FIX_CHANNELS}, {"fix-channels", 0, NULL, ARG_FIX_CHANNELS},
@ -908,6 +928,20 @@ int main(int argc, char *argv[]) {
channel_map_set = true; channel_map_set = true;
break; break;
case ARG_ENCODING:
if ((encoding = pa_encoding_from_string(optarg)) == PA_ENCODING_INVALID) {
pa_log(_("Invalid encoding '%s'"), optarg);
goto quit;
}
if (encoding == PA_ENCODING_PCM) {
pa_log(_("The encoding parameter is only supported with non-PCM formats."));
goto quit;
}
encoding_set = true;
break;
case ARG_FIX_CHANNELS: case ARG_FIX_CHANNELS:
flags |= PA_STREAM_FIX_CHANNELS; flags |= PA_STREAM_FIX_CHANNELS;
break; break;
@ -1007,7 +1041,30 @@ int main(int argc, char *argv[]) {
} }
} }
if (!pa_sample_spec_valid(&sample_spec)) { if (encoding_set && !raw) {
pa_log(_("Cannot set encoding for non-raw mode"));
goto quit;
}
/* The capture path uses the sample spec to know how much to read, so let's
* not support that for now. */
if (encoding_set && mode != PLAYBACK) {
pa_log(_("Cannot set encoding for capture"));
goto quit;
}
if (encoding_set) {
formats[0] = pa_format_info_new();
formats[0]->encoding = encoding;
pa_format_info_set_rate(formats[0], sample_spec.rate);
pa_format_info_set_channels(formats[0], sample_spec.channels);
if (!pa_format_info_valid(formats[0])) {
pa_log(_("Invalid format specification."));
goto quit;
}
} else if (!pa_sample_spec_valid(&sample_spec)) {
pa_log(_("Invalid sample specification")); pa_log(_("Invalid sample specification"));
goto quit; goto quit;
} }
@ -1132,12 +1189,20 @@ int main(int argc, char *argv[]) {
} }
if (verbose) { if (verbose) {
char tss[PA_SAMPLE_SPEC_SNPRINT_MAX], tcm[PA_CHANNEL_MAP_SNPRINT_MAX]; if (!encoding_set) {
char tss[PA_SAMPLE_SPEC_SNPRINT_MAX], tcm[PA_CHANNEL_MAP_SNPRINT_MAX];
pa_log(_("Opening a %s stream with sample specification '%s' and channel map '%s'."), pa_log(_("Opening a %s stream with sample specification '%s' and channel map '%s'."),
mode == RECORD ? _("recording") : _("playback"), mode == RECORD ? _("recording") : _("playback"),
pa_sample_spec_snprint(tss, sizeof(tss), &sample_spec), pa_sample_spec_snprint(tss, sizeof(tss), &sample_spec),
pa_channel_map_snprint(tcm, sizeof(tcm), &channel_map)); pa_channel_map_snprint(tcm, sizeof(tcm), &channel_map));
} else {
char tsf[PA_FORMAT_INFO_SNPRINT_MAX];
pa_log(_("Opening a %s stream with format specification '%s'."),
mode == RECORD ? _("recording") : _("playback"),
pa_format_info_snprint(tsf, sizeof(tsf), formats[0]));
}
} }
/* Fill in client name if none was set */ /* Fill in client name if none was set */
@ -1164,7 +1229,7 @@ int main(int argc, char *argv[]) {
} }
} }
if (raw && mode == PLAYBACK) if (raw && !encoding_set && mode == PLAYBACK)
partialframe_buf = pa_xmalloc(pa_frame_size(&sample_spec)); partialframe_buf = pa_xmalloc(pa_frame_size(&sample_spec));
/* Set up a new main loop */ /* Set up a new main loop */