Skip to content

Commit

Permalink
Add support for RAW audio (WAV) recording
Browse files Browse the repository at this point in the history
RAW audio forwarding was supported but not for recording.

Add support for recording a raw audio stream to a `.wav` file (and
`.mkv`).
  • Loading branch information
rom1v committed Nov 15, 2023
1 parent 1713422 commit 2004881
Show file tree
Hide file tree
Showing 8 changed files with 43 additions and 15 deletions.
2 changes: 1 addition & 1 deletion app/data/bash-completion/scrcpy
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion app/data/zsh-completion/_scrcpy
Original file line number Diff line number Diff line change
Expand Up @@ -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)'
Expand Down
2 changes: 1 addition & 1 deletion app/scrcpy.1
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
26 changes: 19 additions & 7 deletions app/src/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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");
Expand All @@ -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) {
Expand Down
4 changes: 3 additions & 1 deletion app/src/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand Down
18 changes: 14 additions & 4 deletions app/src/recorder.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand All @@ -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);
}

Expand All @@ -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);
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down
2 changes: 2 additions & 0 deletions app/src/recorder.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
2 changes: 2 additions & 0 deletions doc/recording.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```

Expand All @@ -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.

Expand Down

0 comments on commit 2004881

Please sign in to comment.