[FFmpeg-trac] #5514(avcodec:open): Interlaced HEVC Steam not Decoded Properly

FFmpeg trac at avcodec.org
Wed Oct 30 20:02:54 EET 2024


#5514: Interlaced HEVC Steam not Decoded Properly
---------------------------------------+-----------------------------------
             Reporter:  Jose Santiago  |                    Owner:  (none)
                 Type:  defect         |                   Status:  open
             Priority:  normal         |                Component:  avcodec
              Version:  git-master     |               Resolution:
             Keywords:  hevc           |               Blocked By:
             Blocking:                 |  Reproduced by developer:  1
Analyzed by developer:  0              |
---------------------------------------+-----------------------------------
Comment (by Jose Santiago):

 Here is a diff -Naur patch that applies for me:


 {{{
 diff -Naur ort-vendor-ffmpeg-orig/src/libavcodec/hevc/hevcdec.c ort-
 vendor-ffmpeg-patched/src/libavcodec/hevc/hevcdec.c
 --- ort-vendor-ffmpeg-orig/src/libavcodec/hevc/hevcdec.c        2024-10-30
 09:34:17.373950807 -0500
 +++ ort-vendor-ffmpeg-patched/src/libavcodec/hevc/hevcdec.c     2024-10-30
 10:09:25.139087029 -0500
 @@ -359,7 +359,18 @@
      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
 @@ -3844,6 +3855,7 @@
      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;
 @@ -3874,6 +3886,8 @@
      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++) {
 @@ -3930,6 +3944,11 @@
      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);
 @@ -3984,6 +4003,8 @@
          }
      }

 +    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]);

 @@ -4047,6 +4068,7 @@
      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 -Naur ort-vendor-ffmpeg-orig/src/libavcodec/hevc/hevcdec.h ort-
 vendor-ffmpeg-patched/src/libavcodec/hevc/hevcdec.h
 --- ort-vendor-ffmpeg-orig/src/libavcodec/hevc/hevcdec.h        2024-10-30
 09:34:17.375950807 -0500
 +++ ort-vendor-ffmpeg-patched/src/libavcodec/hevc/hevcdec.h     2024-10-30
 10:09:25.144087008 -0500
 @@ -375,6 +375,10 @@
      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;
 @@ -490,6 +494,8 @@
      struct FFRefStructPool *rpl_tab_pool;
  } HEVCLayerContext;

 +struct HEVCOutputFrameConstructionContext;
 +
  typedef struct HEVCContext {
      const AVClass *c;  // needed by private avoptions
      AVCodecContext *avctx;
 @@ -508,6 +514,9 @@
      /** 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;
 @@ -677,6 +686,10 @@
      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 -Naur ort-vendor-ffmpeg-orig/src/libavcodec/hevc/refs.c ort-vendor-
 ffmpeg-patched/src/libavcodec/hevc/refs.c
 --- ort-vendor-ffmpeg-orig/src/libavcodec/hevc/refs.c   2024-10-11
 14:08:54.987769044 -0500
 +++ ort-vendor-ffmpeg-patched/src/libavcodec/hevc/refs.c        2024-10-30
 10:22:08.033505443 -0500
 @@ -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;
 +
 +    // Decoder 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 Counter.
 +    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;
 +
 +        ff_mutex_lock(&ctx->mutex);
 +
 +       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,
 +                  "");
 +       }
 +
 +       ff_mutex_unlock(&ctx->mutex);
 +    }
 +
 +    ff_refstruct_unref(&s->output_frame_construction_ctx);
 +}
 +
  void ff_hevc_unref_frame(HEVCFrame *frame, int flags)
  {
      frame->flags &= ~flags;
 @@ -151,11 +248,15 @@
          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;
 +        }

          ret = ff_hwaccel_frame_priv_alloc(s->avctx,
 &frame->hwaccel_picture_private);
          if (ret < 0)
 @@ -223,6 +324,81 @@
      }
  }

 +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 +441,232 @@
              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;
 +                        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;
 +                            }
 +                            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 (output_poc != dpb_poc &&
 +
 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 -Naur ort-vendor-ffmpeg-orig/src/libavcodec/hevc/sei.c ort-vendor-
 ffmpeg-patched/src/libavcodec/hevc/sei.c
 --- ort-vendor-ffmpeg-orig/src/libavcodec/hevc/sei.c    2024-10-11
 14:08:54.988769042 -0500
 +++ ort-vendor-ffmpeg-patched/src/libavcodec/hevc/sei.c 2024-10-30
 10:09:25.151086977 -0500
 @@ -67,21 +67,7 @@
          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 -Naur ort-vendor-ffmpeg-orig/src/libavcodec/hevc/sei.h ort-vendor-
 ffmpeg-patched/src/libavcodec/hevc/sei.h
 --- ort-vendor-ffmpeg-orig/src/libavcodec/hevc/sei.h    2024-10-11
 14:08:54.988769042 -0500
 +++ ort-vendor-ffmpeg-patched/src/libavcodec/hevc/sei.h 2024-10-30
 10:15:30.231707140 -0500
 @@ -38,10 +38,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;

 }}}
-- 
Ticket URL: <https://trac.ffmpeg.org/ticket/5514#comment:20>
FFmpeg <https://ffmpeg.org>
FFmpeg issue tracker


More information about the FFmpeg-trac mailing list