mp4 recording code from @LemonSkin

This commit is contained in:
F. Duncanh
2026-01-22 00:18:31 -05:00
parent 1bd37fd5c8
commit 8d8c0e78c9
5 changed files with 417 additions and 5 deletions

View File

@@ -67,6 +67,7 @@
#include "lib/crypto.h"
#include "renderers/video_renderer.h"
#include "renderers/audio_renderer.h"
#include "renderers/mux_renderer.h"
#ifdef DBUS
#include <dbus/dbus.h>
#endif
@@ -197,6 +198,8 @@ static std::string ble_filename = "";
static std::string rtp_pipeline = "";
static std::string audio_rtp_pipeline = "";
static GMainLoop *gmainloop = NULL;
static bool mux_to_file = false;
static std::string mux_filename = "recording";
//Support for D-Bus-based screensaver inhibition (org.freedesktop.ScreenSaver)
static unsigned int scrsv;
@@ -906,6 +909,8 @@ static void print_info (char *name) {
printf("-n name Specify network name of the AirPlay server (UTF-8/ascii)\n");
printf("-nh Do not add \"@hostname\" at the end of AirPlay server name\n");
printf("-h265 Support h265 (4K) video (with h265 versions of h264 plugins)\n");
printf("-mp4 [fn] Record (non-HLS)audio/video to mp4 file \"fn.[n].[format].mp4\"\n");
printf(" n=1,2,.. format = H264/5, ALAC/AAC. Default fn=\"recording\"\n");
printf("-hls [v] Support HTTP Live Streaming (HLS), Youtube app video only: \n");
printf(" v = 2 or 3 (default 3) optionally selects video player version\n");
printf("-lang xx HLS language preferences (\"fr:es:..\", overrides $LANGUAGE)\n");
@@ -951,6 +956,7 @@ static void print_info (char *name) {
printf("-vrtp pl Use rtph26[4,5]pay to send decoded video elsewhere: \"pl\"\n");
printf(" is the remaining pipeline, starting with rtph26*pay options:\n");
printf(" e.g. \"config-interval=1 ! udpsink host=127.0.0.1 port=5000\"\n");
printf(" Writes output to \"fn.N.mp4\"\n");
printf("-v4l2 Use Video4Linux2 for GPU hardware h264 decoding\n");
printf("-bt709 Sometimes needed for Raspberry Pi models using Video4Linux2 \n");
printf("-srgb Display \"Full range\" [0-255] color, not \"Limited Range\"[16-235]\n");
@@ -985,6 +991,9 @@ static void print_info (char *name) {
printf("-key [fn] Store private key in $HOME/.uxplay.pem (or in file \"fn\")\n");
printf("-dacp [fn]Export client DACP information to file $HOME/.uxplay.dacp\n");
printf(" (option to use file \"fn\" instead); used for client remote\n");
printf("-ble [fn] For BluetoothLE beacon: write data to file ~/.uxplay.ble\n");
printf(" optional: write to file \"fn\" (\"fn\" = \"off\" to cancel)\n");
printf("-d [n] Enable debug logging; optional: n=1 to skip normal packet data\n");
printf("-vdmp [n] Dump h264 video output to \"fn.h264\"; fn=\"videodump\",change\n");
printf(" with \"-vdmp [n] filename\". If [n] is given, file fn.x.h264\n");
printf(" x=1,2,.. opens whenever a new SPS/PPS NAL arrives, and <=n\n");
@@ -993,9 +1002,6 @@ static void print_info (char *name) {
printf(" =1,2,..; fn=\"audiodump\"; change with \"-admp [n] filename\".\n");
printf(" x increases when audio format changes. If n is given, <= n\n");
printf(" audio packets are dumped. \"aud\"= unknown format.\n");
printf("-ble [fn] For BluetoothLE beacon: write data to file ~/.uxplay.ble\n");
printf(" optional: write to file \"fn\" (\"fn\" = \"off\" to cancel)\n");
printf("-d [n] Enable debug logging; optional: n=1 to skip normal packet data\n");
printf("-v Displays version information\n");
printf("-h Displays this help\n");
printf("-rc fn Read startup options from file \"fn\" instead of ~/.uxplayrc, etc\n");
@@ -1480,6 +1486,17 @@ static void parse_arguments (int argc, char *argv[]) {
exit(1);
}
}
} else if (arg == "-mp4"){
mux_to_file = true;
if (i < argc - 1 && *argv[i+1] != '-') {
mux_filename.erase();
mux_filename.append(argv[++i]);
const char *fn = mux_filename.c_str();
if (!file_has_write_access(fn)) {
fprintf(stderr, "%s cannot be written to:\noption \"-mp4 <fn>\" must be to a file with write access\n", fn);
exit(1);
}
}
} else if (arg == "-admp") {
dump_audio = true;
if (i < argc - 1 && *argv[i+1] != '-') {
@@ -2117,6 +2134,9 @@ extern "C" void video_reset(void *cls, reset_type_t type) {
extern "C" int video_set_codec(void *cls, video_codec_t codec) {
bool video_is_h265 = (codec == VIDEO_CODEC_H265);
if (mux_to_file) {
mux_renderer_choose_video_codec(video_is_h265);
}
if (!use_video) {
return 0;
}
@@ -2176,7 +2196,10 @@ extern "C" void conn_destroy (void *cls) {
}
if (dacpfile.length()) {
remove (dacpfile.c_str());
}
}
if (mux_to_file) {
mux_renderer_stop();
}
}
}
@@ -2233,6 +2256,9 @@ extern "C" void audio_process (void *cls, raop_ntp_t *ntp, audio_decode_struct *
if (dump_audio) {
dump_audio_to_file(data->data, data->data_len, (data->data)[0] & 0xf0);
}
if (mux_to_file) {
mux_renderer_push_audio(data->data, data->data_len, data->ntp_time_remote);
}
if (use_audio) {
if (!remote_clock_offset) {
uint64_t local_time = (data->ntp_time_local ? data->ntp_time_local : get_local_time());
@@ -2265,6 +2291,9 @@ extern "C" void video_process (void *cls, raop_ntp_t *ntp, video_decode_struct *
if (dump_video) {
dump_video_to_file(data->data, data->data_len);
}
if (mux_to_file) {
mux_renderer_push_video(data->data, data->data_len, data->ntp_time_remote);
}
if (use_video) {
if (!remote_clock_offset) {
uint64_t local_time = (data->ntp_time_local ? data->ntp_time_local : get_local_time());
@@ -2403,6 +2432,10 @@ extern "C" void audio_get_format (void *cls, unsigned char *ct, unsigned short *
audio_renderer_start(ct);
}
if (mux_to_file) {
mux_renderer_choose_audio_codec(*ct);
}
if (coverart_filename.length()) {
write_coverart(coverart_filename.c_str(), (const void *) empty_image, sizeof(empty_image));
}
@@ -3069,6 +3102,10 @@ int main (int argc, char *argv[]) {
#endif
}
if (mux_to_file) {
mux_renderer_init(render_logger, mux_filename.c_str(), use_audio, use_video);
}
if (udp[0]) {
LOGI("using network ports UDP %d %d %d TCP %d %d %d", udp[0], udp[1], udp[2], tcp[0], tcp[1], tcp[2]);
}
@@ -3169,6 +3206,9 @@ int main (int argc, char *argv[]) {
raop_start_httpd(raop, &port);
raop_set_port(raop, port);
}
if (mux_to_file) {
mux_renderer_stop();
}
goto reconnect;
} else {
LOGI("Stopping RAOP Server...");