Adjust daapcache so it serves User-Agent to httpd_daap's reply handlers

This commit is contained in:
ejurgensen 2014-08-23 00:02:01 +02:00
parent 624dd40c59
commit cf091e8d8b
7 changed files with 99 additions and 91 deletions

View File

@ -38,34 +38,43 @@
#include "daap_cache.h"
/* The DAAP cache will only cache raw daap replies for these queries.
* Remove session_id and revision-number from the query, if you add a new one
* Remove session_id and revision-number from the query, if you add a new one.
* You can't add queries where the canonical reply is not HTTP_OK, because
* daap_request will use that as default for cache replies.
*
* TODO: Don't hardcode, detect slow queries and add them dynamically
*/
static const char *daapcache_queries[] =
{
// Remote 4.2, Playlist 1 - Library
"/databases/1/containers/1/items?meta=dmap.itemname,dmap.itemid,daap.songartist,daap.songalbumartist,daap.songalbum,com.apple.itunes.cloud-id,dmap.containeritemid,com.apple.itunes.has-video,com.apple.itunes.itms-songid,com.apple.itunes.extended-media-kind,dmap.downloadstatus,daap.songdisabled&type=music&sort=name&include-sort-headers=1&query=('com.apple.itunes.extended-media-kind:1','com.apple.itunes.extended-media-kind:32')",
// Remote 4.2, Albums
"/databases/1/groups?meta=dmap.itemname,dmap.itemid,dmap.persistentid,daap.songartist,com.apple.itunes.cloud-id,daap.songartistid,daap.songalbumid,dmap.persistentid,daap.songtime,daap.songdatereleased,dmap.downloadstatus&type=music&group-type=albums&sort=album&include-sort-headers=0&query=('daap.songalbum!:'%2B('com.apple.itunes.extended-media-kind:1','com.apple.itunes.extended-media-kind:32'))",
// Remote 4.2, Artists
"/databases/1/groups?meta=dmap.itemname,dmap.itemid,dmap.persistentid,daap.songartist,daap.groupalbumcount,daap.songartistid&type=music&group-type=artists&sort=album&include-sort-headers=1&query=('daap.songartist!:'%2B('com.apple.itunes.extended-media-kind:1','com.apple.itunes.extended-media-kind:32'))",
// iTunes 11.3, DB song list
"/databases/1/items?delta=0&type=music&meta=all",
// iTunes 11.3, Playlist 1 - Library
"/databases/1/containers/1/items?delta=0&type=music&meta=dmap.itemkind,dmap.itemid,dmap.containeritemid",
// iTunes 11.3, Playlist 2 - Music
"/databases/1/containers/2/items?delta=0&type=music&meta=dmap.itemkind,dmap.itemid,dmap.containeritemid",
// TunesRemote+, Albums
"/databases/1/groups?meta=dmap.itemname,dmap.itemid,dmap.persistentid,daap.songartist&type=music&group-type=albums&sort=album&include-sort-headers=1",
// TunesRemote+, Artists
"/databases/1/browse/artists?include-sort-headers=1",
// Retune, Artists
"/databases/1/groups?meta=dmap.itemname,dmap.itemid,dmap.persistentid,daap.songartist,daap.groupalbumcount&type=music&group-type=artists&sort=album&include-sort-headers=1&query=(('com.apple.itunes.mediakind:1','com.apple.itunes.mediakind:32')+'daap.songartist!:')",
// Retune, Albums
"/databases/1/groups?meta=dmap.itemname,dmap.itemid,dmap.persistentid,daap.songartist,daap.songdatereleased,dmap.itemcount,daap.songtime&type=music&group-type=albums&sort=album&include-sort-headers=1&query=(('com.apple.itunes.mediakind:1','com.apple.itunes.mediakind:32')+'daap.songalbum!:')",
// Retune, Playlist 1 - Library
"/databases/1/containers/1/items?meta=dmap.itemname,dmap.itemid,daap.songartist,daap.songalbum,daap.songtime,dmap.containeritemid,com.apple.tunes.has-video,com.apple.itunes.can-be-genius-seed&type=music&sort=artist&include-sort-headers=1&query=(('com.apple.itunes.mediakind:1','com.apple.itunes.mediakind:32'))",
};
struct daapcache_query_t
{
const char *ua;
const char *query;
};
static const struct daapcache_query_t daapcache_queries[] =
{
// Remote 4.2, Playlist 1 - Library
{ "Remote", "/databases/1/containers/1/items?meta=dmap.itemname,dmap.itemid,daap.songartist,daap.songalbumartist,daap.songalbum,com.apple.itunes.cloud-id,dmap.containeritemid,com.apple.itunes.has-video,com.apple.itunes.itms-songid,com.apple.itunes.extended-media-kind,dmap.downloadstatus,daap.songdisabled&type=music&sort=name&include-sort-headers=1&query=('com.apple.itunes.extended-media-kind:1','com.apple.itunes.extended-media-kind:32')" },
// Remote 4.2, Albums
{ "Remote", "/databases/1/groups?meta=dmap.itemname,dmap.itemid,dmap.persistentid,daap.songartist,com.apple.itunes.cloud-id,daap.songartistid,daap.songalbumid,dmap.persistentid,daap.songtime,daap.songdatereleased,dmap.downloadstatus&type=music&group-type=albums&sort=album&include-sort-headers=0&query=('daap.songalbum!:'%2B('com.apple.itunes.extended-media-kind:1','com.apple.itunes.extended-media-kind:32'))" },
// Remote 4.2, Artists
{ "Remote", "/databases/1/groups?meta=dmap.itemname,dmap.itemid,dmap.persistentid,daap.songartist,daap.groupalbumcount,daap.songartistid&type=music&group-type=artists&sort=album&include-sort-headers=1&query=('daap.songartist!:'%2B('com.apple.itunes.extended-media-kind:1','com.apple.itunes.extended-media-kind:32'))" },
// iTunes 11.3, DB song list
{ "iTunes", "/databases/1/items?delta=0&type=music&meta=all" },
// iTunes 11.3, Playlist 1 - Library
{ "iTunes", "/databases/1/containers/1/items?delta=0&type=music&meta=dmap.itemkind,dmap.itemid,dmap.containeritemid" },
// iTunes 11.3, Playlist 2 - Music
{ "iTunes", "/databases/1/containers/2/items?delta=0&type=music&meta=dmap.itemkind,dmap.itemid,dmap.containeritemid" },
// TunesRemote+, Albums
{ "Remote", "/databases/1/groups?meta=dmap.itemname,dmap.itemid,dmap.persistentid,daap.songartist&type=music&group-type=albums&sort=album&include-sort-headers=1" },
// TunesRemote+, Artists
{ "Remote", "/databases/1/browse/artists?include-sort-headers=1" },
// Retune, Artists
{ "Remote", "/databases/1/groups?meta=dmap.itemname,dmap.itemid,dmap.persistentid,daap.songartist,daap.groupalbumcount&type=music&group-type=artists&sort=album&include-sort-headers=1&query=(('com.apple.itunes.mediakind:1','com.apple.itunes.mediakind:32')+'daap.songartist!:')" },
// Retune, Albums
{ "Remote", "/databases/1/groups?meta=dmap.itemname,dmap.itemid,dmap.persistentid,daap.songartist,daap.songdatereleased,dmap.itemcount,daap.songtime&type=music&group-type=albums&sort=album&include-sort-headers=1&query=(('com.apple.itunes.mediakind:1','com.apple.itunes.mediakind:32')+'daap.songalbum!:')" },
// Retune, Playlist 1 - Library
{ "Remote", "/databases/1/containers/1/items?meta=dmap.itemname,dmap.itemid,daap.songartist,daap.songalbum,daap.songtime,dmap.containeritemid,com.apple.tunes.has-video,com.apple.itunes.can-be-genius-seed&type=music&sort=artist&include-sort-headers=1&query=(('com.apple.itunes.mediakind:1','com.apple.itunes.mediakind:32'))" },
};
struct daapcache_command;
@ -432,9 +441,9 @@ daapcache_update_cb(int fd, short what, void *arg)
for (i = 0; i < (sizeof(daapcache_queries) / sizeof(daapcache_queries[0])); i++)
{
query = strdup(daapcache_queries[i]);
query = strdup(daapcache_queries[i].query);
evbuf = daap_reply_build(query);
evbuf = daap_reply_build(query, daapcache_queries[i].ua);
if (!evbuf)
{
DPRINTF(E_LOG, L_DCACHE, "Error building DAAP reply for query: %s\n", query);

View File

@ -357,6 +357,8 @@ httpd_stream_file(struct evhttp_request *req, int id)
struct evkeyvalq *output_headers;
const char *param;
const char *param_end;
const char *ua;
const char *client_codecs;
char buf[64];
int64_t offset;
int64_t end_offset;
@ -431,7 +433,10 @@ httpd_stream_file(struct evhttp_request *req, int id)
memset(st, 0, sizeof(struct stream_ctx));
st->fd = -1;
transcode = transcode_needed(input_headers, mfi->codectype);
ua = evhttp_find_header(input_headers, "User-Agent");
client_codecs = evhttp_find_header(input_headers, "Accept-Codecs");
transcode = transcode_needed(ua, client_codecs, mfi->codectype);
output_headers = evhttp_request_get_output_headers(req);
@ -665,6 +670,9 @@ httpd_send_reply(struct evhttp_request *req, int code, const char *reason, struc
int zret;
int ret;
if (!req)
return;
if (!evbuf || (EVBUFFER_LENGTH(evbuf) == 0))
{
DPRINTF(E_DBG, L_HTTPD, "Not gzipping body-less reply\n");

View File

@ -72,7 +72,7 @@ extern struct event_base *evbase_httpd;
struct uri_map {
regex_t preg;
char *regexp;
int (*handler)(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query);
int (*handler)(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua);
};
struct daap_session {
@ -760,7 +760,7 @@ parse_meta(struct evhttp_request *req, char *tag, const char *param, const struc
static int
daap_reply_server_info(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query)
daap_reply_server_info(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua)
{
struct evbuffer *content;
struct evkeyvalq *headers;
@ -857,7 +857,7 @@ daap_reply_server_info(struct evhttp_request *req, struct evbuffer *evbuf, char
}
static int
daap_reply_content_codes(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query)
daap_reply_content_codes(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua)
{
const struct dmap_field *dmap_fields;
int nfields;
@ -899,12 +899,10 @@ daap_reply_content_codes(struct evhttp_request *req, struct evbuffer *evbuf, cha
}
static int
daap_reply_login(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query)
daap_reply_login(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua)
{
struct pairing_info pi;
struct daap_session *s;
struct evkeyvalq *headers;
const char *ua;
const char *param;
int request_session_id;
int ret;
@ -918,8 +916,6 @@ daap_reply_login(struct evhttp_request *req, struct evbuffer *evbuf, char **uri,
return -1;
}
headers = evhttp_request_get_input_headers(req);
ua = evhttp_find_header(headers, "User-Agent");
if (ua && (strncmp(ua, "Remote", strlen("Remote")) == 0))
{
param = evhttp_find_header(query, "pairing-guid");
@ -978,7 +974,7 @@ daap_reply_login(struct evhttp_request *req, struct evbuffer *evbuf, char **uri,
}
static int
daap_reply_logout(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query)
daap_reply_logout(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua)
{
struct daap_session *s;
@ -994,7 +990,7 @@ daap_reply_logout(struct evhttp_request *req, struct evbuffer *evbuf, char **uri
}
static int
daap_reply_update(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query)
daap_reply_update(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua)
{
struct timeval tv;
struct daap_session *s;
@ -1094,7 +1090,7 @@ daap_reply_update(struct evhttp_request *req, struct evbuffer *evbuf, char **uri
}
static int
daap_reply_activity(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query)
daap_reply_activity(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua)
{
/* That's so nice, thanks for letting us know */
evhttp_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf);
@ -1103,7 +1099,7 @@ daap_reply_activity(struct evhttp_request *req, struct evbuffer *evbuf, char **u
}
static int
daap_reply_dblist(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query)
daap_reply_dblist(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua)
{
struct evbuffer *content;
struct daap_session *s;
@ -1159,7 +1155,7 @@ daap_reply_dblist(struct evhttp_request *req, struct evbuffer *evbuf, char **uri
}
static int
daap_reply_songlist_generic(struct evhttp_request *req, struct evbuffer *evbuf, int playlist, struct evkeyvalq *query)
daap_reply_songlist_generic(struct evhttp_request *req, struct evbuffer *evbuf, int playlist, struct evkeyvalq *query, const char *ua)
{
struct daap_session *s;
struct query_params qp;
@ -1170,6 +1166,7 @@ daap_reply_songlist_generic(struct evhttp_request *req, struct evbuffer *evbuf,
const struct dmap_field **meta;
struct sort_ctx *sctx;
const char *param;
const char *client_codecs;
char *tag;
int nmeta;
int sort_headers;
@ -1263,8 +1260,8 @@ daap_reply_songlist_generic(struct evhttp_request *req, struct evbuffer *evbuf,
memset(&qp, 0, sizeof(struct query_params));
get_query_params(query, &sort_headers, &qp);
if (req && (playlist == -1))
user_agent_filter(s->user_agent, &qp);
if (playlist == -1)
user_agent_filter(ua, &qp);
sctx = NULL;
if (sort_headers)
@ -1305,13 +1302,14 @@ daap_reply_songlist_generic(struct evhttp_request *req, struct evbuffer *evbuf,
{
nsongs++;
// TODO
if (req) {
headers = evhttp_request_get_input_headers(req);
transcode = transcode_needed(headers, dbmfi.codectype);
} else {
transcode = 1;
}
client_codecs = NULL;
if (req)
{
headers = evhttp_request_get_input_headers(req);
client_codecs = evhttp_find_header(headers, "Accept-Codecs");
}
transcode = transcode_needed(ua, client_codecs, dbmfi.codectype);
ret = dmap_encode_file_metadata(songlist, song, &dbmfi, meta, nmeta, sort_headers, transcode);
if (ret < 0)
@ -1410,8 +1408,7 @@ if (req) {
}
}
if (req)
httpd_send_reply(req, HTTP_OK, "OK", evbuf);
httpd_send_reply(req, HTTP_OK, "OK", evbuf);
return 0;
@ -1432,13 +1429,13 @@ if (req) {
}
static int
daap_reply_dbsonglist(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query)
daap_reply_dbsonglist(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua)
{
return daap_reply_songlist_generic(req, evbuf, -1, query);
return daap_reply_songlist_generic(req, evbuf, -1, query, ua);
}
static int
daap_reply_plsonglist(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query)
daap_reply_plsonglist(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua)
{
int playlist;
int ret;
@ -1451,11 +1448,11 @@ daap_reply_plsonglist(struct evhttp_request *req, struct evbuffer *evbuf, char *
return -1;
}
return daap_reply_songlist_generic(req, evbuf, playlist, query);
return daap_reply_songlist_generic(req, evbuf, playlist, query, ua);
}
static int
daap_reply_playlists(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query)
daap_reply_playlists(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua)
{
struct query_params qp;
struct db_playlist_info dbpli;
@ -1701,7 +1698,7 @@ daap_reply_playlists(struct evhttp_request *req, struct evbuffer *evbuf, char **
}
static int
daap_reply_groups(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query)
daap_reply_groups(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua)
{
struct query_params qp;
struct db_group_info dbgri;
@ -1730,8 +1727,7 @@ daap_reply_groups(struct evhttp_request *req, struct evbuffer *evbuf, char **uri
get_query_params(query, &sort_headers, &qp);
if (req)
user_agent_filter(s->user_agent, &qp);
user_agent_filter(ua, &qp);
param = evhttp_find_header(query, "group-type");
if (strcmp(param, "artists") == 0)
@ -1981,8 +1977,7 @@ daap_reply_groups(struct evhttp_request *req, struct evbuffer *evbuf, char **uri
}
}
if (req)
httpd_send_reply(req, HTTP_OK, "OK", evbuf);
httpd_send_reply(req, HTTP_OK, "OK", evbuf);
return 0;
@ -2003,7 +1998,7 @@ daap_reply_groups(struct evhttp_request *req, struct evbuffer *evbuf, char **uri
}
static int
daap_reply_browse(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query)
daap_reply_browse(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua)
{
struct query_params qp;
struct daap_session *s;
@ -2017,13 +2012,14 @@ daap_reply_browse(struct evhttp_request *req, struct evbuffer *evbuf, char **uri
int ret;
s = daap_session_find(req, query, evbuf);
if (!s)
if (!s && req)
return -1;
memset(&qp, 0, sizeof(struct query_params));
get_query_params(query, &sort_headers, &qp);
user_agent_filter(s->user_agent, &qp);
user_agent_filter(ua, &qp);
if (strcmp(uri[3], "artists") == 0)
{
@ -2203,7 +2199,7 @@ daap_reply_browse(struct evhttp_request *req, struct evbuffer *evbuf, char **uri
/* NOTE: We only handle artwork at the moment */
static int
daap_reply_extra_data(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query)
daap_reply_extra_data(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua)
{
char clen[32];
struct daap_session *s;
@ -2294,7 +2290,7 @@ daap_reply_extra_data(struct evhttp_request *req, struct evbuffer *evbuf, char *
}
static int
daap_stream(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query)
daap_stream(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua)
{
struct daap_session *s;
int id;
@ -2356,7 +2352,7 @@ static const struct dmap_field dmap_TST8 = { "test.long", "TST8", NULL, DMA
static const struct dmap_field dmap_TST9 = { "test.string", "TST9", NULL, DMAP_TYPE_STRING };
static int
daap_reply_dmap_test(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query)
daap_reply_dmap_test(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua)
{
char buf[64];
struct evbuffer *test;
@ -2694,7 +2690,7 @@ daap_request(struct evhttp_request *req)
evhttp_parse_query(full_uri, &query);
daap_handlers[handler].handler(req, evbuf, uri_parts, &query);
daap_handlers[handler].handler(req, evbuf, uri_parts, &query, ua);
evhttp_clear_headers(&query);
evbuffer_free(evbuf);
@ -2735,7 +2731,7 @@ daap_is_request(struct evhttp_request *req, char *uri)
}
struct evbuffer *
daap_reply_build(char *full_uri)
daap_reply_build(char *full_uri, const char *ua)
{
char *uri;
char *ptr;
@ -2802,7 +2798,7 @@ daap_reply_build(char *full_uri)
evhttp_parse_query(full_uri, &query);
ret = daap_handlers[handler].handler(NULL, evbuf, uri_parts, &query);
ret = daap_handlers[handler].handler(NULL, evbuf, uri_parts, &query, ua);
if (ret < 0)
{
evbuffer_free(evbuf);

View File

@ -22,6 +22,6 @@ int
daap_is_request(struct evhttp_request *req, char *uri);
struct evbuffer *
daap_reply_build(char *query);
daap_reply_build(char *full_uri, const char *ua);
#endif /* !__HTTPD_DAAP_H__ */

View File

@ -439,6 +439,8 @@ rsp_reply_playlist(struct evhttp_request *req, char **uri, struct evkeyvalq *que
struct db_media_file_info dbmfi;
struct evkeyvalq *headers;
const char *param;
const char *ua;
const char *client_codecs;
char **strval;
mxml_node_t *reply;
mxml_node_t *status;
@ -531,7 +533,11 @@ rsp_reply_playlist(struct evhttp_request *req, char **uri, struct evkeyvalq *que
while (((ret = db_query_fetch_file(&qp, &dbmfi)) == 0) && (dbmfi.id))
{
headers = evhttp_request_get_input_headers(req);
transcode = transcode_needed(headers, dbmfi.codectype);
ua = evhttp_find_header(headers, "User-Agent");
client_codecs = evhttp_find_header(headers, "Accept-Codecs");
transcode = transcode_needed(ua, client_codecs, dbmfi.codectype);
/* Item block (one item) */
item = mxmlNewElement(items, "item");

View File

@ -712,15 +712,17 @@ transcode_cleanup(struct transcode_ctx *ctx)
int
transcode_needed(struct evkeyvalq *headers, char *file_codectype)
transcode_needed(const char *user_agent, const char *client_codecs, char *file_codectype)
{
const char *client_codecs;
const char *user_agent;
char *codectype;
cfg_t *lib;
int size;
int i;
// If client is a Remote we will AirPlay, which means we will transcode to PCM
if (user_agent && strcasestr(user_agent, "remote"))
return 1;
if (!file_codectype)
{
DPRINTF(E_LOG, L_XCODE, "Can't proceed, codectype is unknown (null)\n");
@ -763,10 +765,8 @@ transcode_needed(struct evkeyvalq *headers, char *file_codectype)
}
}
client_codecs = evhttp_find_header(headers, "Accept-Codecs");
if (!client_codecs)
{
user_agent = evhttp_find_header(headers, "User-Agent");
if (user_agent)
{
DPRINTF(E_DBG, L_XCODE, "User-Agent: %s\n", user_agent);
@ -787,12 +787,6 @@ transcode_needed(struct evkeyvalq *headers, char *file_codectype)
{
DPRINTF(E_DBG, L_XCODE, "Client is Front Row, using iTunes codecs\n");
client_codecs = itunes_codecs;
}
else if (strncmp(user_agent, "Remote", strlen("Remote")) == 0)
{
DPRINTF(E_DBG, L_XCODE, "Client is Remote, using iTunes codecs\n");
client_codecs = itunes_codecs;
}
else if (strncmp(user_agent, "AppleCoreMedia", strlen("AppleCoreMedia")) == 0)

View File

@ -3,11 +3,6 @@
#define __TRANSCODE_H__
#include <event.h>
#ifdef HAVE_LIBEVENT2
# include <event2/http.h>
#else
# include "evhttp/evhttp.h"
#endif
struct transcode_ctx;
@ -24,6 +19,6 @@ void
transcode_cleanup(struct transcode_ctx *ctx);
int
transcode_needed(struct evkeyvalq *headers, char *file_codectype);
transcode_needed(const char *user_agent, const char *client_codecs, char *file_codectype);
#endif /* !__TRANSCODE_H__ */