Merge branch 'ffmpeg_update2'

This commit is contained in:
ejurgensen 2017-08-07 00:13:31 +02:00
commit 0f4f8a9a40
15 changed files with 4449 additions and 1847 deletions

View File

@ -237,23 +237,30 @@ FORK_MODULES_CHECK([FORKED], [LIBAV],
[Define to 1 if you have ffmpeg (not libav)])],
[[is_ffmpeg=no]])
AC_MSG_RESULT([$is_ffmpeg])
FORK_CHECK_DECLS([av_buffersrc_add_frame_flags],
[libavfilter/buffersrc.h])
FORK_CHECK_DECLS([av_buffersink_get_frame],
[libavfilter/buffersink.h])
FORK_CHECK_DECLS([avfilter_graph_parse_ptr],
[libavfilter/avfilter.h])
FORK_CHECK_DECLS([av_packet_unref], [libavcodec/avcodec.h])
FORK_CHECK_DECLS([av_packet_rescale_ts], [libavcodec/avcodec.h])
FORK_CHECK_DECLS([avformat_alloc_output_context2],
[libavformat/avformat.h])
FORK_CHECK_DECLS([av_frame_alloc], [libavutil/frame.h])
FORK_CHECK_DECLS([av_frame_get_best_effort_timestamp],
[libavutil/frame.h])
FORK_CHECK_DECLS([av_image_fill_arrays], [libavutil/imgutils.h])
FORK_CHECK_DECLS([av_image_get_buffer_size], [libavutil/imgutils.h])
AC_CHECK_HEADERS([libavutil/channel_layout.h libavutil/mathematics.h])
dnl Check if we have modern or legacy AV apis
FORK_CHECK_DECLS([avcodec_send_packet, avcodec_parameters_from_context],
[libavcodec/avcodec.h],
[[modern_av_apis=yes]],
[[modern_av_apis=no]
FORK_CHECK_DECLS([av_buffersrc_add_frame_flags],
[libavfilter/buffersrc.h])
FORK_CHECK_DECLS([av_buffersink_get_frame],
[libavfilter/buffersink.h])
FORK_CHECK_DECLS([avfilter_graph_parse_ptr],
[libavfilter/avfilter.h])
FORK_CHECK_DECLS([av_packet_unref], [libavcodec/avcodec.h])
FORK_CHECK_DECLS([av_packet_rescale_ts], [libavcodec/avcodec.h])
FORK_CHECK_DECLS([avformat_alloc_output_context2],
[libavformat/avformat.h])
FORK_CHECK_DECLS([av_frame_alloc], [libavutil/frame.h])
FORK_CHECK_DECLS([av_frame_get_best_effort_timestamp],
[libavutil/frame.h])
FORK_CHECK_DECLS([av_image_fill_arrays], [libavutil/imgutils.h])
FORK_CHECK_DECLS([av_image_get_buffer_size], [libavutil/imgutils.h])
AC_CHECK_HEADERS([libavutil/channel_layout.h libavutil/mathematics.h])
])
])
AM_CONDITIONAL([COND_FFMPEG_LEGACY], [[test "x$modern_av_apis" = "xno"]])
AC_CHECK_SIZEOF([void *])

View File

@ -43,6 +43,12 @@ else
MDNS_SRC=mdns_dnssd.c
endif
if COND_FFMPEG_LEGACY
FFMPEG_SRC=transcode_legacy.c artwork_legacy.c ffmpeg-compat.h
else
FFMPEG_SRC=transcode.c artwork.c
endif
GPERF_FILES = \
daap_query.gperf \
rsp_query.gperf \
@ -105,8 +111,7 @@ forked_daapd_SOURCES = main.c \
httpd_streaming.c httpd_streaming.h \
http.c http.h \
dmap_common.c dmap_common.h \
transcode.c transcode.h \
artwork.c artwork.h \
$(FFMPEG_SRC) \
misc.c misc.h \
rng.c rng.h \
rsp_query.c rsp_query.h \
@ -125,7 +130,7 @@ forked_daapd_SOURCES = main.c \
$(MPD_SRC) \
listener.c listener.h \
commands.c commands.h \
ffmpeg-compat.h mxml-compat.h \
mxml-compat.h \
$(GPERF_SRC) \
$(ANTLR_SRC)

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015-2016 Espen Jürgensen <espenjurgensen@gmail.com>
* Copyright (C) 2015-2017 Espen Jürgensen <espenjurgensen@gmail.com>
* Copyright (C) 2010-2011 Julien BLACHE <jb@jblache.org>
*
* This program is free software; you can redistribute it and/or modify
@ -30,27 +30,20 @@
#include <fcntl.h>
#include <limits.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#include "db.h"
#include "misc.h"
#include "logger.h"
#include "conffile.h"
#include "cache.h"
#include "http.h"
#include "transcode.h"
#include "avio_evbuffer.h"
#include "artwork.h"
#ifdef HAVE_SPOTIFY_H
# include "spotify.h"
#endif
#include "ffmpeg-compat.h"
/* This artwork module will look for artwork by consulting a set of sources one
* at a time. A source is for instance the local library, the cache or a cover
* art database. For each source there is a handler function, which will do the
@ -278,43 +271,42 @@ artwork_read(struct evbuffer *evbuf, char *path)
/* Will the source image fit inside requested size. If not, what size should it
* be rescaled to to maintain aspect ratio.
*
* @in src Image source
* @in max_w Requested width
* @in max_h Requested height
* @out target_w Rescaled width
* @out target_h Rescaled height
* @return 0 no rescaling needed, 1 rescaling needed
* @in width Actual width
* @in height Actual height
* @in max_w Requested width
* @in max_h Requested height
* @return -1 no rescaling needed, otherwise 0
*/
static int
rescale_needed(AVCodecContext *src, int max_w, int max_h, int *target_w, int *target_h)
rescale_calculate(int *target_w, int *target_h, int width, int height, int max_w, int max_h)
{
DPRINTF(E_DBG, L_ART, "Original image dimensions: w %d h %d\n", src->width, src->height);
DPRINTF(E_DBG, L_ART, "Original image dimensions: w %d h %d\n", width, height);
*target_w = src->width;
*target_h = src->height;
*target_w = width;
*target_h = height;
if ((src->width == 0) || (src->height == 0)) /* Unknown source size, can't rescale */
return 0;
if ((width == 0) || (height == 0)) /* Unknown source size, can't rescale */
return -1;
if ((max_w <= 0) || (max_h <= 0)) /* No valid target dimensions, use original */
return 0;
return -1;
if ((src->width <= max_w) && (src->height <= max_h)) /* Smaller than target */
return 0;
if ((width <= max_w) && (height <= max_h)) /* Smaller than target */
return -1;
if (src->width * max_h > src->height * max_w) /* Wider aspect ratio than target */
if (width * max_h > height * max_w) /* Wider aspect ratio than target */
{
*target_w = max_w;
*target_h = (double)max_w * ((double)src->height / (double)src->width);
*target_h = (double)max_w * ((double)height / (double)width);
}
else /* Taller or equal aspect ratio */
{
*target_w = (double)max_h * ((double)src->width / (double)src->height);
*target_w = (double)max_h * ((double)width / (double)height);
*target_h = max_h;
}
DPRINTF(E_DBG, L_ART, "Raw destination width %d height %d\n", *target_w, *target_h);
if ((*target_h > max_h) && (max_h > 0))
*target_h = max_h;
@ -324,341 +316,28 @@ rescale_needed(AVCodecContext *src, int max_w, int max_h, int *target_w, int *ta
if ((*target_w > max_w) && (max_w > 0))
*target_w = max_w - (max_w % 2);
DPRINTF(E_DBG, L_ART, "Destination width %d height %d\n", *target_w, *target_h);
DPRINTF(E_DBG, L_ART, "Rescale required, destination width %d height %d\n", *target_w, *target_h);
return 1;
}
/* Rescale an image
*
* @out evbuf Rescaled image data
* @in src_ctx Image source
* @in s Index of stream containing image
* @in out_w Rescaled width
* @in out_h Rescaled height
* @return ART_FMT_* on success, -1 on error
*/
static int
artwork_rescale(struct evbuffer *evbuf, AVFormatContext *src_ctx, int s, int out_w, int out_h)
{
uint8_t *buf;
AVCodecContext *src;
AVFormatContext *dst_ctx;
AVCodecContext *dst;
AVOutputFormat *dst_fmt;
AVStream *dst_st;
AVCodec *img_decoder;
AVCodec *img_encoder;
AVFrame *i_frame;
AVFrame *o_frame;
struct SwsContext *swsctx;
AVPacket pkt;
int have_frame;
int ret;
src = src_ctx->streams[s]->codec;
// Avoids threading issue in both ffmpeg and libav that prevents decoding embedded png's
src->thread_count = 1;
img_decoder = avcodec_find_decoder(src->codec_id);
if (!img_decoder)
{
DPRINTF(E_LOG, L_ART, "No suitable decoder found for artwork %s\n", src_ctx->filename);
return -1;
}
ret = avcodec_open2(src, img_decoder, NULL);
if (ret < 0)
{
DPRINTF(E_LOG, L_ART, "Could not open codec for decoding: %s\n", strerror(AVUNERROR(ret)));
return -1;
}
if (src->pix_fmt < 0)
{
DPRINTF(E_LOG, L_ART, "Unknown pixel format for artwork %s\n", src_ctx->filename);
ret = -1;
goto out_close_src;
}
/* Set up output */
dst_fmt = av_guess_format("image2", NULL, NULL);
if (!dst_fmt)
{
DPRINTF(E_LOG, L_ART, "ffmpeg image2 muxer not available\n");
ret = -1;
goto out_close_src;
}
dst_fmt->video_codec = AV_CODEC_ID_NONE;
/* Try to keep same codec if possible */
if (src->codec_id == AV_CODEC_ID_PNG)
dst_fmt->video_codec = AV_CODEC_ID_PNG;
else if (src->codec_id == AV_CODEC_ID_MJPEG)
dst_fmt->video_codec = AV_CODEC_ID_MJPEG;
/* If not possible, select new codec */
if (dst_fmt->video_codec == AV_CODEC_ID_NONE)
{
dst_fmt->video_codec = AV_CODEC_ID_PNG;
}
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();
if (!dst_ctx)
{
DPRINTF(E_LOG, L_ART, "Out of memory for format context\n");
ret = -1;
goto out_close_src;
}
dst_ctx->oformat = dst_fmt;
dst_fmt->flags &= ~AVFMT_NOFILE;
dst_st = avformat_new_stream(dst_ctx, NULL);
if (!dst_st)
{
DPRINTF(E_LOG, L_ART, "Out of memory for new output stream\n");
ret = -1;
goto out_free_dst_ctx;
}
dst = dst_st->codec;
avcodec_get_context_defaults3(dst, NULL);
if (dst_fmt->flags & AVFMT_GLOBALHEADER)
dst->flags |= CODEC_FLAG_GLOBAL_HEADER;
dst->codec_id = dst_fmt->video_codec;
dst->codec_type = AVMEDIA_TYPE_VIDEO;
dst->pix_fmt = avcodec_default_get_format(dst, img_encoder->pix_fmts);
if (dst->pix_fmt < 0)
{
DPRINTF(E_LOG, L_ART, "Could not determine best pixel format\n");
ret = -1;
goto out_free_dst_ctx;
}
dst->time_base.num = 1;
dst->time_base.den = 25;
dst->width = out_w;
dst->height = out_h;
/* Open encoder */
ret = avcodec_open2(dst, img_encoder, NULL);
if (ret < 0)
{
DPRINTF(E_LOG, L_ART, "Could not open codec for encoding: %s\n", strerror(AVUNERROR(ret)));
ret = -1;
goto out_free_dst_ctx;
}
i_frame = av_frame_alloc();
o_frame = av_frame_alloc();
if (!i_frame || !o_frame)
{
DPRINTF(E_LOG, L_ART, "Could not allocate input/output frame\n");
ret = -1;
goto out_free_frames;
}
ret = av_image_get_buffer_size(dst->pix_fmt, src->width, src->height, 1);
DPRINTF(E_DBG, L_ART, "Artwork buffer size: %d\n", ret);
buf = (uint8_t *)av_malloc(ret);
if (!buf)
{
DPRINTF(E_LOG, L_ART, "Out of memory for artwork buffer\n");
ret = -1;
goto out_free_frames;
}
#if HAVE_DECL_AV_IMAGE_FILL_ARRAYS
av_image_fill_arrays(o_frame->data, o_frame->linesize, buf, dst->pix_fmt, src->width, src->height, 1);
#else
avpicture_fill((AVPicture *)o_frame, buf, dst->pix_fmt, src->width, src->height);
#endif
o_frame->height = dst->height;
o_frame->width = dst->width;
o_frame->format = dst->pix_fmt;
swsctx = sws_getContext(src->width, src->height, src->pix_fmt,
dst->width, dst->height, dst->pix_fmt,
SWS_BICUBIC, NULL, NULL, NULL);
if (!swsctx)
{
DPRINTF(E_LOG, L_ART, "Could not get SWS context\n");
ret = -1;
goto out_free_buf;
}
/* Get frame */
have_frame = 0;
while (av_read_frame(src_ctx, &pkt) == 0)
{
if (pkt.stream_index != s)
{
av_packet_unref(&pkt);
continue;
}
avcodec_decode_video2(src, i_frame, &have_frame, &pkt);
break;
}
if (!have_frame)
{
DPRINTF(E_LOG, L_ART, "Could not decode artwork\n");
av_packet_unref(&pkt);
sws_freeContext(swsctx);
ret = -1;
goto out_free_buf;
}
/* Scale */
sws_scale(swsctx, (const uint8_t * const *)i_frame->data, i_frame->linesize, 0, src->height, o_frame->data, o_frame->linesize);
sws_freeContext(swsctx);
av_packet_unref(&pkt);
/* Open output file */
dst_ctx->pb = avio_output_evbuffer_open(evbuf);
if (!dst_ctx->pb)
{
DPRINTF(E_LOG, L_ART, "Could not open artwork destination buffer\n");
ret = -1;
goto out_free_buf;
}
/* Encode frame */
av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;
ret = avcodec_encode_video2(dst, &pkt, o_frame, &have_frame);
if (ret < 0)
{
DPRINTF(E_LOG, L_ART, "Could not encode artwork\n");
ret = -1;
goto out_fclose_dst;
}
ret = avformat_write_header(dst_ctx, NULL);
if (ret != 0)
{
DPRINTF(E_LOG, L_ART, "Could not write artwork header: %s\n", strerror(AVUNERROR(ret)));
ret = -1;
goto out_fclose_dst;
}
ret = av_interleaved_write_frame(dst_ctx, &pkt);
if (ret != 0)
{
DPRINTF(E_LOG, L_ART, "Error writing artwork\n");
ret = -1;
goto out_fclose_dst;
}
ret = av_write_trailer(dst_ctx);
if (ret != 0)
{
DPRINTF(E_LOG, L_ART, "Could not write artwork trailer: %s\n", strerror(AVUNERROR(ret)));
ret = -1;
goto out_fclose_dst;
}
switch (dst_fmt->video_codec)
{
case AV_CODEC_ID_PNG:
ret = ART_FMT_PNG;
break;
case AV_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:
avio_evbuffer_close(dst_ctx->pb);
av_packet_unref(&pkt);
out_free_buf:
av_free(buf);
out_free_frames:
if (i_frame)
av_frame_free(&i_frame);
if (o_frame)
av_frame_free(&o_frame);
avcodec_close(dst);
out_free_dst_ctx:
avformat_free_context(dst_ctx);
out_close_src:
avcodec_close(src);
return ret;
return 0;
}
/* 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)
{
AVFormatContext *src_ctx;
int s;
struct decode_ctx *xcode_decode;
struct encode_ctx *xcode_encode;
void *frame;
int width;
int height;
int target_w;
int target_h;
int format_ok;
@ -666,71 +345,71 @@ 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);
src_ctx = NULL;
ret = avformat_open_input(&src_ctx, path, NULL, NULL);
if (ret < 0)
xcode_decode = transcode_decode_setup(XCODE_JPEG, DATA_KIND_FILE, path, inbuf, 0); // Covers XCODE_PNG too
if (!xcode_decode)
{
DPRINTF(E_WARN, L_ART, "Cannot open artwork file '%s': %s\n", path, strerror(AVUNERROR(ret)));
return ART_E_ERROR;
DPRINTF(E_DBG, L_ART, "No artwork found in '%s'\n", path);
return ART_E_NONE;
}
ret = avformat_find_stream_info(src_ctx, NULL);
if (ret < 0)
{
DPRINTF(E_WARN, L_ART, "Cannot get stream info: %s\n", strerror(AVUNERROR(ret)));
avformat_close_input(&src_ctx);
return ART_E_ERROR;
}
format_ok = 0;
for (s = 0; s < src_ctx->nb_streams; s++)
{
if (src_ctx->streams[s]->codec->codec_id == AV_CODEC_ID_PNG)
{
format_ok = ART_FMT_PNG;
break;
}
else if (src_ctx->streams[s]->codec->codec_id == AV_CODEC_ID_MJPEG)
{
format_ok = ART_FMT_JPEG;
break;
}
}
if (s == src_ctx->nb_streams)
if (transcode_decode_query(xcode_decode, "is_jpeg"))
format_ok = ART_FMT_JPEG;
else if (transcode_decode_query(xcode_decode, "is_png"))
format_ok = ART_FMT_PNG;
else
{
DPRINTF(E_LOG, L_ART, "Artwork file '%s' not a PNG or JPEG file\n", path);
avformat_close_input(&src_ctx);
return ART_E_ERROR;
goto fail_free_decode;
}
ret = rescale_needed(src_ctx->streams[s]->codec, max_w, max_h, &target_w, &target_h);
width = transcode_decode_query(xcode_decode, "width");
height = transcode_decode_query(xcode_decode, "height");
/* Fastpath */
if (!ret && format_ok)
ret = rescale_calculate(&target_w, &target_h, width, height, max_w, max_h);
if (ret < 0)
{
ret = artwork_read(evbuf, path);
if (ret == 0)
ret = format_ok;
}
else
ret = artwork_rescale(evbuf, src_ctx, s, target_w, target_h);
// No rescaling required, just read the raw file into the evbuf
if (!path || artwork_read(evbuf, path) != 0)
goto fail_free_decode;
avformat_close_input(&src_ctx);
transcode_decode_cleanup(&xcode_decode);
return format_ok;
}
if (format_ok == ART_FMT_JPEG)
xcode_encode = transcode_encode_setup(XCODE_JPEG, xcode_decode, NULL, target_w, target_h);
else
xcode_encode = transcode_encode_setup(XCODE_PNG, xcode_decode, NULL, target_w, target_h);
if (!xcode_encode)
{
DPRINTF(E_WARN, L_ART, "Cannot open artwork file for rescaling '%s'\n", path);
goto fail_free_decode;
}
// We don't use transcode() because we just want to process one frame
ret = transcode_decode(&frame, xcode_decode);
if (ret < 0)
goto fail_free_encode;
ret = transcode_encode(evbuf, xcode_encode, frame, 1);
transcode_encode_cleanup(&xcode_encode);
transcode_decode_cleanup(&xcode_decode);
if (ret < 0)
{
if (evbuffer_get_length(evbuf) > 0)
evbuffer_drain(evbuf, evbuffer_get_length(evbuf));
ret = ART_E_ERROR;
evbuffer_drain(evbuf, evbuffer_get_length(evbuf));
return ART_E_ERROR;
}
return ret;
return format_ok;
fail_free_encode:
transcode_encode_cleanup(&xcode_encode);
fail_free_decode:
transcode_decode_cleanup(&xcode_decode);
return ART_E_ERROR;
}
/* Looks for an artwork file in a directory. Will rescale if needed.
@ -840,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);
}
@ -949,92 +628,11 @@ source_item_cache_get(struct artwork_ctx *ctx)
static int
source_item_embedded_get(struct artwork_ctx *ctx)
{
AVFormatContext *src_ctx;
AVStream *src_st;
int s;
int target_w;
int target_h;
int format;
int ret;
DPRINTF(E_SPAM, L_ART, "Trying embedded artwork in %s\n", ctx->dbmfi->path);
src_ctx = NULL;
snprintf(ctx->path, sizeof(ctx->path), "%s", ctx->dbmfi->path);
ret = avformat_open_input(&src_ctx, ctx->dbmfi->path, NULL, NULL);
if (ret < 0)
{
DPRINTF(E_WARN, L_ART, "Cannot open media file '%s': %s\n", ctx->dbmfi->path, strerror(AVUNERROR(ret)));
return ART_E_ERROR;
}
ret = avformat_find_stream_info(src_ctx, NULL);
if (ret < 0)
{
DPRINTF(E_WARN, L_ART, "Cannot get stream info: %s\n", strerror(AVUNERROR(ret)));
avformat_close_input(&src_ctx);
return ART_E_ERROR;
}
format = 0;
for (s = 0; s < src_ctx->nb_streams; s++)
{
if (src_ctx->streams[s]->disposition & AV_DISPOSITION_ATTACHED_PIC)
{
if (src_ctx->streams[s]->codec->codec_id == AV_CODEC_ID_PNG)
{
format = ART_FMT_PNG;
break;
}
else if (src_ctx->streams[s]->codec->codec_id == AV_CODEC_ID_MJPEG)
{
format = ART_FMT_JPEG;
break;
}
}
}
if (s == src_ctx->nb_streams)
{
avformat_close_input(&src_ctx);
return ART_E_NONE;
}
src_st = src_ctx->streams[s];
ret = rescale_needed(src_st->codec, ctx->max_w, ctx->max_h, &target_w, &target_h);
/* Fastpath */
if (!ret && format)
{
DPRINTF(E_SPAM, L_ART, "Artwork not too large, using original image\n");
ret = evbuffer_add(ctx->evbuf, src_st->attached_pic.data, src_st->attached_pic.size);
if (ret < 0)
DPRINTF(E_LOG, L_ART, "Could not add embedded image to event buffer\n");
else
ret = format;
}
else
{
DPRINTF(E_SPAM, L_ART, "Artwork too large, rescaling image\n");
ret = artwork_rescale(ctx->evbuf, src_ctx, s, target_w, target_h);
}
avformat_close_input(&src_ctx);
if (ret < 0)
{
if (evbuffer_get_length(ctx->evbuf) > 0)
evbuffer_drain(ctx->evbuf, evbuffer_get_length(ctx->evbuf));
ret = ART_E_ERROR;
}
else
snprintf(ctx->path, sizeof(ctx->path), "%s", ctx->dbmfi->path);
return ret;
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
@ -1088,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);
}
/*
@ -1177,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();
@ -1223,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

1627
src/artwork_legacy.c Normal file

File diff suppressed because it is too large Load Diff

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

@ -167,7 +167,7 @@ stream_end(struct stream_ctx *st, int failed)
event_free(st->ev);
if (st->xcode)
transcode_cleanup(st->xcode);
transcode_cleanup(&st->xcode);
else
{
free(st->buf);
@ -309,11 +309,10 @@ stream_chunk_xcode_cb(int fd, short event, void *arg)
struct timeval tv;
int xcoded;
int ret;
int dummy;
st = (struct stream_ctx *)arg;
xcoded = transcode(st->evbuf, STREAM_CHUNK_SIZE, st->xcode, &dummy);
xcoded = transcode(st->evbuf, NULL, st->xcode, STREAM_CHUNK_SIZE);
if (xcoded <= 0)
{
if (xcoded == 0)
@ -552,7 +551,7 @@ httpd_stream_file(struct evhttp_request *req, int id)
stream_cb = stream_chunk_xcode_cb;
st->xcode = transcode_setup(mfi->data_kind, mfi->path, mfi->song_length, XCODE_PCM16_HEADER, &st->size);
st->xcode = transcode_setup(XCODE_PCM16_HEADER, mfi->data_kind, mfi->path, mfi->song_length, &st->size);
if (!st->xcode)
{
DPRINTF(E_WARN, L_HTTPD, "Transcoding setup failed, aborting streaming\n");
@ -750,7 +749,7 @@ httpd_stream_file(struct evhttp_request *req, int id)
if (st->evbuf)
evbuffer_free(st->evbuf);
if (st->xcode)
transcode_cleanup(st->xcode);
transcode_cleanup(&st->xcode);
if (st->buf)
free(st->buf);
if (st->fd > 0)

View File

@ -123,7 +123,7 @@ streaming_send_cb(evutil_socket_t fd, short event, void *arg)
{
struct streaming_session *session;
struct evbuffer *evbuf;
struct decoded_frame *decoded;
void *frame;
uint8_t *buf;
int len;
int ret;
@ -138,15 +138,15 @@ streaming_send_cb(evutil_socket_t fd, short event, void *arg)
if (!streaming_sessions)
return;
decoded = transcode_raw2frame(streaming_rawbuf, STREAMING_RAWBUF_SIZE);
if (!decoded)
frame = transcode_frame_new(XCODE_MP3, streaming_rawbuf, STREAMING_RAWBUF_SIZE);
if (!frame)
{
DPRINTF(E_LOG, L_STREAMING, "Could not convert raw PCM to frame\n");
return;
}
ret = transcode_encode(streaming_encoded_data, decoded, streaming_encode_ctx);
transcode_decoded_free(decoded);
ret = transcode_encode(streaming_encoded_data, streaming_encode_ctx, frame, 0);
transcode_frame_free(frame);
if (ret < 0)
return;
}
@ -288,7 +288,7 @@ int
streaming_init(void)
{
struct decode_ctx *decode_ctx;
struct decoded_frame *decoded;
void *frame;
int remaining;
int ret;
@ -299,8 +299,8 @@ streaming_init(void)
return -1;
}
streaming_encode_ctx = transcode_encode_setup(decode_ctx, XCODE_MP3, NULL);
transcode_decode_cleanup(decode_ctx);
streaming_encode_ctx = transcode_encode_setup(XCODE_MP3, decode_ctx, NULL, 0, 0);
transcode_decode_cleanup(&decode_ctx);
if (!streaming_encode_ctx)
{
DPRINTF(E_LOG, L_STREAMING, "Will not be able to stream mp3, libav does not support mp3 encoding\n");
@ -345,15 +345,15 @@ streaming_init(void)
remaining = STREAMING_SILENCE_INTERVAL * STOB(44100);
while (remaining > STREAMING_RAWBUF_SIZE)
{
decoded = transcode_raw2frame(streaming_rawbuf, STREAMING_RAWBUF_SIZE);
if (!decoded)
frame = transcode_frame_new(XCODE_MP3, streaming_rawbuf, STREAMING_RAWBUF_SIZE);
if (!frame)
{
DPRINTF(E_LOG, L_STREAMING, "Could not convert raw PCM to frame\n");
goto silence_fail;
}
ret = transcode_encode(streaming_encoded_data, decoded, streaming_encode_ctx);
transcode_decoded_free(decoded);
ret = transcode_encode(streaming_encoded_data, streaming_encode_ctx, frame, 0);
transcode_frame_free(frame);
if (ret < 0)
{
DPRINTF(E_LOG, L_STREAMING, "Could not encode silence buffer\n");
@ -399,7 +399,7 @@ streaming_init(void)
close(streaming_pipe[0]);
close(streaming_pipe[1]);
pipe_fail:
transcode_encode_cleanup(streaming_encode_ctx);
transcode_encode_cleanup(&streaming_encode_ctx);
return -1;
}
@ -432,7 +432,7 @@ streaming_deinit(void)
close(streaming_pipe[0]);
close(streaming_pipe[1]);
transcode_encode_cleanup(streaming_encode_ctx);
transcode_encode_cleanup(&streaming_encode_ctx);
evbuffer_free(streaming_encoded_data);
free(streaming_silence_data);
}

View File

@ -483,7 +483,7 @@ input_flush(short *flags)
pthread_mutex_unlock(&input_buffer.mutex);
#ifdef DEBUG
DPRINTF(E_DBG, L_PLAYER, "Flush with flags %d\n", *flags);
DPRINTF(E_DBG, L_PLAYER, "Flushing %zu bytes with flags %d\n", len, *flags);
#endif
}

View File

@ -31,7 +31,7 @@
static int
setup(struct player_source *ps)
{
ps->input_ctx = transcode_setup(ps->data_kind, ps->path, ps->len_ms, XCODE_PCM16_NOHEADER, NULL);
ps->input_ctx = transcode_setup(XCODE_PCM16_NOHEADER, ps->data_kind, ps->path, ps->len_ms, NULL);
if (!ps->input_ctx)
return -1;
@ -70,7 +70,7 @@ start(struct player_source *ps)
{
// We set "wanted" to 1 because the read size doesn't matter to us
// TODO optimize?
ret = transcode(evbuf, 1, ps->input_ctx, &icy_timer);
ret = transcode(evbuf, &icy_timer, ps->input_ctx, 1);
if (ret < 0)
break;
@ -90,7 +90,9 @@ start(struct player_source *ps)
static int
stop(struct player_source *ps)
{
transcode_cleanup(ps->input_ctx);
struct transcode_ctx *ctx = ps->input_ctx;
transcode_cleanup(&ctx);
ps->input_ctx = NULL;
ps->setup_done = 0;

View File

@ -240,17 +240,9 @@ static const struct metadata_map md_map_id3[] =
static int
#if LIBAVUTIL_VERSION_MAJOR >= 52 || (LIBAVUTIL_VERSION_MAJOR == 51 && LIBAVUTIL_VERSION_MINOR >= 5)
extract_metadata_core(struct media_file_info *mfi, AVDictionary *md, const struct metadata_map *md_map)
#else
extract_metadata_core(struct media_file_info *mfi, AVMetadata *md, const struct metadata_map *md_map)
#endif
{
#if LIBAVUTIL_VERSION_MAJOR >= 52 || (LIBAVUTIL_VERSION_MAJOR == 51 && LIBAVUTIL_VERSION_MINOR >= 5)
AVDictionaryEntry *mdt;
#else
AVMetadataTag *mdt;
#endif
char **strval;
uint32_t *intval;
int mdcount;
@ -260,11 +252,7 @@ extract_metadata_core(struct media_file_info *mfi, AVMetadata *md, const struct
#if 0
/* Dump all the metadata reported by ffmpeg */
mdt = NULL;
#if LIBAVUTIL_VERSION_MAJOR >= 52 || (LIBAVUTIL_VERSION_MAJOR == 51 && LIBAVUTIL_VERSION_MINOR >= 5)
while ((mdt = av_dict_get(md, "", mdt, AV_DICT_IGNORE_SUFFIX)) != NULL)
#else
while ((mdt = av_metadata_get(md, "", mdt, AV_METADATA_IGNORE_SUFFIX)) != NULL)
#endif
fprintf(stderr, " -> %s = %s\n", mdt->key, mdt->value);
#endif
@ -273,11 +261,7 @@ extract_metadata_core(struct media_file_info *mfi, AVMetadata *md, const struct
/* Extract actual metadata */
for (i = 0; md_map[i].key != NULL; i++)
{
#if LIBAVUTIL_VERSION_MAJOR >= 52 || (LIBAVUTIL_VERSION_MAJOR == 51 && LIBAVUTIL_VERSION_MINOR >= 5)
mdt = av_dict_get(md, md_map[i].key, NULL, 0);
#else
mdt = av_metadata_get(md, md_map[i].key, NULL, 0);
#endif
if (mdt == NULL)
continue;
@ -367,19 +351,16 @@ scan_metadata_ffmpeg(const char *file, struct media_file_info *mfi)
AVDictionary *options;
const struct metadata_map *extra_md_map;
struct http_icy_metadata *icy_metadata;
#if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35)
enum AVMediaType codec_type;
enum AVCodecID codec_id;
enum AVCodecID video_codec_id;
enum AVCodecID audio_codec_id;
#else
enum CodecID codec_id;
enum CodecID video_codec_id;
enum CodecID audio_codec_id;
#endif
enum AVSampleFormat sample_fmt;
AVStream *video_stream;
AVStream *audio_stream;
char *path;
int mdcount;
int sample_rate;
int i;
int ret;
@ -387,14 +368,13 @@ scan_metadata_ffmpeg(const char *file, struct media_file_info *mfi)
options = NULL;
path = strdup(file);
#if LIBAVFORMAT_VERSION_MAJOR >= 54 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVFORMAT_VERSION_MINOR >= 3)
if (mfi->data_kind == DATA_KIND_HTTP)
{
# ifndef HAVE_FFMPEG
#ifndef HAVE_FFMPEG
// Without this, libav is slow to probe some internet streams
ctx = avformat_alloc_context();
ctx->probesize = 64000;
# endif
#endif
free(path);
ret = http_stream_setup(&path, file);
@ -409,9 +389,7 @@ scan_metadata_ffmpeg(const char *file, struct media_file_info *mfi)
if (options)
av_dict_free(&options);
#else
ret = av_open_input_file(&ctx, path, NULL, 0, NULL);
#endif
if (ret != 0)
{
DPRINTF(E_WARN, L_SCAN, "Cannot open media file '%s': %s\n", path, err2str(ret));
@ -422,20 +400,12 @@ scan_metadata_ffmpeg(const char *file, struct media_file_info *mfi)
free(path);
#if LIBAVFORMAT_VERSION_MAJOR >= 54 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVFORMAT_VERSION_MINOR >= 3)
ret = avformat_find_stream_info(ctx, NULL);
#else
ret = av_find_stream_info(ctx);
#endif
if (ret < 0)
{
DPRINTF(E_WARN, L_SCAN, "Cannot get stream info of '%s': %s\n", path, err2str(ret));
#if LIBAVFORMAT_VERSION_MAJOR >= 54 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVFORMAT_VERSION_MINOR >= 21)
avformat_close_input(&ctx);
#else
av_close_input_file(ctx);
#endif
return -1;
}
@ -447,29 +417,28 @@ scan_metadata_ffmpeg(const char *file, struct media_file_info *mfi)
DPRINTF(E_DBG, L_SCAN, "File has %d streams\n", ctx->nb_streams);
/* Extract codec IDs, check for video */
#if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35)
video_codec_id = AV_CODEC_ID_NONE;
video_stream = NULL;
audio_codec_id = AV_CODEC_ID_NONE;
audio_stream = NULL;
#else
video_codec_id = CODEC_ID_NONE;
video_stream = NULL;
audio_codec_id = CODEC_ID_NONE;
audio_stream = NULL;
#endif
for (i = 0; i < ctx->nb_streams; i++)
{
switch (ctx->streams[i]->codec->codec_type)
{
#if LIBAVCODEC_VERSION_MAJOR >= 53 || (LIBAVCODEC_VERSION_MAJOR == 52 && LIBAVCODEC_VERSION_MINOR >= 64)
case AVMEDIA_TYPE_VIDEO:
#if HAVE_DECL_AVCODEC_PARAMETERS_FROM_CONTEXT
codec_type = ctx->streams[i]->codecpar->codec_type;
codec_id = ctx->streams[i]->codecpar->codec_id;
sample_rate = ctx->streams[i]->codecpar->sample_rate;
sample_fmt = ctx->streams[i]->codecpar->format;
#else
case CODEC_TYPE_VIDEO:
codec_type = ctx->streams[i]->codec->codec_type;
codec_id = ctx->streams[i]->codec->codec_id;
sample_rate = ctx->streams[i]->codec->sample_rate;
sample_fmt = ctx->streams[i]->codec->sample_fmt;
#endif
switch (codec_type)
{
case AVMEDIA_TYPE_VIDEO:
#if LIBAVFORMAT_VERSION_MAJOR >= 55 || (LIBAVFORMAT_VERSION_MAJOR == 54 && LIBAVFORMAT_VERSION_MINOR >= 6)
if (ctx->streams[i]->disposition & AV_DISPOSITION_ATTACHED_PIC)
{
@ -487,21 +456,23 @@ scan_metadata_ffmpeg(const char *file, struct media_file_info *mfi)
{
DPRINTF(E_DBG, L_SCAN, "File has video (stream %d)\n", i);
mfi->has_video = 1;
video_stream = ctx->streams[i];
video_codec_id = video_stream->codec->codec_id;
video_codec_id = codec_id;
mfi->has_video = 1;
}
break;
#if LIBAVCODEC_VERSION_MAJOR >= 53 || (LIBAVCODEC_VERSION_MAJOR == 52 && LIBAVCODEC_VERSION_MINOR >= 64)
case AVMEDIA_TYPE_AUDIO:
#else
case CODEC_TYPE_AUDIO:
#endif
if (!audio_stream)
{
audio_stream = ctx->streams[i];
audio_codec_id = audio_stream->codec->codec_id;
audio_codec_id = codec_id;
mfi->samplerate = sample_rate;
mfi->bits_per_sample = 8 * av_get_bytes_per_sample(sample_fmt);
if (mfi->bits_per_sample == 0)
mfi->bits_per_sample = av_get_bits_per_sample(codec_id);
}
break;
@ -510,19 +481,11 @@ scan_metadata_ffmpeg(const char *file, struct media_file_info *mfi)
}
}
#if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35)
if (audio_codec_id == AV_CODEC_ID_NONE)
#else
if (audio_codec_id == CODEC_ID_NONE)
#endif
{
DPRINTF(E_DBG, L_SCAN, "File has no audio streams, discarding\n");
#if LIBAVFORMAT_VERSION_MAJOR >= 54 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVFORMAT_VERSION_MINOR >= 21)
avformat_close_input(&ctx);
#else
av_close_input_file(ctx);
#endif
return -1;
}
@ -578,61 +541,26 @@ scan_metadata_ffmpeg(const char *file, struct media_file_info *mfi)
http_icy_metadata_free(icy_metadata, 0);
}
/* Get some more information on the audio stream */
if (audio_stream)
{
if (audio_stream->codec->sample_rate != 0)
mfi->samplerate = audio_stream->codec->sample_rate;
/* Try sample format first */
#if LIBAVUTIL_VERSION_MAJOR >= 52 || (LIBAVUTIL_VERSION_MAJOR == 51 && LIBAVUTIL_VERSION_MINOR >= 4)
mfi->bits_per_sample = 8 * av_get_bytes_per_sample(audio_stream->codec->sample_fmt);
#elif LIBAVCODEC_VERSION_MAJOR >= 53
mfi->bits_per_sample = av_get_bits_per_sample_fmt(audio_stream->codec->sample_fmt);
#else
mfi->bits_per_sample = av_get_bits_per_sample_format(audio_stream->codec->sample_fmt);
#endif
if (mfi->bits_per_sample == 0)
{
/* Try codec */
mfi->bits_per_sample = av_get_bits_per_sample(audio_codec_id);
}
DPRINTF(E_DBG, L_SCAN, "samplerate %d, bps %d\n", mfi->samplerate, mfi->bits_per_sample);
}
/* Check codec */
extra_md_map = NULL;
codec_id = (mfi->has_video) ? video_codec_id : audio_codec_id;
switch (codec_id)
{
#if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35)
case AV_CODEC_ID_AAC:
#else
case CODEC_ID_AAC:
#endif
DPRINTF(E_DBG, L_SCAN, "AAC\n");
mfi->type = strdup("m4a");
mfi->codectype = strdup("mp4a");
mfi->description = strdup("AAC audio file");
break;
#if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35)
case AV_CODEC_ID_ALAC:
#else
case CODEC_ID_ALAC:
#endif
DPRINTF(E_DBG, L_SCAN, "ALAC\n");
mfi->type = strdup("m4a");
mfi->codectype = strdup("alac");
mfi->description = strdup("Apple Lossless audio file");
break;
#if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35)
case AV_CODEC_ID_FLAC:
#else
case CODEC_ID_FLAC:
#endif
DPRINTF(E_DBG, L_SCAN, "FLAC\n");
mfi->type = strdup("flac");
mfi->codectype = strdup("flac");
@ -641,37 +569,23 @@ scan_metadata_ffmpeg(const char *file, struct media_file_info *mfi)
extra_md_map = md_map_vorbis;
break;
#if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35)
case AV_CODEC_ID_APE:
#else
case CODEC_ID_APE:
#endif
DPRINTF(E_DBG, L_SCAN, "APE\n");
mfi->type = strdup("ape");
mfi->codectype = strdup("ape");
mfi->description = strdup("Monkey's audio");
break;
#if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35)
case AV_CODEC_ID_MUSEPACK7:
case AV_CODEC_ID_MUSEPACK8:
#else
case CODEC_ID_MUSEPACK7:
case CODEC_ID_MUSEPACK8:
#endif
DPRINTF(E_DBG, L_SCAN, "Musepack\n");
mfi->type = strdup("mpc");
mfi->codectype = strdup("mpc");
mfi->description = strdup("Musepack audio file");
break;
#if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35)
case AV_CODEC_ID_MPEG4: /* Video */
case AV_CODEC_ID_H264:
#else
case CODEC_ID_MPEG4: /* Video */
case CODEC_ID_H264:
#endif
DPRINTF(E_DBG, L_SCAN, "MPEG4 video\n");
mfi->type = strdup("m4v");
mfi->codectype = strdup("mp4v");
@ -680,11 +594,7 @@ scan_metadata_ffmpeg(const char *file, struct media_file_info *mfi)
extra_md_map = md_map_tv;
break;
#if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35)
case AV_CODEC_ID_MP3:
#else
case CODEC_ID_MP3:
#endif
DPRINTF(E_DBG, L_SCAN, "MP3\n");
mfi->type = strdup("mp3");
mfi->codectype = strdup("mpeg");
@ -693,11 +603,7 @@ scan_metadata_ffmpeg(const char *file, struct media_file_info *mfi)
extra_md_map = md_map_id3;
break;
#if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35)
case AV_CODEC_ID_VORBIS:
#else
case CODEC_ID_VORBIS:
#endif
DPRINTF(E_DBG, L_SCAN, "VORBIS\n");
mfi->type = strdup("ogg");
mfi->codectype = strdup("ogg");
@ -706,48 +612,30 @@ scan_metadata_ffmpeg(const char *file, struct media_file_info *mfi)
extra_md_map = md_map_vorbis;
break;
#if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35)
case AV_CODEC_ID_WMAV1:
case AV_CODEC_ID_WMAV2:
case AV_CODEC_ID_WMAVOICE:
#else
case CODEC_ID_WMAV1:
case CODEC_ID_WMAV2:
case CODEC_ID_WMAVOICE:
#endif
DPRINTF(E_DBG, L_SCAN, "WMA Voice\n");
mfi->type = strdup("wma");
mfi->codectype = strdup("wmav");
mfi->description = strdup("WMA audio file");
break;
#if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35)
case AV_CODEC_ID_WMAPRO:
#else
case CODEC_ID_WMAPRO:
#endif
DPRINTF(E_DBG, L_SCAN, "WMA Pro\n");
mfi->type = strdup("wmap");
mfi->codectype = strdup("wma");
mfi->description = strdup("WMA audio file");
break;
#if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35)
case AV_CODEC_ID_WMALOSSLESS:
#else
case CODEC_ID_WMALOSSLESS:
#endif
DPRINTF(E_DBG, L_SCAN, "WMA Lossless\n");
mfi->type = strdup("wma");
mfi->codectype = strdup("wmal");
mfi->description = strdup("WMA audio file");
break;
#if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35)
case AV_CODEC_ID_PCM_S16LE ... AV_CODEC_ID_PCM_F64LE:
#else
case CODEC_ID_PCM_S16LE ... CODEC_ID_PCM_F64LE:
#endif
if (strcmp(ctx->iformat->name, "aiff") == 0)
{
DPRINTF(E_DBG, L_SCAN, "AIFF\n");
@ -818,11 +706,7 @@ scan_metadata_ffmpeg(const char *file, struct media_file_info *mfi)
}
skip_extract:
#if LIBAVFORMAT_VERSION_MAJOR >= 54 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVFORMAT_VERSION_MINOR >= 21)
avformat_close_input(&ctx);
#else
av_close_input_file(ctx);
#endif
if (mdcount == 0)
DPRINTF(E_WARN, L_SCAN, "ffmpeg/libav could not extract any metadata\n");

View File

@ -179,9 +179,9 @@ logger_ffmpeg(void *ptr, int level, const char *fmt, va_list ap)
else if (level <= AV_LOG_WARNING)
severity = E_WARN;
else if (level <= AV_LOG_VERBOSE)
severity = E_INFO;
else if (level <= AV_LOG_DEBUG)
severity = E_DBG;
else if (level <= AV_LOG_DEBUG)
severity = E_SPAM;
else
severity = E_SPAM;

View File

@ -1271,6 +1271,8 @@ raop_make_sdp(struct raop_session *rs, struct evrtsp_request *req, char *address
return -1;
}
DPRINTF(E_INFO, L_RAOP, "Setting up AirPlay session %u (%s -> %s)\n", session_id, address, rs->address);
return 0;
#undef SDP_PLD_FMT

File diff suppressed because it is too large Load Diff

View File

@ -6,35 +6,32 @@
#include "db.h"
#include "http.h"
#define XCODE_WAVHEADER (1 << 14)
#define XCODE_HAS_VIDEO (1 << 15)
enum transcode_profile
{
// Transcodes the best available audio stream into PCM16 (does not add wav header)
XCODE_PCM16_NOHEADER = 1,
// Transcodes the best available audio stream into PCM16 (with wav header)
XCODE_PCM16_HEADER = XCODE_WAVHEADER | 2,
// Transcodes the best available audio stream into MP3
XCODE_MP3 = 3,
// Transcodes video + audio + subtitle streams (not tested - for future use)
XCODE_H264_AAC = XCODE_HAS_VIDEO | 4,
// Transcodes the best audio stream into PCM16 (does not add wav header)
XCODE_PCM16_NOHEADER,
// Transcodes the best audio stream into PCM16 (with wav header)
XCODE_PCM16_HEADER,
// Transcodes the best audio stream into MP3
XCODE_MP3,
// Transcodes the best video stream into JPEG/PNG
XCODE_JPEG,
XCODE_PNG,
};
struct decode_ctx;
struct encode_ctx;
struct transcode_ctx;
struct decoded_frame;
// Setting up
struct decode_ctx *
transcode_decode_setup(enum data_kind data_kind, const char *path, uint32_t song_length, int decode_video);
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(struct decode_ctx *src_ctx, enum transcode_profile profile, off_t *est_size);
transcode_encode_setup(enum transcode_profile profile, struct decode_ctx *src_ctx, off_t *est_size, int width, int height);
struct transcode_ctx *
transcode_setup(enum data_kind data_kind, const char *path, uint32_t song_length, enum transcode_profile profile, off_t *est_size);
transcode_setup(enum transcode_profile profile, enum data_kind data_kind, const char *path, uint32_t song_length, off_t *est_size);
struct decode_ctx *
transcode_decode_setup_raw(void);
@ -44,58 +41,82 @@ transcode_needed(const char *user_agent, const char *client_codecs, char *file_c
// Cleaning up
void
transcode_decode_cleanup(struct decode_ctx *ctx);
transcode_decode_cleanup(struct decode_ctx **ctx);
void
transcode_encode_cleanup(struct encode_ctx *ctx);
transcode_encode_cleanup(struct encode_ctx **ctx);
void
transcode_cleanup(struct transcode_ctx *ctx);
void
transcode_decoded_free(struct decoded_frame *decoded);
transcode_cleanup(struct transcode_ctx **ctx);
// Transcoding
/* Demuxes and decodes the next packet from the input.
*
* @out decoded A newly allocated struct with a pointer to the frame and the
* stream. Must be freed with transcode_decoded_free().
* @in ctx Decode context
* @return Positive if OK, negative if error, 0 if EOF
* @out frame A pointer to the frame. Caller should not free it, that will
* be done by the next call to the function or by the cleanup
* function.
* @in ctx Decode context
* @return Positive if OK, negative if error, 0 if EOF
*/
int
transcode_decode(struct decoded_frame **decoded, struct decode_ctx *ctx);
transcode_decode(void **frame, struct decode_ctx *ctx);
/* Encodes and remuxes a frame. Also resamples if needed.
*
* @out evbuf An evbuffer filled with remuxed data
* @in frame The frame to encode, e.g. from transcode_decode
* @in wanted Bytes that the caller wants processed
* @in ctx Encode context
* @return Length of evbuf if OK, negative if error
* @out evbuf An evbuffer filled with remuxed data
* @in ctx Encode context
* @in frame The decoded frame to encode, e.g. from transcode_decode
* @in eof If true the muxer will write a trailer to the output
* @return Bytes added if OK, negative if error
*/
int
transcode_encode(struct evbuffer *evbuf, struct decoded_frame *decoded, struct encode_ctx *ctx);
transcode_encode(struct evbuffer *evbuf, struct encode_ctx *ctx, void *frame, int eof);
/* Demuxes, decodes, encodes and remuxes the next packet from the input.
/* Demuxes, decodes, encodes and remuxes from the input.
*
* @out evbuf An evbuffer filled with remuxed data
* @in wanted Bytes that the caller wants processed
* @in ctx Transcode context
* @out icy_timer True if METADATA_ICY_INTERVAL has elapsed
* @return Bytes processed if OK, negative if error, 0 if EOF
* @out evbuf An evbuffer filled with remuxed data
* @out icy_timer True if METADATA_ICY_INTERVAL has elapsed
* @in ctx Transcode context
* @in want_bytes Minimum number of bytes the caller wants added to the evbuffer
* - set want_bytes to 0 to transcode everything until EOF/error
* - set want_bytes to 1 to get one encoded packet
* @return Bytes added if OK, negative if error, 0 if EOF
*/
int
transcode(struct evbuffer *evbuf, int wanted, struct transcode_ctx *ctx, int *icy_timer);
transcode(struct evbuffer *evbuf, int *icy_timer, struct transcode_ctx *ctx, int want_bytes);
struct decoded_frame *
transcode_raw2frame(uint8_t *data, size_t size);
/* Converts a buffer with raw data to a frame that can be passed directly to the
* transcode_encode() function
*
* @in profile Tells the function what kind of frame to create
* @in data Buffer with raw data
* @in size Size of buffer
* @return Opaque pointer to frame if OK, otherwise NULL
*/
void *
transcode_frame_new(enum transcode_profile profile, uint8_t *data, size_t size);
void
transcode_frame_free(void *frame);
// Seeking
/* Seek to the specified position - next transcode() will return this packet
*
* @in ctx Transcode context
* @in seek Requested seek position in ms
* @return Negative if error, otherwise actual seek position
*/
int
transcode_seek(struct transcode_ctx *ctx, int ms);
/* Query for information about a media file opened by transcode_decode_setup()
*
* @in ctx Decode context
* @in query Query - see implementation for supported queries
* @return Negative if error, otherwise query dependent
*/
int
transcode_decode_query(struct decode_ctx *ctx, const char *query);
// Metadata
struct http_icy_metadata *
transcode_metadata(struct transcode_ctx *ctx, int *changed);

1677
src/transcode_legacy.c Normal file

File diff suppressed because it is too large Load Diff