[xcode] Add support for seekable custom I/O (evbuffer input to transcode)

This commit is contained in:
ejurgensen 2021-04-06 16:16:32 +02:00
parent 57449a126a
commit 2bbc5f16c5
4 changed files with 79 additions and 18 deletions

View File

@ -605,6 +605,7 @@ artwork_get(struct evbuffer *evbuf, char *path, struct evbuffer *in_buf, bool is
{
struct decode_ctx *xcode_decode = NULL;
struct encode_ctx *xcode_encode = NULL;
struct transcode_evbuf_io xcode_evbuf_io = { 0 };
struct evbuffer *xcode_buf = NULL;
void *frame;
int src_width;
@ -633,9 +634,15 @@ artwork_get(struct evbuffer *evbuf, char *path, struct evbuffer *in_buf, bool is
ret = ART_E_ERROR;
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 (path)

View File

@ -46,8 +46,10 @@ struct playback_ctx
{
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;
int read_fd;
size_t read_bytes;
uint32_t len_ms;
@ -286,10 +288,13 @@ static int
playback_xcode_setup(struct playback_ctx *playback)
{
struct transcode_ctx *xcode;
struct transcode_evbuf_io xcode_evbuf_io = { 0 };
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)
goto error;

View File

@ -188,6 +188,8 @@ enum probe_type
struct avio_evbuffer {
struct evbuffer *evbuf;
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;
}
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 *
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;
AVIOContext *s;
@ -832,12 +853,14 @@ avio_evbuffer_open(struct evbuffer *evbuf, int is_output)
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)
s = avio_alloc_context(ae->buffer, AVIO_BUFFER_SIZE, 1, ae, NULL, avio_evbuffer_write, NULL);
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)
{
@ -848,21 +871,30 @@ avio_evbuffer_open(struct evbuffer *evbuf, int is_output)
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;
return s;
}
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 *
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
@ -933,7 +965,7 @@ open_decoder(AVCodecContext **dec_ctx, unsigned int *stream_index, struct decode
}
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;
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->timestamp = av_gettime();
if (evbuf)
if (evbuf_io)
{
ifmt = av_find_input_format(ctx->settings.in_format);
if (!ifmt)
@ -982,7 +1014,7 @@ open_input(struct decode_ctx *ctx, const char *path, struct evbuffer *evbuf, enu
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;
ret = avformat_open_input(&ctx->ifmt_ctx, NULL, ifmt, &options);
@ -1344,7 +1376,7 @@ close_filters(struct encode_ctx *ctx)
/* Setup */
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;
int ret;
@ -1362,14 +1394,14 @@ transcode_decode_setup(enum transcode_profile profile, struct media_quality *qua
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
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
ret = open_input(ctx, path, evbuf, PROBE_TYPE_DEFAULT);
ret = open_input(ctx, path, evbuf_io, PROBE_TYPE_DEFAULT);
if (ret < 0)
goto fail_free;

View File

@ -33,6 +33,16 @@ enum transcode_profile
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 encode_ctx;
struct transcode_ctx
@ -41,11 +51,18 @@ struct transcode_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
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 *
transcode_encode_setup(enum transcode_profile profile, struct media_quality *quality, struct decode_ctx *src_ctx, off_t *est_size, int width, int height);