mirror of
https://github.com/owntone/owntone-server.git
synced 2025-04-25 04:43:09 -04:00
[xcode] Add support for seekable custom I/O (evbuffer input to transcode)
This commit is contained in:
parent
57449a126a
commit
2bbc5f16c5
@ -605,6 +605,7 @@ artwork_get(struct evbuffer *evbuf, char *path, struct evbuffer *in_buf, bool is
|
|||||||
{
|
{
|
||||||
struct decode_ctx *xcode_decode = NULL;
|
struct decode_ctx *xcode_decode = NULL;
|
||||||
struct encode_ctx *xcode_encode = NULL;
|
struct encode_ctx *xcode_encode = NULL;
|
||||||
|
struct transcode_evbuf_io xcode_evbuf_io = { 0 };
|
||||||
struct evbuffer *xcode_buf = NULL;
|
struct evbuffer *xcode_buf = NULL;
|
||||||
void *frame;
|
void *frame;
|
||||||
int src_width;
|
int src_width;
|
||||||
@ -633,9 +634,15 @@ artwork_get(struct evbuffer *evbuf, char *path, struct evbuffer *in_buf, bool is
|
|||||||
ret = ART_E_ERROR;
|
ret = ART_E_ERROR;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
xcode_evbuf_io.evbuf = xcode_buf;
|
||||||
|
xcode_decode = transcode_decode_setup(XCODE_JPEG, NULL, data_kind, NULL, &xcode_evbuf_io, 0); // Covers XCODE_PNG too
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
xcode_decode = transcode_decode_setup(XCODE_JPEG, NULL, data_kind, path, NULL, 0); // Covers XCODE_PNG too
|
||||||
}
|
}
|
||||||
|
|
||||||
xcode_decode = transcode_decode_setup(XCODE_JPEG, NULL, data_kind, path, xcode_buf, 0); // Covers XCODE_PNG too
|
|
||||||
if (!xcode_decode)
|
if (!xcode_decode)
|
||||||
{
|
{
|
||||||
if (path)
|
if (path)
|
||||||
|
@ -46,8 +46,10 @@ struct playback_ctx
|
|||||||
{
|
{
|
||||||
struct transcode_ctx *xcode;
|
struct transcode_ctx *xcode;
|
||||||
|
|
||||||
int read_fd;
|
// This buffer gets fairly large, since it reads and holds the Ogg track that
|
||||||
|
// spotifyc downloads. It has no write limit, unlike the input buffer.
|
||||||
struct evbuffer *read_buf;
|
struct evbuffer *read_buf;
|
||||||
|
int read_fd;
|
||||||
size_t read_bytes;
|
size_t read_bytes;
|
||||||
|
|
||||||
uint32_t len_ms;
|
uint32_t len_ms;
|
||||||
@ -286,10 +288,13 @@ static int
|
|||||||
playback_xcode_setup(struct playback_ctx *playback)
|
playback_xcode_setup(struct playback_ctx *playback)
|
||||||
{
|
{
|
||||||
struct transcode_ctx *xcode;
|
struct transcode_ctx *xcode;
|
||||||
|
struct transcode_evbuf_io xcode_evbuf_io = { 0 };
|
||||||
|
|
||||||
CHECK_NULL(L_SPOTIFY, xcode = malloc(sizeof(struct transcode_ctx)));
|
CHECK_NULL(L_SPOTIFY, xcode = malloc(sizeof(struct transcode_ctx)));
|
||||||
|
|
||||||
xcode->decode_ctx = transcode_decode_setup(XCODE_OGG, NULL, DATA_KIND_SPOTIFY, NULL, playback->read_buf, playback->len_ms);
|
xcode_evbuf_io.evbuf = playback->read_buf;
|
||||||
|
|
||||||
|
xcode->decode_ctx = transcode_decode_setup(XCODE_OGG, NULL, DATA_KIND_SPOTIFY, NULL, &xcode_evbuf_io, playback->len_ms);
|
||||||
if (!xcode->decode_ctx)
|
if (!xcode->decode_ctx)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
@ -188,6 +188,8 @@ enum probe_type
|
|||||||
struct avio_evbuffer {
|
struct avio_evbuffer {
|
||||||
struct evbuffer *evbuf;
|
struct evbuffer *evbuf;
|
||||||
uint8_t *buffer;
|
uint8_t *buffer;
|
||||||
|
transcode_seekfn seekfn;
|
||||||
|
void *seekfn_arg;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -809,8 +811,27 @@ avio_evbuffer_write(void *opaque, uint8_t *buf, int size)
|
|||||||
return (ret == 0) ? size : -1;
|
return (ret == 0) ? size : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int64_t
|
||||||
|
avio_evbuffer_seek(void *opaque, int64_t offset, int whence)
|
||||||
|
{
|
||||||
|
struct avio_evbuffer *ae = (struct avio_evbuffer *)opaque;
|
||||||
|
enum transcode_seek_type seek_type;
|
||||||
|
|
||||||
|
// Caller shouldn't need to know about ffmpeg defines
|
||||||
|
if (whence & AVSEEK_SIZE)
|
||||||
|
seek_type = XCODE_SEEK_SIZE;
|
||||||
|
else if (whence == SEEK_SET)
|
||||||
|
seek_type = XCODE_SEEK_SET;
|
||||||
|
else if (whence == SEEK_CUR)
|
||||||
|
seek_type = XCODE_SEEK_CUR;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return ae->seekfn(ae->seekfn_arg, offset, seek_type);
|
||||||
|
}
|
||||||
|
|
||||||
static AVIOContext *
|
static AVIOContext *
|
||||||
avio_evbuffer_open(struct evbuffer *evbuf, int is_output)
|
avio_evbuffer_open(struct transcode_evbuf_io *evbuf_io, int is_output)
|
||||||
{
|
{
|
||||||
struct avio_evbuffer *ae;
|
struct avio_evbuffer *ae;
|
||||||
AVIOContext *s;
|
AVIOContext *s;
|
||||||
@ -832,12 +853,14 @@ avio_evbuffer_open(struct evbuffer *evbuf, int is_output)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ae->evbuf = evbuf;
|
ae->evbuf = evbuf_io->evbuf;
|
||||||
|
ae->seekfn = evbuf_io->seekfn;
|
||||||
|
ae->seekfn_arg = evbuf_io->seekfn_arg;
|
||||||
|
|
||||||
if (is_output)
|
if (is_output)
|
||||||
s = avio_alloc_context(ae->buffer, AVIO_BUFFER_SIZE, 1, ae, NULL, avio_evbuffer_write, NULL);
|
s = avio_alloc_context(ae->buffer, AVIO_BUFFER_SIZE, 1, ae, NULL, avio_evbuffer_write, NULL);
|
||||||
else
|
else
|
||||||
s = avio_alloc_context(ae->buffer, AVIO_BUFFER_SIZE, 0, ae, avio_evbuffer_read, NULL, NULL);
|
s = avio_alloc_context(ae->buffer, AVIO_BUFFER_SIZE, 0, ae, avio_evbuffer_read, NULL, (evbuf_io->seekfn ? avio_evbuffer_seek : NULL));
|
||||||
|
|
||||||
if (!s)
|
if (!s)
|
||||||
{
|
{
|
||||||
@ -848,21 +871,30 @@ avio_evbuffer_open(struct evbuffer *evbuf, int is_output)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 0 here does not seem to mean not seekable, because ffmpeg will still call
|
||||||
|
// avio_evbuffer_seek. If set to AVIO_SEEKABLE_NORMAL then ffmpeg seems to
|
||||||
|
// make random access seek requests during input_open (i.e. asking for start
|
||||||
|
// and end of file), which are hard to fulfill when the source is something
|
||||||
|
// that is downloaded.
|
||||||
s->seekable = 0;
|
s->seekable = 0;
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
static AVIOContext *
|
static AVIOContext *
|
||||||
avio_input_evbuffer_open(struct evbuffer *evbuf)
|
avio_input_evbuffer_open(struct transcode_evbuf_io *evbuf_io)
|
||||||
{
|
{
|
||||||
return avio_evbuffer_open(evbuf, 0);
|
return avio_evbuffer_open(evbuf_io, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static AVIOContext *
|
static AVIOContext *
|
||||||
avio_output_evbuffer_open(struct evbuffer *evbuf)
|
avio_output_evbuffer_open(struct evbuffer *evbuf)
|
||||||
{
|
{
|
||||||
return avio_evbuffer_open(evbuf, 1);
|
struct transcode_evbuf_io evbuf_io = { 0 };
|
||||||
|
|
||||||
|
evbuf_io.evbuf = evbuf;
|
||||||
|
|
||||||
|
return avio_evbuffer_open(&evbuf_io, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -933,7 +965,7 @@ open_decoder(AVCodecContext **dec_ctx, unsigned int *stream_index, struct decode
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
open_input(struct decode_ctx *ctx, const char *path, struct evbuffer *evbuf, enum probe_type probe_type)
|
open_input(struct decode_ctx *ctx, const char *path, struct transcode_evbuf_io *evbuf_io, enum probe_type probe_type)
|
||||||
{
|
{
|
||||||
AVDictionary *options = NULL;
|
AVDictionary *options = NULL;
|
||||||
AVCodecContext *dec_ctx;
|
AVCodecContext *dec_ctx;
|
||||||
@ -973,7 +1005,7 @@ open_input(struct decode_ctx *ctx, const char *path, struct evbuffer *evbuf, enu
|
|||||||
ctx->ifmt_ctx->interrupt_callback.opaque = ctx;
|
ctx->ifmt_ctx->interrupt_callback.opaque = ctx;
|
||||||
ctx->timestamp = av_gettime();
|
ctx->timestamp = av_gettime();
|
||||||
|
|
||||||
if (evbuf)
|
if (evbuf_io)
|
||||||
{
|
{
|
||||||
ifmt = av_find_input_format(ctx->settings.in_format);
|
ifmt = av_find_input_format(ctx->settings.in_format);
|
||||||
if (!ifmt)
|
if (!ifmt)
|
||||||
@ -982,7 +1014,7 @@ open_input(struct decode_ctx *ctx, const char *path, struct evbuffer *evbuf, enu
|
|||||||
goto out_fail;
|
goto out_fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
CHECK_NULL(L_XCODE, ctx->avio = avio_input_evbuffer_open(evbuf));
|
CHECK_NULL(L_XCODE, ctx->avio = avio_input_evbuffer_open(evbuf_io));
|
||||||
|
|
||||||
ctx->ifmt_ctx->pb = ctx->avio;
|
ctx->ifmt_ctx->pb = ctx->avio;
|
||||||
ret = avformat_open_input(&ctx->ifmt_ctx, NULL, ifmt, &options);
|
ret = avformat_open_input(&ctx->ifmt_ctx, NULL, ifmt, &options);
|
||||||
@ -1344,7 +1376,7 @@ close_filters(struct encode_ctx *ctx)
|
|||||||
/* Setup */
|
/* Setup */
|
||||||
|
|
||||||
struct decode_ctx *
|
struct decode_ctx *
|
||||||
transcode_decode_setup(enum transcode_profile profile, struct media_quality *quality, enum data_kind data_kind, const char *path, struct evbuffer *evbuf, uint32_t song_length)
|
transcode_decode_setup(enum transcode_profile profile, struct media_quality *quality, enum data_kind data_kind, const char *path, struct transcode_evbuf_io *evbuf_io, uint32_t song_length)
|
||||||
{
|
{
|
||||||
struct decode_ctx *ctx;
|
struct decode_ctx *ctx;
|
||||||
int ret;
|
int ret;
|
||||||
@ -1362,14 +1394,14 @@ transcode_decode_setup(enum transcode_profile profile, struct media_quality *qua
|
|||||||
|
|
||||||
if (data_kind == DATA_KIND_HTTP)
|
if (data_kind == DATA_KIND_HTTP)
|
||||||
{
|
{
|
||||||
ret = open_input(ctx, path, evbuf, PROBE_TYPE_QUICK);
|
ret = open_input(ctx, path, evbuf_io, PROBE_TYPE_QUICK);
|
||||||
|
|
||||||
// Retry with a default, slower probe size
|
// Retry with a default, slower probe size
|
||||||
if (ret == AVERROR_STREAM_NOT_FOUND)
|
if (ret == AVERROR_STREAM_NOT_FOUND)
|
||||||
ret = open_input(ctx, path, evbuf, PROBE_TYPE_DEFAULT);
|
ret = open_input(ctx, path, evbuf_io, PROBE_TYPE_DEFAULT);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
ret = open_input(ctx, path, evbuf, PROBE_TYPE_DEFAULT);
|
ret = open_input(ctx, path, evbuf_io, PROBE_TYPE_DEFAULT);
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto fail_free;
|
goto fail_free;
|
||||||
|
@ -33,6 +33,16 @@ enum transcode_profile
|
|||||||
XCODE_VP8,
|
XCODE_VP8,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum transcode_seek_type
|
||||||
|
{
|
||||||
|
XCODE_SEEK_SIZE,
|
||||||
|
XCODE_SEEK_SET,
|
||||||
|
XCODE_SEEK_CUR,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void transcode_frame;
|
||||||
|
typedef int64_t(*transcode_seekfn)(void *arg, int64_t offset, enum transcode_seek_type seek_type);
|
||||||
|
|
||||||
struct decode_ctx;
|
struct decode_ctx;
|
||||||
struct encode_ctx;
|
struct encode_ctx;
|
||||||
struct transcode_ctx
|
struct transcode_ctx
|
||||||
@ -41,11 +51,18 @@ struct transcode_ctx
|
|||||||
struct encode_ctx *encode_ctx;
|
struct encode_ctx *encode_ctx;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef void transcode_frame;
|
struct transcode_evbuf_io
|
||||||
|
{
|
||||||
|
struct evbuffer *evbuf;
|
||||||
|
|
||||||
|
// Set to null if no seek support required
|
||||||
|
transcode_seekfn seekfn;
|
||||||
|
void *seekfn_arg;
|
||||||
|
};
|
||||||
|
|
||||||
// Setting up
|
// Setting up
|
||||||
struct decode_ctx *
|
struct decode_ctx *
|
||||||
transcode_decode_setup(enum transcode_profile profile, struct media_quality *quality, enum data_kind data_kind, const char *path, struct evbuffer *evbuf, uint32_t song_length);
|
transcode_decode_setup(enum transcode_profile profile, struct media_quality *quality, enum data_kind data_kind, const char *path, struct transcode_evbuf_io *evbuf_io, uint32_t song_length);
|
||||||
|
|
||||||
struct encode_ctx *
|
struct encode_ctx *
|
||||||
transcode_encode_setup(enum transcode_profile profile, struct media_quality *quality, struct decode_ctx *src_ctx, off_t *est_size, int width, int height);
|
transcode_encode_setup(enum transcode_profile profile, struct media_quality *quality, struct decode_ctx *src_ctx, off_t *est_size, int width, int height);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user