[vlc-commits] [Git][videolan/vlc][master] 9 commits: codec: videotoolbox: account fields in pic_pacer

Steve Lhomme (@robUx4) gitlab at videolan.org
Sun Dec 3 08:16:54 UTC 2023



Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
deedd6ce by François Cartegnie at 2023-12-03T07:48:23+00:00
codec: videotoolbox: account fields in pic_pacer

- - - - -
b5a9d953 by François Cartegnie at 2023-12-03T07:48:23+00:00
codec: videotoolbox: rename DPB vars

- - - - -
6413b9af by François Cartegnie at 2023-12-03T07:48:23+00:00
codec: videotoolbox: move dpb variables in their own struct

- - - - -
33992951 by François Cartegnie at 2023-12-03T07:48:23+00:00
codec: videotoolbox: move DPB grow out of output loop

minimal foc was already on list head

- - - - -
b860a7be by François Cartegnie at 2023-12-03T07:48:23+00:00
codec: videotoolbox: only use DPB context for DPB management

- - - - -
65ad721a by François Cartegnie at 2023-12-03T07:48:23+00:00
codec: videotoolbox: split DPB code

- - - - -
18b46467 by François Cartegnie at 2023-12-03T07:48:23+00:00
codec: videotoolbox: add DPB test

- - - - -
44c0ccc2 by François Cartegnie at 2023-12-03T07:48:23+00:00
codec: videotoolbox: output only single field from DPB

- - - - -
4464af83 by François Cartegnie at 2023-12-03T07:48:23+00:00
codec: videotoolbox: manage DPB as dual field buffers

- - - - -


7 changed files:

- modules/codec/Makefile.am
- modules/codec/videotoolbox/decoder.c
- + modules/codec/videotoolbox/dpb.c
- + modules/codec/videotoolbox/dpb.h
- + modules/codec/videotoolbox/dpb_test.c
- modules/codec/videotoolbox/pacer.c
- modules/codec/videotoolbox/pacer.h


Changes:

=====================================
modules/codec/Makefile.am
=====================================
@@ -351,6 +351,8 @@ EXTRA_LTLIBRARIES += liboggspots_plugin.la
 codec_LTLIBRARIES += $(LTLIBoggspots)
 
 libvideotoolbox_plugin_la_SOURCES = codec/videotoolbox/decoder.c \
+                                    codec/videotoolbox/dpb.c \
+                                    codec/videotoolbox/dpb.h \
                                     codec/videotoolbox/pacer.c \
                                     codec/videotoolbox/pacer.h
 libvideotoolbox_plugin_la_LIBADD = libchroma_copy.la libvlc_hxxxhelper.la libvlc_vtutils.la
@@ -359,6 +361,14 @@ if HAVE_DARWIN
 codec_LTLIBRARIES += libvideotoolbox_plugin.la
 endif
 
+videotoolbox_dpb_test_SOURCES = codec/videotoolbox/dpb_test.c \
+                                codec/videotoolbox/dpb.c \
+                                codec/videotoolbox/dpb.h
+videotoolbox_dpb_test_CFLAGS = $(AM_CFLAGS) -DDPB_DEBUG
+
+check_PROGRAMS += videotoolbox_dpb_test
+TESTS += videotoolbox_dpb_test
+
 libvideotoolbox_enc_plugin_la_SOURCES = codec/videotoolbox/encoder.c
 libvideotoolbox_enc_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) # Trigger MODULE_NAME declaration
 libvideotoolbox_enc_plugin_la_LIBADD = libvlc_hxxxhelper.la libvlc_vtutils.la


=====================================
modules/codec/videotoolbox/decoder.c
=====================================
@@ -38,6 +38,7 @@
 #import "../../packetizer/hxxx_nal.h"
 #import "../../packetizer/hxxx_sei.h"
 #import "pacer.h"
+#import "dpb.h"
 
 #import <VideoToolbox/VideoToolbox.h>
 #import <VideoToolbox/VTErrors.h>
@@ -78,33 +79,8 @@ static bool deviceSupports42010bitRendering();
 static Boolean deviceSupportsAdvancedProfiles();
 static Boolean deviceSupportsAdvancedLevels();
 
-typedef struct frame_info_t frame_info_t;
-
-struct frame_info_t
-{
-    picture_t *p_picture;
-    int i_poc;
-    int i_foc;
-    vlc_tick_t pts;
-    vlc_tick_t dts;
-    unsigned field_rate_num;
-    unsigned field_rate_den;
-    bool b_flush;
-    bool b_eos;
-    bool b_keyframe;
-    bool b_leading;
-    bool b_field;
-    bool b_progressive;
-    bool b_top_field_first;
-    uint8_t i_num_ts;
-    uint8_t i_max_reorder;
-    unsigned i_length;
-    frame_info_t *p_next;
-};
-
 #pragma mark - decoder structure
 
-#define H264_MAX_DPB 16
 #define VT_MAX_SEI_COUNT 16
 
 #define DEFAULT_FRAME_RATE_NUM 30000
@@ -149,12 +125,7 @@ typedef struct decoder_sys_t
     vlc_mutex_t                 lock;
     bool                        b_discard_decoder_output;
 
-    frame_info_t               *p_pic_reorder;
-    uint8_t                     i_pic_reorder;
-    uint8_t                     i_pic_reorder_max;
-    bool                        b_strict_reorder;
-    bool                        b_invalid_pic_reorder_max;
-    bool                        b_poc_based_reorder;
+    struct dpb_s                dpb;
 
     bool                        b_format_propagated;
 
@@ -316,7 +287,7 @@ static bool FillReorderInfoH264(decoder_t *p_dec, const block_t *p_block,
                 p_info->i_num_ts = h264_get_num_ts(p_sps, &slice, sei.i_pic_struct,
                                                    p_info->i_foc, bFOC);
                 unsigned dummy;
-                h264_get_dpb_values(p_sps, &p_info->i_max_reorder, &dummy);
+                h264_get_dpb_values(p_sps, &p_info->i_max_pics_buffering, &dummy);
 
                 if (!p_info->b_progressive)
                     p_info->b_top_field_first = (sei.i_pic_struct % 2 == 1);
@@ -377,6 +348,7 @@ static bool InitH264(decoder_t *p_dec)
         return false;
     }
     p_sys->p_codec_context = ctx;
+    p_sys->dpb.i_fields_per_buffer = 2;
     return true;
 }
 
@@ -706,9 +678,8 @@ static bool FillReorderInfoHEVC(decoder_t *p_dec, const block_t *p_block,
                 p_info->i_foc = POC; /* clearly looks wrong :/ */
                 p_info->i_num_ts = hevc_get_num_clock_ts(p_sps, sei.p_timing);
                 uint8_t dummy;
-                hevc_get_dpb_values(p_sps, &p_info->i_max_reorder, &dummy, &dummy);
+                hevc_get_dpb_values(p_sps, &p_info->i_max_pics_buffering, &dummy, &dummy);
                 VLC_UNUSED(dummy);
-
                 p_info->b_flush = (POC == 0) ||
                                   (i_nal_type >= HEVC_NAL_IDR_N_LP &&
                                    i_nal_type <= HEVC_NAL_IRAP_VCL23);
@@ -814,131 +785,18 @@ static CFDictionaryRef CopyDecoderExtradataMPEG4(decoder_t *p_dec)
 
 /* !Codec Specific */
 
-static void InsertIntoDPB(decoder_sys_t *p_sys, frame_info_t *p_info)
-{
-    frame_info_t **pp_lead_in = &p_sys->p_pic_reorder;
-
-    for ( ;; pp_lead_in = & ((*pp_lead_in)->p_next))
-    {
-        bool b_insert;
-        if (*pp_lead_in == NULL)
-            b_insert = true;
-        else if (p_sys->b_poc_based_reorder)
-            b_insert = ((*pp_lead_in)->i_foc > p_info->i_foc);
-        else
-            b_insert = ((*pp_lead_in)->pts >= p_info->pts);
-
-        if (b_insert)
-        {
-            p_info->p_next = *pp_lead_in;
-            *pp_lead_in = p_info;
-            p_sys->i_pic_reorder++;
-            break;
-        }
-    }
-#if 0
-    for (frame_info_t *p_in=p_sys->p_pic_reorder; p_in; p_in = p_in->p_next)
-        printf(" %d", p_in->i_foc);
-    printf("\n");
-#endif
-}
-
-static int RemoveOneFrameFromDPB(decoder_sys_t *p_sys, picture_t **pp_ret)
-{
-    frame_info_t *p_info = p_sys->p_pic_reorder;
-    if (p_info == NULL)
-    {
-        *pp_ret = NULL;
-        return VLC_EGENERIC;
-    }
-
-    const int i_framepoc = p_info->i_poc;
-    const vlc_tick_t i_framepts = p_info->pts;
-
-    picture_t **pp_ret_last = pp_ret;
-    bool b_dequeue;
-
-    do
-    {
-        /* Asynchronous fallback time init */
-        if(date_Get(&p_sys->pts) == VLC_TICK_INVALID)
-        {
-            date_Set(&p_sys->pts, p_info->pts != VLC_TICK_INVALID ?
-                                  p_info->pts : p_info->dts );
-        }
-
-        /* Compute time from output if missing */
-        if (p_info->pts == VLC_TICK_INVALID)
-            p_info->pts = date_Get(&p_sys->pts);
-        else
-            date_Set(&p_sys->pts, p_info->pts);
-
-        /* Update frame rate (used on interpolation) */
-        if(p_info->field_rate_num != p_sys->pts.i_divider_num ||
-           p_info->field_rate_den != p_sys->pts.i_divider_den)
-        {
-            /* no date_Change due to possible invalid num */
-            date_Init(&p_sys->pts, p_info->field_rate_num,
-                                   p_info->field_rate_den);
-            date_Set(&p_sys->pts, p_info->pts);
-        }
-
-        /* Set next picture time, in case it is missing */
-        if (p_info->i_length)
-            date_Set(&p_sys->pts, p_info->pts + p_info->i_length);
-        else
-            date_Increment(&p_sys->pts, p_info->i_num_ts);
-
-        if( p_info->p_picture ) /* Can have no picture attached to entry on error */
-        {
-            if( p_info->p_picture->date == VLC_TICK_INVALID )
-                p_info->p_picture->date = p_info->pts;
-
-            /* Extract attached field to output list */
-            *pp_ret_last = p_info->p_picture;
-            pp_ret_last = &p_info->p_picture->p_next;
-        }
-
-        p_sys->i_pic_reorder--;
-
-        p_sys->p_pic_reorder = p_info->p_next;
-        free(p_info);
-        p_info = p_sys->p_pic_reorder;
-
-        if (p_info)
-        {
-            if (p_sys->b_poc_based_reorder)
-                b_dequeue = (p_info->i_poc == i_framepoc);
-            else
-                b_dequeue = (p_info->pts == i_framepts);
-        }
-        else
-        {
-            b_dequeue = false;
-        }
-
-    } while(b_dequeue);
-
-    return VLC_SUCCESS;
-}
-
 static void DrainDPBLocked(decoder_t *p_dec, bool flush)
 {
     decoder_sys_t *p_sys = p_dec->p_sys;
-    for ( ;; )
+    while (p_sys->dpb.i_size > 0)
     {
-        picture_t *p_fields;
-        if(RemoveOneFrameFromDPB(p_sys, &p_fields) != VLC_SUCCESS)
-            break;
-        for ( ; p_fields; )
+        picture_t *p_output = OutputNextFrameFromDPB(&p_sys->dpb, &p_sys->pts);
+        if(p_output)
         {
-            picture_t *p_next = p_fields->p_next;
-            p_fields->p_next = NULL;
             if (flush)
-                picture_Release(p_fields);
+                picture_Release(p_output);
             else
-                decoder_QueueVideo(p_dec, p_fields);
-            p_fields = p_next;
+                decoder_QueueVideo(p_dec, p_output);
         }
     }
 }
@@ -991,52 +849,45 @@ static void OnDecodedFrame(decoder_t *p_dec, frame_info_t *p_info)
 {
     decoder_sys_t *p_sys = p_dec->p_sys;
 
-    if(!p_sys->b_invalid_pic_reorder_max &&
-       p_info->i_max_reorder != p_sys->i_pic_reorder_max)
+    if(!p_sys->dpb.b_invalid_pic_reorder_max &&
+       p_info->i_max_pics_buffering != p_sys->dpb.i_max_pics)
     {
-        p_sys->i_pic_reorder_max = p_info->i_max_reorder;
-        pic_pacer_UpdateMaxBuffering(p_sys->pic_pacer, p_sys->i_pic_reorder_max);
+        p_sys->dpb.i_max_pics = p_info->i_max_pics_buffering;
+        pic_pacer_UpdateMaxBuffering(p_sys->pic_pacer, p_sys->dpb.i_max_pics);
     }
 
-    while(p_info->b_flush || p_sys->i_pic_reorder >= p_sys->i_pic_reorder_max)
+    /* First check if DPB sizing was correct before removing one frame */
+    if (!p_sys->dpb.b_strict_reorder && !p_info->b_flush &&
+        p_sys->dpb.i_size == p_sys->dpb.i_max_pics &&
+        p_sys->dpb.i_size && p_sys->dpb.i_max_pics < DPB_MAX_PICS)
     {
-        /* First check if DPB sizing was correct before removing one frame */
-        if (p_sys->p_pic_reorder && !p_sys->b_strict_reorder && !p_info->b_flush &&
-            p_sys->i_pic_reorder_max < H264_MAX_DPB)
+        if (p_sys->dpb.b_poc_based_reorder && p_sys->dpb.p_entries->i_foc > p_info->i_foc)
         {
-            if (p_sys->b_poc_based_reorder && p_sys->p_pic_reorder->i_foc > p_info->i_foc)
-            {
-                p_sys->b_invalid_pic_reorder_max = true;
-                p_sys->i_pic_reorder_max++;
-                pic_pacer_UpdateMaxBuffering(p_sys->pic_pacer, p_sys->i_pic_reorder_max);
-                msg_Dbg(p_dec, "Raising max DPB to %"PRIu8, p_sys->i_pic_reorder_max);
-                break;
-            }
-            else if (!p_sys->b_poc_based_reorder &&
-                     p_info->pts > VLC_TICK_INVALID &&
-                     p_sys->p_pic_reorder->pts > p_info->pts)
-            {
-                p_sys->b_invalid_pic_reorder_max = true;
-                p_sys->i_pic_reorder_max++;
-                pic_pacer_UpdateMaxBuffering(p_sys->pic_pacer, p_sys->i_pic_reorder_max);
-                msg_Dbg(p_dec, "Raising max DPB to %"PRIu8, p_sys->i_pic_reorder_max);
-                break;
-            }
+            p_sys->dpb.b_invalid_pic_reorder_max = true;
+            p_sys->dpb.i_max_pics++;
+            pic_pacer_UpdateMaxBuffering(p_sys->pic_pacer, p_sys->dpb.i_max_pics);
+            msg_Dbg(p_dec, "Raising max DPB to %"PRIu8, p_sys->dpb.i_max_pics);
         }
-
-        picture_t *p_fields;
-        if(RemoveOneFrameFromDPB(p_sys, &p_fields) != VLC_SUCCESS)
-            break;
-        for(; p_fields;)
+        else if (!p_sys->dpb.b_poc_based_reorder &&
+                 p_info->pts > VLC_TICK_INVALID &&
+                 p_sys->dpb.p_entries->pts > p_info->pts)
         {
-            picture_t *p_next = p_fields->p_next;
-            p_fields->p_next = NULL;
-            decoder_QueueVideo(p_dec, p_fields);
-            p_fields = p_next;
+            p_sys->dpb.b_invalid_pic_reorder_max = true;
+            p_sys->dpb.i_max_pics++;
+            pic_pacer_UpdateMaxBuffering(p_sys->pic_pacer, p_sys->dpb.i_max_pics);
+            msg_Dbg(p_dec, "Raising max DPB to %"PRIu8, p_sys->dpb.i_max_pics);
         }
     }
 
-    InsertIntoDPB(p_sys, p_info);
+    while(p_info->b_flush || p_sys->dpb.i_size >= p_sys->dpb.i_max_pics)
+    {
+        picture_t *p_output = OutputNextFrameFromDPB(&p_sys->dpb, &p_sys->pts);
+        if(!p_output)
+            break;
+        decoder_QueueVideo(p_dec, p_output);
+    }
+
+    InsertIntoDPB(&p_sys->dpb, p_info);
 }
 
 static CMVideoCodecType CodecPrecheck(decoder_t *p_dec)
@@ -1464,7 +1315,8 @@ static int OpenDecoder(vlc_object_t *p_this)
     p_sys->session = NULL;
     p_sys->codec = codec;
     p_sys->videoFormatDescription = NULL;
-    p_sys->i_pic_reorder_max = 4;
+    p_sys->dpb.i_max_pics = 4;
+    p_sys->dpb.i_fields_per_buffer = 1;
     p_sys->vtsession_status = VTSESSION_STATUS_OK;
     p_sys->b_cvpx_format_forced = false;
     /* will be fixed later */
@@ -1521,8 +1373,8 @@ static int OpenDecoder(vlc_object_t *p_this)
             p_sys->pf_configure_vout = ConfigureVoutH264;
             p_sys->pf_copy_extradata = CopyDecoderExtradataH264;
             p_sys->pf_fill_reorder_info = FillReorderInfoH264;
-            p_sys->b_strict_reorder = false;
-            p_sys->b_poc_based_reorder = true;
+            p_sys->dpb.b_strict_reorder = false;
+            p_sys->dpb.b_poc_based_reorder = true;
             p_sys->start_sync_state = STATE_BITSTREAM_WAITING_RAP;
             break;
 
@@ -1537,8 +1389,8 @@ static int OpenDecoder(vlc_object_t *p_this)
             p_sys->pf_configure_vout = ConfigureVoutHEVC;
             p_sys->pf_copy_extradata = CopyDecoderExtradataHEVC;
             p_sys->pf_fill_reorder_info = FillReorderInfoHEVC;
-            p_sys->b_strict_reorder = true;
-            p_sys->b_poc_based_reorder = true;
+            p_sys->dpb.b_strict_reorder = true;
+            p_sys->dpb.b_poc_based_reorder = true;
             p_sys->start_sync_state = STATE_BITSTREAM_WAITING_RAP;
             break;
 
@@ -1744,7 +1596,7 @@ static CMSampleBufferRef VTSampleBufferCreate(decoder_t *p_dec,
     CMBlockBufferRef  block_buf = NULL;
     CMSampleBufferRef sample_buf = NULL;
     CMTime pts;
-    if (!p_sys->b_poc_based_reorder && p_block->i_pts == VLC_TICK_INVALID)
+    if (!p_sys->dpb.b_poc_based_reorder && p_block->i_pts == VLC_TICK_INVALID)
         pts = CMTimeMake(p_block->i_dts, CLOCK_FREQ);
     else
         pts = CMTimeMake(p_block->i_pts, CLOCK_FREQ);
@@ -1906,8 +1758,8 @@ static void Drain(decoder_t *p_dec, bool flush)
 
     vlc_mutex_lock(&p_sys->lock);
     DrainDPBLocked(p_dec, flush);
-    picture_t *p_output;
-    assert(RemoveOneFrameFromDPB(p_sys, &p_output) == VLC_EGENERIC);
+    assert(p_sys->dpb.i_size == 0);
+    assert(p_sys->dpb.p_entries == NULL);
     p_sys->b_discard_decoder_output = false;
     p_sys->sync_state = p_sys->start_sync_state;
     vlc_mutex_unlock(&p_sys->lock);
@@ -2094,7 +1946,7 @@ static int DecodeBlock(decoder_t *p_dec, block_t *p_block)
         goto skip;
     }
 
-    pic_pacer_WaitAllocatableSlot(p_sys->pic_pacer);
+    pic_pacer_WaitAllocatableSlot(p_sys->pic_pacer, p_info->b_field);
 
     VTDecodeInfoFlags flagOut;
     VTDecodeFrameFlags decoderFlags = kVTDecodeFrame_EnableAsynchronousDecompression;
@@ -2106,7 +1958,7 @@ static int DecodeBlock(decoder_t *p_dec, block_t *p_block)
     enum vtsession_status vtsession_status;
     if (HandleVTStatus(p_dec, status, &vtsession_status) == VLC_SUCCESS)
     {
-        pic_pacer_AccountScheduledDecode(p_sys->pic_pacer);
+        pic_pacer_AccountScheduledDecode(p_sys->pic_pacer, p_info->b_field);
 
         if(p_sys->decoder_state != STATE_DECODER_STARTED)
         {
@@ -2216,9 +2068,8 @@ video_context_OnPicReleased(vlc_video_context *vctx, unsigned nb_fields)
 {
     struct pic_pacer *pic_pacer =
         vlc_video_context_GetCVPXPrivate(vctx, CVPX_VIDEO_CONTEXT_VIDEOTOOLBOX);
-    VLC_UNUSED(nb_fields);
 
-    pic_pacer_AccountDeallocation(pic_pacer);
+    pic_pacer_AccountDeallocation(pic_pacer, nb_fields == 1);
 }
 
 static void DecoderCallback(void *decompressionOutputRefCon,
@@ -2233,6 +2084,7 @@ static void DecoderCallback(void *decompressionOutputRefCon,
     decoder_t *p_dec = (decoder_t *)decompressionOutputRefCon;
     decoder_sys_t *p_sys = p_dec->p_sys;
     frame_info_t *p_info = (frame_info_t *) sourceFrameRefCon;
+    const bool b_field = p_info->b_field;
 
     vlc_mutex_lock(&p_sys->lock);
     if (p_sys->b_discard_decoder_output)
@@ -2305,7 +2157,7 @@ static void DecoderCallback(void *decompressionOutputRefCon,
                  * allocating way too many frames. This can be problematic for 4K
                  * 10bits. To fix this issue, we ensure that we don't have too many
                  * output frames allocated by waiting for the vout to release them. */
-            pic_pacer_AccountAllocation(p_sys->pic_pacer);
+            pic_pacer_AccountAllocation(p_sys->pic_pacer, p_info->b_field);
         }
     }
 
@@ -2322,7 +2174,7 @@ static void DecoderCallback(void *decompressionOutputRefCon,
 end:
     free(p_info);
     vlc_mutex_unlock(&p_sys->lock);
-    pic_pacer_AccountFinishedDecode(p_sys->pic_pacer);
+    pic_pacer_AccountFinishedDecode(p_sys->pic_pacer, b_field);
     return;
 }
 


=====================================
modules/codec/videotoolbox/dpb.c
=====================================
@@ -0,0 +1,116 @@
+/*****************************************************************************
+ * dpb.c: decoder picture output pacing
+ *****************************************************************************
+ * Copyright © 2015-2023 VideoLabs, VideoLAN and VLC authors
+  *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "dpb.h"
+
+#include <assert.h>
+
+//#define DPB_DEBUG
+
+void InsertIntoDPB(struct dpb_s *dpb, frame_info_t *p_info)
+{
+    frame_info_t **pp_lead_in = &dpb->p_entries;
+
+    for ( ;; pp_lead_in = & ((*pp_lead_in)->p_next))
+    {
+        bool b_insert;
+        if (*pp_lead_in == NULL)
+            b_insert = true;
+        else if (dpb->b_poc_based_reorder)
+            b_insert = ((*pp_lead_in)->i_foc > p_info->i_foc);
+        else
+            b_insert = ((*pp_lead_in)->pts >= p_info->pts);
+
+        if (b_insert)
+        {
+            p_info->p_next = *pp_lead_in;
+            *pp_lead_in = p_info;
+            dpb->i_stored_fields += (p_info->b_field ? 1 : 2);
+            if(dpb->i_fields_per_buffer == 2)
+                dpb->i_size = (dpb->i_stored_fields + 1)/2;
+            else
+                dpb->i_size++;
+            break;
+        }
+    }
+#ifdef DPB_DEBUG
+    for (frame_info_t *p_in=dpb->p_entries; p_in; p_in = p_in->p_next)
+        printf(" %d", p_in->i_foc);
+    printf("\n");
+#endif
+}
+
+picture_t * OutputNextFrameFromDPB(struct dpb_s *dpb, date_t *ptsdate)
+{
+    frame_info_t *p_info = dpb->p_entries;
+    if (p_info == NULL)
+        return NULL;
+
+    /* Asynchronous fallback time init */
+    if(date_Get(ptsdate) == VLC_TICK_INVALID)
+    {
+        date_Set(ptsdate, p_info->pts != VLC_TICK_INVALID ?
+                              p_info->pts : p_info->dts );
+    }
+
+    /* Compute time from output if missing */
+    if (p_info->pts == VLC_TICK_INVALID)
+        p_info->pts = date_Get(ptsdate);
+    else
+        date_Set(ptsdate, p_info->pts);
+
+    /* Update frame rate (used on interpolation) */
+    if(p_info->field_rate_num != ptsdate->i_divider_num ||
+        p_info->field_rate_den != ptsdate->i_divider_den)
+    {
+        /* no date_Change due to possible invalid num */
+        date_Init(ptsdate, p_info->field_rate_num,
+                  p_info->field_rate_den);
+        date_Set(ptsdate, p_info->pts);
+    }
+
+    /* Set next picture time, in case it is missing */
+    if (p_info->i_length)
+        date_Set(ptsdate, p_info->pts + p_info->i_length);
+    else
+        date_Increment(ptsdate, p_info->i_num_ts);
+
+    /* Extract attached field to output */
+    picture_t *p_output = p_info->p_picture;
+    if( p_info->p_picture ) /* Can have no picture attached to entry on error */
+    {
+        if( p_info->p_picture->date == VLC_TICK_INVALID )
+            p_info->p_picture->date = p_info->pts;
+    }
+
+    dpb->i_stored_fields -= (p_info->b_field ? 1 : 2);
+    if(dpb->i_fields_per_buffer == 2)
+        dpb->i_size = (dpb->i_stored_fields + 1)/2;
+    else
+        dpb->i_size--;
+
+    dpb->p_entries = p_info->p_next;
+    free(p_info);
+
+    return p_output;
+}


=====================================
modules/codec/videotoolbox/dpb.h
=====================================
@@ -0,0 +1,69 @@
+/*****************************************************************************
+ * dpb.h: decoder picture output pacing
+ *****************************************************************************
+ * Copyright © 2015-2023 VideoLabs, VideoLAN and VLC authors
+  *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifndef VIDEOTOOLBOX_DPB_H
+#define VIDEOTOOLBOX_DPB_H
+
+#define DPB_MAX_PICS 16
+
+#include <vlc_common.h>
+#include <vlc_tick.h>
+#include <vlc_picture.h>
+
+typedef struct frame_info_t frame_info_t;
+
+struct frame_info_t
+{
+    picture_t *p_picture;
+    int i_poc;
+    int i_foc;
+    vlc_tick_t pts;
+    vlc_tick_t dts;
+    unsigned field_rate_num;
+    unsigned field_rate_den;
+    bool b_flush;
+    bool b_eos;
+    bool b_keyframe;
+    bool b_leading;
+    bool b_field;
+    bool b_progressive;
+    bool b_top_field_first;
+    uint8_t i_num_ts;
+    uint8_t i_max_pics_buffering;
+    unsigned i_length;
+    frame_info_t *p_next;
+};
+
+struct dpb_s
+{
+    frame_info_t *p_entries;
+    uint8_t i_size; /* number of virtual buffers used (depends on i_fields_per_buffer) */
+    uint8_t i_stored_fields;
+    uint8_t i_max_pics;
+    uint8_t i_fields_per_buffer; /* stores 2 fields or 1 field/frame per buffer */
+    bool b_strict_reorder;
+    bool b_invalid_pic_reorder_max;
+    bool b_poc_based_reorder;
+};
+
+void InsertIntoDPB(struct dpb_s *, frame_info_t *);
+
+picture_t * OutputNextFrameFromDPB(struct dpb_s *, date_t *);
+
+#endif // VIDEOTOOLBOX_DPB_H


=====================================
modules/codec/videotoolbox/dpb_test.c
=====================================
@@ -0,0 +1,358 @@
+/*****************************************************************************
+ * dpb_test.c:
+ *****************************************************************************
+ * Copyright © 2023 VideoLabs, VideoLAN and VLC authors
+  *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#undef NDEBUG
+
+#ifndef DPB_DEBUG
+# warning "DPB_DEBUG not defined, no useful test info"
+#endif
+
+#include "dpb.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+static void pic_release(picture_t *p)
+{
+    free(p);
+}
+
+static inline frame_info_t *withpic(frame_info_t *info, int poc)
+{
+    info->p_picture = calloc(1, sizeof(*info->p_picture));
+    if(!info->p_picture)
+        abort();
+    info->p_picture->p_sys = (void *)(uint64_t)poc;
+    return info;
+}
+
+static inline frame_info_t *infocopy(const frame_info_t *ref)
+{
+    frame_info_t *frame;
+    if(ref)
+        frame = malloc(2 * sizeof(*frame));
+    else
+        frame = calloc(2, sizeof(*frame));
+    if(!frame)
+        abort();
+    if(ref)
+    {
+        *frame = *ref;
+        frame->p_picture = NULL;
+        frame->p_next = NULL;
+    }
+    return frame;
+}
+
+static void VaCheckOutput(picture_t *output, va_list ap)
+{
+    for(;;)
+    {
+        int poc = va_arg(ap, int);
+        if(poc == -1)
+        {
+            fprintf(stderr, "no output expected\n");
+            assert(output == NULL);
+            break;
+        }
+        if(output == NULL)
+        {
+            fprintf(stderr, "no output, was expected %d", poc);
+            abort();
+        }
+        int outpoc = ((uint64_t)output->p_sys);
+        fprintf(stderr, "output %d, expected %d\n", outpoc, poc);
+        assert(outpoc == poc);
+        picture_t *next = output->p_next;
+        output->p_next = NULL;
+        pic_release(output);
+        output = next;
+    };
+    assert(output == NULL);
+}
+
+static void CheckDrain(struct dpb_s *dpb, date_t *ptsdate, ...)
+{
+    fprintf(stderr,"drain\n");
+    picture_t *output = NULL;
+    picture_t **next = &output;
+    while(dpb->i_size)
+    {
+        *next = OutputNextFrameFromDPB(dpb, ptsdate);
+        if(!*next)
+            break;
+        next = &((*next)->p_next);
+    }
+    va_list args;
+    va_start(args, ptsdate);
+    VaCheckOutput(output, args);
+    va_end(args);
+}
+
+static void CheckOutput(struct dpb_s *dpb, date_t *ptsdate, frame_info_t *info, ...)
+{
+    fprintf(stderr, "enqueing foc %d flush %d dpb sz %d\n", info->i_foc,
+            info->b_flush, dpb->i_size);
+    dpb->i_max_pics = info->i_max_pics_buffering;
+    picture_t *output = NULL;
+    picture_t **next = &output;
+    while(info->b_flush || dpb->i_size >= dpb->i_max_pics)
+    {
+        *next = OutputNextFrameFromDPB(dpb, ptsdate);
+        if(!*next)
+            break;
+        next = &((*next)->p_next);
+    }
+    assert(dpb->i_size < DPB_MAX_PICS);
+    va_list args;
+    va_start(args, info);
+    VaCheckOutput(output, args);
+    va_end(args);
+    InsertIntoDPB(dpb, info);
+}
+
+static void CheckDPBWithFramesTest()
+{
+    struct dpb_s dpb = {0};
+    dpb.b_strict_reorder = true;
+    dpb.b_poc_based_reorder = true;
+    dpb.i_fields_per_buffer = 2;
+
+    frame_info_t info = {0};
+    info.field_rate_num = 30000;
+    info.field_rate_den = 1000;
+    info.b_progressive = true;
+    info.b_top_field_first = true;
+    info.i_num_ts = 2;
+    info.i_max_pics_buffering = 4;
+
+    date_t pts;
+    date_Init(&pts, info.field_rate_num, info.field_rate_den);
+    date_Set(&pts, VLC_TICK_0);
+
+    info.i_foc = 0;
+    info.i_poc = info.i_foc & ~1;
+    info.b_flush = (info.i_foc == 0);
+    CheckOutput(&dpb, &pts, withpic(infocopy(&info), info.i_foc), -1);
+
+    info.i_foc = 4;
+    info.i_poc = info.i_foc & ~1;
+    info.b_flush = (info.i_foc == 0);
+    CheckOutput(&dpb, &pts, withpic(infocopy(&info), info.i_foc), -1);
+
+    info.i_foc = 2;
+    info.i_poc = info.i_foc & ~1;
+    info.b_flush = (info.i_foc == 0);
+    CheckOutput(&dpb, &pts, withpic(infocopy(&info), info.i_foc), -1);
+
+    info.i_foc = 0;
+    info.i_poc = info.i_foc & ~1;
+    info.b_flush = (info.i_foc == 0);
+    CheckOutput(&dpb, &pts, withpic(infocopy(&info), info.i_foc), 0, 2, 4, -1);
+
+    info.i_foc = 8;
+    info.i_poc = info.i_foc & ~1;
+    info.b_flush = (info.i_foc == 0);
+    CheckOutput(&dpb, &pts, withpic(infocopy(&info), info.i_foc), -1);
+
+    info.i_foc = 2;
+    info.i_poc = info.i_foc & ~1;
+    info.b_flush = (info.i_foc == 0);
+    CheckOutput(&dpb, &pts, withpic(infocopy(&info), info.i_foc), -1);
+
+    info.i_foc = 6;
+    info.i_poc = info.i_foc & ~1;
+    info.b_flush = (info.i_foc == 0);
+    CheckOutput(&dpb, &pts, withpic(infocopy(&info), info.i_foc), -1);
+
+    info.i_foc = 4;
+    info.i_poc = info.i_foc & ~1;
+    info.b_flush = (info.i_foc == 0);
+    CheckOutput(&dpb, &pts, withpic(infocopy(&info), info.i_foc), 0, -1);
+
+    /* depth reduction */
+    info.i_max_pics_buffering = 2;
+
+    info.i_foc = 10;
+    info.i_poc = info.i_foc & ~1;
+    info.b_flush = (info.i_foc == 0);
+    CheckOutput(&dpb, &pts, withpic(infocopy(&info), info.i_foc), 2, 4, 6, -1);
+
+    info.i_foc = 0;
+    info.i_poc = info.i_foc & ~1;
+    info.b_flush = (info.i_foc == 0);
+    CheckOutput(&dpb, &pts, withpic(infocopy(&info), info.i_foc), 8, 10, -1);
+    assert(dpb.i_size == 1);
+
+    CheckDrain(&dpb, &pts, 0, -1);
+
+    assert(dpb.i_size == 0);
+}
+
+static void CheckDPBWithFieldsTest()
+{
+    struct dpb_s dpb = {0};
+    dpb.b_strict_reorder = true;
+    dpb.b_poc_based_reorder = true;
+    dpb.i_fields_per_buffer = 1;
+
+    frame_info_t info = {0};
+    info.field_rate_num = 30000;
+    info.field_rate_den = 1000;
+    info.b_progressive = true;
+    info.b_top_field_first = true;
+    info.i_num_ts = 1;
+    info.i_max_pics_buffering = 2;
+
+    /* Codec stores 1 field per buffer */
+    date_t pts;
+    date_Init(&pts, info.field_rate_num, info.field_rate_den);
+    date_Set(&pts, VLC_TICK_0);
+
+    info.b_field = true;
+
+    info.i_foc = 0;
+    info.i_poc = info.i_foc & ~1;
+    info.b_flush = (info.i_foc == 0);
+    CheckOutput(&dpb, &pts, withpic(infocopy(&info), info.i_foc), -1);
+
+    info.i_foc = 2;
+    info.i_poc = info.i_foc & ~1;
+    info.b_flush = (info.i_foc == 0);
+    CheckOutput(&dpb, &pts, withpic(infocopy(&info), info.i_foc), -1);
+
+    assert(dpb.i_stored_fields == 2);
+    assert(dpb.i_size == 2);
+
+    info.i_foc = 1;
+    info.i_poc = info.i_foc & ~1;
+    info.b_flush = (info.i_foc == 0);
+    CheckOutput(&dpb, &pts, withpic(infocopy(&info), info.i_foc), 0, -1);
+
+    CheckDrain(&dpb, &pts, 1, 2, -1);
+
+    assert(dpb.i_stored_fields == 0);
+    assert(dpb.i_size == 0);
+
+    /* Codec stores 2 fields per buffer */
+    dpb.i_fields_per_buffer = 2;
+
+    info.i_foc = 0;
+    info.i_poc = info.i_foc & ~1;
+    info.b_flush = (info.i_foc == 0);
+    CheckOutput(&dpb, &pts, withpic(infocopy(&info), info.i_foc), -1);
+
+    info.i_foc = 2;
+    info.i_poc = info.i_foc & ~1;
+    info.b_flush = (info.i_foc == 0);
+    CheckOutput(&dpb, &pts, withpic(infocopy(&info), info.i_foc), -1);
+
+    assert(dpb.i_stored_fields == 2);
+    assert(dpb.i_size == 1);
+
+    info.i_foc = 1;
+    info.i_poc = info.i_foc & ~1;
+    info.b_flush = (info.i_foc == 0);
+    CheckOutput(&dpb, &pts, withpic(infocopy(&info), info.i_foc), -1);
+
+    assert(dpb.i_stored_fields == 3);
+    assert(dpb.i_size == 2);
+
+    CheckDrain(&dpb, &pts, 0, 1, 2, -1);
+
+    assert(dpb.i_stored_fields == 0);
+    assert(dpb.i_size == 0);
+
+    /* progressive/mbaff/field mix for fun 1 field per buffer */
+    dpb.i_fields_per_buffer = 1;
+    info.i_max_pics_buffering = 3;
+
+    info.b_field = false;
+    info.i_foc = 0;
+    info.i_poc = info.i_foc & ~1;
+    info.b_flush = (info.i_foc == 0);
+    CheckOutput(&dpb, &pts, withpic(infocopy(&info), info.i_foc), -1);
+
+    info.b_field = true;
+    info.i_foc = 3;
+    info.i_poc = info.i_foc & ~1;
+    info.b_flush = (info.i_foc == 0);
+    CheckOutput(&dpb, &pts, withpic(infocopy(&info), info.i_foc), -1);
+
+    assert(dpb.i_stored_fields == 3);
+    assert(dpb.i_size == 2);
+
+    info.i_foc = 2;
+    info.i_poc = info.i_foc & ~1;
+    info.b_flush = (info.i_foc == 0);
+    CheckOutput(&dpb, &pts, withpic(infocopy(&info), info.i_foc), -1);
+
+    assert(dpb.i_stored_fields == 4);
+    assert(dpb.i_size == 3);
+
+    CheckDrain(&dpb, &pts, 0, 2, 3, -1);
+
+    assert(dpb.i_stored_fields == 0);
+    assert(dpb.i_size == 0);
+
+    /* progressive/mbaff/field mix for fun 2 fields per buffer */
+    dpb.i_fields_per_buffer = 2;
+    info.i_max_pics_buffering = 3;
+
+    info.b_field = false;
+    info.i_foc = 0;
+    info.i_poc = info.i_foc & ~1;
+    info.b_flush = (info.i_foc == 0);
+    CheckOutput(&dpb, &pts, withpic(infocopy(&info), info.i_foc), -1);
+
+    info.b_field = true;
+    info.i_foc = 3;
+    info.i_poc = info.i_foc & ~1;
+    info.b_flush = (info.i_foc == 0);
+    CheckOutput(&dpb, &pts, withpic(infocopy(&info), info.i_foc), -1);
+
+    assert(dpb.i_stored_fields == 3);
+    assert(dpb.i_size == 2);
+
+    info.i_foc = 2;
+    info.i_poc = info.i_foc & ~1;
+    info.b_flush = (info.i_foc == 0);
+    CheckOutput(&dpb, &pts, withpic(infocopy(&info), info.i_foc), -1);
+
+    assert(dpb.i_stored_fields == 4);
+    assert(dpb.i_size == 2);
+
+    CheckDrain(&dpb, &pts, 0, 2, 3, -1);
+
+    assert(dpb.i_stored_fields == 0);
+    assert(dpb.i_size == 0);
+}
+
+int main(void)
+{
+    CheckDPBWithFramesTest();
+    CheckDPBWithFieldsTest();
+    return 0;
+}


=====================================
modules/codec/videotoolbox/pacer.c
=====================================
@@ -41,62 +41,63 @@ void pic_pacer_Init(struct pic_pacer *pic_pacer)
 {
     vlc_mutex_init(&pic_pacer->lock);
     vlc_cond_init(&pic_pacer->wait);
-    pic_pacer->nb_out = 0;
-    pic_pacer->allocated_max = 6;
-    pic_pacer->allocated_next = pic_pacer->allocated_max;
-    pic_pacer->queued_for_decode = 0;
+    pic_pacer->nb_fields_out = 0;
+    pic_pacer->allocated_fields_max = 6 /* pics */ * 2;
+    pic_pacer->allocated_fields_next = pic_pacer->allocated_fields_max;
+    pic_pacer->queued_fields_for_decode = 0;
 }
 
-void pic_pacer_AccountAllocation(struct pic_pacer *pic_pacer)
+void pic_pacer_AccountAllocation(struct pic_pacer *pic_pacer, bool b_field)
 {
     vlc_mutex_lock(&pic_pacer->lock);
-    pic_pacer->nb_out += 1;
+    pic_pacer->nb_fields_out += b_field ? 1 : 2;
     vlc_mutex_unlock(&pic_pacer->lock);
 }
 
-void pic_pacer_AccountScheduledDecode(struct pic_pacer *pic_pacer)
+void pic_pacer_AccountScheduledDecode(struct pic_pacer *pic_pacer, bool b_field)
 {
     vlc_mutex_lock(&pic_pacer->lock);
-    pic_pacer->queued_for_decode += 1;
+    pic_pacer->queued_fields_for_decode += b_field ? 1 : 2;
     vlc_mutex_unlock(&pic_pacer->lock);
 }
 
-void pic_pacer_AccountFinishedDecode(struct pic_pacer *pic_pacer)
+void pic_pacer_AccountFinishedDecode(struct pic_pacer *pic_pacer, bool b_field)
 {
     vlc_mutex_lock(&pic_pacer->lock);
-    pic_pacer->queued_for_decode -= 1;
+    pic_pacer->queued_fields_for_decode -= b_field ? 1 : 2;
     vlc_cond_signal(&pic_pacer->wait);
     vlc_mutex_unlock(&pic_pacer->lock);
 }
 
-void pic_pacer_WaitAllocatableSlot(struct pic_pacer *pic_pacer)
+void pic_pacer_WaitAllocatableSlot(struct pic_pacer *pic_pacer, bool b_field)
 {
     vlc_mutex_lock(&pic_pacer->lock);
-    uint8_t allocatable_total = pic_pacer->allocated_max + PIC_PACER_DECODE_QUEUE;
+    uint8_t allocatable_fields_total = pic_pacer->allocated_fields_max + PIC_PACER_DECODE_QUEUE;
 
-    while( pic_pacer->queued_for_decode + pic_pacer->nb_out >= allocatable_total )
+    while( pic_pacer->queued_fields_for_decode +
+               pic_pacer->nb_fields_out + (1 - !!b_field) >= allocatable_fields_total )
     {
 #ifdef PIC_PACER_DEBUG
         fprintf(stderr, "input pacing %d+%d >= %d\n",
-                pic_pacer->queued_for_decode, pic_pacer->nb_out, allocatable_total);
+                pic_pacer->queued_fields_for_decode, pic_pacer->nb_fields_out, allocatable_fields_total);
 #endif
         vlc_cond_wait(&pic_pacer->wait, &pic_pacer->lock);
         /*update*/
-        allocatable_total = pic_pacer->allocated_max + PIC_PACER_DECODE_QUEUE;
+        allocatable_fields_total = pic_pacer->allocated_fields_max + PIC_PACER_DECODE_QUEUE;
     }
     vlc_mutex_unlock(&pic_pacer->lock);
 }
 
-void pic_pacer_AccountDeallocation(struct pic_pacer *pic_pacer)
+void pic_pacer_AccountDeallocation(struct pic_pacer *pic_pacer, bool b_field)
 {
     vlc_mutex_lock(&pic_pacer->lock);
-    assert(pic_pacer->nb_out > 0);
-    pic_pacer->nb_out -= 1;
+    assert(pic_pacer->nb_fields_out > 0);
+    pic_pacer->nb_fields_out -= (b_field ? 1 : 2);
 
     /* our shrink condition */
-    if(pic_pacer->allocated_next < pic_pacer->allocated_max &&
-        pic_pacer->nb_out <= pic_pacer->allocated_next)
-        pic_pacer->allocated_max = pic_pacer->allocated_next;
+    if(pic_pacer->allocated_fields_next < pic_pacer->allocated_fields_max &&
+        pic_pacer->nb_fields_out <= pic_pacer->allocated_fields_next)
+        pic_pacer->allocated_fields_max = pic_pacer->allocated_fields_next;
 
     vlc_cond_signal(&pic_pacer->wait);
 
@@ -107,21 +108,21 @@ void pic_pacer_UpdateMaxBuffering(struct pic_pacer *pic_pacer, uint8_t pic_max)
 {
     vlc_mutex_lock(&pic_pacer->lock);
 
-    pic_max += PIC_PACER_ALLOCATABLE_MAX;
-    bool b_growing  = pic_max > pic_pacer->allocated_max;
+    const uint8_t fields_max = (pic_max + PIC_PACER_ALLOCATABLE_MAX) * 2;
+    bool b_growing  = fields_max > pic_pacer->allocated_fields_max;
 #ifdef PIC_PACER_DEBUG
     fprintf(stderr, "updating pacer max %d/%d to %d\n",
-            pic_pacer->nb_out, pic_pacer->allocated_max, pic_reorder_max);
+            pic_pacer->nb_fields_out, pic_pacer->allocated_fields_max / 2, pic_max);
 #endif
     if(b_growing)
     {
-        pic_pacer->allocated_max = pic_max;
-        pic_pacer->allocated_next = pic_max;
+        pic_pacer->allocated_fields_max = fields_max;
+        pic_pacer->allocated_fields_next = fields_max;
         vlc_cond_signal(&pic_pacer->wait);
     }
     else
     {
-        pic_pacer->allocated_next = pic_max;
+        pic_pacer->allocated_fields_next = fields_max;
     }
 
     vlc_mutex_unlock(&pic_pacer->lock);


=====================================
modules/codec/videotoolbox/pacer.h
=====================================
@@ -24,25 +24,25 @@ struct pic_pacer
 {
     vlc_mutex_t lock;
     vlc_cond_t  wait;
-    uint8_t     nb_out;
-    uint8_t     allocated_max;
-    uint8_t     allocated_next;
-    uint8_t     queued_for_decode;
+    uint8_t     nb_fields_out;
+    uint8_t     allocated_fields_max;
+    uint8_t     allocated_fields_next;
+    uint8_t     queued_fields_for_decode;
 };
 
 void pic_pacer_Clean(struct pic_pacer *);
 
 void pic_pacer_Init(struct pic_pacer *);
 
-void pic_pacer_AccountAllocation(struct pic_pacer *);
+void pic_pacer_AccountAllocation(struct pic_pacer *, bool b_field);
 
-void pic_pacer_AccountScheduledDecode(struct pic_pacer *);
+void pic_pacer_AccountScheduledDecode(struct pic_pacer *, bool b_field);
 
-void pic_pacer_AccountFinishedDecode(struct pic_pacer *);
+void pic_pacer_AccountFinishedDecode(struct pic_pacer *, bool b_field);
 
-void pic_pacer_WaitAllocatableSlot(struct pic_pacer *);
+void pic_pacer_WaitAllocatableSlot(struct pic_pacer *, bool b_field);
 
-void pic_pacer_AccountDeallocation(struct pic_pacer *);
+void pic_pacer_AccountDeallocation(struct pic_pacer *, bool b_field);
 
 void pic_pacer_UpdateMaxBuffering(struct pic_pacer *, uint8_t);
 



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/f5f2eb803d6b5cd90219c71dba2a78491ac10650...4464af834ec0617ff6d6f4b39438c8112ce8d273

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/f5f2eb803d6b5cd90219c71dba2a78491ac10650...4464af834ec0617ff6d6f4b39438c8112ce8d273
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list