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
|
return
|
||||||
;;
|
;;
|
||||||
--record-format)
|
--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
|
return
|
||||||
;;
|
;;
|
||||||
--render-driver)
|
--render-driver)
|
||||||
|
@ -65,7 +65,7 @@ arguments=(
|
|||||||
'--push-target=[Set the target directory for pushing files to the device by drag and drop]'
|
'--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'
|
{-r,--record=}'[Record screen to file]:record file:_files'
|
||||||
'--raw-key-events[Inject key events for all input keys, and ignore text events]'
|
'--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)'
|
'--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]'
|
'--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)'
|
'--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
|
.TP
|
||||||
.BI "\-\-record\-format " format
|
.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
|
.TP
|
||||||
.BI "\-\-render\-driver " name
|
.BI "\-\-render\-driver " name
|
||||||
|
@ -594,8 +594,8 @@ static const struct sc_option options[] = {
|
|||||||
.longopt_id = OPT_RECORD_FORMAT,
|
.longopt_id = OPT_RECORD_FORMAT,
|
||||||
.longopt = "record-format",
|
.longopt = "record-format",
|
||||||
.argdesc = "format",
|
.argdesc = "format",
|
||||||
.text = "Force recording format (mp4, mkv, m4a, mka, opus, aac or "
|
.text = "Force recording format (mp4, mkv, m4a, mka, opus, aac, flac "
|
||||||
"flac).",
|
"or wav).",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.longopt_id = OPT_RENDER_DRIVER,
|
.longopt_id = OPT_RENDER_DRIVER,
|
||||||
@ -1630,6 +1630,9 @@ get_record_format(const char *name) {
|
|||||||
if (!strcmp(name, "flac")) {
|
if (!strcmp(name, "flac")) {
|
||||||
return SC_RECORD_FORMAT_FLAC;
|
return SC_RECORD_FORMAT_FLAC;
|
||||||
}
|
}
|
||||||
|
if (!strcmp(name, "wav")) {
|
||||||
|
return SC_RECORD_FORMAT_WAV;
|
||||||
|
}
|
||||||
return 0;
|
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
|
if (opts->video
|
||||||
&& sc_record_format_is_audio_only(opts->record_format)) {
|
&& sc_record_format_is_audio_only(opts->record_format)) {
|
||||||
LOGE("Audio container does not support video stream");
|
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)");
|
"(try with --audio-codec=flac)");
|
||||||
return false;
|
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) {
|
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_OPUS,
|
||||||
SC_RECORD_FORMAT_AAC,
|
SC_RECORD_FORMAT_AAC,
|
||||||
SC_RECORD_FORMAT_FLAC,
|
SC_RECORD_FORMAT_FLAC,
|
||||||
|
SC_RECORD_FORMAT_WAV,
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline bool
|
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_MKA
|
||||||
|| fmt == SC_RECORD_FORMAT_OPUS
|
|| fmt == SC_RECORD_FORMAT_OPUS
|
||||||
|| fmt == SC_RECORD_FORMAT_AAC
|
|| fmt == SC_RECORD_FORMAT_AAC
|
||||||
|| fmt == SC_RECORD_FORMAT_FLAC;
|
|| fmt == SC_RECORD_FORMAT_FLAC
|
||||||
|
|| fmt == SC_RECORD_FORMAT_WAV;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum sc_codec {
|
enum sc_codec {
|
||||||
|
@ -71,6 +71,8 @@ sc_recorder_get_format_name(enum sc_record_format format) {
|
|||||||
return "opus";
|
return "opus";
|
||||||
case SC_RECORD_FORMAT_FLAC:
|
case SC_RECORD_FORMAT_FLAC:
|
||||||
return "flac";
|
return "flac";
|
||||||
|
case SC_RECORD_FORMAT_WAV:
|
||||||
|
return "wav";
|
||||||
default:
|
default:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -168,13 +170,14 @@ sc_recorder_close_output_file(struct sc_recorder *recorder) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline bool
|
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)) {
|
if (recorder->video && sc_vecdeque_is_empty(&recorder->video_queue)) {
|
||||||
// The video queue is empty
|
// The video queue is empty
|
||||||
return true;
|
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)
|
// The audio queue is empty (when audio is enabled)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -190,7 +193,7 @@ sc_recorder_process_header(struct sc_recorder *recorder) {
|
|||||||
while (!recorder->stopped &&
|
while (!recorder->stopped &&
|
||||||
((recorder->video && !recorder->video_init)
|
((recorder->video && !recorder->video_init)
|
||||||
|| (recorder->audio && !recorder->audio_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);
|
sc_cond_wait(&recorder->cond, &recorder->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,7 +212,8 @@ sc_recorder_process_header(struct sc_recorder *recorder) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AVPacket *audio_pkt = NULL;
|
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);
|
assert(recorder->audio);
|
||||||
audio_pkt = sc_vecdeque_pop(&recorder->audio_queue);
|
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;
|
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;
|
recorder->audio_init = true;
|
||||||
sc_cond_signal(&recorder->cond);
|
sc_cond_signal(&recorder->cond);
|
||||||
sc_mutex_unlock(&recorder->mutex);
|
sc_mutex_unlock(&recorder->mutex);
|
||||||
@ -709,6 +717,8 @@ sc_recorder_init(struct sc_recorder *recorder, const char *filename,
|
|||||||
recorder->video_init = false;
|
recorder->video_init = false;
|
||||||
recorder->audio_init = false;
|
recorder->audio_init = false;
|
||||||
|
|
||||||
|
recorder->audio_expects_config_packet = false;
|
||||||
|
|
||||||
sc_recorder_stream_init(&recorder->video_stream);
|
sc_recorder_stream_init(&recorder->video_stream);
|
||||||
sc_recorder_stream_init(&recorder->audio_stream);
|
sc_recorder_stream_init(&recorder->audio_stream);
|
||||||
|
|
||||||
|
@ -50,6 +50,8 @@ struct sc_recorder {
|
|||||||
bool video_init;
|
bool video_init;
|
||||||
bool audio_init;
|
bool audio_init;
|
||||||
|
|
||||||
|
bool audio_expects_config_packet;
|
||||||
|
|
||||||
struct sc_recorder_stream video_stream;
|
struct sc_recorder_stream video_stream;
|
||||||
struct sc_recorder_stream audio_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 --record=file.opus
|
||||||
scrcpy --no-video --audio-codec=aac --record=file.aac
|
scrcpy --no-video --audio-codec=aac --record=file.aac
|
||||||
scrcpy --no-video --audio-codec=flac --record=file.flac
|
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
|
# .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`)
|
- Matroska (`.mkv`, `.mka`)
|
||||||
- OPUS (`.opus`)
|
- OPUS (`.opus`)
|
||||||
- FLAC (`.flac`)
|
- FLAC (`.flac`)
|
||||||
|
- WAV (`.wav`)
|
||||||
|
|
||||||
The container is automatically selected based on the filename.
|
The container is automatically selected based on the filename.
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user