[FFmpeg-devel] [PATCH V5] Patch to add interlaced HEVC decoding to HEVCDEC
Jose Santiago
jsantiago at haivision.com
Thu Oct 31 20:27:46 EET 2024
From 6cbe272375c746905238020f861633562c8b465e Mon Sep 17 00:00:00 2001
From: Jose Santiago <jsantiago at haivision.com>
Date: Thu, 31 Oct 2024 13:17:16 -0500
Subject: [PATCH V5] Patch to add interlaced HEVC decoding to HEVCDEC
This version fixes a memory leak and does some code cleanup.
---
libavcodec/hevc/hevcdec.c | 24 ++-
libavcodec/hevc/hevcdec.h | 13 ++
libavcodec/hevc/refs.c | 417 +++++++++++++++++++++++++++++++++++++-
libavcodec/hevc/sei.c | 16 +-
libavcodec/hevc/sei.h | 129 +++++++++++-
5 files changed, 574 insertions(+), 25 deletions(-)
diff --git a/libavcodec/hevc/hevcdec.c b/libavcodec/hevc/hevcdec.c
index 1ea8df0fa0..d7179bdcf7 100644
--- a/libavcodec/hevc/hevcdec.c
+++ b/libavcodec/hevc/hevcdec.c
@@ -359,7 +359,18 @@ static void export_stream_params(HEVCContext *s, const HEVCSPS *sps)
avctx->profile = sps->ptl.general_ptl.profile_idc;
avctx->level = sps->ptl.general_ptl.level_idc;
- ff_set_sar(avctx, sps->vui.common.sar);
+ // There are some streams in the wild that were encode field pitcures
+ // and set double height aspect ratio so that some players that do not
+ // support interlaced HEVC display the field pictures with double height.
+ // Since we are now combining the field pictures into a single interlaced
+ // frame, fix the sample aspect ratio to restore the correct shape for the
+ // reconstructed interlaced frames.
+ if (ff_hevc_sei_pict_struct_is_field_picture(s->sei.picture_timing.picture_struct) &&
+ sps->vui.common.sar.num == 1 && sps->vui.common.sar.den == 2) {
+ ff_set_sar(avctx, (AVRational){1, 1});
+ } else {
+ ff_set_sar(avctx, sps->vui.common.sar);
+ }
if (sps->vui.common.video_signal_type_present_flag)
avctx->color_range = sps->vui.common.video_full_range_flag ? AVCOL_RANGE_JPEG
@@ -3821,6 +3832,7 @@ static int hevc_ref_frame(HEVCFrame *dst, const HEVCFrame *src)
dst->rpl = ff_refstruct_ref(src->rpl);
dst->nb_rpl_elems = src->nb_rpl_elems;
+ dst->sei_pic_struct = src->sei_pic_struct;
dst->poc = src->poc;
dst->ctb_count = src->ctb_count;
dst->flags = src->flags;
@@ -3851,6 +3863,8 @@ static av_cold int hevc_decode_free(AVCodecContext *avctx)
av_freep(&s->md5_ctx);
av_freep(&s->h274db);
+ ff_hevc_output_frame_construction_ctx_unref(s);
+
ff_container_fifo_free(&s->output_fifo);
for (int layer = 0; layer < FF_ARRAY_ELEMS(s->layers); layer++) {
@@ -3895,6 +3909,11 @@ static av_cold int hevc_init_context(AVCodecContext *avctx)
s->local_ctx[0].logctx = avctx;
s->local_ctx[0].common_cabac_state = &s->cabac;
+ if (ff_hevc_output_frame_construction_ctx_alloc(s) != 0 ||
+ !s->output_frame_construction_ctx) {
+ return AVERROR(ENOMEM);
+ }
+
s->output_fifo = ff_container_fifo_alloc_avframe(0);
if (!s->output_fifo)
return AVERROR(ENOMEM);
@@ -3949,6 +3968,8 @@ static int hevc_update_thread_context(AVCodecContext *dst,
}
}
+ ff_hevc_output_frame_construction_ctx_replace(s, s0);
+
for (int i = 0; i < FF_ARRAY_ELEMS(s->ps.vps_list); i++)
ff_refstruct_replace(&s->ps.vps_list[i], s0->ps.vps_list[i]);
@@ -4012,6 +4033,7 @@ static int hevc_update_thread_context(AVCodecContext *dst,
s->sei.common.content_light = s0->sei.common.content_light;
s->sei.common.aom_film_grain = s0->sei.common.aom_film_grain;
s->sei.tdrdi = s0->sei.tdrdi;
+ s->sei.picture_timing = s0->sei.picture_timing;
return 0;
}
diff --git a/libavcodec/hevc/hevcdec.h b/libavcodec/hevc/hevcdec.h
index 73b792c880..4ce764f287 100644
--- a/libavcodec/hevc/hevcdec.h
+++ b/libavcodec/hevc/hevcdec.h
@@ -369,6 +369,10 @@ typedef struct HEVCFrame {
int ctb_count;
int poc;
+ // SEI Picture Timing Picture Structure Type.
+ // HEVC_SEI_PicStructType.
+ int sei_pic_struct;
+
const HEVCPPS *pps; ///< RefStruct reference
RefPicListTab *rpl; ///< RefStruct reference
int nb_rpl_elems;
@@ -484,6 +488,8 @@ typedef struct HEVCLayerContext {
struct FFRefStructPool *rpl_tab_pool;
} HEVCLayerContext;
+struct HEVCOutputFrameConstructionContext;
+
typedef struct HEVCContext {
const AVClass *c; // needed by private avoptions
AVCodecContext *avctx;
@@ -502,6 +508,9 @@ typedef struct HEVCContext {
/** 1 if the independent slice segment header was successfully parsed */
uint8_t slice_initialized;
+ // Interlaced Frame Construction Context.
+ struct HEVCOutputFrameConstructionContext *output_frame_construction_ctx; ///< RefStruct reference
+
struct ContainerFifo *output_fifo;
HEVCParamSets ps;
@@ -664,6 +673,10 @@ static av_always_inline int ff_hevc_nal_is_nonref(enum HEVCNALUnitType type)
return 0;
}
+int ff_hevc_output_frame_construction_ctx_alloc(HEVCContext *s);
+void ff_hevc_output_frame_construction_ctx_replace(HEVCContext *dst, HEVCContext *src);
+void ff_hevc_output_frame_construction_ctx_unref(HEVCContext *s);
+
/**
* Find frames in the DPB that are ready for output and either write them to the
* output FIFO or drop their output flag, depending on the value of discard.
diff --git a/libavcodec/hevc/refs.c b/libavcodec/hevc/refs.c
index 6ba667e9f5..ceab90af83 100644
--- a/libavcodec/hevc/refs.c
+++ b/libavcodec/hevc/refs.c
@@ -20,9 +20,13 @@
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-
+
+#include "libavutil/avassert.h"
#include "libavutil/mem.h"
+#include "libavutil/pixdesc.h"
#include "libavutil/stereo3d.h"
+#include "libavutil/thread.h"
+#include "libavutil/timestamp.h"
#include "container_fifo.h"
#include "decode.h"
@@ -31,6 +35,99 @@
#include "progressframe.h"
#include "refstruct.h"
+typedef struct HEVCOutputFrameConstructionContext {
+ // Thread Data Access/Synchronization.
+ AVMutex mutex;
+
+ // DPB Output Tracking.
+ uint64_t dpb_counter;
+ int dpb_poc;
+ uint64_t dpb_poc_ooorder_counter;
+
+ // Collect the First Field.
+ int have_first_field;
+ int first_field_poc;
+ int first_field_sei_pic_struct;
+ AVFrame *first_field;
+
+ uint64_t orphaned_field_pictures;
+
+ // Reconstructed Interlaced Frames From Field Pictures for Output.
+ AVFrame *constructed_frame;
+
+ // Output Frame Tracking.
+ uint64_t output_counter;
+ int output_poc;
+ uint64_t output_poc_ooorder_counter;
+} HEVCOutputFrameConstructionContext;
+
+static void hevc_output_frame_construction_ctx_free(FFRefStructOpaque opaque, void *obj)
+{
+ HEVCOutputFrameConstructionContext * ctx = (HEVCOutputFrameConstructionContext *)obj;
+
+ if (!ctx)
+ return;
+
+ av_frame_free(&ctx->first_field);
+ av_frame_free(&ctx->constructed_frame);
+ av_assert0(ff_mutex_destroy(&ctx->mutex) == 0);
+}
+
+int ff_hevc_output_frame_construction_ctx_alloc(HEVCContext *s)
+{
+ if (s->output_frame_construction_ctx) {
+ av_log(s->avctx, AV_LOG_ERROR,
+ "s->output_frame_construction_ctx is already set.\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ s->output_frame_construction_ctx =
+ ff_refstruct_alloc_ext(sizeof(*(s->output_frame_construction_ctx)),
+ 0, NULL, hevc_output_frame_construction_ctx_free);
+ if (!s->output_frame_construction_ctx)
+ return AVERROR(ENOMEM);
+
+ av_assert0(ff_mutex_init(&s->output_frame_construction_ctx->mutex, NULL) == 0);
+
+ return 0;
+}
+
+void ff_hevc_output_frame_construction_ctx_replace(HEVCContext *dst, HEVCContext *src)
+{
+ ff_refstruct_replace(&dst->output_frame_construction_ctx,
+ src->output_frame_construction_ctx);
+}
+
+void ff_hevc_output_frame_construction_ctx_unref(HEVCContext *s)
+{
+ if (s->output_frame_construction_ctx &&
+ ff_refstruct_exclusive(s->output_frame_construction_ctx)) {
+
+ HEVCOutputFrameConstructionContext * ctx = s->output_frame_construction_ctx;
+
+ av_assert0(ff_mutex_lock(&ctx->mutex) == 0);
+
+ if (ctx->dpb_counter) {
+ av_log(s->avctx, AV_LOG_ERROR,
+ "[HEVCOutputFrameConstructionContext @ 0x%p]:\n"
+ " DPB: Counter=%" PRIu64 " POCOutOfOrder=%" PRIu64 " Orphaned=%" PRIu64 "\n"
+ " Output: Counter=%" PRIu64 " POCOutOfOrder=%" PRIu64 "\n"
+ "%s",
+ ctx,
+ ctx->dpb_counter,
+ ctx->dpb_poc_ooorder_counter,
+ ctx->orphaned_field_pictures,
+ ctx->output_counter,
+ ctx->output_poc_ooorder_counter,
+ "");
+ }
+
+ av_assert0(ff_mutex_unlock(&ctx->mutex) == 0);
+ }
+
+ ff_refstruct_unref(&s->output_frame_construction_ctx);
+}
+
void ff_hevc_unref_frame(HEVCFrame *frame, int flags)
{
frame->flags &= ~flags;
@@ -151,11 +248,19 @@ static HEVCFrame *alloc_frame(HEVCContext *s, HEVCLayerContext *l)
for (j = 0; j < frame->ctb_count; j++)
frame->rpl_tab[j] = frame->rpl;
- if (s->sei.picture_timing.picture_struct == AV_PICTURE_STRUCTURE_TOP_FIELD)
- frame->f->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST;
- if ((s->sei.picture_timing.picture_struct == AV_PICTURE_STRUCTURE_TOP_FIELD) ||
- (s->sei.picture_timing.picture_struct == AV_PICTURE_STRUCTURE_BOTTOM_FIELD))
+ frame->sei_pic_struct = s->sei.picture_timing.picture_struct;
+ if (ff_hevc_sei_pic_struct_is_interlaced(frame->sei_pic_struct)) {
frame->f->flags |= AV_FRAME_FLAG_INTERLACED;
+ if (ff_hevc_sei_pic_struct_is_tff(frame->sei_pic_struct))
+ frame->f->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST;
+ if (frame->sei_pic_struct == HEVC_SEI_PIC_STRUCT_FRAME_TFBFTF ||
+ frame->sei_pic_struct == HEVC_SEI_PIC_STRUCT_FRAME_BFTFBF)
+ frame->f->repeat_pict = 1;
+ else if (frame->sei_pic_struct == HEVC_SEI_PIC_STRUCT_FRAME_DOUBLING)
+ frame->f->repeat_pict = 2;
+ else if (frame->sei_pic_struct == HEVC_SEI_PIC_STRUCT_FRAME_TRIPLING)
+ frame->f->repeat_pict = 3;
+ }
ret = ff_hwaccel_frame_priv_alloc(s->avctx, &frame->hwaccel_picture_private);
if (ret < 0)
@@ -223,6 +328,81 @@ static void unref_missing_refs(HEVCLayerContext *l)
}
}
+static void copy_field2(AVFrame *_dst, const AVFrame *_src)
+{
+ const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(_src->format);
+ int i, j, planes_nb = 0;
+ for (i = 0; i < desc->nb_components; i++)
+ planes_nb = FFMAX(planes_nb, desc->comp[i].plane + 1);
+ for (i = 0; i < planes_nb; i++) {
+ int h = _src->height;
+ uint8_t *dst = _dst->data[i] + (_dst->linesize[i] / 2);
+ uint8_t *src = _src->data[i];
+ if (i == 1 || i == 2) {
+ h = FF_CEIL_RSHIFT(_src->height, desc->log2_chroma_h);
+ }
+ for (j = 0; j < h; j++) {
+ memcpy(dst, src, _src->linesize[i]);
+ dst += _dst->linesize[i];
+ src += _src->linesize[i];
+ }
+ }
+}
+
+static int interlaced_frame_from_fields(AVFrame *dst,
+ const AVFrame *field1,
+ const AVFrame *field2)
+{
+ int i, ret = 0;
+
+ av_frame_unref(dst);
+
+ dst->format = field1->format;
+ dst->width = field1->width;
+ dst->height = field1->height * 2;
+ dst->nb_samples = field1->nb_samples;
+ ret = av_channel_layout_copy(&dst->ch_layout, &field1->ch_layout);
+ if (ret < 0)
+ return ret;
+
+ ret = av_frame_copy_props(dst, field1);
+ if (ret < 0)
+ return ret;
+ if (field1->duration > 0 && field1->duration != AV_NOPTS_VALUE)
+ dst->duration = field2->duration * 2;
+ else if (field2->duration > 0 && field2->duration != AV_NOPTS_VALUE)
+ dst->duration = field2->duration * 2;
+
+ for (i = 0; i < field2->nb_side_data; i++) {
+ const AVFrameSideData *sd_src = field2->side_data[i];
+ AVFrameSideData *sd_dst;
+ AVBufferRef *ref = av_buffer_ref(sd_src->buf);
+ sd_dst = av_frame_new_side_data_from_buf(dst, sd_src->type, ref);
+ if (!sd_dst) {
+ av_buffer_unref(&ref);
+ return AVERROR(ENOMEM);
+ }
+ }
+
+ for (i = 0; i < AV_NUM_DATA_POINTERS; i++)
+ dst->linesize[i] = field1->linesize[i]*2;
+
+ ret = av_frame_get_buffer(dst, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = av_frame_copy(dst, field1);
+ if (ret < 0)
+ av_frame_unref(dst);
+
+ copy_field2(dst, field2);
+
+ for (i = 0; i < AV_NUM_DATA_POINTERS; i++)
+ dst->linesize[i] = field1->linesize[i];
+
+ return ret;
+}
+
int ff_hevc_output_frames(HEVCContext *s,
unsigned layers_active_decode, unsigned layers_active_output,
unsigned max_output, unsigned max_dpb, int discard)
@@ -265,10 +445,233 @@ int ff_hevc_output_frames(HEVCContext *s,
AVFrame *f = frame->needs_fg ? frame->frame_grain : frame->f;
int output = !discard && (layers_active_output & (1 << min_layer));
+ if (frame->poc != s->poc) {
+ if (s->avctx->active_thread_type == FF_THREAD_FRAME)
+ {
+ // Wait for other thread to finish decoding this frame/field picture.
+ // Otherwise I have seen image corruption for some streams..
+ av_log(s->avctx, AV_LOG_DEBUG,
+ "Waiting on Frame POC: %d.\n",
+ frame->poc);
+ ff_progress_frame_await(&frame->tf, INT_MAX);
+ }
+ } else {
+ // This is the Context currently decoding..
+ // Skip it to ensure that this frame is completely decoded and finalized.
+ // This will allow the next context to process it
+ // Otherwise I have seen image corruption for some streams.
+ av_log(s->avctx, AV_LOG_DEBUG,
+ "Schedule Frame for Next Pass POC: %d.\n",
+ frame->poc);
+ return 0;
+ }
+
+ av_assert0(s->output_frame_construction_ctx);
+ av_assert0(ff_mutex_lock(&s->output_frame_construction_ctx->mutex) == 0);
+
if (output) {
- f->pkt_dts = s->pkt_dts;
- ret = ff_container_fifo_write(s->output_fifo, f);
+ const int dpb_poc = frame->poc;
+ const int dpb_sei_pic_struct = frame->sei_pic_struct;
+ AVFrame *output_frame = f;
+ int output_poc = dpb_poc;
+ int output_sei_pic_struct = dpb_sei_pic_struct;
+
+ s->output_frame_construction_ctx->dpb_counter++;
+ if (s->output_frame_construction_ctx->dpb_counter > 1 &&
+ dpb_poc < s->output_frame_construction_ctx->dpb_poc &&
+ dpb_poc > 0) {
+ s->output_frame_construction_ctx->dpb_poc_ooorder_counter++;
+ av_log(s->avctx, AV_LOG_ERROR,
+ "DPB POC Out of Order POC %d < PrevPOC %d "
+ ": Counter=%" PRIu64 " OORCounter=%" PRIu64 ".\n",
+ dpb_poc,
+ s->output_frame_construction_ctx->dpb_poc,
+ s->output_frame_construction_ctx->dpb_counter,
+ s->output_frame_construction_ctx->dpb_poc_ooorder_counter);
+ }
+ s->output_frame_construction_ctx->dpb_poc = dpb_poc;
+
+ if (ff_hevc_sei_pict_struct_is_field_picture(dpb_sei_pic_struct)) {
+ const int have_first_field = s->output_frame_construction_ctx->have_first_field;
+ const int is_first_field =
+ (ff_hevc_sei_pic_struct_is_tff(dpb_sei_pic_struct) &&
+ ff_hevc_sei_pic_struct_is_tf(dpb_sei_pic_struct)) ||
+ (ff_hevc_sei_pic_struct_is_bff(dpb_sei_pic_struct) &&
+ ff_hevc_sei_pic_struct_is_bf(dpb_sei_pic_struct)) ||
+ (!s->output_frame_construction_ctx->have_first_field &&
+ (dpb_poc % 2) == 0) ||
+ (s->output_frame_construction_ctx->have_first_field &&
+ s->output_frame_construction_ctx->first_field_sei_pic_struct == dpb_sei_pic_struct &&
+ (dpb_poc % 2) == 0 &&
+ dpb_poc > s->output_frame_construction_ctx->first_field_poc);
+
+ output_frame = NULL;
+
+ if (!s->output_frame_construction_ctx->first_field)
+ {
+ s->output_frame_construction_ctx->first_field = av_frame_alloc();
+ if (!s->output_frame_construction_ctx->first_field) {
+ av_log(s->avctx, AV_LOG_ERROR, "AVERROR(ENOMEM)");
+ ret = AVERROR(ENOMEM);
+ goto unref_frame_and_check_ret;
+ }
+ }
+ if (!s->output_frame_construction_ctx->constructed_frame) {
+ s->output_frame_construction_ctx->constructed_frame = av_frame_alloc();
+ if (!s->output_frame_construction_ctx->constructed_frame) {
+ av_log(s->avctx, AV_LOG_ERROR, "AVERROR(ENOMEM)");
+ ret = AVERROR(ENOMEM);
+ goto unref_frame_and_check_ret;
+ }
+ }
+
+ if (is_first_field) {
+ // This is a first field picture.
+ av_log(s->avctx, AV_LOG_DEBUG,
+ "Found first field picture POC %d.\n",
+ dpb_poc);
+ if (s->output_frame_construction_ctx->have_first_field) {
+ // We were waiting for a second field, but got another frist
+ // field instead.
+ av_log(s->avctx, AV_LOG_ERROR,
+ "Discarded Orphaned First Field with POC %d.\n",
+ s->output_frame_construction_ctx->first_field_poc);
+ }
+ s->output_frame_construction_ctx->have_first_field = 1;
+ s->output_frame_construction_ctx->first_field_sei_pic_struct = dpb_sei_pic_struct;
+ s->output_frame_construction_ctx->first_field_poc = dpb_poc;
+ av_frame_unref(s->output_frame_construction_ctx->first_field);
+ ret = av_frame_ref(s->output_frame_construction_ctx->first_field, f);
+ if (ret < 0) {
+ av_log(s->avctx, AV_LOG_ERROR,
+ "Failure updating first Field picture POC %d.\n",
+ dpb_poc);
+ s->output_frame_construction_ctx->have_first_field = 0;
+ s->output_frame_construction_ctx->orphaned_field_pictures++;
+ goto unref_frame_and_check_ret;
+ }
+ } else if (have_first_field) {
+ // We Found the next field.
+ if (f->width == s->output_frame_construction_ctx->first_field->width &&
+ f->height == s->output_frame_construction_ctx->first_field->height) {
+ // Combine the top and bottom fields into one frame for output.
+ AVFrame *constructed_frame = s->output_frame_construction_ctx->constructed_frame;
+ AVFrame *top_field;
+ AVFrame *bottom_field;
+ int tfPoc, bfPoc;
+ if (ff_hevc_sei_pic_struct_is_tf(dpb_sei_pic_struct)) {
+ top_field = f;
+ tfPoc = dpb_poc;
+ bottom_field = s->output_frame_construction_ctx->first_field;
+ bfPoc = s->output_frame_construction_ctx->first_field_poc;
+ } else {
+ top_field = s->output_frame_construction_ctx->first_field;
+ tfPoc = s->output_frame_construction_ctx->first_field_poc;
+ bottom_field = f;
+ bfPoc = dpb_poc;
+ }
+ av_frame_unref(constructed_frame);
+ ret = interlaced_frame_from_fields(constructed_frame, top_field, bottom_field);
+ if (ret >= 0) {
+ output_frame = constructed_frame;
+ output_poc = s->output_frame_construction_ctx->first_field_poc;
+ output_sei_pic_struct = s->output_frame_construction_ctx->first_field_sei_pic_struct;
+ output_frame->flags |= AV_FRAME_FLAG_INTERLACED;
+ if (!ff_hevc_sei_pic_struct_is_bf(output_sei_pic_struct)) {
+ output_frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST;
+ } else {
+ output_frame->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST;
+ }
+ } else {
+ av_log(s->avctx, AV_LOG_ERROR,
+ "Interlaced Frame Construction Failure POCs: %d %d.\n",
+ tfPoc, bfPoc);
+ s->output_frame_construction_ctx->orphaned_field_pictures += 2;
+ }
+ } else if ((dpb_poc % 2) == 0) {
+ av_log(s->avctx, AV_LOG_ERROR,
+ "Discarded orphaned first field pictures POC: %d.\n",
+ s->output_frame_construction_ctx->first_field_poc);
+ s->output_frame_construction_ctx->orphaned_field_pictures++;
+ // This may be the next first field.
+ s->output_frame_construction_ctx->have_first_field = 0;
+ av_assert0(ff_mutex_unlock(&s->output_frame_construction_ctx->mutex) == 0);
+ continue;
+ } else {
+ av_log(s->avctx, AV_LOG_ERROR,
+ "Discarded mismatched field pictures POCs: %d %d.\n",
+ s->output_frame_construction_ctx->first_field_poc,
+ dpb_poc);
+ s->output_frame_construction_ctx->orphaned_field_pictures++;
+ }
+ // Find the next first field.
+ s->output_frame_construction_ctx->have_first_field = 0;
+ } else {
+ // We have a second field without a first field.
+ av_log(s->avctx, AV_LOG_ERROR,
+ "Discarded orphaned second field picture with POC %d.\n",
+ dpb_poc);
+ s->output_frame_construction_ctx->orphaned_field_pictures++;
+ }
+ } else if (s->output_frame_construction_ctx->have_first_field) {
+ av_log(s->avctx, AV_LOG_ERROR,
+ "Discarded orphaned first field pictures POC: %d.\n",
+ s->output_frame_construction_ctx->first_field_poc);
+ s->output_frame_construction_ctx->orphaned_field_pictures++;
+ // Find the next first field.
+ s->output_frame_construction_ctx->have_first_field = 0;
+ }
+
+ if (output_frame) {
+ output_frame->pkt_dts = s->pkt_dts;
+
+ //av_log(s->avctx, AV_LOG_ERROR,
+ av_log(s->avctx, AV_LOG_DEBUG,
+ "s=0x%" PRIx64 " s->avctx=0x%" PRIx64 "\n"
+ " ====Output: FrameType:%s\n"
+ " === POC=%d PKTDTS=%s PTS=%s Duration=%s\n"
+ " === SEIPic=%d Interlaced=%s TFF=%s PictType='%c' Key=%s\n"
+ " === WxH=%dx%d SAR=%dx%d\n"
+ "%s",
+ (uint64_t)s, (uint64_t)s->avctx,
+ (output_frame->flags & AV_FRAME_FLAG_INTERLACED) ? "Interlaced" : "Progressive",
+ output_poc,
+ av_ts2str(output_frame->pkt_dts),
+ av_ts2str(output_frame->pts),
+ av_ts2str(output_frame->duration),
+ output_sei_pic_struct,
+ (output_frame->flags & AV_FRAME_FLAG_INTERLACED) ? "Yes" : "No",
+ (output_frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? "Yes" : "No",
+ av_get_picture_type_char(output_frame->pict_type),
+ (output_frame->flags & AV_FRAME_FLAG_KEY) ? "Yes" : "No",
+ output_frame->width, output_frame->height,
+ (int)output_frame->sample_aspect_ratio.num,
+ (int)output_frame->sample_aspect_ratio.den,
+ "");
+
+ s->output_frame_construction_ctx->output_counter++;
+ if (s->output_frame_construction_ctx->output_counter > 1 &&
+ output_poc < s->output_frame_construction_ctx->output_poc &&
+ output_poc > 0) {
+ s->output_frame_construction_ctx->output_poc_ooorder_counter++;
+ av_log(s->avctx, AV_LOG_ERROR,
+ "Output POC Out of Order POC %d < PrevPOC %d "
+ ": Counter=%" PRIu64 " OORCounter=%" PRIu64 ".\n",
+ output_poc,
+ s->output_frame_construction_ctx->output_poc,
+ s->output_frame_construction_ctx->output_counter,
+ s->output_frame_construction_ctx->output_poc_ooorder_counter);
+ }
+ s->output_frame_construction_ctx->output_poc = output_poc;
+
+ ret = ff_container_fifo_write(s->output_fifo, output_frame);
+ }
}
+
+unref_frame_and_check_ret:
+
+ av_assert0(ff_mutex_unlock(&s->output_frame_construction_ctx->mutex) == 0);
+
ff_hevc_unref_frame(frame, HEVC_FRAME_FLAG_OUTPUT);
if (ret < 0)
return ret;
diff --git a/libavcodec/hevc/sei.c b/libavcodec/hevc/sei.c
index e11a33773c..50b669c34b 100644
--- a/libavcodec/hevc/sei.c
+++ b/libavcodec/hevc/sei.c
@@ -59,21 +59,7 @@ static int decode_nal_sei_pic_timing(HEVCSEI *s, GetBitContext *gb,
return AVERROR_INVALIDDATA;
if (sps->vui.frame_field_info_present_flag) {
- int pic_struct = get_bits(gb, 4);
- h->picture_struct = AV_PICTURE_STRUCTURE_UNKNOWN;
- if (pic_struct == 2 || pic_struct == 10 || pic_struct == 12) {
- av_log(logctx, AV_LOG_DEBUG, "BOTTOM Field\n");
- h->picture_struct = AV_PICTURE_STRUCTURE_BOTTOM_FIELD;
- } else if (pic_struct == 1 || pic_struct == 9 || pic_struct == 11) {
- av_log(logctx, AV_LOG_DEBUG, "TOP Field\n");
- h->picture_struct = AV_PICTURE_STRUCTURE_TOP_FIELD;
- } else if (pic_struct == 7) {
- av_log(logctx, AV_LOG_DEBUG, "Frame/Field Doubling\n");
- h->picture_struct = HEVC_SEI_PIC_STRUCT_FRAME_DOUBLING;
- } else if (pic_struct == 8) {
- av_log(logctx, AV_LOG_DEBUG, "Frame/Field Tripling\n");
- h->picture_struct = HEVC_SEI_PIC_STRUCT_FRAME_TRIPLING;
- }
+ h->picture_struct = get_bits(gb, 4);
}
return 0;
diff --git a/libavcodec/hevc/sei.h b/libavcodec/hevc/sei.h
index 806540fac6..bfecd548c3 100644
--- a/libavcodec/hevc/sei.h
+++ b/libavcodec/hevc/sei.h
@@ -33,10 +33,135 @@
typedef enum {
- HEVC_SEI_PIC_STRUCT_FRAME_DOUBLING = 7,
- HEVC_SEI_PIC_STRUCT_FRAME_TRIPLING = 8
+ // SEI Picture Timing Picture Structure.
+ // From the ITU-T H.265 Standards Document v3 (04/2015):
+ // Table D.2: Interpretation of pic_struct:
+ // When present, pic_struct is constrained to use one of the following:
+ // - all pictures in CSV are one of: 0, 7 or 8.
+ // - all pictures in CSV are one of: 1, 2, 9, 10, 11, or 12..
+ // - all pictures in CSV are one of: 3, 4, 5 or 6...
+
+ // progressive frame.
+ HEVC_SEI_PIC_STRUCT_FRAME_PROGRESSIVE = 0,
+
+ // top field.
+ HEVC_SEI_PIC_STRUCT_FIELD_TOP = 1,
+ // bottom field.
+ HEVC_SEI_PIC_STRUCT_FIELD_BOTTOM = 2,
+
+ // top field, bottom field, in that order. Top Field First.
+ HEVC_SEI_PIC_STRUCT_FRAME_TFBF = 3,
+ // bottom Field, top field, in that order. Bottom Field First.
+ HEVC_SEI_PIC_STRUCT_FRAME_BFTF = 4,
+
+ // top field, bottom field, top field repeated, Top Field First.
+ HEVC_SEI_PIC_STRUCT_FRAME_TFBFTF = 5,
+ // bottom field, top field, bottom field repeated, Bottom Field First.
+ HEVC_SEI_PIC_STRUCT_FRAME_BFTFBF = 6,
+
+ // frame doubling.
+ HEVC_SEI_PIC_STRUCT_FRAME_DOUBLING = 7,
+ // frame trippling.
+ HEVC_SEI_PIC_STRUCT_FRAME_TRIPLING = 8,
+
+ // top field paired with previous bottom field. Bottom Field First.
+ HEVC_SEI_PIC_STRUCT_FIELD_TFPBF = 9,
+ // bottom field paired with previous top field. Top Field First.
+ HEVC_SEI_PIC_STRUCT_FIELD_BFPTF = 10,
+
+ // top field paired with next bottom field. Top Field First.
+ HEVC_SEI_PIC_STRUCT_FIELD_TFNBF = 11,
+ // bottom field paired with next top field. Bottom Field First.
+ HEVC_SEI_PIC_STRUCT_FIELD_BFNTF = 12,
} HEVC_SEI_PicStructType;
+// Returns 1 - when type is interlaced, 0 - otherwise.
+static inline int ff_hevc_sei_pic_struct_is_interlaced(HEVC_SEI_PicStructType type)
+{
+ switch (type) {
+ case HEVC_SEI_PIC_STRUCT_FIELD_TOP:
+ case HEVC_SEI_PIC_STRUCT_FIELD_BOTTOM:
+ case HEVC_SEI_PIC_STRUCT_FRAME_TFBF:
+ case HEVC_SEI_PIC_STRUCT_FRAME_BFTF:
+ case HEVC_SEI_PIC_STRUCT_FRAME_TFBFTF:
+ case HEVC_SEI_PIC_STRUCT_FRAME_BFTFBF:
+ case HEVC_SEI_PIC_STRUCT_FIELD_TFPBF:
+ case HEVC_SEI_PIC_STRUCT_FIELD_BFPTF:
+ case HEVC_SEI_PIC_STRUCT_FIELD_TFNBF:
+ case HEVC_SEI_PIC_STRUCT_FIELD_BFNTF:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+// Returns 1 - when type is top field first, 0 - otherwise.
+static inline int ff_hevc_sei_pic_struct_is_tff(HEVC_SEI_PicStructType type)
+{
+ switch (type) {
+ case HEVC_SEI_PIC_STRUCT_FRAME_TFBF:
+ case HEVC_SEI_PIC_STRUCT_FRAME_TFBFTF:
+ case HEVC_SEI_PIC_STRUCT_FIELD_BFPTF:
+ case HEVC_SEI_PIC_STRUCT_FIELD_TFNBF:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+// Returns 1 - when type is bottom field first, 0 - otherwise.
+static inline int ff_hevc_sei_pic_struct_is_bff(HEVC_SEI_PicStructType type)
+{
+ switch (type) {
+ case HEVC_SEI_PIC_STRUCT_FRAME_BFTF:
+ case HEVC_SEI_PIC_STRUCT_FRAME_BFTFBF:
+ case HEVC_SEI_PIC_STRUCT_FIELD_TFPBF:
+ case HEVC_SEI_PIC_STRUCT_FIELD_BFNTF:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+// Returns 1 - when type is top field, 0 - otherwise.
+static inline int ff_hevc_sei_pic_struct_is_tf(HEVC_SEI_PicStructType type)
+{
+ switch (type) {
+ case HEVC_SEI_PIC_STRUCT_FIELD_TOP:
+ case HEVC_SEI_PIC_STRUCT_FIELD_TFPBF:
+ case HEVC_SEI_PIC_STRUCT_FIELD_TFNBF:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+// Returns 1 - when type is bottom field, 0 - otherwise.
+static inline int ff_hevc_sei_pic_struct_is_bf(HEVC_SEI_PicStructType type)
+{
+ switch (type) {
+ case HEVC_SEI_PIC_STRUCT_FIELD_BOTTOM:
+ case HEVC_SEI_PIC_STRUCT_FIELD_BFPTF:
+ case HEVC_SEI_PIC_STRUCT_FIELD_BFNTF:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+// Returns 1 - when type is a field picture, 0 - otherwise.
+static inline int ff_hevc_sei_pict_struct_is_field_picture(HEVC_SEI_PicStructType type)
+{
+ return (ff_hevc_sei_pic_struct_is_tf(type) || ff_hevc_sei_pic_struct_is_bf(type)) ? 1 : 0;
+}
+
+// Returns 1 - when type is a frame picture, 0 - otherwise.
+static inline int ff_hevc_sei_pict_struct_is_frame_picture(HEVC_SEI_PicStructType type)
+{
+ return ff_hevc_sei_pict_struct_is_field_picture(type) ? 0 : 1;
+}
+
+
typedef struct HEVCSEIPictureHash {
uint8_t md5[3][16];
uint8_t is_md5;
--
2.46.1
-------------- next part --------------
A non-text attachment was scrubbed...
Name: ffmpeg-7.2-dev-0001-hevcdec-interlaced-v5.git.patch
Type: text/x-patch
Size: 33599 bytes
Desc: not available
URL: <https://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20241031/54b83e75/attachment.bin>
More information about the ffmpeg-devel
mailing list