[vlc-commits] [Git][videolan/vlc][master] 11 commits: codec: videotoolbox: fix flush cases for hevc reorder with rasl output
Jean-Baptiste Kempf (@jbk)
gitlab at videolan.org
Sat Feb 18 16:44:59 UTC 2023
Jean-Baptiste Kempf pushed to branch master at VideoLAN / VLC
Commits:
d2ecef01 by Francois Cartegnie at 2023-02-18T16:29:36+00:00
codec: videotoolbox: fix flush cases for hevc reorder with rasl output
- - - - -
c5de4fe8 by Francois Cartegnie at 2023-02-18T16:29:36+00:00
codec: videotoolbox: process DPB on any decoding result
- - - - -
eb1fdc8f by Francois Cartegnie at 2023-02-18T16:29:36+00:00
codec: videotoolbox: only update reorder depth on next pic
- - - - -
fcdbabb2 by Francois Cartegnie at 2023-02-18T16:29:36+00:00
codec: videotoolbox: set strict DPB for HEVC
- - - - -
0c3688e7 by Francois Cartegnie at 2023-02-18T16:29:36+00:00
codec: videotoolbox: simplify by removing exclusive states booleans
- - - - -
45585498 by Francois Cartegnie at 2023-02-18T16:29:36+00:00
codec: videotoolbox: rename discard flag
- - - - -
5d545255 by Francois Cartegnie at 2023-02-18T16:29:36+00:00
codec: videotoolbox: do not discard decoder output on drain
- - - - -
1d2be66b by Francois Cartegnie at 2023-02-18T16:29:36+00:00
codec: videotoolbox: ensure flush on request
- - - - -
78c5d581 by Francois Cartegnie at 2023-02-18T16:29:36+00:00
codec: videotoolbox: have asynchronous pts/rate updates
- - - - -
13ae92c2 by Francois Cartegnie at 2023-02-18T16:29:36+00:00
codec: videotoolbox: fix async pacing
- - - - -
98a5b3ba by Francois Cartegnie at 2023-02-18T16:29:36+00:00
codec: videotoolbox: rework pacing
- - - - -
1 changed file:
- modules/codec/videotoolbox/decoder.c
Changes:
=====================================
modules/codec/videotoolbox/decoder.c
=====================================
@@ -83,13 +83,19 @@ 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;
};
@@ -99,6 +105,9 @@ struct frame_info_t
#define H264_MAX_DPB 16
#define VT_MAX_SEI_COUNT 16
+#define DEFAULT_FRAME_RATE_NUM 30000
+#define DEFAULT_FRAME_RATE_DEN 1001
+
typedef struct decoder_sys_t
{
CMVideoCodecType codec;
@@ -120,16 +129,28 @@ typedef struct decoder_sys_t
/* !Codec specific callbacks */
- bool b_vt_feed;
- bool b_vt_flush;
- bool b_vt_need_keyframe;
+ enum
+ {
+ STATE_BITSTREAM_WAITING_RAP = -2,
+ STATE_BITSTREAM_DISCARD_LEADING = -1,
+ STATE_BITSTREAM_SYNCED = 0,
+ } sync_state, start_sync_state;
+ enum
+ {
+ STATE_DECODER_WAITING_RAP = 0,
+ STATE_DECODER_STARTED,
+ } decoder_state;
VTDecompressionSessionRef session;
CMVideoFormatDescriptionRef videoFormatDescription;
+ /* Decoder Callback Synchronization */
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;
@@ -142,7 +163,6 @@ typedef struct decoder_sys_t
h264_poc_context_t h264_pocctx;
hevc_poc_ctx_t hevc_pocctx;
- bool b_drop_blocks;
date_t pts;
vlc_video_context *vctx;
@@ -150,16 +170,79 @@ typedef struct decoder_sys_t
} decoder_sys_t;
/* Picture pacer to work-around the VT session allocating too much CVPX buffers
- * that can lead to a OOM. cf. pic_pacer_Wait usage in DecoderCallback() */
+ * that can lead to a OOM. */
struct pic_pacer
{
vlc_mutex_t lock;
vlc_cond_t wait;
- uint8_t nb_field_out;
- uint8_t field_reorder_max;
+ uint8_t nb_out;
+ uint8_t allocated_max;
+ uint8_t allocated_next;
+ uint8_t queued_for_decode;
};
-static void pic_pacer_UpdateReorderMax(struct pic_pacer *, uint8_t, uint8_t);
+#define PIC_PACER_ALLOCATABLE_MAX (1 /* callback, pre-reorder */ \
+ + 2 /* filters */ \
+ + 1 /* display */ \
+ + 1 /* next/prev display */)
+#define PIC_PACER_DECODE_QUEUE 4 /* max async decode before callback */
+//#define PIC_PACER_DEBUG
+static void pic_pacer_UpdateReorderMax(struct pic_pacer *, uint8_t);
+
+static void pic_pacer_Destroy(void *priv)
+{
+ (void) priv;
+}
+
+static 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;
+}
+
+static void pic_pacer_AccountAllocation(struct pic_pacer *pic_pacer)
+{
+ vlc_mutex_lock(&pic_pacer->lock);
+ pic_pacer->nb_out += 1;
+ vlc_mutex_unlock(&pic_pacer->lock);
+}
+
+static void pic_pacer_AccountScheduledDecode(struct pic_pacer *pic_pacer)
+{
+ vlc_mutex_lock(&pic_pacer->lock);
+ pic_pacer->queued_for_decode += 1;
+ vlc_mutex_unlock(&pic_pacer->lock);
+}
+
+static void pic_pacer_AccountFinishedDecode(struct pic_pacer *pic_pacer)
+{
+ vlc_mutex_lock(&pic_pacer->lock);
+ pic_pacer->queued_for_decode -= 1;
+ vlc_cond_signal(&pic_pacer->wait);
+ vlc_mutex_unlock(&pic_pacer->lock);
+}
+
+static void pic_pacer_WaitAllocatableSlot(struct pic_pacer *pic_pacer)
+{
+ vlc_mutex_lock(&pic_pacer->lock);
+ uint8_t allocatable_total = pic_pacer->allocated_max + PIC_PACER_DECODE_QUEUE;
+
+ while( pic_pacer->queued_for_decode + pic_pacer->nb_out >= allocatable_total )
+ {
+#ifdef PIC_PACER_DEBUG
+ fprintf(stderr, "input pacing %d+%d >= %d\n",
+ pic_pacer->queued_for_decode, pic_pacer->nb_out, allocatable_total);
+#endif
+ vlc_cond_wait(&pic_pacer->wait, &pic_pacer->lock);
+ /*update*/
+ allocatable_total = pic_pacer->allocated_max + PIC_PACER_DECODE_QUEUE;
+ }
+ vlc_mutex_unlock(&pic_pacer->lock);
+}
#pragma mark - start & stop
@@ -297,32 +380,18 @@ 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);
if (!p_info->b_progressive)
p_info->b_top_field_first = (sei.i_pic_struct % 2 == 1);
/* Set frame rate for timings in case of missing rate */
- if ( (!p_dec->fmt_in->video.i_frame_rate_base ||
- !p_dec->fmt_in->video.i_frame_rate) &&
- p_sps->vui.i_time_scale && p_sps->vui.i_num_units_in_tick )
+ if (p_sps->vui.i_time_scale && p_sps->vui.i_num_units_in_tick)
{
- date_Change( &p_sys->pts, p_sps->vui.i_time_scale,
- p_sps->vui.i_num_units_in_tick );
+ p_info->field_rate_num = p_sps->vui.i_time_scale;
+ p_info->field_rate_den = p_sps->vui.i_num_units_in_tick;
}
-
- if(!p_sys->b_invalid_pic_reorder_max && i_nal_type == H264_NAL_SLICE_IDR)
- {
- unsigned dummy;
- uint8_t i_reorder;
- h264_get_dpb_values(p_sps, &i_reorder, &dummy);
- vlc_mutex_lock(&p_sys->lock);
- p_sys->i_pic_reorder_max = i_reorder;
- pic_pacer_UpdateReorderMax(p_sys->pic_pacer,
- p_sys->i_pic_reorder_max,
- p_info->i_num_ts);
- vlc_mutex_unlock(&p_sys->lock);
- }
-
}
return true; /* No need to parse further NAL */
@@ -625,27 +694,6 @@ static bool FillReorderInfoHEVC(decoder_t *p_dec, const block_t *p_block,
if (!p_sli)
return false;
- /* XXX: Work-around a VT bug on recent devices (iPhone X, MacBook
- * Pro 2017). The VT session will report a BadDataErr if you send a
- * RASL frame just after a CRA one. Indeed, RASL frames are
- * corrupted if the decoding start at an IRAP frame (IDR/CRA), VT
- * is likely failing to handle this case. */
- if (!p_sys->b_vt_feed && (i_nal_type != HEVC_NAL_IDR_W_RADL &&
- i_nal_type != HEVC_NAL_IDR_N_LP))
- p_sys->b_drop_blocks = true;
- else if (p_sys->b_drop_blocks)
- {
- if (i_nal_type == HEVC_NAL_RASL_N || i_nal_type == HEVC_NAL_RASL_R)
- {
- hevc_rbsp_release_slice_header(p_sli);
- return false;
- }
- else
- {
- p_sys->b_drop_blocks = false;
- }
- }
-
p_info->b_keyframe = i_nal_type >= HEVC_NAL_BLA_W_LP;
enum hevc_slice_type_e slice_type;
if (hevc_get_slice_type(p_sli, &slice_type))
@@ -673,34 +721,30 @@ static bool FillReorderInfoHEVC(decoder_t *p_dec, const block_t *p_block,
p_info->i_poc = POC;
p_info->i_foc = POC; /* clearly looks wrong :/ */
p_info->i_num_ts = hevc_get_num_clock_ts(p_sps, sei.p_timing);
- p_info->b_flush = (POC == 0);
+ p_info->i_max_reorder = hevc_get_max_num_reorder(p_vps);
+ p_info->b_flush = (POC == 0) ||
+ (i_nal_type >= HEVC_NAL_IDR_N_LP &&
+ i_nal_type <= HEVC_NAL_IRAP_VCL23);
p_info->b_field = (p_info->i_num_ts == 1);
p_info->b_progressive = hevc_frame_is_progressive(p_sps, sei.p_timing);
/* Set frame rate for timings in case of missing rate */
- if ( (!p_dec->fmt_in->video.i_frame_rate_base ||
- !p_dec->fmt_in->video.i_frame_rate) )
- {
- unsigned num, den;
- if (hevc_get_frame_rate(p_sps, p_vps, &num, &den))
- date_Change(&p_sys->pts, num, den);
- }
+ if(hevc_get_frame_rate(p_sps, p_vps, &p_info->field_rate_num,
+ &p_info->field_rate_den))
+ p_info->field_rate_num *= 2;
if (sei.p_timing)
hevc_release_sei_pic_timing(sei.p_timing);
-
- if (!p_sys->b_invalid_pic_reorder_max && p_vps)
- {
- vlc_mutex_lock(&p_sys->lock);
- p_sys->i_pic_reorder_max = hevc_get_max_num_reorder(p_vps);
- pic_pacer_UpdateReorderMax(p_sys->pic_pacer,
- p_sys->i_pic_reorder_max,
- p_info->i_num_ts);
- vlc_mutex_unlock(&p_sys->lock);
- }
-
}
+ /* RASL Open GOP */
+ /* XXX: Work-around a VT bug on recent devices (iPhone X, MacBook
+ * Pro 2017). The VT session will report a BadDataErr if you send a
+ * RASL frame just after a CRA one. Indeed, RASL frames are
+ * corrupted if the decoding start at an IRAP frame (IDR/CRA), VT
+ * is likely failing to handle this case. */
+ p_info->b_leading = ( i_nal_type == HEVC_NAL_RASL_N || i_nal_type == HEVC_NAL_RASL_R );
+
hevc_rbsp_release_slice_header(p_sli);
return true; /* No need to parse further NAL */
}
@@ -784,7 +828,7 @@ static void InsertIntoDPB(decoder_sys_t *p_sys, frame_info_t *p_info)
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)->p_picture->date >= p_info->p_picture->date);
+ b_insert = ((*pp_lead_in)->pts >= p_info->pts);
if (b_insert)
{
@@ -801,36 +845,61 @@ static void InsertIntoDPB(decoder_sys_t *p_sys, frame_info_t *p_info)
#endif
}
-static picture_t * RemoveOneFrameFromDPB(decoder_sys_t *p_sys)
+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)
- return 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 *p_ret = NULL;
- picture_t **pp_ret_last = &p_ret;
+ picture_t **pp_ret_last = pp_ret;
bool b_dequeue;
do
{
- picture_t *p_field = p_info->p_picture;
+ /* 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 if missing */
- if (p_field->date == VLC_TICK_INVALID)
- p_field->date = date_Get(&p_sys->pts);
+ /* 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_field->date);
+ 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_field->date + 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);
- *pp_ret_last = p_field;
- pp_ret_last = &p_field->p_next;
+ 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--;
@@ -843,7 +912,7 @@ static picture_t * RemoveOneFrameFromDPB(decoder_sys_t *p_sys)
if (p_sys->b_poc_based_reorder)
b_dequeue = (p_info->i_poc == i_framepoc);
else
- b_dequeue = (p_field->date == p_info->p_picture->date);
+ b_dequeue = (p_info->pts == i_framepts);
}
else
{
@@ -852,7 +921,7 @@ static picture_t * RemoveOneFrameFromDPB(decoder_sys_t *p_sys)
} while(b_dequeue);
- return p_ret;
+ return VLC_SUCCESS;
}
static void DrainDPBLocked(decoder_t *p_dec, bool flush)
@@ -860,10 +929,10 @@ static void DrainDPBLocked(decoder_t *p_dec, bool flush)
decoder_sys_t *p_sys = p_dec->p_sys;
for ( ;; )
{
- picture_t *p_fields = RemoveOneFrameFromDPB(p_sys);
- if (p_fields == NULL)
+ picture_t *p_fields;
+ if(RemoveOneFrameFromDPB(p_sys, &p_fields) != VLC_SUCCESS)
break;
- do
+ for ( ; p_fields; )
{
picture_t *p_next = p_fields->p_next;
p_fields->p_next = NULL;
@@ -872,7 +941,7 @@ static void DrainDPBLocked(decoder_t *p_dec, bool flush)
else
decoder_QueueVideo(p_dec, p_fields);
p_fields = p_next;
- } while(p_fields != NULL);
+ }
}
}
@@ -899,59 +968,74 @@ static frame_info_t * CreateReorderInfo(decoder_t *p_dec, const block_t *p_block
p_info->b_keyframe = true;
}
+ p_info->dts = p_block->i_dts;
+ p_info->pts = p_block->i_pts;
p_info->i_length = p_block->i_length;
+ if(p_dec->fmt_in->video.i_frame_rate && p_dec->fmt_in->video.i_frame_rate_base) /* demux forced rate */
+ {
+ p_info->field_rate_num = p_dec->fmt_in->video.i_frame_rate * 2;
+ p_info->field_rate_den = p_dec->fmt_in->video.i_frame_rate_base;
+ }
+ else if(!p_info->field_rate_num || !p_info->field_rate_den) /* full fallback */
+ {
+ p_info->field_rate_num = DEFAULT_FRAME_RATE_NUM * 2;
+ p_info->field_rate_den = DEFAULT_FRAME_RATE_DEN;
+ }
+
/* required for still pictures/menus */
p_info->b_eos = (p_block->i_flags & BLOCK_FLAG_END_OF_SEQUENCE);
- if (date_Get(&p_sys->pts) == VLC_TICK_INVALID)
- date_Set(&p_sys->pts, p_block->i_dts);
-
return p_info;
}
static void OnDecodedFrame(decoder_t *p_dec, frame_info_t *p_info)
{
decoder_sys_t *p_sys = p_dec->p_sys;
- assert(p_info->p_picture);
+
+ if(!p_sys->b_invalid_pic_reorder_max &&
+ p_info->i_max_reorder != p_sys->i_pic_reorder_max)
+ {
+ p_sys->i_pic_reorder_max = p_info->i_max_reorder;
+ pic_pacer_UpdateReorderMax(p_sys->pic_pacer, p_sys->i_pic_reorder_max);
+ }
+
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->p_pic_reorder && !p_info->b_flush &&
+ 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->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_UpdateReorderMax(p_sys->pic_pacer,
- p_sys->i_pic_reorder_max, p_info->i_num_ts);
+ pic_pacer_UpdateReorderMax(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->p_picture->date > VLC_TICK_INVALID &&
- p_sys->p_pic_reorder->p_picture->date > p_info->p_picture->date)
+ 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_UpdateReorderMax(p_sys->pic_pacer,
- p_sys->i_pic_reorder_max, p_info->i_num_ts);
+ pic_pacer_UpdateReorderMax(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;
}
}
- picture_t *p_fields = RemoveOneFrameFromDPB(p_sys);
- if (p_fields == NULL)
+ picture_t *p_fields;
+ if(RemoveOneFrameFromDPB(p_sys, &p_fields) != VLC_SUCCESS)
break;
- do
+ for(; p_fields;)
{
picture_t *p_next = p_fields->p_next;
p_fields->p_next = NULL;
decoder_QueueVideo(p_dec, p_fields);
p_fields = p_next;
- } while(p_fields != NULL);
+ }
}
InsertIntoDPB(p_sys, p_info);
@@ -1132,21 +1216,6 @@ static CFMutableDictionaryRef CreateSessionDescriptionFormat(decoder_t *p_dec,
return decoderConfiguration;
}
-static void PtsInit(decoder_t *p_dec)
-{
- decoder_sys_t *p_sys = p_dec->p_sys;
-
- if (p_dec->fmt_in->video.i_frame_rate_base && p_dec->fmt_in->video.i_frame_rate)
- {
- date_Init(&p_sys->pts, p_dec->fmt_in->video.i_frame_rate * 2,
- p_dec->fmt_in->video.i_frame_rate_base);
- }
- else
- {
- date_Init(&p_sys->pts, 2 * 30000, 1001);
- }
-}
-
static int StartVideoToolbox(decoder_t *p_dec)
{
decoder_sys_t *p_sys = p_dec->p_sys;
@@ -1268,25 +1337,12 @@ static void StopVideoToolbox(decoder_t *p_dec)
CFRelease(p_sys->videoFormatDescription);
p_sys->videoFormatDescription = NULL;
}
- p_sys->b_vt_feed = false;
- p_sys->b_drop_blocks = false;
+ p_sys->sync_state = p_sys->start_sync_state;
+ p_sys->decoder_state = STATE_DECODER_WAITING_RAP;
}
#pragma mark - module open and close
-static void pic_pacer_Destroy(void *priv)
-{
- (void) priv;
-}
-
-static void pic_pacer_Init(struct pic_pacer *pic_pacer, uint8_t pic_reorder_max)
-{
- vlc_mutex_init(&pic_pacer->lock);
- vlc_cond_init(&pic_pacer->wait);
- pic_pacer->nb_field_out = 0;
- pic_pacer->field_reorder_max = pic_reorder_max * 2;
-}
-
static int
CreateVideoContext(decoder_t *p_dec)
{
@@ -1320,7 +1376,7 @@ CreateVideoContext(decoder_t *p_dec)
CVPX_VIDEO_CONTEXT_VIDEOTOOLBOX);
assert(p_sys->pic_pacer);
- pic_pacer_Init(p_sys->pic_pacer, p_sys->i_pic_reorder_max);
+ pic_pacer_Init(p_sys->pic_pacer);
return VLC_SUCCESS;
}
@@ -1369,6 +1425,9 @@ static int OpenDecoder(vlc_object_t *p_this)
p_sys->i_pic_reorder_max = 4;
p_sys->vtsession_status = VTSESSION_STATUS_OK;
p_sys->b_cvpx_format_forced = false;
+ /* will be fixed later */
+ date_Init(&p_sys->pts, p_dec->fmt_in->video.i_frame_rate,
+ p_dec->fmt_in->video.i_frame_rate_base);
char *cvpx_chroma = var_InheritString(p_dec, "videotoolbox-cvpx-chroma");
if (cvpx_chroma != NULL)
@@ -1402,8 +1461,6 @@ static int OpenDecoder(vlc_object_t *p_this)
return i_ret;
}
- p_sys->b_vt_need_keyframe = false;
-
vlc_mutex_init(&p_sys->lock);
p_dec->pf_decode = DecodeBlock;
@@ -1421,8 +1478,9 @@ 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->b_vt_need_keyframe = true;
+ p_sys->start_sync_state = STATE_BITSTREAM_WAITING_RAP;
break;
case kCMVideoCodecType_HEVC:
@@ -1435,8 +1493,9 @@ 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->b_vt_need_keyframe = true;
+ p_sys->start_sync_state = STATE_BITSTREAM_WAITING_RAP;
break;
case kCMVideoCodecType_MPEG4Video:
@@ -1448,6 +1507,8 @@ static int OpenDecoder(vlc_object_t *p_this)
break;
}
+ p_sys->sync_state = p_sys->start_sync_state;
+
if (p_sys->pf_codec_init && !p_sys->pf_codec_init(p_dec))
{
CloseDecoder(p_this);
@@ -1461,7 +1522,7 @@ static int OpenDecoder(vlc_object_t *p_this)
i_ret = StartVideoToolbox(p_dec);
if (i_ret == VLC_SUCCESS) {
- PtsInit(p_dec);
+ date_Set(&p_sys->pts, VLC_TICK_INVALID);
msg_Info(p_dec, "Using Video Toolbox to decode '%4.4s'",
(char *)&p_dec->fmt_in->i_codec);
} else {
@@ -1769,10 +1830,8 @@ static int HandleVTStatus(decoder_t *p_dec, OSStatus status,
static void RequestFlush(decoder_t *p_dec)
{
decoder_sys_t *p_sys = p_dec->p_sys;
-
- vlc_mutex_lock(&p_sys->lock);
- p_sys->b_vt_flush = true;
- vlc_mutex_unlock(&p_sys->lock);
+ Drain(p_dec, true);
+ date_Set(&p_sys->pts, VLC_TICK_INVALID);
}
static void Drain(decoder_t *p_dec, bool flush)
@@ -1781,18 +1840,18 @@ static void Drain(decoder_t *p_dec, bool flush)
/* draining: return last pictures of the reordered queue */
vlc_mutex_lock(&p_sys->lock);
- p_sys->b_vt_flush = true;
- DrainDPBLocked(p_dec, flush);
+ p_sys->b_discard_decoder_output = flush;
vlc_mutex_unlock(&p_sys->lock);
- if (p_sys->session && p_sys->b_vt_feed)
+ if (p_sys->session && p_sys->decoder_state == STATE_DECODER_STARTED)
VTDecompressionSessionWaitForAsynchronousFrames(p_sys->session);
vlc_mutex_lock(&p_sys->lock);
- assert(RemoveOneFrameFromDPB(p_sys) == NULL);
- p_sys->b_vt_flush = false;
- p_sys->b_vt_feed = false;
- p_sys->b_drop_blocks = false;
+ DrainDPBLocked(p_dec, flush);
+ picture_t *p_output;
+ assert(RemoveOneFrameFromDPB(p_sys, &p_output) == VLC_EGENERIC);
+ p_sys->b_discard_decoder_output = false;
+ p_sys->sync_state = p_sys->start_sync_state;
vlc_mutex_unlock(&p_sys->lock);
}
@@ -1800,11 +1859,7 @@ static int DecodeBlock(decoder_t *p_dec, block_t *p_block)
{
decoder_sys_t *p_sys = p_dec->p_sys;
- if (p_sys->b_vt_flush)
- {
- Drain(p_dec, true);
- PtsInit(p_dec);
- }
+ pic_pacer_WaitAllocatableSlot(p_sys->pic_pacer);
if (p_block == NULL)
{
@@ -1902,10 +1957,10 @@ static int DecodeBlock(decoder_t *p_dec, block_t *p_block)
if (unlikely(p_block->i_flags&(BLOCK_FLAG_CORRUPTED)))
{
- if (p_sys->b_vt_feed)
+ if (p_sys->sync_state == STATE_BITSTREAM_SYNCED)
{
Drain(p_dec, false);
- PtsInit(p_dec);
+ date_Set(&p_sys->pts, VLC_TICK_INVALID);
}
goto skip;
}
@@ -1954,10 +2009,25 @@ static int DecodeBlock(decoder_t *p_dec, block_t *p_block)
}
}
- if (!p_sys->b_vt_feed && p_sys->b_vt_need_keyframe && !p_info->b_keyframe)
+ if (p_sys->sync_state == STATE_BITSTREAM_WAITING_RAP)
{
- free(p_info);
- goto skip;
+ if(!p_info->b_keyframe)
+ {
+ msg_Dbg(p_dec, "discarding non recovery frame %"PRId64, p_info->pts);
+ free(p_info);
+ goto skip;
+ }
+ p_sys->sync_state = STATE_BITSTREAM_DISCARD_LEADING;
+ }
+ else if(p_sys->sync_state == STATE_BITSTREAM_DISCARD_LEADING)
+ {
+ if(p_info->b_leading)
+ {
+ msg_Dbg(p_dec, "discarding skipped leading frame %"PRId64, p_info->pts);
+ free(p_info);
+ goto skip;
+ }
+ p_sys->sync_state = STATE_BITSTREAM_SYNCED;
}
CMSampleBufferRef sampleBuffer =
@@ -1978,12 +2048,20 @@ 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)
{
- p_sys->b_vt_feed = true;
+ pic_pacer_AccountScheduledDecode(p_sys->pic_pacer);
+
+ if(p_sys->decoder_state != STATE_DECODER_STARTED)
+ {
+ msg_Dbg(p_dec, "session accepted first frame %"PRId64, p_info->pts);
+ p_sys->decoder_state = STATE_DECODER_STARTED;
+ }
if (p_block->i_flags & BLOCK_FLAG_END_OF_SEQUENCE)
Drain( p_dec, false );
}
else
{
+ msg_Dbg(p_dec, "session rejected frame %"PRId64" with status %d", p_info->pts, status);
+ p_sys->sync_state = p_sys->start_sync_state;
vlc_mutex_lock(&p_sys->lock);
p_sys->vtsession_status = vtsession_status;
/* In case of abort, the decoder module will be reloaded next time
@@ -2080,46 +2158,45 @@ 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);
vlc_mutex_lock(&pic_pacer->lock);
- assert((int) pic_pacer->nb_field_out - nb_fields >= 0);
- pic_pacer->nb_field_out -= nb_fields;
- vlc_cond_signal(&pic_pacer->wait);
- vlc_mutex_unlock(&pic_pacer->lock);
-}
+ assert(pic_pacer->nb_out > 0);
+ pic_pacer->nb_out -= 1;
-static void
-pic_pacer_UpdateReorderMax(struct pic_pacer *pic_pacer, uint8_t pic_reorder_max,
- uint8_t nb_field)
-{
- vlc_mutex_lock(&pic_pacer->lock);
+ /* 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;
- pic_pacer->field_reorder_max = pic_reorder_max * (nb_field < 2 ? 2 : nb_field);
vlc_cond_signal(&pic_pacer->wait);
vlc_mutex_unlock(&pic_pacer->lock);
}
-static int pic_pacer_Wait(struct pic_pacer *pic_pacer, const picture_t *pic)
+static void
+pic_pacer_UpdateReorderMax(struct pic_pacer *pic_pacer, uint8_t pic_reorder_max)
{
- const uint8_t reserved_fields = 2 * (pic->i_nb_fields < 2 ? 2 : pic->i_nb_fields);
-
vlc_mutex_lock(&pic_pacer->lock);
- /* Wait 200 ms max. We can't really know what the video output will do with
- * output pictures (will they be rendered immediately ?), so don't wait
- * infinitely. The output will be paced anyway by the vlc_cond_timedwait()
- * call. */
- vlc_tick_t deadline = vlc_tick_now() + VLC_TICK_FROM_MS(200);
- int ret = 0;
- while (ret == 0 && pic_pacer->field_reorder_max != 0
- && pic_pacer->nb_field_out >= pic_pacer->field_reorder_max + reserved_fields)
- ret = vlc_cond_timedwait(&pic_pacer->wait, &pic_pacer->lock, deadline);
- pic_pacer->nb_field_out += pic->i_nb_fields;
+ pic_reorder_max += PIC_PACER_ALLOCATABLE_MAX;
+ bool b_growing = pic_reorder_max > pic_pacer->allocated_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);
+#endif
+ if(b_growing)
+ {
+ pic_pacer->allocated_max = pic_reorder_max;
+ pic_pacer->allocated_next = pic_reorder_max;
+ vlc_cond_signal(&pic_pacer->wait);
+ }
+ else
+ {
+ pic_pacer->allocated_next = pic_reorder_max;
+ }
vlc_mutex_unlock(&pic_pacer->lock);
-
- return ret;
}
static void DecoderCallback(void *decompressionOutputRefCon,
@@ -2136,7 +2213,7 @@ static void DecoderCallback(void *decompressionOutputRefCon,
frame_info_t *p_info = (frame_info_t *) sourceFrameRefCon;
vlc_mutex_lock(&p_sys->lock);
- if (p_sys->b_vt_flush)
+ if (p_sys->b_discard_decoder_output)
goto end;
enum vtsession_status vtsession_status;
@@ -2177,19 +2254,17 @@ static void DecoderCallback(void *decompressionOutputRefCon,
if (CVPixelBufferGetDataSize(imageBuffer) == 0)
goto end;
- if (likely(p_info))
- {
- /* Unlock the mutex because decoder_NewPicture() is blocking. Indeed,
- * it can wait indefinitely when the input is paused. */
-
- vlc_mutex_unlock(&p_sys->lock);
+ if (unlikely(p_info == NULL))
+ goto end;
- picture_t *p_pic = decoder_NewPicture(p_dec);
- if (!p_pic)
- goto unlocked_end;
+ /* Unlock the mutex because decoder_NewPicture() is blocking. Indeed,
+ * it can wait indefinitely when the input is paused. */
- p_info->p_picture = p_pic;
+ vlc_mutex_unlock(&p_sys->lock);
+ picture_t *p_pic = decoder_NewPicture(p_dec);
+ if (p_pic)
+ {
p_pic->date = pts.value;
p_pic->b_force = p_info->b_eos;
p_pic->b_still = p_info->b_eos;
@@ -2201,36 +2276,31 @@ static void DecoderCallback(void *decompressionOutputRefCon,
}
if (cvpxpic_attach(p_pic, imageBuffer, p_sys->vctx,
- video_context_OnPicReleased) != VLC_SUCCESS)
+ video_context_OnPicReleased) == VLC_SUCCESS)
{
- picture_Release(p_pic);
- goto unlocked_end;
+ /* VT is not pacing frame allocation. If we are not fast enough to
+ * render (release) the output pictures, the VT session can end up
+ * 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);
}
+ }
- /* VT is not pacing frame allocation. If we are not fast enough to
- * render (release) the output pictures, the VT session can end up
- * 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. */
- if (pic_pacer_Wait(p_sys->pic_pacer, p_pic))
- msg_Warn(p_dec, "pic_pacer_Wait timed out");
-
- vlc_mutex_lock(&p_sys->lock);
+ vlc_mutex_lock(&p_sys->lock);
- if (p_sys->b_vt_flush)
- {
- picture_Release(p_pic);
- goto end;
- }
+ if (p_sys->b_discard_decoder_output)
+ picture_Release(p_pic);
+ else
+ p_info->p_picture = p_pic;
- OnDecodedFrame( p_dec, p_info );
- p_info = NULL;
- }
+ OnDecodedFrame( p_dec, p_info );
+ p_info = NULL;
end:
- vlc_mutex_unlock(&p_sys->lock);
-unlocked_end:
free(p_info);
+ vlc_mutex_unlock(&p_sys->lock);
+ pic_pacer_AccountFinishedDecode(p_sys->pic_pacer);
return;
}
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/85d2e507f7d9ebac02c6ec2fe554949d638208e9...98a5b3ba8471231107e48253186543073062b197
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/85d2e507f7d9ebac02c6ec2fe554949d638208e9...98a5b3ba8471231107e48253186543073062b197
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