Browse Source

MT#62544 support in-memory recording

Change-Id: I17348db1d6dd53fa582aa5198877c0bb717b7049
pull/1945/head
Richard Fuchs 7 months ago
parent
commit
d0a3bae13e
7 changed files with 112 additions and 51 deletions
  1. +5
    -2
      docs/rtpengine-recording.md
  2. +38
    -31
      recording-daemon/db.c
  3. +1
    -1
      recording-daemon/db.h
  4. +2
    -0
      recording-daemon/main.c
  5. +1
    -0
      recording-daemon/main.h
  6. +63
    -17
      recording-daemon/output.c
  7. +2
    -0
      recording-daemon/types.h

+ 5
- 2
docs/rtpengine-recording.md View File

@ -138,12 +138,15 @@ sufficient for a standard installation of rtpengine.
Points to the shared object file (__.so__) containing the reference Points to the shared object file (__.so__) containing the reference
implementation for the EVS codec. See the `README` for more details. implementation for the EVS codec. See the `README` for more details.
- __\-\-output-storage=file__\|__db__\|__both__
- __\-\-output-storage=file__\|__db__\|__db-mem__\|__both__
Where to store media files. By default, media files are written directly to the Where to store media files. By default, media files are written directly to the
file system (see __output-dir__). They can also be stored as a __BLOB__ in a file system (see __output-dir__). They can also be stored as a __BLOB__ in a
MySQL database, either instead of, or in addition to, being written to the file MySQL database, either instead of, or in addition to, being written to the file
system.
system. Database storage can either be facilitated using temporary files
(__db__) which are then read back and deleted, or without temporary files
(__db-mem__) by spooling the audio data in memory until the recording is
complete.
- __\-\-output-dir=__*PATH* - __\-\-output-dir=__*PATH*


+ 38
- 31
recording-daemon/db.c View File

@ -370,7 +370,7 @@ void db_close_call(metafile_t *mf) {
} }
} }
void db_close_stream(output_t *op, FILE *fp) {
void db_close_stream(output_t *op, FILE *fp, GString *gs) {
if (check_conn() || op->db_id == 0) { if (check_conn() || op->db_id == 0) {
if (fp) if (fp)
fclose(fp); fclose(fp);
@ -383,40 +383,48 @@ void db_close_stream(output_t *op, FILE *fp) {
MYSQL_BIND b[3]; MYSQL_BIND b[3];
if ((output_storage & OUTPUT_STORAGE_DB)) { if ((output_storage & OUTPUT_STORAGE_DB)) {
FILE *f = fp;
if (!f)
f = fopen(op->filename, "rb");
if (!f) {
ilog(LOG_ERR, "Failed to open file: %s%s%s", FMT_M(op->filename));
if ((output_storage & OUTPUT_STORAGE_FILE))
goto file;
return;
if (gs) {
if (fp)
fclose(fp);
stream.len = gs->len;
stream.s = g_string_free(gs, FALSE);
} }
fseek(f, 0, SEEK_END);
long pos = ftell(f);
if (pos < 0) {
ilog(LOG_ERR, "Failed to get file position: %s", strerror(errno));
fclose(f);
if ((output_storage & OUTPUT_STORAGE_FILE))
goto file;
return;
}
stream.len = pos;
fseek(f, 0, SEEK_SET);
stream.s = malloc(stream.len);
if (stream.s) {
size_t count = fread(stream.s, 1, stream.len, f);
if (count != stream.len) {
stream.len = 0;
ilog(LOG_ERR, "Failed to read from stream");
else {
FILE *f = fp;
if (!f)
f = fopen(op->filename, "rb");
if (!f) {
ilog(LOG_ERR, "Failed to open file: %s%s%s", FMT_M(op->filename));
if ((output_storage & OUTPUT_STORAGE_FILE))
goto file;
return;
}
fseek(f, 0, SEEK_END);
long pos = ftell(f);
if (pos < 0) {
ilog(LOG_ERR, "Failed to get file position: %s", strerror(errno));
fclose(f); fclose(f);
if ((output_storage & OUTPUT_STORAGE_FILE)) if ((output_storage & OUTPUT_STORAGE_FILE))
goto file; goto file;
free(stream.s);
return; return;
} }
stream.len = pos;
fseek(f, 0, SEEK_SET);
stream.s = g_malloc(stream.len);
if (stream.s) {
size_t count = fread(stream.s, 1, stream.len, f);
if (count != stream.len) {
stream.len = 0;
ilog(LOG_ERR, "Failed to read from stream");
fclose(f);
if ((output_storage & OUTPUT_STORAGE_FILE))
goto file;
g_free(stream.s);
return;
}
}
fclose(f);
} }
fclose(f);
} }
else if (fp) else if (fp)
fclose(fp); fclose(fp);
@ -430,9 +438,8 @@ file:;
execute_wrap(&stm_close_stream, b, NULL); execute_wrap(&stm_close_stream, b, NULL);
if (stream.s)
free(stream.s);
if (!(output_storage & OUTPUT_STORAGE_FILE))
g_free(stream.s);
if (op->filename && !(output_storage & OUTPUT_STORAGE_FILE))
if (unlink(op->filename)) if (unlink(op->filename))
ilog(LOG_ERR, "Failed to delete file '%s': %s", op->filename, strerror(errno)); ilog(LOG_ERR, "Failed to delete file '%s': %s", op->filename, strerror(errno));
} }


+ 1
- 1
recording-daemon/db.h View File

@ -7,7 +7,7 @@
void db_do_call(metafile_t *); void db_do_call(metafile_t *);
void db_close_call(metafile_t *); void db_close_call(metafile_t *);
void db_do_stream(metafile_t *mf, output_t *op, stream_t *, unsigned long ssrc); void db_do_stream(metafile_t *mf, output_t *op, stream_t *, unsigned long ssrc);
void db_close_stream(output_t *op, FILE *);
void db_close_stream(output_t *op, FILE *, GString *);
void db_delete_stream(metafile_t *, output_t *op); void db_delete_stream(metafile_t *, output_t *op);
void db_config_stream(output_t *op); void db_config_stream(output_t *op);
void db_thread_end(void); void db_thread_end(void);


+ 2
- 0
recording-daemon/main.c View File

@ -285,6 +285,8 @@ static void options(int *argc, char ***argv) {
output_storage = OUTPUT_STORAGE_FILE; output_storage = OUTPUT_STORAGE_FILE;
else if (!strcmp(os_str, "db")) else if (!strcmp(os_str, "db"))
output_storage = OUTPUT_STORAGE_DB; output_storage = OUTPUT_STORAGE_DB;
else if (!strcmp(os_str, "db-mem"))
output_storage = OUTPUT_STORAGE_DB | OUTPUT_STORAGE_DB_MEMORY;
else if (!strcmp(os_str, "both")) else if (!strcmp(os_str, "both"))
output_storage = OUTPUT_STORAGE_BOTH; output_storage = OUTPUT_STORAGE_BOTH;
else else


+ 1
- 0
recording-daemon/main.h View File

@ -11,6 +11,7 @@ enum output_storage_enum {
OUTPUT_STORAGE_FILE = 0x1, OUTPUT_STORAGE_FILE = 0x1,
OUTPUT_STORAGE_DB = 0x2, OUTPUT_STORAGE_DB = 0x2,
OUTPUT_STORAGE_BOTH = 0x3, OUTPUT_STORAGE_BOTH = 0x3,
OUTPUT_STORAGE_DB_MEMORY = 0x4,
}; };
enum mix_method { enum mix_method {
MM_DIRECT = 0, MM_DIRECT = 0,


+ 63
- 17
recording-daemon/output.c View File

@ -24,7 +24,7 @@ int mp3_bitrate;
static bool output_shutdown(output_t *output, FILE **);
static bool output_shutdown(output_t *output, FILE **, GString **);
@ -269,6 +269,24 @@ int64_t output_avio_seek(void *opaque, int64_t offset, int whence) {
return ftell(o->fp); return ftell(o->fp);
} }
int output_avio_mem_write(void *opaque, const uint8_t *buf, int buf_size) {
output_t *o = opaque;
g_string_overwrite_len(o->membuf, o->mempos, (const char *) buf, buf_size);
o->mempos += buf_size;
return buf_size;
}
int64_t output_avio_mem_seek(void *opaque, int64_t offset, int whence) {
output_t *o = opaque;
if (whence == SEEK_SET)
o->mempos = offset;
else if (whence == SEEK_CUR)
o->mempos += offset;
else if (whence == SEEK_END)
o->mempos = o->membuf->len + offset;
return o->mempos;
}
int output_config(output_t *output, const format_t *requested_format, format_t *actual_format) { int output_config(output_t *output, const format_t *requested_format, format_t *actual_format) {
const char *err; const char *err;
int av_ret = 0; int av_ret = 0;
@ -284,7 +302,7 @@ int output_config(output_t *output, const format_t *requested_format, format_t *
if (G_LIKELY(format_eq(&req_fmt, &output->requested_format))) if (G_LIKELY(format_eq(&req_fmt, &output->requested_format)))
goto done; goto done;
output_shutdown(output, NULL);
output_shutdown(output, NULL, NULL);
err = "failed to alloc format context"; err = "failed to alloc format context";
output->fmtctx = avformat_alloc_context(); output->fmtctx = avformat_alloc_context();
@ -328,6 +346,10 @@ int output_config(output_t *output, const format_t *requested_format, format_t *
#endif #endif
char *full_fn = NULL; char *full_fn = NULL;
if ((output_storage & OUTPUT_STORAGE_DB_MEMORY))
goto no_output_file;
char suff[16] = ""; char suff[16] = "";
for (int i = 1; i < 20; i++) { for (int i = 1; i < 20; i++) {
if (!output->skip_filename_extension) { if (!output->skip_filename_extension) {
@ -350,9 +372,11 @@ got_fn:
output->filename = full_fn; output->filename = full_fn;
err = "failed to open output file"; err = "failed to open output file";
output->fp = fopen(full_fn, (output_storage & OUTPUT_STORAGE_DB) ? "wb+" : "wb");
if (!output->fp)
goto err;
if (!(output_storage & OUTPUT_STORAGE_DB_MEMORY)) {
output->fp = fopen(full_fn, (output_storage & OUTPUT_STORAGE_DB) ? "wb+" : "wb");
if (!output->fp)
goto err;
}
if (output_buffer > 0) { if (output_buffer > 0) {
err = "failed to alloc I/O buffer"; err = "failed to alloc I/O buffer";
@ -370,13 +394,20 @@ got_fn:
goto err; goto err;
} }
no_output_file:
err = "failed to alloc avio buffer"; err = "failed to alloc avio buffer";
void *avio_buf = av_malloc(DEFAULT_AVIO_BUFSIZE); void *avio_buf = av_malloc(DEFAULT_AVIO_BUFSIZE);
if (!avio_buf) if (!avio_buf)
goto err; goto err;
output->avioctx = avio_alloc_context(avio_buf, DEFAULT_AVIO_BUFSIZE, 1, output,
NULL, output_avio_write, output_avio_seek);
if (!(output_storage & OUTPUT_STORAGE_DB_MEMORY))
output->avioctx = avio_alloc_context(avio_buf, DEFAULT_AVIO_BUFSIZE, 1, output,
NULL, output_avio_write, output_avio_seek);
else {
output->membuf = g_string_new("");
output->avioctx = avio_alloc_context(avio_buf, DEFAULT_AVIO_BUFSIZE, 1, output,
NULL, output_avio_mem_write, output_avio_mem_seek);
}
err = "failed to alloc AVIOContext"; err = "failed to alloc AVIOContext";
if (!output->avioctx) { if (!output->avioctx) {
av_freep(&avio_buf); av_freep(&avio_buf);
@ -390,12 +421,12 @@ got_fn:
if (av_ret) if (av_ret)
goto err; goto err;
if (output_chmod)
if (output_chmod && output->filename)
if (chmod(output->filename, output_chmod)) if (chmod(output->filename, output_chmod))
ilog(LOG_WARN, "Failed to change file mode of '%s%s%s': %s", ilog(LOG_WARN, "Failed to change file mode of '%s%s%s': %s",
FMT_M(output->filename), strerror(errno)); FMT_M(output->filename), strerror(errno));
if (output_chown != -1 || output_chgrp != -1)
if ((output_chown != -1 || output_chgrp != -1) && output->filename)
if (chown(output->filename, output_chown, output_chgrp)) if (chown(output->filename, output_chown, output_chgrp))
ilog(LOG_WARN, "Failed to change file owner/group of '%s%s%s': %s", ilog(LOG_WARN, "Failed to change file owner/group of '%s%s%s': %s",
FMT_M(output->filename), strerror(errno)); FMT_M(output->filename), strerror(errno));
@ -405,14 +436,14 @@ got_fn:
} }
db_config_stream(output); db_config_stream(output);
ilog(LOG_INFO, "Opened output media file '%s' for writing", full_fn);
ilog(LOG_INFO, "Opened output media file '%s' for writing", full_fn ?: "(mem stream)");
done: done:
if (actual_format) if (actual_format)
*actual_format = output->actual_format; *actual_format = output->actual_format;
return 0; return 0;
err: err:
output_shutdown(output, NULL);
output_shutdown(output, NULL, NULL);
ilog(LOG_ERR, "Error configuring media output: %s", err); ilog(LOG_ERR, "Error configuring media output: %s", err);
if (av_ret) if (av_ret)
ilog(LOG_ERR, "Error returned from libav: %s", av_error(av_ret)); ilog(LOG_ERR, "Error returned from libav: %s", av_error(av_ret));
@ -420,13 +451,13 @@ err:
} }
static bool output_shutdown(output_t *output, FILE **fp) {
static bool output_shutdown(output_t *output, FILE **fp, GString **gs) {
if (!output) if (!output)
return false; return false;
if (!output->fmtctx) if (!output->fmtctx)
return false; return false;
ilog(LOG_INFO, "Closing output media file '%s'", output->filename);
ilog(LOG_INFO, "Closing output media file '%s'", output->filename ?: "(mem stream)");
bool ret = false; bool ret = false;
if (output->fmtctx->pb) if (output->fmtctx->pb)
@ -443,6 +474,15 @@ static bool output_shutdown(output_t *output, FILE **fp) {
fclose(output->fp); fclose(output->fp);
output->fp = NULL; output->fp = NULL;
} }
else if (output->membuf) {
if (output->membuf->len) {
if (gs) {
*gs = output->membuf;
output->membuf = NULL;
}
ret = true;
}
}
if (output->avioctx) { if (output->avioctx) {
if (output->avioctx->buffer) if (output->avioctx->buffer)
av_freep(&output->avioctx->buffer); av_freep(&output->avioctx->buffer);
@ -468,16 +508,20 @@ void output_close(metafile_t *mf, output_t *output, tag_t *tag, bool discard) {
if (!output) if (!output)
return; return;
if (!discard) { if (!discard) {
GString *membuf = NULL;
FILE *fp = NULL; FILE *fp = NULL;
if (output_shutdown(output, &fp)) {
db_close_stream(output, fp);
if (output_shutdown(output, &fp, &membuf)) {
db_close_stream(output, fp, membuf);
notify_push_output(output, mf, tag); notify_push_output(output, mf, tag);
} }
else
else {
db_delete_stream(mf, output); db_delete_stream(mf, output);
if (membuf)
g_string_free(membuf, TRUE);
}
} }
else { else {
output_shutdown(output, NULL);
output_shutdown(output, NULL, NULL);
if (output->filename && unlink(output->filename)) if (output->filename && unlink(output->filename))
ilog(LOG_WARN, "Failed to unlink '%s%s%s': %s", ilog(LOG_WARN, "Failed to unlink '%s%s%s': %s",
FMT_M(output->filename), strerror(errno)); FMT_M(output->filename), strerror(errno));
@ -489,6 +533,8 @@ void output_close(metafile_t *mf, output_t *output, tag_t *tag, bool discard) {
g_clear_pointer(&output->file_name, g_free); g_clear_pointer(&output->file_name, g_free);
g_clear_pointer(&output->filename, g_free); g_clear_pointer(&output->filename, g_free);
g_clear_pointer(&output->iobuf, g_free); g_clear_pointer(&output->iobuf, g_free);
if (output->membuf)
g_string_free(output->membuf, TRUE);
g_free(output); g_free(output);
} }


+ 2
- 0
recording-daemon/types.h View File

@ -176,6 +176,8 @@ struct output_s {
FILE *fp; FILE *fp;
char *iobuf; char *iobuf;
GString *membuf;
size_t mempos;
AVIOContext *avioctx; AVIOContext *avioctx;
AVFormatContext *fmtctx; AVFormatContext *fmtctx;
AVStream *avst; AVStream *avst;


Loading…
Cancel
Save