mirror of
https://github.com/owntone/owntone-server.git
synced 2025-01-27 06:33:21 -05:00
Add support for JPEG as a valid artwork output format
Reduce CPU usage by avoiding unneeded JPEG -> PNG conversions; it appears all the clients we care about support JPEG as well as PNG.
This commit is contained in:
parent
5cf5cac9c2
commit
c7209ab699
151
src/artwork.c
151
src/artwork.c
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2010 Julien BLACHE <jb@jblache.org>
|
* Copyright (C) 2010-2011 Julien BLACHE <jb@jblache.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -96,7 +96,7 @@ artwork_read(char *filename, struct evbuffer *evbuf)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
artwork_rescale(AVFormatContext *src_ctx, int s, int out_w, int out_h, struct evbuffer *evbuf)
|
artwork_rescale(AVFormatContext *src_ctx, int s, int out_w, int out_h, int format, struct evbuffer *evbuf)
|
||||||
{
|
{
|
||||||
uint8_t *buf;
|
uint8_t *buf;
|
||||||
uint8_t *outbuf;
|
uint8_t *outbuf;
|
||||||
@ -109,7 +109,10 @@ artwork_rescale(AVFormatContext *src_ctx, int s, int out_w, int out_h, struct ev
|
|||||||
AVStream *dst_st;
|
AVStream *dst_st;
|
||||||
|
|
||||||
AVCodec *img_decoder;
|
AVCodec *img_decoder;
|
||||||
AVCodec *png_encoder;
|
AVCodec *img_encoder;
|
||||||
|
|
||||||
|
int64_t pix_fmt_mask;
|
||||||
|
const enum PixelFormat *pix_fmts;
|
||||||
|
|
||||||
AVFrame *i_frame;
|
AVFrame *i_frame;
|
||||||
AVFrame *o_frame;
|
AVFrame *o_frame;
|
||||||
@ -156,7 +159,31 @@ artwork_rescale(AVFormatContext *src_ctx, int s, int out_w, int out_h, struct ev
|
|||||||
goto out_close_src;
|
goto out_close_src;
|
||||||
}
|
}
|
||||||
|
|
||||||
dst_fmt->video_codec = CODEC_ID_PNG;
|
dst_fmt->video_codec = CODEC_ID_NONE;
|
||||||
|
|
||||||
|
/* Try to keep same codec if possible */
|
||||||
|
if ((src->codec_id == CODEC_ID_PNG) && (format & ART_CAN_PNG))
|
||||||
|
dst_fmt->video_codec = CODEC_ID_PNG;
|
||||||
|
else if ((src->codec_id == CODEC_ID_MJPEG) && (format & ART_CAN_JPEG))
|
||||||
|
dst_fmt->video_codec = CODEC_ID_MJPEG;
|
||||||
|
|
||||||
|
/* If not possible, select new codec */
|
||||||
|
if (dst_fmt->video_codec == CODEC_ID_NONE)
|
||||||
|
{
|
||||||
|
if (format & ART_CAN_PNG)
|
||||||
|
dst_fmt->video_codec = CODEC_ID_PNG;
|
||||||
|
else if (format & ART_CAN_JPEG)
|
||||||
|
dst_fmt->video_codec = CODEC_ID_MJPEG;
|
||||||
|
}
|
||||||
|
|
||||||
|
img_encoder = avcodec_find_encoder(dst_fmt->video_codec);
|
||||||
|
if (!img_encoder)
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_ART, "No suitable encoder found for codec ID %d\n", dst_fmt->video_codec);
|
||||||
|
|
||||||
|
ret = -1;
|
||||||
|
goto out_close_src;
|
||||||
|
}
|
||||||
|
|
||||||
dst_ctx = avformat_alloc_context();
|
dst_ctx = avformat_alloc_context();
|
||||||
if (!dst_ctx)
|
if (!dst_ctx)
|
||||||
@ -196,7 +223,26 @@ artwork_rescale(AVFormatContext *src_ctx, int s, int out_w, int out_h, struct ev
|
|||||||
|
|
||||||
dst->codec_id = dst_fmt->video_codec;
|
dst->codec_id = dst_fmt->video_codec;
|
||||||
dst->codec_type = CODEC_TYPE_VIDEO;
|
dst->codec_type = CODEC_TYPE_VIDEO;
|
||||||
dst->pix_fmt = PIX_FMT_RGB24;
|
|
||||||
|
pix_fmt_mask = 0;
|
||||||
|
pix_fmts = img_encoder->pix_fmts;
|
||||||
|
while (pix_fmts && (*pix_fmts != -1))
|
||||||
|
{
|
||||||
|
pix_fmt_mask |= (1 << *pix_fmts);
|
||||||
|
pix_fmts++;
|
||||||
|
}
|
||||||
|
|
||||||
|
dst->pix_fmt = avcodec_find_best_pix_fmt(pix_fmt_mask, src->pix_fmt, 1, NULL);
|
||||||
|
|
||||||
|
if (dst->pix_fmt < 0)
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_ART, "Could not determine best pixel format\n");
|
||||||
|
|
||||||
|
goto out_free_dst;
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
DPRINTF(E_DBG, L_ART, "Selected pixel format: %d\n", dst->pix_fmt);
|
||||||
|
|
||||||
dst->time_base.num = 1;
|
dst->time_base.num = 1;
|
||||||
dst->time_base.den = 25;
|
dst->time_base.den = 25;
|
||||||
@ -214,16 +260,7 @@ artwork_rescale(AVFormatContext *src_ctx, int s, int out_w, int out_h, struct ev
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Open encoder */
|
/* Open encoder */
|
||||||
png_encoder = avcodec_find_encoder(dst_fmt->video_codec);
|
ret = avcodec_open(dst, img_encoder);
|
||||||
if (!png_encoder)
|
|
||||||
{
|
|
||||||
DPRINTF(E_LOG, L_ART, "No suitable encoder found for PNG\n");
|
|
||||||
|
|
||||||
ret = -1;
|
|
||||||
goto out_free_dst;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = avcodec_open(dst, png_encoder);
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_ART, "Could not open codec for encoding: %s\n", strerror(AVUNERROR(ret)));
|
DPRINTF(E_LOG, L_ART, "Could not open codec for encoding: %s\n", strerror(AVUNERROR(ret)));
|
||||||
@ -379,7 +416,21 @@ artwork_rescale(AVFormatContext *src_ctx, int s, int out_w, int out_h, struct ev
|
|||||||
goto out_fclose_dst;
|
goto out_fclose_dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = 0;
|
switch (dst_fmt->video_codec)
|
||||||
|
{
|
||||||
|
case CODEC_ID_PNG:
|
||||||
|
ret = ART_FMT_PNG;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CODEC_ID_MJPEG:
|
||||||
|
ret = ART_FMT_JPEG;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
DPRINTF(E_LOG, L_ART, "Unhandled rescale output format\n");
|
||||||
|
ret = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
out_fclose_dst:
|
out_fclose_dst:
|
||||||
url_fclose(dst_ctx->pb);
|
url_fclose(dst_ctx->pb);
|
||||||
@ -409,7 +460,7 @@ artwork_rescale(AVFormatContext *src_ctx, int s, int out_w, int out_h, struct ev
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
artwork_get(char *filename, int max_w, int max_h, struct evbuffer *evbuf)
|
artwork_get(char *filename, int max_w, int max_h, int format, struct evbuffer *evbuf)
|
||||||
{
|
{
|
||||||
AVFormatContext *src_ctx;
|
AVFormatContext *src_ctx;
|
||||||
AVCodecContext *src;
|
AVCodecContext *src;
|
||||||
@ -417,6 +468,7 @@ artwork_get(char *filename, int max_w, int max_h, struct evbuffer *evbuf)
|
|||||||
int target_w;
|
int target_w;
|
||||||
int target_h;
|
int target_h;
|
||||||
int need_rescale;
|
int need_rescale;
|
||||||
|
int format_ok;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = av_open_input_file(&src_ctx, filename, NULL, 0, NULL);
|
ret = av_open_input_file(&src_ctx, filename, NULL, 0, NULL);
|
||||||
@ -436,12 +488,19 @@ artwork_get(char *filename, int max_w, int max_h, struct evbuffer *evbuf)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
format_ok = 0;
|
||||||
for (s = 0; s < src_ctx->nb_streams; s++)
|
for (s = 0; s < src_ctx->nb_streams; s++)
|
||||||
{
|
{
|
||||||
if (src_ctx->streams[s]->codec->codec_id == CODEC_ID_PNG)
|
if (src_ctx->streams[s]->codec->codec_id == CODEC_ID_PNG)
|
||||||
break;
|
{
|
||||||
|
format_ok = (format & ART_CAN_PNG) ? ART_FMT_PNG : 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
else if (src_ctx->streams[s]->codec->codec_id == CODEC_ID_MJPEG)
|
else if (src_ctx->streams[s]->codec->codec_id == CODEC_ID_MJPEG)
|
||||||
break;
|
{
|
||||||
|
format_ok = (format & ART_CAN_JPEG) ? ART_FMT_JPEG : 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s == src_ctx->nb_streams)
|
if (s == src_ctx->nb_streams)
|
||||||
@ -495,11 +554,15 @@ artwork_get(char *filename, int max_w, int max_h, struct evbuffer *evbuf)
|
|||||||
|
|
||||||
DPRINTF(E_DBG, L_ART, "Destination width %d height %d\n", target_w, target_h);
|
DPRINTF(E_DBG, L_ART, "Destination width %d height %d\n", target_w, target_h);
|
||||||
|
|
||||||
/* Fastpath for PNG */
|
/* Fastpath */
|
||||||
if ((src->codec_id == CODEC_ID_PNG) && !need_rescale)
|
if (!need_rescale && format_ok)
|
||||||
ret = artwork_read(filename, evbuf);
|
{
|
||||||
|
ret = artwork_read(filename, evbuf);
|
||||||
|
if (ret == 0)
|
||||||
|
ret = format_ok;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
ret = artwork_rescale(src_ctx, s, target_w, target_h, evbuf);
|
ret = artwork_rescale(src_ctx, s, target_w, target_h, format, evbuf);
|
||||||
|
|
||||||
av_close_input_file(src_ctx);
|
av_close_input_file(src_ctx);
|
||||||
|
|
||||||
@ -514,7 +577,7 @@ artwork_get(char *filename, int max_w, int max_h, struct evbuffer *evbuf)
|
|||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
artwork_get_own_image(char *path, int max_w, int max_h, struct evbuffer *evbuf)
|
artwork_get_own_image(char *path, int max_w, int max_h, int format, struct evbuffer *evbuf)
|
||||||
{
|
{
|
||||||
char artwork[PATH_MAX];
|
char artwork[PATH_MAX];
|
||||||
char *ptr;
|
char *ptr;
|
||||||
@ -558,11 +621,11 @@ artwork_get_own_image(char *path, int max_w, int max_h, struct evbuffer *evbuf)
|
|||||||
if (i == (sizeof(cover_extension) / sizeof(cover_extension[0])))
|
if (i == (sizeof(cover_extension) / sizeof(cover_extension[0])))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return artwork_get(artwork, max_w, max_h, evbuf);
|
return artwork_get(artwork, max_w, max_h, format, evbuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
artwork_get_dir_image(char *path, int isdir, int max_w, int max_h, struct evbuffer *evbuf)
|
artwork_get_dir_image(char *path, int isdir, int max_w, int max_h, int format, struct evbuffer *evbuf)
|
||||||
{
|
{
|
||||||
char artwork[PATH_MAX];
|
char artwork[PATH_MAX];
|
||||||
char *ptr;
|
char *ptr;
|
||||||
@ -616,12 +679,12 @@ artwork_get_dir_image(char *path, int isdir, int max_w, int max_h, struct evbuff
|
|||||||
if (i == (sizeof(cover_basename) / sizeof(cover_basename[0])))
|
if (i == (sizeof(cover_basename) / sizeof(cover_basename[0])))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return artwork_get(artwork, max_w, max_h, evbuf);
|
return artwork_get(artwork, max_w, max_h, format, evbuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
artwork_get_item(int id, int max_w, int max_h, struct evbuffer *evbuf)
|
artwork_get_item(int id, int max_w, int max_h, int format, struct evbuffer *evbuf)
|
||||||
{
|
{
|
||||||
char *filename;
|
char *filename;
|
||||||
int ret;
|
int ret;
|
||||||
@ -635,26 +698,24 @@ artwork_get_item(int id, int max_w, int max_h, struct evbuffer *evbuf)
|
|||||||
/* FUTURE: look at embedded artwork */
|
/* FUTURE: look at embedded artwork */
|
||||||
|
|
||||||
/* Look for basename(filename).{png,jpg} */
|
/* Look for basename(filename).{png,jpg} */
|
||||||
ret = artwork_get_own_image(filename, max_w, max_h, evbuf);
|
ret = artwork_get_own_image(filename, max_w, max_h, format, evbuf);
|
||||||
if (ret == 0)
|
if (ret > 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/* Look for basedir(filename)/{artwork,cover}.{png,jpg} */
|
/* Look for basedir(filename)/{artwork,cover}.{png,jpg} */
|
||||||
ret = artwork_get_dir_image(filename, 0, max_w, max_h, evbuf);
|
ret = artwork_get_dir_image(filename, 0, max_w, max_h, format, evbuf);
|
||||||
if (ret == 0)
|
if (ret > 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
DPRINTF(E_DBG, L_ART, "No artwork found for item id %d\n", id);
|
DPRINTF(E_DBG, L_ART, "No artwork found for item id %d\n", id);
|
||||||
|
|
||||||
ret = -1;
|
|
||||||
|
|
||||||
out:
|
out:
|
||||||
free(filename);
|
free(filename);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
artwork_get_group(int id, int max_w, int max_h, struct evbuffer *evbuf)
|
artwork_get_group(int id, int max_w, int max_h, int format, struct evbuffer *evbuf)
|
||||||
{
|
{
|
||||||
struct query_params qp;
|
struct query_params qp;
|
||||||
struct db_media_file_info dbmfi;
|
struct db_media_file_info dbmfi;
|
||||||
@ -679,18 +740,18 @@ artwork_get_group(int id, int max_w, int max_h, struct evbuffer *evbuf)
|
|||||||
goto files_art;
|
goto files_art;
|
||||||
}
|
}
|
||||||
|
|
||||||
got_art = 0;
|
got_art = -1;
|
||||||
while ((!got_art) && ((ret = db_query_fetch_string(&qp, &dir)) == 0) && (dir))
|
while ((got_art < 0) && ((ret = db_query_fetch_string(&qp, &dir)) == 0) && (dir))
|
||||||
{
|
{
|
||||||
got_art = ! artwork_get_dir_image(dir, 1, max_w, max_h, evbuf);
|
got_art = artwork_get_dir_image(dir, 1, max_w, max_h, format, evbuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
db_query_end(&qp);
|
db_query_end(&qp);
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
DPRINTF(E_LOG, L_ART, "Error fetching Q_GROUP_DIRS results\n");
|
DPRINTF(E_LOG, L_ART, "Error fetching Q_GROUP_DIRS results\n");
|
||||||
else if (got_art)
|
else if (got_art > 0)
|
||||||
return 0;
|
return got_art;
|
||||||
|
|
||||||
|
|
||||||
/* Then try individual files */
|
/* Then try individual files */
|
||||||
@ -708,18 +769,18 @@ artwork_get_group(int id, int max_w, int max_h, struct evbuffer *evbuf)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
got_art = 0;
|
got_art = -1;
|
||||||
while ((!got_art) && ((ret = db_query_fetch_file(&qp, &dbmfi)) == 0) && (dbmfi.id))
|
while ((got_art < 0) && ((ret = db_query_fetch_file(&qp, &dbmfi)) == 0) && (dbmfi.id))
|
||||||
{
|
{
|
||||||
got_art = ! artwork_get_own_image(dbmfi.path, max_w, max_h, evbuf);
|
got_art = artwork_get_own_image(dbmfi.path, max_w, max_h, format, evbuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
db_query_end(&qp);
|
db_query_end(&qp);
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
DPRINTF(E_LOG, L_ART, "Error fetching Q_GROUPITEMS results\n");
|
DPRINTF(E_LOG, L_ART, "Error fetching Q_GROUPITEMS results\n");
|
||||||
else if (got_art)
|
else if (got_art > 0)
|
||||||
return 0;
|
return got_art;
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,17 @@
|
|||||||
#ifndef __ARTWORK_H__
|
#ifndef __ARTWORK_H__
|
||||||
#define __ARTWORK_H__
|
#define __ARTWORK_H__
|
||||||
|
|
||||||
int
|
#define ART_CAN_PNG (1 << 0)
|
||||||
artwork_get_item(int id, int max_w, int max_h, struct evbuffer *evbuf);
|
#define ART_CAN_JPEG (1 << 1)
|
||||||
|
|
||||||
|
#define ART_FMT_PNG 1
|
||||||
|
#define ART_FMT_JPEG 2
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
artwork_get_group(int id, int max_w, int max_h, struct evbuffer *evbuf);
|
artwork_get_item(int id, int max_w, int max_h, int format, struct evbuffer *evbuf);
|
||||||
|
|
||||||
|
int
|
||||||
|
artwork_get_group(int id, int max_w, int max_h, int format, struct evbuffer *evbuf);
|
||||||
|
|
||||||
#endif /* !__ARTWORK_H__ */
|
#endif /* !__ARTWORK_H__ */
|
||||||
|
@ -2696,9 +2696,9 @@ daap_reply_extra_data(struct evhttp_request *req, struct evbuffer *evbuf, char *
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(uri[2], "groups") == 0)
|
if (strcmp(uri[2], "groups") == 0)
|
||||||
ret = artwork_get_group(id, max_w, max_h, evbuf);
|
ret = artwork_get_group(id, max_w, max_h, ART_CAN_PNG, evbuf);
|
||||||
else if (strcmp(uri[2], "items") == 0)
|
else if (strcmp(uri[2], "items") == 0)
|
||||||
ret = artwork_get_item(id, max_w, max_h, evbuf);
|
ret = artwork_get_item(id, max_w, max_h, ART_CAN_PNG, evbuf);
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
|
@ -1269,7 +1269,7 @@ dacp_reply_nowplayingartwork(struct evhttp_request *req, struct evbuffer *evbuf,
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto no_artwork;
|
goto no_artwork;
|
||||||
|
|
||||||
ret = artwork_get_item(id, max_w, max_h, evbuf);
|
ret = artwork_get_item(id, max_w, max_h, ART_CAN_PNG, evbuf);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
if (EVBUFFER_LENGTH(evbuf) > 0)
|
if (EVBUFFER_LENGTH(evbuf) > 0)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user