mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-26 23:25:56 -05:00
[artwork/transcode] Also let transcode.c handle rescaling of non-file Spotify artwork
This commit is contained in:
parent
e7f888645f
commit
441ad006a6
@ -324,13 +324,14 @@ rescale_calculate(int *target_w, int *target_h, int width, int height, int max_w
|
||||
/* Get an artwork file from the filesystem. Will rescale if needed.
|
||||
*
|
||||
* @out evbuf Image data
|
||||
* @in path Path to the artwork
|
||||
* @in path Path to the artwork (alternative to inbuf)
|
||||
* @in inbuf Buffer with the artwork (alternative to path)
|
||||
* @in max_w Requested width
|
||||
* @in max_h Requested height
|
||||
* @return ART_FMT_* on success, ART_E_ERROR on error
|
||||
*/
|
||||
static int
|
||||
artwork_get(struct evbuffer *evbuf, char *path, int max_w, int max_h)
|
||||
artwork_get(struct evbuffer *evbuf, char *path, struct evbuffer *inbuf, int max_w, int max_h)
|
||||
{
|
||||
struct decode_ctx *xcode_decode;
|
||||
struct encode_ctx *xcode_encode;
|
||||
@ -344,7 +345,7 @@ artwork_get(struct evbuffer *evbuf, char *path, int max_w, int max_h)
|
||||
|
||||
DPRINTF(E_SPAM, L_ART, "Getting artwork (max destination width %d height %d)\n", max_w, max_h);
|
||||
|
||||
xcode_decode = transcode_decode_setup(XCODE_PNG, DATA_KIND_FILE, path, 0); // Good for XCODE_JPEG too
|
||||
xcode_decode = transcode_decode_setup(XCODE_JPEG, DATA_KIND_FILE, path, inbuf, 0); // Covers XCODE_PNG too
|
||||
if (!xcode_decode)
|
||||
{
|
||||
DPRINTF(E_DBG, L_ART, "No artwork found in '%s'\n", path);
|
||||
@ -368,7 +369,7 @@ artwork_get(struct evbuffer *evbuf, char *path, int max_w, int max_h)
|
||||
if (ret < 0)
|
||||
{
|
||||
// No rescaling required, just read the raw file into the evbuf
|
||||
if (artwork_read(evbuf, path) != 0)
|
||||
if (!path || artwork_read(evbuf, path) != 0)
|
||||
goto fail_free_decode;
|
||||
|
||||
transcode_decode_cleanup(&xcode_decode);
|
||||
@ -518,7 +519,7 @@ artwork_get_dir_image(struct evbuffer *evbuf, char *dir, int max_w, int max_h, c
|
||||
|
||||
snprintf(out_path, PATH_MAX, "%s", path);
|
||||
|
||||
return artwork_get(evbuf, path, max_w, max_h);
|
||||
return artwork_get(evbuf, path, NULL, max_w, max_h);
|
||||
}
|
||||
|
||||
|
||||
@ -631,7 +632,7 @@ source_item_embedded_get(struct artwork_ctx *ctx)
|
||||
|
||||
snprintf(ctx->path, sizeof(ctx->path), "%s", ctx->dbmfi->path);
|
||||
|
||||
return artwork_get(ctx->evbuf, ctx->path, ctx->max_w, ctx->max_h);
|
||||
return artwork_get(ctx->evbuf, ctx->path, NULL, ctx->max_w, ctx->max_h);
|
||||
}
|
||||
|
||||
/* Looks for basename(in_path).{png,jpg}, so if in_path is /foo/bar.mp3 it
|
||||
@ -685,7 +686,7 @@ source_item_own_get(struct artwork_ctx *ctx)
|
||||
|
||||
snprintf(ctx->path, sizeof(ctx->path), "%s", path);
|
||||
|
||||
return artwork_get(ctx->evbuf, path, ctx->max_w, ctx->max_h);
|
||||
return artwork_get(ctx->evbuf, path, NULL, ctx->max_w, ctx->max_h);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -774,13 +775,8 @@ source_item_stream_get(struct artwork_ctx *ctx)
|
||||
static int
|
||||
source_item_spotify_get(struct artwork_ctx *ctx)
|
||||
{
|
||||
AVFormatContext *src_ctx;
|
||||
AVIOContext *avio;
|
||||
AVInputFormat *ifmt;
|
||||
struct evbuffer *raw;
|
||||
struct evbuffer *evbuf;
|
||||
int target_w;
|
||||
int target_h;
|
||||
int ret;
|
||||
|
||||
raw = evbuffer_new();
|
||||
@ -820,75 +816,29 @@ source_item_spotify_get(struct artwork_ctx *ctx)
|
||||
goto out_free_evbuf;
|
||||
}
|
||||
|
||||
// Now evbuf will be processed by ffmpeg, since it probably needs to be rescaled
|
||||
src_ctx = avformat_alloc_context();
|
||||
if (!src_ctx)
|
||||
// For non-file input, artwork_get() will also fail if no rescaling is required
|
||||
ret = artwork_get(ctx->evbuf, NULL, evbuf, ctx->max_w, ctx->max_h);
|
||||
if (ret == ART_E_ERROR)
|
||||
{
|
||||
DPRINTF(E_LOG, L_ART, "Out of memory for source context\n");
|
||||
goto out_free_evbuf;
|
||||
DPRINTF(E_DBG, L_ART, "Not rescaling Spotify image\n");
|
||||
ret = evbuffer_add_buffer(ctx->evbuf, raw);
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_ART, "Could not add or rescale image to output evbuf\n");
|
||||
goto out_free_evbuf;
|
||||
}
|
||||
}
|
||||
|
||||
avio = avio_input_evbuffer_open(evbuf);
|
||||
if (!avio)
|
||||
{
|
||||
DPRINTF(E_LOG, L_ART, "Could not alloc input evbuffer\n");
|
||||
goto out_free_ctx;
|
||||
}
|
||||
|
||||
src_ctx->pb = avio;
|
||||
|
||||
ifmt = av_find_input_format("mjpeg");
|
||||
if (!ifmt)
|
||||
{
|
||||
DPRINTF(E_LOG, L_ART, "Could not find mjpeg input format\n");
|
||||
goto out_close_avio;
|
||||
}
|
||||
|
||||
ret = avformat_open_input(&src_ctx, NULL, ifmt, NULL);
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_ART, "Could not open input\n");
|
||||
goto out_close_avio;
|
||||
}
|
||||
|
||||
ret = avformat_find_stream_info(src_ctx, NULL);
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_ART, "Could not find stream info\n");
|
||||
goto out_close_input;
|
||||
}
|
||||
|
||||
ret = rescale_needed(src_ctx->streams[0]->codec, ctx->max_w, ctx->max_h, &target_w, &target_h);
|
||||
if (!ret)
|
||||
ret = evbuffer_add_buffer(ctx->evbuf, raw);
|
||||
else
|
||||
ret = artwork_rescale(ctx->evbuf, src_ctx, 0, target_w, target_h);
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_ART, "Could not add or rescale image to output evbuf\n");
|
||||
goto out_close_input;
|
||||
}
|
||||
|
||||
avformat_close_input(&src_ctx);
|
||||
avio_evbuffer_close(avio);
|
||||
evbuffer_free(evbuf);
|
||||
evbuffer_free(raw);
|
||||
|
||||
return ART_FMT_JPEG;
|
||||
|
||||
out_close_input:
|
||||
avformat_close_input(&src_ctx);
|
||||
out_close_avio:
|
||||
avio_evbuffer_close(avio);
|
||||
out_free_ctx:
|
||||
if (src_ctx)
|
||||
avformat_free_context(src_ctx);
|
||||
out_free_evbuf:
|
||||
evbuffer_free(evbuf);
|
||||
evbuffer_free(raw);
|
||||
|
||||
return ART_E_ERROR;
|
||||
|
||||
}
|
||||
#else
|
||||
static int
|
||||
|
@ -125,6 +125,9 @@ avio_evbuffer_close(AVIOContext *s)
|
||||
{
|
||||
struct avio_evbuffer *ae;
|
||||
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
ae = (struct avio_evbuffer *)s->opaque;
|
||||
|
||||
avio_flush(s);
|
||||
|
@ -70,6 +70,9 @@ struct settings_ctx
|
||||
// Output format (for the muxer)
|
||||
const char *format;
|
||||
|
||||
// Input format (for the demuxer)
|
||||
const char *in_format;
|
||||
|
||||
// Audio settings
|
||||
enum AVCodecID audio_codec;
|
||||
const char *audio_codec_name;
|
||||
@ -111,6 +114,9 @@ struct decode_ctx
|
||||
// Input format context
|
||||
AVFormatContext *ifmt_ctx;
|
||||
|
||||
// IO Context for non-file input
|
||||
AVIOContext *avio;
|
||||
|
||||
// Stream and decoder data
|
||||
struct stream_ctx audio_stream;
|
||||
struct stream_ctx video_stream;
|
||||
@ -219,6 +225,7 @@ init_settings(struct settings_ctx *settings, enum transcode_profile profile)
|
||||
settings->encode_video = 1;
|
||||
settings->silent = 1;
|
||||
settings->format = "image2";
|
||||
settings->in_format = "mjpeg";
|
||||
settings->video_codec = AV_CODEC_ID_MJPEG;
|
||||
break;
|
||||
|
||||
@ -712,10 +719,11 @@ open_decoder(unsigned int *stream_index, struct decode_ctx *ctx, enum AVMediaTyp
|
||||
}
|
||||
|
||||
static int
|
||||
open_input(struct decode_ctx *ctx, const char *path)
|
||||
open_input(struct decode_ctx *ctx, const char *path, struct evbuffer *evbuf)
|
||||
{
|
||||
AVDictionary *options = NULL;
|
||||
AVCodecContext *dec_ctx;
|
||||
AVInputFormat *ifmt;
|
||||
unsigned int stream_index;
|
||||
int ret;
|
||||
|
||||
@ -735,7 +743,24 @@ open_input(struct decode_ctx *ctx, const char *path)
|
||||
ctx->ifmt_ctx->interrupt_callback.opaque = ctx;
|
||||
ctx->timestamp = av_gettime();
|
||||
|
||||
ret = avformat_open_input(&ctx->ifmt_ctx, path, NULL, &options);
|
||||
if (evbuf)
|
||||
{
|
||||
ifmt = av_find_input_format(ctx->settings.in_format);
|
||||
if (!ifmt)
|
||||
{
|
||||
DPRINTF(E_LOG, L_XCODE, "Could not find input format: '%s'\n", ctx->settings.in_format);
|
||||
return -1;
|
||||
}
|
||||
|
||||
CHECK_NULL(L_XCODE, ctx->avio = avio_input_evbuffer_open(evbuf));
|
||||
|
||||
ctx->ifmt_ctx->pb = ctx->avio;
|
||||
ret = avformat_open_input(&ctx->ifmt_ctx, NULL, ifmt, &options);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = avformat_open_input(&ctx->ifmt_ctx, path, NULL, &options);
|
||||
}
|
||||
|
||||
if (options)
|
||||
av_dict_free(&options);
|
||||
@ -782,6 +807,7 @@ open_input(struct decode_ctx *ctx, const char *path)
|
||||
return 0;
|
||||
|
||||
out_fail:
|
||||
avio_evbuffer_close(ctx->avio);
|
||||
avcodec_free_context(&ctx->audio_stream.codec);
|
||||
avcodec_free_context(&ctx->video_stream.codec);
|
||||
avformat_close_input(&ctx->ifmt_ctx);
|
||||
@ -792,6 +818,7 @@ open_input(struct decode_ctx *ctx, const char *path)
|
||||
static void
|
||||
close_input(struct decode_ctx *ctx)
|
||||
{
|
||||
avio_evbuffer_close(ctx->avio);
|
||||
avcodec_free_context(&ctx->audio_stream.codec);
|
||||
avcodec_free_context(&ctx->video_stream.codec);
|
||||
avformat_close_input(&ctx->ifmt_ctx);
|
||||
@ -1075,7 +1102,7 @@ close_filters(struct encode_ctx *ctx)
|
||||
/* Setup */
|
||||
|
||||
struct decode_ctx *
|
||||
transcode_decode_setup(enum transcode_profile profile, enum data_kind data_kind, const char *path, uint32_t song_length)
|
||||
transcode_decode_setup(enum transcode_profile profile, enum data_kind data_kind, const char *path, struct evbuffer *evbuf, uint32_t song_length)
|
||||
{
|
||||
struct decode_ctx *ctx;
|
||||
|
||||
@ -1086,7 +1113,7 @@ transcode_decode_setup(enum transcode_profile profile, enum data_kind data_kind,
|
||||
ctx->duration = song_length;
|
||||
ctx->data_kind = data_kind;
|
||||
|
||||
if ((init_settings(&ctx->settings, profile) < 0) || (open_input(ctx, path) < 0))
|
||||
if ((init_settings(&ctx->settings, profile) < 0) || (open_input(ctx, path, evbuf) < 0))
|
||||
goto fail_free;
|
||||
|
||||
return ctx;
|
||||
@ -1143,7 +1170,7 @@ transcode_setup(enum transcode_profile profile, enum data_kind data_kind, const
|
||||
|
||||
CHECK_NULL(L_XCODE, ctx = calloc(1, sizeof(struct transcode_ctx)));
|
||||
|
||||
ctx->decode_ctx = transcode_decode_setup(profile, data_kind, path, song_length);
|
||||
ctx->decode_ctx = transcode_decode_setup(profile, data_kind, path, NULL, song_length);
|
||||
if (!ctx->decode_ctx)
|
||||
{
|
||||
free(ctx);
|
||||
@ -1403,8 +1430,6 @@ transcode_encode(struct evbuffer *evbuf, struct encode_ctx *ctx, void *frame, in
|
||||
|
||||
ret = evbuffer_get_length(ctx->obuf) - start_length;
|
||||
|
||||
// TODO Shouldn't we flush and let the muxer write the trailer now?
|
||||
|
||||
evbuffer_add_buffer(evbuf, ctx->obuf);
|
||||
|
||||
return ret;
|
||||
|
@ -25,7 +25,7 @@ struct transcode_ctx;
|
||||
|
||||
// Setting up
|
||||
struct decode_ctx *
|
||||
transcode_decode_setup(enum transcode_profile profile, enum data_kind data_kind, const char *path, uint32_t song_length);
|
||||
transcode_decode_setup(enum transcode_profile profile, enum data_kind data_kind, const char *path, struct evbuffer *evbuf, uint32_t song_length);
|
||||
|
||||
struct encode_ctx *
|
||||
transcode_encode_setup(enum transcode_profile profile, struct decode_ctx *src_ctx, off_t *est_size, int width, int height);
|
||||
|
Loading…
Reference in New Issue
Block a user