Add support for RAW audio (WAV) recording

RAW audio forwarding was supported but not for recording.

Add support for recording a raw audio stream to a `.wav` file (and
`.mkv`).
This commit is contained in:
Romain Vimont 2023-11-13 09:35:18 +01:00
parent 1713422c13
commit 200488111e
8 changed files with 43 additions and 15 deletions

View File

@ -125,7 +125,7 @@ _scrcpy() {
return
;;
--record-format)
COMPREPLY=($(compgen -W 'mp4 mkv m4a mka opus aac flac' -- "$cur"))
COMPREPLY=($(compgen -W 'mp4 mkv m4a mka opus aac flac wav' -- "$cur"))
return
;;
--render-driver)

View File

@ -65,7 +65,7 @@ arguments=(
'--push-target=[Set the target directory for pushing files to the device by drag and drop]'
{-r,--record=}'[Record screen to file]:record file:_files'
'--raw-key-events[Inject key events for all input keys, and ignore text events]'
'--record-format=[Force recording format]:format:(mp4 mkv m4a mka opus aac flac)'
'--record-format=[Force recording format]:format:(mp4 mkv m4a mka opus aac flac wav)'
'--render-driver=[Request SDL to use the given render driver]:driver name:(direct3d opengl opengles2 opengles metal software)'
'--require-audio=[Make scrcpy fail if audio is enabled but does not work]'
'--rotation=[Set the initial display rotation]:rotation values:(0 1 2 3)'

View File

@ -355,7 +355,7 @@ Inject key events for all input keys, and ignore text events.
.TP
.BI "\-\-record\-format " format
Force recording format (mp4, mkv, m4a, mka, opus, aac or flac).
Force recording format (mp4, mkv, m4a, mka, opus, aac, flac or wav).
.TP
.BI "\-\-render\-driver " name

View File

@ -594,8 +594,8 @@ static const struct sc_option options[] = {
.longopt_id = OPT_RECORD_FORMAT,
.longopt = "record-format",
.argdesc = "format",
.text = "Force recording format (mp4, mkv, m4a, mka, opus, aac or "
"flac).",
.text = "Force recording format (mp4, mkv, m4a, mka, opus, aac, flac "
"or wav).",
},
{
.longopt_id = OPT_RENDER_DRIVER,
@ -1630,6 +1630,9 @@ get_record_format(const char *name) {
if (!strcmp(name, "flac")) {
return SC_RECORD_FORMAT_FLAC;
}
if (!strcmp(name, "wav")) {
return SC_RECORD_FORMAT_WAV;
}
return 0;
}
@ -2373,11 +2376,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
}
}
if (opts->audio_codec == SC_CODEC_RAW) {
LOGE("Recording does not support RAW audio codec");
return false;
}
if (opts->video
&& sc_record_format_is_audio_only(opts->record_format)) {
LOGE("Audio container does not support video stream");
@ -2403,6 +2401,20 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
"(try with --audio-codec=flac)");
return false;
}
if (opts->record_format == SC_RECORD_FORMAT_WAV
&& opts->audio_codec != SC_CODEC_RAW) {
LOGE("Recording to WAV file requires a RAW audio stream "
"(try with --audio-codec=raw)");
return false;
}
if ((opts->record_format == SC_RECORD_FORMAT_MP4 ||
opts->record_format == SC_RECORD_FORMAT_M4A)
&& opts->audio_codec == SC_CODEC_RAW) {
LOGE("Recording to MP4 container does not support RAW audio");
return false;
}
}
if (opts->audio_codec == SC_CODEC_FLAC && opts->audio_bit_rate) {

View File

@ -26,6 +26,7 @@ enum sc_record_format {
SC_RECORD_FORMAT_OPUS,
SC_RECORD_FORMAT_AAC,
SC_RECORD_FORMAT_FLAC,
SC_RECORD_FORMAT_WAV,
};
static inline bool
@ -34,7 +35,8 @@ sc_record_format_is_audio_only(enum sc_record_format fmt) {
|| fmt == SC_RECORD_FORMAT_MKA
|| fmt == SC_RECORD_FORMAT_OPUS
|| fmt == SC_RECORD_FORMAT_AAC
|| fmt == SC_RECORD_FORMAT_FLAC;
|| fmt == SC_RECORD_FORMAT_FLAC
|| fmt == SC_RECORD_FORMAT_WAV;
}
enum sc_codec {

View File

@ -71,6 +71,8 @@ sc_recorder_get_format_name(enum sc_record_format format) {
return "opus";
case SC_RECORD_FORMAT_FLAC:
return "flac";
case SC_RECORD_FORMAT_WAV:
return "wav";
default:
return NULL;
}
@ -168,13 +170,14 @@ sc_recorder_close_output_file(struct sc_recorder *recorder) {
}
static inline bool
sc_recorder_has_empty_queues(struct sc_recorder *recorder) {
sc_recorder_must_wait_for_config_packets(struct sc_recorder *recorder) {
if (recorder->video && sc_vecdeque_is_empty(&recorder->video_queue)) {
// The video queue is empty
return true;
}
if (recorder->audio && sc_vecdeque_is_empty(&recorder->audio_queue)) {
if (recorder->audio && recorder->audio_expects_config_packet
&& sc_vecdeque_is_empty(&recorder->audio_queue)) {
// The audio queue is empty (when audio is enabled)
return true;
}
@ -190,7 +193,7 @@ sc_recorder_process_header(struct sc_recorder *recorder) {
while (!recorder->stopped &&
((recorder->video && !recorder->video_init)
|| (recorder->audio && !recorder->audio_init)
|| sc_recorder_has_empty_queues(recorder))) {
|| sc_recorder_must_wait_for_config_packets(recorder))) {
sc_cond_wait(&recorder->cond, &recorder->mutex);
}
@ -209,7 +212,8 @@ sc_recorder_process_header(struct sc_recorder *recorder) {
}
AVPacket *audio_pkt = NULL;
if (!sc_vecdeque_is_empty(&recorder->audio_queue)) {
if (recorder->audio_expects_config_packet &&
!sc_vecdeque_is_empty(&recorder->audio_queue)) {
assert(recorder->audio);
audio_pkt = sc_vecdeque_pop(&recorder->audio_queue);
}
@ -597,6 +601,10 @@ sc_recorder_audio_packet_sink_open(struct sc_packet_sink *sink,
recorder->audio_stream.index = stream->index;
// A config packet is provided for all supported formats except raw audio
recorder->audio_expects_config_packet =
ctx->codec_id != AV_CODEC_ID_PCM_S16LE;
recorder->audio_init = true;
sc_cond_signal(&recorder->cond);
sc_mutex_unlock(&recorder->mutex);
@ -709,6 +717,8 @@ sc_recorder_init(struct sc_recorder *recorder, const char *filename,
recorder->video_init = false;
recorder->audio_init = false;
recorder->audio_expects_config_packet = false;
sc_recorder_stream_init(&recorder->video_stream);
sc_recorder_stream_init(&recorder->audio_stream);

View File

@ -50,6 +50,8 @@ struct sc_recorder {
bool video_init;
bool audio_init;
bool audio_expects_config_packet;
struct sc_recorder_stream video_stream;
struct sc_recorder_stream audio_stream;

View File

@ -19,6 +19,7 @@ To record only the audio:
scrcpy --no-video --record=file.opus
scrcpy --no-video --audio-codec=aac --record=file.aac
scrcpy --no-video --audio-codec=flac --record=file.flac
scrcpy --no-video --audio-codec=raw --record=file.wav
# .m4a/.mp4 and .mka/.mkv are also supported for opus, aac and flac
```
@ -37,6 +38,7 @@ client side. Several formats (containers) are supported:
- Matroska (`.mkv`, `.mka`)
- OPUS (`.opus`)
- FLAC (`.flac`)
- WAV (`.wav`)
The container is automatically selected based on the filename.