}
h->param.i_frame_reference = x264_clip3( h->param.i_frame_reference, 1, 16 );
+ h->param.i_dpb_size = x264_clip3( h->param.i_dpb_size, 1, 16 );
if( h->param.i_keyint_max <= 0 )
h->param.i_keyint_max = 1;
if( h->param.i_scenecut_threshold < 0 )
x264_log( h, X264_LOG_WARNING, "b-pyramid normal + intra-refresh is not supported\n" );
h->param.i_bframe_pyramid = X264_B_PYRAMID_STRICT;
}
- if( h->param.b_intra_refresh && h->param.i_frame_reference > 1 )
+ if( h->param.b_intra_refresh && (h->param.i_frame_reference > 1 || h->param.i_dpb_size > 1) )
{
x264_log( h, X264_LOG_WARNING, "ref > 1 + intra-refresh is not supported\n" );
h->param.i_frame_reference = 1;
+ h->param.i_dpb_size = 1;
}
if( h->param.b_intra_refresh && h->param.i_open_gop )
{
for( int i = 0; h->frames.reference[i]; i++ )
{
+ if( h->frames.reference[i]->b_corrupt )
+ continue;
if( h->frames.reference[i]->i_poc < i_poc )
h->fref0[h->i_ref0++] = h->frames.reference[i];
else if( h->frames.reference[i]->i_poc > i_poc )
h->b_queued_intra_refresh = 1;
}
+int x264_encoder_invalidate_reference( x264_t *h, int64_t pts )
+{
+ if( h->param.i_bframe )
+ {
+ x264_log( h, X264_LOG_ERROR, "x264_encoder_invalidate_reference is not supported with B-frames enabled\n" );
+ return -1;
+ }
+ if( h->param.b_intra_refresh )
+ {
+ x264_log( h, X264_LOG_ERROR, "x264_encoder_invalidate_reference is not supported with intra refresh enabled\n" );
+ return -1;
+ }
+ h = h->thread[h->i_thread_phase];
+ h->i_reference_invalidate_pts = pts;
+ return 0;
+}
+
/****************************************************************************
* x264_encoder_encode:
* XXX: i_poc : is the poc of the current given picture
h->fenc->param->param_free( h->fenc->param );
}
+ if( h->i_reference_invalidate_pts )
+ {
+ if( h->i_reference_invalidate_pts >= h->i_last_idr_pts )
+ for( int i = 0; h->frames.reference[i]; i++ )
+ if( h->i_reference_invalidate_pts <= h->frames.reference[i]->i_pts )
+ h->frames.reference[i]->b_corrupt = 1;
+ h->i_reference_invalidate_pts = 0;
+ }
+
+ if( !IS_X264_TYPE_I( h->fenc->i_type ) )
+ {
+ int valid_refs_left = 0;
+ for( int i = 0; h->frames.reference[i]; i++ )
+ if( !h->frames.reference[i]->b_corrupt )
+ valid_refs_left++;
+ /* No valid reference frames left: force an IDR. */
+ if( !valid_refs_left )
+ {
+ h->fenc->b_keyframe = 1;
+ h->fenc->i_type = X264_TYPE_IDR;
+ }
+ }
+
if( h->fenc->b_keyframe )
{
h->frames.i_last_keyframe = h->fenc->i_frame;
h->fenc->b_kept_as_ref =
h->fdec->b_kept_as_ref = i_nal_ref_idc != NAL_PRIORITY_DISPOSABLE && h->param.i_keyint_max > 1;
-
+ h->fdec->i_pts = h->fenc->i_pts *= h->i_dts_compress_multiplier;
+ if( h->frames.i_bframe_delay )
+ {
+ int64_t *prev_reordered_pts = thread_current->frames.i_prev_reordered_pts;
+ if( h->i_frame <= h->frames.i_bframe_delay )
+ {
+ if( h->i_dts_compress_multiplier == 1 )
+ h->fdec->i_dts = h->fenc->i_reordered_pts - h->frames.i_bframe_delay_time;
+ else
+ {
+ /* DTS compression */
+ if( h->i_frame == 1 )
+ thread_current->frames.i_init_delta = h->fenc->i_reordered_pts * h->i_dts_compress_multiplier;
+ h->fdec->i_dts = h->i_frame * thread_current->frames.i_init_delta / h->i_dts_compress_multiplier;
+ }
+ }
+ else
+ h->fdec->i_dts = prev_reordered_pts[ (h->i_frame - h->frames.i_bframe_delay) % h->frames.i_bframe_delay ];
+ prev_reordered_pts[ h->i_frame % h->frames.i_bframe_delay ] = h->fenc->i_reordered_pts * h->i_dts_compress_multiplier;
+ }
+ else
+ h->fdec->i_dts = h->fenc->i_reordered_pts;
+ if( h->fenc->i_type == X264_TYPE_IDR )
+ h->i_last_idr_pts = h->fdec->i_pts;
/* ------------------- Init ----------------------------- */
/* build ref list 0/1 */
pic_out->b_keyframe = h->fenc->b_keyframe;
- pic_out->i_pts = h->fenc->i_pts *= h->i_dts_compress_multiplier;
- if( h->frames.i_bframe_delay )
- {
- int64_t *prev_reordered_pts = thread_current->frames.i_prev_reordered_pts;
- if( h->i_frame <= h->frames.i_bframe_delay )
- {
- if( h->i_dts_compress_multiplier == 1 )
- pic_out->i_dts = h->fenc->i_reordered_pts - h->frames.i_bframe_delay_time;
- else
- {
- /* DTS compression */
- if( h->i_frame == 1 )
- thread_current->frames.i_init_delta = h->fenc->i_reordered_pts * h->i_dts_compress_multiplier;
- pic_out->i_dts = h->i_frame * thread_current->frames.i_init_delta / h->i_dts_compress_multiplier;
- }
- }
- else
- pic_out->i_dts = prev_reordered_pts[ (h->i_frame - h->frames.i_bframe_delay) % h->frames.i_bframe_delay ];
- prev_reordered_pts[ h->i_frame % h->frames.i_bframe_delay ] = h->fenc->i_reordered_pts * h->i_dts_compress_multiplier;
- }
- else
- pic_out->i_dts = h->fenc->i_reordered_pts;
+ pic_out->i_pts = h->fdec->i_pts;
+ pic_out->i_dts = h->fdec->i_dts;
+
if( pic_out->i_pts < pic_out->i_dts )
x264_log( h, X264_LOG_WARNING, "invalid DTS: PTS is less than DTS\n" );
#include <stdarg.h>
-#define X264_BUILD 99
+#define X264_BUILD 100
/* x264_t:
* opaque handler for encoder */
/* Bitstream parameters */
int i_frame_reference; /* Maximum number of reference frames */
+ int i_dpb_size; /* Force a DPB size larger than that implied by B-frames and reference frames.
+ * Useful in combination with interactive error resilience. */
int i_keyint_max; /* Force an IDR keyframe at this interval */
int i_keyint_min; /* Scenecuts closer together than this are coded as I, not IDR. */
int i_scenecut_threshold; /* how aggressively to insert extra I frames */
* If an intra refresh is not in progress, begin one with the next P-frame.
* If an intra refresh is in progress, begin one as soon as the current one finishes.
* Requires that b_intra_refresh be set.
+ *
* Useful for interactive streaming where the client can tell the server that packet loss has
* occurred. In this case, keyint can be set to an extremely high value so that intra refreshes
- * only occur when calling x264_encoder_intra_refresh. */
+ * only occur when calling x264_encoder_intra_refresh.
+ *
+ * In multi-pass encoding, if x264_encoder_intra_refresh is called differently in each pass,
+ * behavior is undefined.
+ *
+ * Should not be called during an x264_encoder_encode. */
void x264_encoder_intra_refresh( x264_t * );
+/* x264_encoder_invalidate_reference:
+ * An interactive error resilience tool, designed for use in a low-latency one-encoder-few-clients
+ * system. When the client has packet loss or otherwise incorrectly decodes a frame, the encoder
+ * can be told with this command to "forget" the frame and all frames that depend on it, referencing
+ * only frames that occurred before the loss. This will force a keyframe if no frames are left to
+ * reference after the aforementioned "forgetting".
+ *
+ * It is strongly recommended to use a large i_dpb_size in this case, which allows the encoder to
+ * keep around extra, older frames to fall back on in case more recent frames are all invalidated.
+ * Unlike increasing i_frame_reference, this does not increase the number of frames used for motion
+ * estimation and thus has no speed impact. It is also recommended to set a very large keyframe
+ * interval, so that keyframes are not used except as necessary for error recovery.
+ *
+ * x264_encoder_invalidate_reference is not currently compatible with the use of B-frames or intra
+ * refresh.
+ *
+ * In multi-pass encoding, if x264_encoder_invalidate_reference is called differently in each pass,
+ * behavior is undefined.
+ *
+ * Should not be called during an x264_encoder_encode.
+ *
+ * Returns 0 on success, negative on failure. */
+int x264_encoder_invalidate_reference( x264_t *, int64_t pts );
#endif