diff --git a/libavcodec/evc_parser.c b/libavcodec/evc_parser.c index f08006d005..31d7756849 100644 --- a/libavcodec/evc_parser.c +++ b/libavcodec/evc_parser.c @@ -190,7 +190,10 @@ static int parse_nal_unit(AVCodecParserContext *s, AVCodecContext *avctx, break; } - s->key_frame = (nalu_type == EVC_IDR_NUT) ? 1 : 0; + // An IDR picture or a CRA picture (a non-IDR picture with an I slice) + // is a random access point + s->key_frame = (nalu_type == EVC_IDR_NUT || + (nalu_type == EVC_NOIDR_NUT && sh.slice_type == EVC_SLICE_TYPE_I)) ? 1 : 0; // POC (picture order count of the current picture) derivation // @see ISO/IEC 23094-1:2020(E) 8.3.1 Decoding process for picture order count diff --git a/libavcodec/libxevd.c b/libavcodec/libxevd.c index c8321e705d..a41425e9ab 100644 --- a/libavcodec/libxevd.c +++ b/libavcodec/libxevd.c @@ -576,7 +576,10 @@ static int libxevd_receive_frame(AVCodecContext *avctx, AVFrame *frame) } } - return ret; + // The access unit was consumed without producing a frame yet (e.g. all + // of its NAL units were skipped leading pictures after CRA random + // access) - ask for more input instead of returning an empty frame. + return AVERROR(EAGAIN); } /** @@ -585,6 +588,23 @@ static int libxevd_receive_frame(AVCodecContext *avctx, AVFrame *frame) * @param avctx codec context * @return 0 on success */ +static void libxevd_flush(AVCodecContext *avctx) +{ + XevdContext *xectx = avctx->priv_data; + + /* Recreate the decoder to drop all decoder state (reference pictures, + * parsed parameter sets). Raw EVC streams carry SPS/PPS only once before + * the first picture, so after a seek the demuxer re-injects them. */ + if (xectx->id) { + xevd_delete(xectx->id); + xectx->id = xevd_create(&(xectx->cdsc), NULL); + if (xectx->id == NULL) + av_log(avctx, AV_LOG_ERROR, "Cannot re-create XEVD decoder\n"); + } + + xectx->draining_mode = 0; +} + static av_cold int libxevd_close(AVCodecContext *avctx) { XevdContext *xectx = avctx->priv_data; @@ -606,6 +626,7 @@ const FFCodec ff_libxevd_decoder = { .p.id = AV_CODEC_ID_EVC, .init = libxevd_init, FF_CODEC_RECEIVE_FRAME_CB(libxevd_receive_frame), + .flush = libxevd_flush, .close = libxevd_close, .priv_data_size = sizeof(XevdContext), .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | diff --git a/libavformat/evcdec.c b/libavformat/evcdec.c index 6a5d78f141..5a07a7b6d2 100644 --- a/libavformat/evcdec.c +++ b/libavformat/evcdec.c @@ -23,6 +23,7 @@ #include "libavcodec/evc.h" #include "libavcodec/bsf.h" +#include "libavutil/mem.h" #include "libavutil/opt.h" #include "avformat.h" @@ -41,6 +42,14 @@ typedef struct EVCDemuxContext { AVBSFContext *bsf; int64_t au_count; + // Cached parameter set NAL units (with length prefixes, as found in the + // stream). They are re-injected after a seek because a raw EVC stream + // carries SPS/PPS only once, before the first picture. + uint8_t *sps_buf; + size_t sps_size; + uint8_t *pps_buf; + size_t pps_size; + } EVCDemuxContext; #define DEC AV_OPT_FLAG_DECODING_PARAM @@ -150,6 +159,57 @@ static int evc_read_packet(AVFormatContext *s, AVPacket *pkt) int au_end_found = 0; EVCDemuxContext *const c = s->priv_data; + if (s->io_repositioned) { + AVStream *st = s->streams[0]; + + s->io_repositioned = 0; + av_bsf_flush(c->bsf); + // The stream carries no timing information, so access units are + // stamped by counting them. After a seek, restart the count at the + // seek point (cur_dts, set by the generic seek code) to keep packets + // consistent with the index. A pending linear scan starts reading at + // the beginning of the stream, so restart the count from zero then. + if (avio_tell(s->pb) > ffformatcontext(s)->data_offset && + ffstream(st)->cur_dts != AV_NOPTS_VALUE) + c->au_count = av_rescale_q(ffstream(st)->cur_dts, st->time_base, c->framerate); + else + c->au_count = 0; + if (c->sps_size || c->pps_size) { + // Re-send the cached parameter sets so that the parser and the + // decoder can resume at the seek point. + size_t size = c->sps_size + c->pps_size; + if (size) { + AVPacket *ps_pkt = av_packet_alloc(); + if (!ps_pkt) + return AVERROR(ENOMEM); + ret = av_new_packet(ps_pkt, size); + if (ret < 0) { + av_packet_free(&ps_pkt); + return ret; + } + // Carry the current position so the access unit eventually + // assembled from this data is indexable/seekable. + ps_pkt->pos = avio_tell(s->pb); + if (c->sps_size) + memcpy(ps_pkt->data, c->sps_buf, c->sps_size); + if (c->pps_size) + memcpy(ps_pkt->data + c->sps_size, c->pps_buf, c->pps_size); + ret = av_bsf_send_packet(c->bsf, ps_pkt); + av_packet_free(&ps_pkt); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Failed to re-send parameter sets to " + "evc_frame_merge filter\n"); + return ret; + } + // Parameter sets alone do not complete an access unit; drain + // the filter so the next send does not hit a pending packet. + ret = av_bsf_receive_packet(c->bsf, pkt); + if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) + return ret; + } + } + } + while(!au_end_found) { uint8_t buf[EVC_NALU_LENGTH_PREFIX_SIZE]; @@ -178,6 +238,31 @@ static int evc_read_packet(AVFormatContext *s, AVPacket *pkt) if (ret != (nalu_size + EVC_NALU_LENGTH_PREFIX_SIZE)) return AVERROR_INVALIDDATA; + // Keep a copy of parameter set NAL units for post-seek re-injection + { + int nalu_type = evc_get_nalu_type(pkt->data + EVC_NALU_LENGTH_PREFIX_SIZE, + nalu_size); + uint8_t **buf = NULL; + size_t *size = NULL; + void *tmp; + + if (nalu_type == EVC_SPS_NUT) { + buf = &c->sps_buf; + size = &c->sps_size; + } else if (nalu_type == EVC_PPS_NUT) { + buf = &c->pps_buf; + size = &c->pps_size; + } + if (buf) { + tmp = av_realloc(*buf, nalu_size + EVC_NALU_LENGTH_PREFIX_SIZE); + if (!tmp) + return AVERROR(ENOMEM); + *buf = tmp; + memcpy(*buf, pkt->data, nalu_size + EVC_NALU_LENGTH_PREFIX_SIZE); + *size = nalu_size + EVC_NALU_LENGTH_PREFIX_SIZE; + } + } + end: ret = av_bsf_send_packet(c->bsf, pkt); if (ret < 0) { @@ -196,14 +281,19 @@ static int evc_read_packet(AVFormatContext *s, AVPacket *pkt) } if (ret >= 0) { - // raw input carries no timing; stamp access units at the configured - // frame rate (decode order, so presentation reordering is left to the - // decoder) + // raw input carries no timing; stamp access units in decode order at + // the configured frame rate. Shift pts by the reorder delay of B + // pictures so that pts != dts; a packet with pts == dts would make + // the core discard the dts of reordered streams, breaking indexing + // and seeking. AVStream *st = s->streams[0]; AVRational dur = av_inv_q(c->framerate); - pkt->pts = pkt->dts = av_rescale_q(c->au_count++, dur, st->time_base); + pkt->dts = av_rescale_q(c->au_count, dur, st->time_base); + pkt->pts = pkt->dts - + av_rescale_q(st->codecpar->video_delay, dur, st->time_base); pkt->duration = av_rescale_q(1, dur, st->time_base); + c->au_count++; } return ret; @@ -214,6 +304,9 @@ static int evc_read_close(AVFormatContext *s) EVCDemuxContext *const c = s->priv_data; av_bsf_free(&c->bsf); + av_freep(&c->sps_buf); + av_freep(&c->pps_buf); + c->sps_size = c->pps_size = 0; return 0; }