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
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
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
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*


+ 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 (fp)
fclose(fp);
@ -383,40 +383,48 @@ void db_close_stream(output_t *op, FILE *fp) {
MYSQL_BIND b[3];
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);
if ((output_storage & OUTPUT_STORAGE_FILE))
goto file;
free(stream.s);
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)
fclose(fp);
@ -430,9 +438,8 @@ file:;
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))
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_close_call(metafile_t *);
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_config_stream(output_t *op);
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;
else if (!strcmp(os_str, "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"))
output_storage = OUTPUT_STORAGE_BOTH;
else


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

@ -11,6 +11,7 @@ enum output_storage_enum {
OUTPUT_STORAGE_FILE = 0x1,
OUTPUT_STORAGE_DB = 0x2,
OUTPUT_STORAGE_BOTH = 0x3,
OUTPUT_STORAGE_DB_MEMORY = 0x4,
};
enum mix_method {
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);
}
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) {
const char *err;
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)))
goto done;
output_shutdown(output, NULL);
output_shutdown(output, NULL, NULL);
err = "failed to alloc format context";
output->fmtctx = avformat_alloc_context();
@ -328,6 +346,10 @@ int output_config(output_t *output, const format_t *requested_format, format_t *
#endif
char *full_fn = NULL;
if ((output_storage & OUTPUT_STORAGE_DB_MEMORY))
goto no_output_file;
char suff[16] = "";
for (int i = 1; i < 20; i++) {
if (!output->skip_filename_extension) {
@ -350,9 +372,11 @@ got_fn:
output->filename = full_fn;
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) {
err = "failed to alloc I/O buffer";
@ -370,13 +394,20 @@ got_fn:
goto err;
}
no_output_file:
err = "failed to alloc avio buffer";
void *avio_buf = av_malloc(DEFAULT_AVIO_BUFSIZE);
if (!avio_buf)
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";
if (!output->avioctx) {
av_freep(&avio_buf);
@ -390,12 +421,12 @@ got_fn:
if (av_ret)
goto err;
if (output_chmod)
if (output_chmod && output->filename)
if (chmod(output->filename, output_chmod))
ilog(LOG_WARN, "Failed to change file mode of '%s%s%s': %s",
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))
ilog(LOG_WARN, "Failed to change file owner/group of '%s%s%s': %s",
FMT_M(output->filename), strerror(errno));
@ -405,14 +436,14 @@ got_fn:
}
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:
if (actual_format)
*actual_format = output->actual_format;
return 0;
err:
output_shutdown(output, NULL);
output_shutdown(output, NULL, NULL);
ilog(LOG_ERR, "Error configuring media output: %s", err);
if (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)
return false;
if (!output->fmtctx)
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;
if (output->fmtctx->pb)
@ -443,6 +474,15 @@ static bool output_shutdown(output_t *output, FILE **fp) {
fclose(output->fp);
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->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)
return;
if (!discard) {
GString *membuf = 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);
}
else
else {
db_delete_stream(mf, output);
if (membuf)
g_string_free(membuf, TRUE);
}
}
else {
output_shutdown(output, NULL);
output_shutdown(output, NULL, NULL);
if (output->filename && unlink(output->filename))
ilog(LOG_WARN, "Failed to unlink '%s%s%s': %s",
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->filename, g_free);
g_clear_pointer(&output->iobuf, g_free);
if (output->membuf)
g_string_free(output->membuf, TRUE);
g_free(output);
}


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

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


Loading…
Cancel
Save