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:
Julien BLACHE 2011-03-30 18:27:23 +02:00
parent 5cf5cac9c2
commit c7209ab699
4 changed files with 119 additions and 51 deletions

View File

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

View File

@ -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__ */

View File

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

View File

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