mirror of https://github.com/Genymobile/scrcpy
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:
parent
1713422c13
commit
200488111e
|
@ -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)
|
||||
|
|
|
@ -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)'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
Loading…
Reference in New Issue