mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-27 15:45:56 -05:00
[transcode] Implement new ffmpeg decoding methods: avcodec_send_packet/avcodec_receive_frame
This commit is contained in:
parent
5afed60a42
commit
e96b9500db
@ -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);
|
||||
@ -750,7 +750,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)
|
||||
|
@ -300,7 +300,7 @@ streaming_init(void)
|
||||
}
|
||||
|
||||
streaming_encode_ctx = transcode_encode_setup(XCODE_MP3, decode_ctx, NULL);
|
||||
transcode_decode_cleanup(decode_ctx);
|
||||
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");
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
452
src/transcode.c
452
src/transcode.c
@ -117,12 +117,14 @@ struct decode_ctx
|
||||
// Data kind (used to determine if ICY metadata is relevant to look for)
|
||||
enum data_kind data_kind;
|
||||
|
||||
// Contains the most recent packet from av_read_frame
|
||||
// Used for resuming after seek and for freeing correctly
|
||||
// in transcode_decode()
|
||||
AVPacket packet;
|
||||
int resume;
|
||||
int resume_offset;
|
||||
// Set to true if we just seeked
|
||||
bool resume;
|
||||
|
||||
// Contains the most recent packet from av_read_frame()
|
||||
AVPacket *packet;
|
||||
|
||||
// Contains the most recent frame from avcodec_receive_frame()
|
||||
AVFrame *decoded_frame;
|
||||
|
||||
// Used to measure if av_read_frame is taking too long
|
||||
int64_t timestamp;
|
||||
@ -164,6 +166,8 @@ struct transcode_ctx
|
||||
{
|
||||
struct decode_ctx *decode_ctx;
|
||||
struct encode_ctx *encode_ctx;
|
||||
|
||||
bool eof;
|
||||
};
|
||||
|
||||
struct decoded_frame
|
||||
@ -407,56 +411,42 @@ static int decode_interrupt_cb(void *arg)
|
||||
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
|
||||
* from the most recent packet.
|
||||
/* Will read the next packet from the source, unless we are resuming after a
|
||||
* seek in which case the most recent packet found by transcode_seek() will be
|
||||
* returned. The packet will be put in ctx->packet.
|
||||
*
|
||||
* @out packet Pointer to an already allocated AVPacket. The content of the
|
||||
* packet will be updated, and packet->data is pointed to the data
|
||||
* returned by av_read_frame(). The packet struct is owned by the
|
||||
* caller, but *not* packet->data, so don't free the packet with
|
||||
* av_free_packet()/av_packet_unref()
|
||||
* @out type Media type of packet
|
||||
* @in ctx Decode context
|
||||
* @return 0 if OK, < 0 on error or end of file
|
||||
*/
|
||||
static int
|
||||
read_packet(AVPacket *packet, enum AVMediaType *type, struct decode_ctx *ctx)
|
||||
read_packet(enum AVMediaType *type, struct decode_ctx *dec_ctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
// We just seeked, so transcode_seek() will have found a new ctx->packet and
|
||||
// we should just use start with that (if the stream is one are ok with)
|
||||
if (dec_ctx->resume)
|
||||
{
|
||||
dec_ctx->resume = 0;
|
||||
*type = stream_find(dec_ctx, dec_ctx->packet->stream_index);
|
||||
if (*type != AVMEDIA_TYPE_UNKNOWN)
|
||||
return 0;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
if (ctx->resume)
|
||||
dec_ctx->timestamp = av_gettime();
|
||||
|
||||
av_packet_unref(dec_ctx->packet);
|
||||
ret = av_read_frame(dec_ctx->ifmt_ctx, dec_ctx->packet);
|
||||
if (ret < 0)
|
||||
{
|
||||
// Copies packet struct, but not actual packet payload, and adjusts
|
||||
// data pointer to somewhere inside the payload if resume_offset is set
|
||||
*packet = ctx->packet;
|
||||
packet->data += ctx->resume_offset;
|
||||
packet->size -= ctx->resume_offset;
|
||||
ctx->resume = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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_packet_unref(&ctx->packet);
|
||||
|
||||
ctx->resume_offset = 0;
|
||||
ctx->timestamp = av_gettime();
|
||||
|
||||
ret = av_read_frame(ctx->ifmt_ctx, &ctx->packet);
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_WARN, L_XCODE, "Could not read frame: %s\n", err2str(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
*packet = ctx->packet;
|
||||
DPRINTF(E_WARN, L_XCODE, "Could not read frame: %s\n", err2str(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
*type = stream_find(ctx, packet->stream_index);
|
||||
*type = stream_find(dec_ctx, dec_ctx->packet->stream_index);
|
||||
}
|
||||
while (*type == AVMEDIA_TYPE_UNKNOWN);
|
||||
|
||||
@ -484,6 +474,10 @@ packet_prepare(AVPacket *pkt, struct stream_ctx *s)
|
||||
av_packet_rescale_ts(pkt, s->codec->time_base, s->stream->time_base);
|
||||
}
|
||||
|
||||
/*
|
||||
* Part 4 of the conversion chain: read -> decode -> filter -> encode -> write
|
||||
*
|
||||
*/
|
||||
static int
|
||||
encode_write(struct encode_ctx *ctx, struct stream_ctx *s, AVFrame *filt_frame)
|
||||
{
|
||||
@ -515,6 +509,12 @@ encode_write(struct encode_ctx *ctx, struct stream_ctx *s, AVFrame *filt_frame)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Part 3 of the conversion chain: read -> decode -> filter -> encode -> write
|
||||
*
|
||||
* transcode_encode() starts here since the caller already has a frame
|
||||
*
|
||||
*/
|
||||
static int
|
||||
filter_encode_write(struct encode_ctx *ctx, struct stream_ctx *s, AVFrame *frame)
|
||||
{
|
||||
@ -554,34 +554,92 @@ filter_encode_write(struct encode_ctx *ctx, struct stream_ctx *s, AVFrame *frame
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Will step through each stream and feed the stream decoder with empty packets
|
||||
* to see if the decoder has more frames lined up. Will return non-zero if a
|
||||
* frame is found. Should be called until it stops returning anything.
|
||||
/*
|
||||
* Part 2 of the conversion chain: read -> decode -> filter -> encode -> write
|
||||
*
|
||||
* If there is no encode_ctx the chain will aborted here
|
||||
*
|
||||
* @out frame AVFrame if there was anything to flush, otherwise undefined
|
||||
* @out stream Set to the AVStream where a decoder returned a frame
|
||||
* @in ctx Decode context
|
||||
* @return Non-zero (true) if frame found, otherwise 0 (false)
|
||||
*/
|
||||
static int
|
||||
flush_decoder(AVFrame *frame, enum AVMediaType *type, struct decode_ctx *ctx)
|
||||
decode_filter_encode_write(struct transcode_ctx *ctx, struct stream_ctx *s, AVPacket *pkt, enum AVMediaType type)
|
||||
{
|
||||
AVPacket dummypacket = { 0 };
|
||||
int got_frame = 0;
|
||||
struct decode_ctx *dec_ctx = ctx->decode_ctx;
|
||||
struct stream_ctx *out_stream = NULL;
|
||||
int ret;
|
||||
|
||||
if (ctx->audio_stream.codec)
|
||||
ret = avcodec_send_packet(s->codec, pkt);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ctx->encode_ctx)
|
||||
{
|
||||
*type = AVMEDIA_TYPE_AUDIO;
|
||||
avcodec_decode_audio4(ctx->audio_stream.codec, frame, &got_frame, &dummypacket);
|
||||
if (type == AVMEDIA_TYPE_AUDIO)
|
||||
out_stream = &ctx->encode_ctx->audio_stream;
|
||||
else if (type == AVMEDIA_TYPE_VIDEO)
|
||||
out_stream = &ctx->encode_ctx->video_stream;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!got_frame && ctx->video_stream.codec)
|
||||
while (1)
|
||||
{
|
||||
*type = AVMEDIA_TYPE_VIDEO;
|
||||
avcodec_decode_video2(ctx->video_stream.codec, frame, &got_frame, &dummypacket);
|
||||
ret = avcodec_receive_frame(s->codec, dec_ctx->decoded_frame);
|
||||
if (ret < 0)
|
||||
{
|
||||
if (ret == AVERROR(EAGAIN))
|
||||
ret = 0;
|
||||
else if (out_stream)
|
||||
ret = filter_encode_write(ctx->encode_ctx, out_stream, NULL); // Flush
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!out_stream)
|
||||
break;
|
||||
|
||||
ret = filter_encode_write(ctx->encode_ctx, out_stream, dec_ctx->decoded_frame);
|
||||
if (ret < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return got_frame;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Part 1 of the conversion chain: read -> decode -> filter -> encode -> write
|
||||
*
|
||||
* Will read exactly one packet from the input and put it in the chain. You
|
||||
* cannot count on anything coming out of the other end from just one packet,
|
||||
* so you probably should loop when calling this and check the contents of
|
||||
* enc_ctx->obuf.
|
||||
*
|
||||
*/
|
||||
static int
|
||||
read_decode_filter_encode_write(struct transcode_ctx *ctx)
|
||||
{
|
||||
struct decode_ctx *dec_ctx = ctx->decode_ctx;
|
||||
enum AVMediaType type;
|
||||
int ret;
|
||||
|
||||
ret = read_packet(&type, dec_ctx);
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_DBG, L_XCODE, "No more input, flushing codecs\n");
|
||||
|
||||
if (dec_ctx->audio_stream.stream)
|
||||
decode_filter_encode_write(ctx, &dec_ctx->audio_stream, NULL, AVMEDIA_TYPE_AUDIO);
|
||||
if (dec_ctx->video_stream.stream)
|
||||
decode_filter_encode_write(ctx, &dec_ctx->video_stream, NULL, AVMEDIA_TYPE_VIDEO);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (type == AVMEDIA_TYPE_AUDIO)
|
||||
ret = decode_filter_encode_write(ctx, &dec_ctx->audio_stream, dec_ctx->packet, type);
|
||||
else if (type == AVMEDIA_TYPE_VIDEO)
|
||||
ret = decode_filter_encode_write(ctx, &dec_ctx->video_stream, dec_ctx->packet, type);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@ -762,6 +820,11 @@ open_output(struct encode_ctx *ctx, struct decode_ctx *src_ctx)
|
||||
goto out_free_streams;
|
||||
}
|
||||
|
||||
if (ctx->settings.wavheader)
|
||||
{
|
||||
evbuffer_add(ctx->obuf, ctx->header, sizeof(ctx->header));
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_streams:
|
||||
@ -987,19 +1050,22 @@ transcode_decode_setup(enum transcode_profile profile, enum data_kind data_kind,
|
||||
struct decode_ctx *ctx;
|
||||
|
||||
CHECK_NULL(L_XCODE, ctx = calloc(1, sizeof(struct decode_ctx)));
|
||||
|
||||
av_init_packet(&ctx->packet);
|
||||
CHECK_NULL(L_XCODE, ctx->decoded_frame = av_frame_alloc());
|
||||
CHECK_NULL(L_XCODE, ctx->packet = av_packet_alloc());
|
||||
|
||||
ctx->duration = song_length;
|
||||
ctx->data_kind = data_kind;
|
||||
|
||||
if ((init_settings(&ctx->settings, profile) < 0) || (open_input(ctx, path) < 0))
|
||||
{
|
||||
free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
goto fail_free;
|
||||
|
||||
return ctx;
|
||||
|
||||
fail_free:
|
||||
av_packet_free(&ctx->packet);
|
||||
av_frame_free(&ctx->decoded_frame);
|
||||
free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct encode_ctx *
|
||||
@ -1011,26 +1077,30 @@ transcode_encode_setup(enum transcode_profile profile, struct decode_ctx *src_ct
|
||||
CHECK_NULL(L_XCODE, ctx->filt_frame = av_frame_alloc());
|
||||
CHECK_NULL(L_XCODE, ctx->encoded_pkt = av_packet_alloc());
|
||||
|
||||
if ((init_settings(&ctx->settings, profile) < 0) || (open_output(ctx, src_ctx) < 0))
|
||||
{
|
||||
free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (open_filters(ctx, src_ctx) < 0)
|
||||
{
|
||||
close_output(ctx);
|
||||
free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (src_ctx->data_kind == DATA_KIND_HTTP)
|
||||
ctx->icy_interval = METADATA_ICY_INTERVAL * ctx->settings.channels * ctx->settings.byte_depth * ctx->settings.sample_rate;
|
||||
if (init_settings(&ctx->settings, profile) < 0)
|
||||
goto fail_free;
|
||||
|
||||
if (ctx->settings.wavheader)
|
||||
make_wav_header(ctx, src_ctx, est_size);
|
||||
|
||||
if (open_output(ctx, src_ctx) < 0)
|
||||
goto fail_free;
|
||||
|
||||
if (open_filters(ctx, src_ctx) < 0)
|
||||
goto fail_close;
|
||||
|
||||
if (src_ctx->data_kind == DATA_KIND_HTTP)
|
||||
ctx->icy_interval = METADATA_ICY_INTERVAL * ctx->settings.channels * ctx->settings.byte_depth * ctx->settings.sample_rate;
|
||||
|
||||
return ctx;
|
||||
|
||||
fail_close:
|
||||
close_output(ctx);
|
||||
fail_free:
|
||||
av_packet_free(&ctx->encoded_pkt);
|
||||
av_frame_free(&ctx->filt_frame);
|
||||
free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct transcode_ctx *
|
||||
@ -1038,7 +1108,7 @@ transcode_setup(enum transcode_profile profile, enum data_kind data_kind, const
|
||||
{
|
||||
struct transcode_ctx *ctx;
|
||||
|
||||
CHECK_NULL(L_XCODE, ctx = malloc(sizeof(struct transcode_ctx)));
|
||||
CHECK_NULL(L_XCODE, ctx = calloc(1, sizeof(struct transcode_ctx)));
|
||||
|
||||
ctx->decode_ctx = transcode_decode_setup(profile, data_kind, path, song_length);
|
||||
if (!ctx->decode_ctx)
|
||||
@ -1050,7 +1120,7 @@ transcode_setup(enum transcode_profile profile, enum data_kind data_kind, const
|
||||
ctx->encode_ctx = transcode_encode_setup(profile, ctx->decode_ctx, est_size);
|
||||
if (!ctx->encode_ctx)
|
||||
{
|
||||
transcode_decode_cleanup(ctx->decode_ctx);
|
||||
transcode_decode_cleanup(&ctx->decode_ctx);
|
||||
free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
@ -1192,43 +1262,54 @@ transcode_needed(const char *user_agent, const char *client_codecs, char *file_c
|
||||
/* Cleanup */
|
||||
|
||||
void
|
||||
transcode_decode_cleanup(struct decode_ctx *ctx)
|
||||
transcode_decode_cleanup(struct decode_ctx **ctx)
|
||||
{
|
||||
av_packet_unref(&ctx->packet);
|
||||
close_input(ctx);
|
||||
free(ctx);
|
||||
if (!(*ctx))
|
||||
return;
|
||||
|
||||
close_input(*ctx);
|
||||
|
||||
av_packet_free(&(*ctx)->packet);
|
||||
av_frame_free(&(*ctx)->decoded_frame);
|
||||
free(*ctx);
|
||||
*ctx = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
transcode_encode_cleanup(struct encode_ctx *ctx)
|
||||
transcode_encode_cleanup(struct encode_ctx **ctx)
|
||||
{
|
||||
if (!*ctx)
|
||||
return;
|
||||
|
||||
// Flush audio encoder
|
||||
if (ctx->audio_stream.stream)
|
||||
filter_encode_write(ctx, &ctx->audio_stream, NULL);
|
||||
if ((*ctx)->audio_stream.stream)
|
||||
filter_encode_write(*ctx, &(*ctx)->audio_stream, NULL);
|
||||
|
||||
// Flush video encoder
|
||||
if (ctx->video_stream.stream)
|
||||
filter_encode_write(ctx, &ctx->video_stream, NULL);
|
||||
if ((*ctx)->video_stream.stream)
|
||||
filter_encode_write(*ctx, &(*ctx)->video_stream, NULL);
|
||||
|
||||
// Flush muxer
|
||||
av_interleaved_write_frame(ctx->ofmt_ctx, NULL);
|
||||
av_interleaved_write_frame((*ctx)->ofmt_ctx, NULL);
|
||||
|
||||
av_write_trailer(ctx->ofmt_ctx);
|
||||
av_write_trailer((*ctx)->ofmt_ctx);
|
||||
|
||||
close_filters(ctx);
|
||||
close_output(ctx);
|
||||
close_filters(*ctx);
|
||||
close_output(*ctx);
|
||||
|
||||
av_packet_free(&ctx->encoded_pkt);
|
||||
av_frame_free(&ctx->filt_frame);
|
||||
free(ctx);
|
||||
av_packet_free(&(*ctx)->encoded_pkt);
|
||||
av_frame_free(&(*ctx)->filt_frame);
|
||||
free(*ctx);
|
||||
*ctx = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
transcode_cleanup(struct transcode_ctx *ctx)
|
||||
transcode_cleanup(struct transcode_ctx **ctx)
|
||||
{
|
||||
transcode_encode_cleanup(ctx->encode_ctx);
|
||||
transcode_decode_cleanup(ctx->decode_ctx);
|
||||
free(ctx);
|
||||
transcode_encode_cleanup(&(*ctx)->encode_ctx);
|
||||
transcode_decode_cleanup(&(*ctx)->decode_ctx);
|
||||
free(*ctx);
|
||||
*ctx = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
@ -1241,103 +1322,11 @@ transcode_decoded_free(struct decoded_frame *decoded)
|
||||
|
||||
/* Encoding, decoding and transcoding */
|
||||
|
||||
|
||||
int
|
||||
transcode_decode(struct decoded_frame **decoded, struct decode_ctx *ctx)
|
||||
transcode_decode(struct decoded_frame **decoded, struct decode_ctx *dec_ctx)
|
||||
{
|
||||
AVPacket packet;
|
||||
AVFrame *frame;
|
||||
enum AVMediaType type;
|
||||
int got_frame;
|
||||
int retry;
|
||||
int ret;
|
||||
int used;
|
||||
|
||||
// Alloc the frame we will return on success
|
||||
frame = av_frame_alloc();
|
||||
if (!frame)
|
||||
{
|
||||
DPRINTF(E_LOG, L_XCODE, "Out of memory for decode frame\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Loop until we either fail or get a frame
|
||||
retry = 0;
|
||||
do
|
||||
{
|
||||
ret = read_packet(&packet, &type, ctx);
|
||||
if (ret < 0)
|
||||
{
|
||||
// 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, will flush decoders\n");
|
||||
|
||||
got_frame = flush_decoder(frame, &type, ctx);
|
||||
if (got_frame)
|
||||
break;
|
||||
|
||||
av_frame_free(&frame);
|
||||
if (ret == AVERROR_EOF)
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
// "used" will tell us how much of the packet was decoded. We may
|
||||
// not get a frame because of insufficient input, in which case we loop to
|
||||
// read another packet.
|
||||
if (type == AVMEDIA_TYPE_AUDIO)
|
||||
used = avcodec_decode_audio4(ctx->audio_stream.codec, frame, &got_frame, &packet);
|
||||
else
|
||||
used = avcodec_decode_video2(ctx->video_stream.codec, frame, &got_frame, &packet);
|
||||
|
||||
// decoder returned an error, but maybe the packet was just a bad apple,
|
||||
// so let's try MAX_BAD_PACKETS times before giving up
|
||||
if (used < 0)
|
||||
{
|
||||
DPRINTF(E_DBG, L_XCODE, "Couldn't decode packet: %s\n", err2str(used));
|
||||
|
||||
retry += 1;
|
||||
if (retry < MAX_BAD_PACKETS)
|
||||
continue;
|
||||
|
||||
DPRINTF(E_LOG, L_XCODE, "Couldn't decode packet after %i retries: %s\n", MAX_BAD_PACKETS, err2str(used));
|
||||
|
||||
av_frame_free(&frame);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// decoder didn't process the entire packet, so flag a resume, meaning
|
||||
// that the next read_packet() will return this same packet, but where the
|
||||
// data pointer is adjusted with an offset
|
||||
if (used < packet.size)
|
||||
{
|
||||
DPRINTF(E_SPAM, L_XCODE, "Decoder did not finish packet, packet will be resumed\n");
|
||||
|
||||
ctx->resume_offset += used;
|
||||
ctx->resume = 1;
|
||||
}
|
||||
}
|
||||
while (!got_frame);
|
||||
|
||||
if (got_frame > 0)
|
||||
{
|
||||
// Return the decoded frame and stream index
|
||||
*decoded = malloc(sizeof(struct decoded_frame));
|
||||
if (!(*decoded))
|
||||
{
|
||||
DPRINTF(E_LOG, L_XCODE, "Out of memory for decoded result\n");
|
||||
|
||||
av_frame_free(&frame);
|
||||
return -1;
|
||||
}
|
||||
|
||||
(*decoded)->frame = frame;
|
||||
(*decoded)->type = type;
|
||||
}
|
||||
|
||||
return got_frame;
|
||||
DPRINTF(E_LOG, L_XCODE, "Bug! Call to transcode_decode(), but the lazy programmer didn't implement it\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Filters and encodes
|
||||
@ -1345,10 +1334,10 @@ int
|
||||
transcode_encode(struct evbuffer *evbuf, struct decoded_frame *decoded, struct encode_ctx *ctx)
|
||||
{
|
||||
struct stream_ctx *s;
|
||||
int encoded_length;
|
||||
size_t start_length;
|
||||
int ret;
|
||||
|
||||
encoded_length = 0;
|
||||
start_length = evbuffer_get_length(ctx->obuf);
|
||||
|
||||
if (decoded->type == AVMEDIA_TYPE_AUDIO)
|
||||
s = &ctx->audio_stream;
|
||||
@ -1357,54 +1346,55 @@ transcode_encode(struct evbuffer *evbuf, struct decoded_frame *decoded, struct e
|
||||
else
|
||||
return -1;
|
||||
|
||||
if (ctx->settings.wavheader)
|
||||
{
|
||||
encoded_length += sizeof(ctx->header);
|
||||
evbuffer_add(evbuf, ctx->header, sizeof(ctx->header));
|
||||
ctx->settings.wavheader = 0;
|
||||
}
|
||||
|
||||
ret = filter_encode_write(ctx, s, decoded->frame);
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_XCODE, "Error occurred: %s\n", err2str(ret));
|
||||
DPRINTF(E_LOG, L_XCODE, "Error occurred while encoding: %s\n", err2str(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
encoded_length += evbuffer_get_length(ctx->obuf);
|
||||
ret = evbuffer_get_length(ctx->obuf) - start_length;
|
||||
|
||||
evbuffer_add_buffer(evbuf, ctx->obuf);
|
||||
|
||||
return encoded_length;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
transcode(struct evbuffer *evbuf, int wanted, struct transcode_ctx *ctx, int *icy_timer)
|
||||
transcode(struct evbuffer *evbuf, int want_bytes, struct transcode_ctx *ctx, int *icy_timer)
|
||||
{
|
||||
struct decoded_frame *decoded;
|
||||
int processed;
|
||||
size_t start_length;
|
||||
int processed = 0;
|
||||
int ret;
|
||||
|
||||
*icy_timer = 0;
|
||||
|
||||
processed = 0;
|
||||
while (processed < wanted)
|
||||
if (ctx->eof)
|
||||
return 0;
|
||||
|
||||
start_length = evbuffer_get_length(ctx->encode_ctx->obuf);
|
||||
|
||||
do
|
||||
{
|
||||
ret = transcode_decode(&decoded, ctx->decode_ctx);
|
||||
if (ret <= 0)
|
||||
return ret;
|
||||
|
||||
ret = transcode_encode(evbuf, decoded, ctx->encode_ctx);
|
||||
transcode_decoded_free(decoded);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
processed += ret;
|
||||
ret = read_decode_filter_encode_write(ctx);
|
||||
processed = evbuffer_get_length(ctx->encode_ctx->obuf) - start_length;
|
||||
}
|
||||
while ((ret == 0) && (!want_bytes || (processed < want_bytes)));
|
||||
|
||||
evbuffer_add_buffer(evbuf, ctx->encode_ctx->obuf);
|
||||
|
||||
ctx->encode_ctx->total_bytes += processed;
|
||||
if (ctx->encode_ctx->icy_interval)
|
||||
*icy_timer = (ctx->encode_ctx->total_bytes % ctx->encode_ctx->icy_interval < processed);
|
||||
|
||||
if (ret == AVERROR_EOF)
|
||||
{
|
||||
ctx->eof = 1;
|
||||
ret = 0;
|
||||
}
|
||||
else if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return processed;
|
||||
}
|
||||
|
||||
@ -1496,11 +1486,10 @@ transcode_seek(struct transcode_ctx *ctx, int ms)
|
||||
s->codec->skip_frame = AVDISCARD_NONREF;
|
||||
while (1)
|
||||
{
|
||||
av_packet_unref(&dec_ctx->packet);
|
||||
|
||||
dec_ctx->timestamp = av_gettime();
|
||||
|
||||
ret = av_read_frame(dec_ctx->ifmt_ctx, &dec_ctx->packet);
|
||||
av_packet_unref(dec_ctx->packet);
|
||||
ret = av_read_frame(dec_ctx->ifmt_ctx, dec_ctx->packet);
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_WARN, L_XCODE, "Could not read more data while seeking: %s\n", err2str(ret));
|
||||
@ -1508,23 +1497,22 @@ transcode_seek(struct transcode_ctx *ctx, int ms)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (stream_find(dec_ctx, dec_ctx->packet.stream_index) == AVMEDIA_TYPE_UNKNOWN)
|
||||
if (stream_find(dec_ctx, dec_ctx->packet->stream_index) == AVMEDIA_TYPE_UNKNOWN)
|
||||
continue;
|
||||
|
||||
// Need a pts to return the real position
|
||||
if (dec_ctx->packet.pts == AV_NOPTS_VALUE)
|
||||
if (dec_ctx->packet->pts == AV_NOPTS_VALUE)
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
s->codec->skip_frame = AVDISCARD_DEFAULT;
|
||||
|
||||
// Tell transcode_decode() to resume with ctx->packet
|
||||
// Tell read_packet() to resume with dec_ctx->packet
|
||||
dec_ctx->resume = 1;
|
||||
dec_ctx->resume_offset = 0;
|
||||
|
||||
// Compute position in ms from pts
|
||||
got_pts = dec_ctx->packet.pts;
|
||||
got_pts = dec_ctx->packet->pts;
|
||||
|
||||
if ((start_time != AV_NOPTS_VALUE) && (start_time > 0))
|
||||
got_pts -= start_time;
|
||||
|
@ -42,13 +42,13 @@ 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);
|
||||
transcode_cleanup(struct transcode_ctx **ctx);
|
||||
|
||||
void
|
||||
transcode_decoded_free(struct decoded_frame *decoded);
|
||||
@ -57,35 +57,36 @@ transcode_decoded_free(struct decoded_frame *decoded);
|
||||
|
||||
/* 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 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
|
||||
*/
|
||||
int
|
||||
transcode_decode(struct decoded_frame **decoded, 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 frame The frame to encode, e.g. from transcode_decode
|
||||
* @in ctx Encode context
|
||||
* @return Bytes added if OK, negative if error
|
||||
*/
|
||||
int
|
||||
transcode_encode(struct evbuffer *evbuf, struct decoded_frame *decoded, struct encode_ctx *ctx);
|
||||
|
||||
/* 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
|
||||
* @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
|
||||
* @in ctx Transcode context
|
||||
* @out icy_timer True if METADATA_ICY_INTERVAL has elapsed
|
||||
* @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 want_bytes, struct transcode_ctx *ctx, int *icy_timer);
|
||||
|
||||
struct decoded_frame *
|
||||
transcode_raw2frame(uint8_t *data, size_t size);
|
||||
|
Loading…
Reference in New Issue
Block a user