|
|
|
|
@ -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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|