mirror of
https://github.com/owntone/owntone-server.git
synced 2025-01-15 08:45:02 -05:00
Merge branch 'ffmpeg_update2'
This commit is contained in:
commit
0f4f8a9a40
39
configure.ac
39
configure.ac
@ -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 *])
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
639
src/artwork.c
639
src/artwork.c
@ -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
1627
src/artwork_legacy.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
1972
src/transcode.c
1972
src/transcode.c
File diff suppressed because it is too large
Load Diff
105
src/transcode.h
105
src/transcode.h
@ -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
1677
src/transcode_legacy.c
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user