mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-27 23:55: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/buffersink.h>
|
||||||
#include <libavfilter/buffersrc.h>
|
#include <libavfilter/buffersrc.h>
|
||||||
#include <libavutil/opt.h>
|
#include <libavutil/opt.h>
|
||||||
|
#include <libavutil/time.h>
|
||||||
#include <libavutil/pixdesc.h>
|
#include <libavutil/pixdesc.h>
|
||||||
|
|
||||||
#ifdef HAVE_LIBAVFILTER
|
#ifdef HAVE_LIBAVFILTER
|
||||||
@ -50,6 +51,8 @@
|
|||||||
#define MAX_STREAMS 64
|
#define MAX_STREAMS 64
|
||||||
// Maximum number of times we retry when we encounter bad packets
|
// Maximum number of times we retry when we encounter bad packets
|
||||||
#define MAX_BAD_PACKETS 5
|
#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 *default_codecs = "mpeg,wav";
|
||||||
static char *roku_codecs = "mpeg,mp4a,wma,wav";
|
static char *roku_codecs = "mpeg,mp4a,wma,wav";
|
||||||
@ -79,6 +82,9 @@ struct decode_ctx {
|
|||||||
AVPacket packet;
|
AVPacket packet;
|
||||||
int resume;
|
int resume;
|
||||||
int resume_offset;
|
int resume_offset;
|
||||||
|
|
||||||
|
// Used to measure if av_read_frame is taking too long
|
||||||
|
int64_t timestamp;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct encode_ctx {
|
struct encode_ctx {
|
||||||
@ -247,6 +253,29 @@ decode_stream(struct decode_ctx *ctx, AVStream *in_stream)
|
|||||||
(in_stream == ctx->subtitle_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
|
/* 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
|
* 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
|
* 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
|
// We are going to read a new packet from source, so now it is safe to
|
||||||
// discard the previous packet and reset resume_offset
|
// discard the previous packet and reset resume_offset
|
||||||
av_free_packet(&ctx->packet);
|
av_free_packet(&ctx->packet);
|
||||||
|
|
||||||
ctx->resume_offset = 0;
|
ctx->resume_offset = 0;
|
||||||
|
ctx->timestamp = av_gettime();
|
||||||
|
|
||||||
ret = av_read_frame(ctx->ifmt_ctx, &ctx->packet);
|
ret = av_read_frame(ctx->ifmt_ctx, &ctx->packet);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
@ -551,22 +582,31 @@ open_input(struct decode_ctx *ctx, struct media_file_info *mfi, int decode_video
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
options = NULL;
|
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
|
# ifndef HAVE_FFMPEG
|
||||||
// Without this, libav is slow to probe some internet streams, which leads to RAOP timeouts
|
// Without this, libav is slow to probe some internet streams, which leads to RAOP timeouts
|
||||||
if (mfi->data_kind == DATA_KIND_HTTP)
|
if (mfi->data_kind == DATA_KIND_HTTP)
|
||||||
{
|
ctx->ifmt_ctx->probesize = 64000;
|
||||||
ctx->ifmt_ctx = avformat_alloc_context();
|
|
||||||
ctx->ifmt_ctx->probesize = 64000;
|
|
||||||
}
|
|
||||||
# endif
|
# endif
|
||||||
if (mfi->data_kind == DATA_KIND_HTTP)
|
if (mfi->data_kind == DATA_KIND_HTTP)
|
||||||
av_dict_set(&options, "icy", "1", 0);
|
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);
|
ret = avformat_open_input(&ctx->ifmt_ctx, mfi->path, NULL, &options);
|
||||||
|
|
||||||
if (options)
|
if (options)
|
||||||
av_dict_free(&options);
|
av_dict_free(&options);
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_XCODE, "Cannot open input path\n");
|
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
|
// Some decoders need to be flushed, meaning the decoder is to be called
|
||||||
// with empty input until no more frames are returned
|
// 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;
|
used = 1;
|
||||||
got_frame = flush_decoder(frame, &in_stream, &stream_index, ctx);
|
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);
|
av_free_packet(&decode_ctx->packet);
|
||||||
|
|
||||||
|
decode_ctx->timestamp = av_gettime();
|
||||||
|
|
||||||
ret = av_read_frame(decode_ctx->ifmt_ctx, &decode_ctx->packet);
|
ret = av_read_frame(decode_ctx->ifmt_ctx, &decode_ctx->packet);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user