mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-27 07:35:57 -05:00
[transcode] Fix freeze problem where av_read_frame would block on broken connections
This commit is contained in:
parent
100cecce3f
commit
93a6765c8c
@ -30,6 +30,7 @@
|
||||
#include <libavfilter/buffersink.h>
|
||||
#include <libavfilter/buffersrc.h>
|
||||
#include <libavutil/opt.h>
|
||||
#include <libavutil/time.h>
|
||||
#include <libavutil/pixdesc.h>
|
||||
|
||||
#ifdef HAVE_LIBAVFILTER
|
||||
@ -50,6 +51,8 @@
|
||||
#define MAX_STREAMS 64
|
||||
// Maximum number of times we retry when we encounter bad packets
|
||||
#define MAX_BAD_PACKETS 5
|
||||
// How long to wait (in microsec) before interrupting av_read_frame
|
||||
#define READ_TIMEOUT 10000000
|
||||
|
||||
static char *default_codecs = "mpeg,wav";
|
||||
static char *roku_codecs = "mpeg,mp4a,wma,wav";
|
||||
@ -79,6 +82,9 @@ struct decode_ctx {
|
||||
AVPacket packet;
|
||||
int resume;
|
||||
int resume_offset;
|
||||
|
||||
// Used to measure if av_read_frame is taking too long
|
||||
int64_t timestamp;
|
||||
};
|
||||
|
||||
struct encode_ctx {
|
||||
@ -247,6 +253,29 @@ decode_stream(struct decode_ctx *ctx, AVStream *in_stream)
|
||||
(in_stream == ctx->subtitle_stream));
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by libavformat while demuxing. Used to interrupt/unblock av_read_frame
|
||||
* in case a source (especially a network stream) becomes unavailable.
|
||||
*
|
||||
* @in arg Will point to the decode context
|
||||
* @return Non-zero if av_read_frame should be interrupted
|
||||
*/
|
||||
static int decode_interrupt_cb(void *arg)
|
||||
{
|
||||
struct decode_ctx *ctx;
|
||||
|
||||
ctx = (struct decode_ctx *)arg;
|
||||
|
||||
if (av_gettime() - ctx->timestamp > READ_TIMEOUT)
|
||||
{
|
||||
DPRINTF(E_LOG, L_XCODE, "Timeout while reading source (connection problem?)\n");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Will read the next packet from the source, unless we are in resume mode, in
|
||||
* which case the most recent packet will be returned, but with an adjusted data
|
||||
* pointer. Use ctx->resume and ctx->resume_offset to make the function resume
|
||||
@ -286,7 +315,9 @@ read_packet(AVPacket *packet, AVStream **stream, unsigned int *stream_index, str
|
||||
// We are going to read a new packet from source, so now it is safe to
|
||||
// discard the previous packet and reset resume_offset
|
||||
av_free_packet(&ctx->packet);
|
||||
|
||||
ctx->resume_offset = 0;
|
||||
ctx->timestamp = av_gettime();
|
||||
|
||||
ret = av_read_frame(ctx->ifmt_ctx, &ctx->packet);
|
||||
if (ret < 0)
|
||||
@ -551,22 +582,31 @@ open_input(struct decode_ctx *ctx, struct media_file_info *mfi, int decode_video
|
||||
int ret;
|
||||
|
||||
options = NULL;
|
||||
ctx->ifmt_ctx = NULL;
|
||||
ctx->ifmt_ctx = avformat_alloc_context();;
|
||||
if (!ctx->ifmt_ctx)
|
||||
{
|
||||
DPRINTF(E_LOG, L_XCODE, "Out of memory for input format context\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
# ifndef HAVE_FFMPEG
|
||||
// Without this, libav is slow to probe some internet streams, which leads to RAOP timeouts
|
||||
if (mfi->data_kind == DATA_KIND_HTTP)
|
||||
{
|
||||
ctx->ifmt_ctx = avformat_alloc_context();
|
||||
ctx->ifmt_ctx->probesize = 64000;
|
||||
}
|
||||
ctx->ifmt_ctx->probesize = 64000;
|
||||
# endif
|
||||
if (mfi->data_kind == DATA_KIND_HTTP)
|
||||
av_dict_set(&options, "icy", "1", 0);
|
||||
|
||||
// TODO Newest versions of ffmpeg have timeout and reconnect options we should use
|
||||
ctx->ifmt_ctx->interrupt_callback.callback = decode_interrupt_cb;
|
||||
ctx->ifmt_ctx->interrupt_callback.opaque = ctx;
|
||||
ctx->timestamp = av_gettime();
|
||||
|
||||
ret = avformat_open_input(&ctx->ifmt_ctx, mfi->path, NULL, &options);
|
||||
|
||||
if (options)
|
||||
av_dict_free(&options);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_XCODE, "Cannot open input path\n");
|
||||
@ -1476,7 +1516,7 @@ transcode_decode(struct decoded_frame **decoded, struct decode_ctx *ctx)
|
||||
{
|
||||
// Some decoders need to be flushed, meaning the decoder is to be called
|
||||
// with empty input until no more frames are returned
|
||||
DPRINTF(E_DBG, L_XCODE, "Could not read packet (eof?), will flush decoders\n");
|
||||
DPRINTF(E_DBG, L_XCODE, "Could not read packet, will flush decoders\n");
|
||||
|
||||
used = 1;
|
||||
got_frame = flush_decoder(frame, &in_stream, &stream_index, ctx);
|
||||
@ -1697,6 +1737,8 @@ transcode_seek(struct transcode_ctx *ctx, int ms)
|
||||
{
|
||||
av_free_packet(&decode_ctx->packet);
|
||||
|
||||
decode_ctx->timestamp = av_gettime();
|
||||
|
||||
ret = av_read_frame(decode_ctx->ifmt_ctx, &decode_ctx->packet);
|
||||
if (ret < 0)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user