[artwork/transcode] Also let transcode.c handle rescaling of non-file Spotify artwork

This commit is contained in:
ejurgensen 2017-03-01 21:29:08 +01:00
parent e7f888645f
commit 441ad006a6
4 changed files with 54 additions and 76 deletions

View File

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

View File

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

View File

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

View File

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