DTS compression is an ugly stupid hack and starting to encroach on unrelated areas like VBV.
Some people want it in the mp4 muxer for devices and/or splitters that don't support Edit Boxes.
We just say "throw these broken devices out the window".
DTS compression will remain as a muxer option, --dts-compress, at the user's own risk.
This option is disabled by default.
param->b_annexb = 1;
param->b_aud = 0;
param->b_vfr_input = 1;
- param->b_dts_compress = 0;
param->i_nal_hrd = X264_NAL_HRD_NONE;
param->b_tff = 1;
param->b_pic_struct = 0;
x264_pps_t *pps;
int i_idr_pic_id;
- /* Timebase multiplier for DTS compression */
- int i_dts_compress_multiplier;
-
/* quantization matrix for decoding, [cqm][qp%6][coef] */
int (*dequant4_mf[4])[16]; /* [4][6][16] */
int (*dequant8_mf[2])[64]; /* [2][6][64] */
int i_bframe_delay;
int64_t i_bframe_delay_time;
int64_t i_first_pts;
- int64_t i_init_delta;
int64_t i_prev_reordered_pts[2];
int64_t i_largest_pts;
int64_t i_second_largest_pts;
h->i_frame = -1;
h->i_frame_num = 0;
h->i_idr_pic_id = 0;
- uint64_t new_timebase_den = h->param.i_timebase_den;
- if( h->param.b_dts_compress )
- {
- /* h->i_dts_compress_multiplier == h->frames.i_bframe_delay + 1 */
- h->i_dts_compress_multiplier = h->param.i_bframe ? (h->param.i_bframe_pyramid ? 3 : 2) : 1;
- if( h->i_dts_compress_multiplier != 1 )
- {
- new_timebase_den = h->param.i_timebase_den * h->i_dts_compress_multiplier;
- x264_log( h, X264_LOG_DEBUG, "DTS compression changed timebase: %u/%u -> %u/%"PRIu64"\n",
- h->param.i_timebase_num, h->param.i_timebase_den,
- h->param.i_timebase_num, new_timebase_den );
- }
- }
- else
- h->i_dts_compress_multiplier = 1;
- if( new_timebase_den * 2 > UINT32_MAX )
+ if( (uint64_t)h->param.i_timebase_den * 2 > UINT32_MAX )
{
- x264_log( h, X264_LOG_ERROR, "Effective timebase denominator %"PRIu64" exceeds H.264 maximum\n", new_timebase_den );
+ x264_log( h, X264_LOG_ERROR, "Effective timebase denominator %u exceeds H.264 maximum\n", h->param.i_timebase_den );
goto fail;
}
- h->param.i_timebase_den = new_timebase_den;
h->sps = &h->sps_array[0];
x264_sps_init( h->sps, h->param.i_sps_id, &h->param );
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;
+ h->fdec->i_pts = h->fenc->i_pts;
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->frames.i_first_pts) * h->i_dts_compress_multiplier;
- h->fdec->i_dts = h->i_frame * thread_current->frames.i_init_delta / h->i_dts_compress_multiplier + h->frames.i_first_pts * 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;
+ h->fdec->i_dts = h->i_frame > h->frames.i_bframe_delay
+ ? prev_reordered_pts[ (h->i_frame - h->frames.i_bframe_delay) % h->frames.i_bframe_delay ]
+ : h->fenc->i_reordered_pts - h->frames.i_bframe_delay_time;
+ prev_reordered_pts[ h->i_frame % h->frames.i_bframe_delay ] = h->fenc->i_reordered_pts;
}
else
h->fdec->i_dts = h->fenc->i_reordered_pts;
else
{
float duration = (float)(2 * h->frames.i_largest_pts - h->frames.i_second_largest_pts - h->frames.i_first_pts)
- * h->i_dts_compress_multiplier * h->param.i_timebase_num / h->param.i_timebase_den;
+ * h->param.i_timebase_num / h->param.i_timebase_den;
f_bitrate = SUM3(h->stat.i_frame_size) / duration / 125;
}
if( h->param.b_vfr_input )
{
if( lookahead_size-- > 1 )
- h->lookahead->next.list[i]->i_duration = 2 * (h->lookahead->next.list[i+1]->i_pts - h->lookahead->next.list[i]->i_pts) * h->i_dts_compress_multiplier;
+ h->lookahead->next.list[i]->i_duration = 2 * (h->lookahead->next.list[i+1]->i_pts - h->lookahead->next.list[i]->i_pts);
else
h->lookahead->next.list[i]->i_duration = h->i_prev_duration;
}
else
- h->lookahead->next.list[i]->i_duration = delta_tfi_divisor[h->lookahead->next.list[i]->i_pic_struct] * h->i_dts_compress_multiplier;
+ h->lookahead->next.list[i]->i_duration = delta_tfi_divisor[h->lookahead->next.list[i]->i_pic_struct];
h->i_prev_duration = h->lookahead->next.list[i]->i_duration;
if( h->lookahead->next.list[i]->i_frame > h->i_disp_fields_last_frame && lookahead_size > 0 )
uint8_t b_write_length;
int64_t i_prev_dts;
- int64_t i_prev_pts;
+ int64_t i_prev_cts;
+ int64_t i_delay_time;
+ int64_t i_init_delta;
+ int i_delay_frames;
- uint32_t i_timebase_num;
- uint32_t i_timebase_den;
+ double d_timebase;
int b_vfr_input;
+ int b_dts_compress;
unsigned start;
} flv_hnd_t;
return flv_flush_data( c );
}
-static int open_file( char *psz_filename, hnd_t *p_handle )
+static int open_file( char *psz_filename, hnd_t *p_handle, cli_output_opt_t *opt )
{
flv_hnd_t *p_flv = malloc( sizeof(*p_flv) );
*p_handle = NULL;
return -1;
memset( p_flv, 0, sizeof(*p_flv) );
+ p_flv->b_dts_compress = opt->use_dts_compress;
+
p_flv->c = flv_create_writer( psz_filename );
if( !p_flv->c )
return -1;
p_flv->i_fps_num = p_param->i_fps_num;
p_flv->i_fps_den = p_param->i_fps_den;
- p_flv->i_timebase_num = p_param->i_timebase_num;
- p_flv->i_timebase_den = p_param->i_timebase_den;
+ p_flv->d_timebase = (double)p_param->i_timebase_num / p_param->i_timebase_den;
p_flv->b_vfr_input = p_param->b_vfr_input;
+ p_flv->i_delay_frames = p_param->i_bframe ? (p_param->i_bframe_pyramid ? 2 : 1) : 0;
return 0;
}
flv_hnd_t *p_flv = handle;
flv_buffer *c = p_flv->c;
- int64_t dts = (int64_t)( (p_picture->i_dts * 1000 * ((double)p_flv->i_timebase_num / p_flv->i_timebase_den)) + 0.5 );
- int64_t cts = (int64_t)( (p_picture->i_pts * 1000 * ((double)p_flv->i_timebase_num / p_flv->i_timebase_den)) + 0.5 );
- int64_t offset = cts - dts;
+#define convert_timebase_ms( timestamp, timebase ) (int64_t)((timestamp) * (timebase) * 1000 + 0.5)
+
+ if( !p_flv->i_framenum )
+ {
+ p_flv->i_delay_time = p_picture->i_dts * -1;
+ if( !p_flv->b_dts_compress && p_flv->i_delay_time )
+ x264_cli_log( "flv", X264_LOG_INFO, "initial delay %"PRId64" ms\n",
+ convert_timebase_ms( p_picture->i_pts + p_flv->i_delay_time, p_flv->d_timebase ) );
+ }
+
+ int64_t dts;
+ int64_t cts;
+ int64_t offset;
+
+ if( p_flv->b_dts_compress )
+ {
+ if( p_flv->i_framenum == 1 )
+ p_flv->i_init_delta = convert_timebase_ms( p_picture->i_dts + p_flv->i_delay_time, p_flv->d_timebase );
+ dts = p_flv->i_framenum > p_flv->i_delay_frames
+ ? convert_timebase_ms( p_picture->i_dts, p_flv->d_timebase )
+ : p_flv->i_framenum * p_flv->i_init_delta / (p_flv->i_delay_frames + 1);
+ cts = convert_timebase_ms( p_picture->i_pts, p_flv->d_timebase );
+ }
+ else
+ {
+ dts = convert_timebase_ms( p_picture->i_dts + p_flv->i_delay_time, p_flv->d_timebase );
+ cts = convert_timebase_ms( p_picture->i_pts + p_flv->i_delay_time, p_flv->d_timebase );
+ }
+ offset = cts - dts;
if( p_flv->i_framenum )
{
- int64_t prev_dts = (int64_t)( (p_flv->i_prev_dts * 1000 * ((double)p_flv->i_timebase_num / p_flv->i_timebase_den)) + 0.5 );
- int64_t prev_cts = (int64_t)( (p_flv->i_prev_pts * 1000 * ((double)p_flv->i_timebase_num / p_flv->i_timebase_den)) + 0.5 );
- if( prev_dts == dts )
- {
- double fps = ((double)p_flv->i_timebase_den / p_flv->i_timebase_num) / (p_picture->i_dts - p_flv->i_prev_dts);
+ if( p_flv->i_prev_dts == dts )
x264_cli_log( "flv", X264_LOG_WARNING, "duplicate DTS %"PRId64" generated by rounding\n"
- " current internal decoding framerate: %.6f fps\n", dts, fps );
- }
- if( prev_cts == cts )
- {
- double fps = ((double)p_flv->i_timebase_den / p_flv->i_timebase_num) / (p_picture->i_pts - p_flv->i_prev_pts);
+ " decoding framerate cannot exceed 1000fps\n", dts );
+ if( p_flv->i_prev_cts == cts )
x264_cli_log( "flv", X264_LOG_WARNING, "duplicate CTS %"PRId64" generated by rounding\n"
- " current internal composition framerate: %.6f fps\n", cts, fps );
- }
+ " composition framerate cannot exceed 1000fps\n", cts );
}
- p_flv->i_prev_dts = p_picture->i_dts;
- p_flv->i_prev_pts = p_picture->i_pts;
+ p_flv->i_prev_dts = dts;
+ p_flv->i_prev_cts = cts;
// A new frame - write packet header
x264_put_byte( c, FLV_TAG_TYPE_VIDEO );
CHECK( flv_flush_data( c ) );
- double total_duration = (double)(2 * largest_pts - second_largest_pts) * p_flv->i_timebase_num / p_flv->i_timebase_den;
+ double total_duration = (2 * largest_pts - second_largest_pts) * p_flv->d_timebase;
if( x264_is_regular_file( c->fp ) )
{
} mkv_hnd_t;
-static int open_file( char *psz_filename, hnd_t *p_handle )
+static int open_file( char *psz_filename, hnd_t *p_handle, cli_output_opt_t *opt )
{
mkv_hnd_t *p_mkv;
GF_ISOSample *p_sample;
int i_track;
uint32_t i_descidx;
- uint32_t i_time_res;
+ uint64_t i_time_res;
int64_t i_time_inc;
+ int64_t i_delay_time;
+ int64_t i_init_delta;
int i_numframe;
- int i_delay_time;
+ int i_delay_frames;
+ int b_dts_compress;
+ int i_dts_compress_multiplier;
} mp4_hnd_t;
static void recompute_bitrate_mp4( GF_ISOFile *p_file, int i_track )
return 0;
}
-static int open_file( char *psz_filename, hnd_t *p_handle )
+static int open_file( char *psz_filename, hnd_t *p_handle, cli_output_opt_t *opt )
{
mp4_hnd_t *p_mp4;
memset( p_mp4, 0, sizeof(mp4_hnd_t) );
p_mp4->p_file = gf_isom_open( psz_filename, GF_ISOM_OPEN_WRITE, NULL );
+ p_mp4->b_dts_compress = opt->use_dts_compress;
+
if( !(p_mp4->p_sample = gf_isom_sample_new()) )
{
close_file( p_mp4, 0, 0 );
{
mp4_hnd_t *p_mp4 = handle;
- p_mp4->i_time_res = p_param->i_timebase_den;
- p_mp4->i_time_inc = p_param->i_timebase_num;
+ p_mp4->i_delay_frames = p_param->i_bframe ? (p_param->i_bframe_pyramid ? 2 : 1) : 0;
+ p_mp4->i_dts_compress_multiplier = p_mp4->b_dts_compress * p_mp4->i_delay_frames + 1;
+
+ p_mp4->i_time_res = p_param->i_timebase_den * p_mp4->i_dts_compress_multiplier;
+ p_mp4->i_time_inc = p_param->i_timebase_num * p_mp4->i_dts_compress_multiplier;
+ FAIL_IF_ERR( p_mp4->i_time_res > UINT32_MAX, "mp4", "MP4 media timescale %"PRIu64" exceeds maximum\n", p_mp4->i_time_res )
p_mp4->i_track = gf_isom_new_track( p_mp4->p_file, 0, GF_ISOM_MEDIA_VISUAL,
p_mp4->i_time_res );
return sei_size + sps_size + pps_size;
}
+
static int write_frame( hnd_t handle, uint8_t *p_nalu, int i_size, x264_picture_t *p_picture )
{
mp4_hnd_t *p_mp4 = handle;
if( !p_mp4->i_numframe )
p_mp4->i_delay_time = p_picture->i_dts * -1;
- dts = (p_picture->i_dts + p_mp4->i_delay_time) * p_mp4->i_time_inc;
- cts = (p_picture->i_pts + p_mp4->i_delay_time) * p_mp4->i_time_inc;
+ if( p_mp4->b_dts_compress )
+ {
+ if( p_mp4->i_numframe == 1 )
+ p_mp4->i_init_delta = (p_picture->i_dts + p_mp4->i_delay_time) * p_mp4->i_time_inc;
+ dts = p_mp4->i_numframe > p_mp4->i_delay_frames
+ ? p_picture->i_dts * p_mp4->i_time_inc
+ : p_mp4->i_numframe * (p_mp4->i_init_delta / p_mp4->i_dts_compress_multiplier);
+ cts = p_picture->i_pts * p_mp4->i_time_inc;
+ }
+ else
+ {
+ dts = (p_picture->i_dts + p_mp4->i_delay_time) * p_mp4->i_time_inc;
+ cts = (p_picture->i_pts + p_mp4->i_delay_time) * p_mp4->i_time_inc;
+ }
p_mp4->p_sample->IsRAP = p_picture->b_keyframe;
p_mp4->p_sample->DTS = dts;
typedef struct
{
- int (*open_file)( char *psz_filename, hnd_t *p_handle );
+ int use_dts_compress;
+} cli_output_opt_t;
+
+typedef struct
+{
+ int (*open_file)( char *psz_filename, hnd_t *p_handle, cli_output_opt_t *opt );
int (*set_param)( hnd_t handle, x264_param_t *p_param );
int (*write_headers)( hnd_t handle, x264_nal_t *p_nal );
int (*write_frame)( hnd_t handle, uint8_t *p_nal, int i_size, x264_picture_t *p_picture );
#include "output.h"
-static int open_file( char *psz_filename, hnd_t *p_handle )
+static int open_file( char *psz_filename, hnd_t *p_handle, cli_output_opt_t *opt )
{
if( !strcmp( psz_filename, "-" ) )
*p_handle = stdout;
H2( " --timebase <int/int> Specify timebase numerator and denominator\n"
" <integer> Specify timebase numerator for input timecode file\n"
" or specify timebase denominator for other input\n" );
+ H2( " --dts-compress Eliminate initial delay with container DTS hack\n" );
H0( "\n" );
H0( "Filtering:\n" );
H0( "\n" );
OPT_VIDEO_FILTER,
OPT_INPUT_RES,
OPT_INPUT_CSP,
- OPT_INPUT_DEPTH
+ OPT_INPUT_DEPTH,
+ OPT_DTS_COMPRESSION
} OptionsOPT;
static char short_options[] = "8A:B:b:f:hI:i:m:o:p:q:r:t:Vvw";
{ "input-res", required_argument, NULL, OPT_INPUT_RES },
{ "input-csp", required_argument, NULL, OPT_INPUT_CSP },
{ "input-depth", required_argument, NULL, OPT_INPUT_DEPTH },
+ { "dts-compress", no_argument, NULL, OPT_DTS_COMPRESSION },
{0, 0, 0, 0}
};
#if HAVE_GPAC
output = mp4_output;
param->b_annexb = 0;
- param->b_dts_compress = 0;
param->b_repeat_headers = 0;
if( param->i_nal_hrd == X264_NAL_HRD_CBR )
{
{
output = mkv_output;
param->b_annexb = 0;
- param->b_dts_compress = 0;
param->b_repeat_headers = 0;
}
else if( !strcasecmp( ext, "flv" ) )
{
output = flv_output;
param->b_annexb = 0;
- param->b_dts_compress = 1;
param->b_repeat_headers = 0;
}
else
int b_user_fps = 0;
int b_user_interlaced = 0;
cli_input_opt_t input_opt;
+ cli_output_opt_t output_opt;
char *preset = NULL;
char *tune = NULL;
memset( opt, 0, sizeof(cli_opt_t) );
memset( &input_opt, 0, sizeof(cli_input_opt_t) );
+ memset( &output_opt, 0, sizeof(cli_output_opt_t) );
input_opt.bit_depth = 8;
opt->b_progress = 1;
case OPT_INPUT_DEPTH:
input_opt.bit_depth = atoi( optarg );
break;
+ case OPT_DTS_COMPRESSION:
+ output_opt.use_dts_compress = 1;
+ break;
default:
generic_option:
{
if( select_output( muxer, output_filename, param ) )
return -1;
- FAIL_IF_ERROR( output.open_file( output_filename, &opt->hout ), "could not open output file `%s'\n", output_filename )
+ FAIL_IF_ERROR( output.open_file( output_filename, &opt->hout, &output_opt ), "could not open output file `%s'\n", output_filename )
input_filename = argv[optind++];
video_info_t info = {0};
int64_t second_largest_pts = -1;
int64_t ticks_per_frame;
double duration;
- int prev_timebase_den;
- int dts_compress_multiplier;
double pulldown_pts = 0;
opt->b_progress &= param->i_log_level < X264_LOG_DEBUG;
param->i_timebase_den = param->i_fps_num * pulldown->fps_factor;
}
- prev_timebase_den = param->i_timebase_den / gcd( param->i_timebase_num, param->i_timebase_den );
-
if( ( h = x264_encoder_open( param ) ) == NULL )
{
x264_cli_log( "x264", X264_LOG_ERROR, "x264_encoder_open failed\n" );
x264_encoder_parameters( h, param );
- dts_compress_multiplier = param->i_timebase_den / prev_timebase_den;
-
if( output.set_param( opt->hout, param ) )
{
x264_cli_log( "x264", X264_LOG_ERROR, "can't set outfile param\n" );
else if( opt->timebase_convert_multiplier )
pic.i_pts = (int64_t)( pic.i_pts * opt->timebase_convert_multiplier + 0.5 );
- int64_t output_pts = pic.i_pts * dts_compress_multiplier; /* pts libx264 returns */
-
if( pic.i_pts <= largest_pts )
{
if( cli_log_level >= X264_LOG_DEBUG || pts_warning_cnt < MAX_PTS_WARNING )
x264_cli_log( "x264", X264_LOG_WARNING, "non-strictly-monotonic pts at frame %d (%"PRId64" <= %"PRId64")\n",
- i_frame, output_pts, largest_pts * dts_compress_multiplier );
+ i_frame, pic.i_pts, largest_pts );
else if( pts_warning_cnt == MAX_PTS_WARNING )
x264_cli_log( "x264", X264_LOG_WARNING, "too many nonmonotonic pts warnings, suppressing further ones\n" );
pts_warning_cnt++;
pic.i_pts = largest_pts + ticks_per_frame;
- output_pts = pic.i_pts * dts_compress_multiplier;
}
second_largest_pts = largest_pts;
largest_pts = pic.i_pts;
if( opt->tcfile_out )
- fprintf( opt->tcfile_out, "%.6f\n", output_pts * ((double)param->i_timebase_num / param->i_timebase_den) * 1e3 );
+ fprintf( opt->tcfile_out, "%.6f\n", pic.i_pts * ((double)param->i_timebase_num / param->i_timebase_den) * 1e3 );
if( opt->qpfile )
parse_qpfile( opt, &pic, i_frame + opt->i_seek );
if( pts_warning_cnt >= MAX_PTS_WARNING && cli_log_level < X264_LOG_DEBUG )
x264_cli_log( "x264", X264_LOG_WARNING, "%d suppressed nonmonotonic pts warnings\n", pts_warning_cnt-MAX_PTS_WARNING );
- largest_pts *= dts_compress_multiplier;
- second_largest_pts *= dts_compress_multiplier;
/* duration algorithm fails when only 1 frame is output */
if( i_frame_output == 1 )
duration = (double)param->i_fps_den / param->i_fps_num;
#include <stdarg.h>
-#define X264_BUILD 106
+#define X264_BUILD 107
/* x264_t:
* opaque handler for encoder */
uint32_t i_fps_den;
uint32_t i_timebase_num; /* Timebase numerator */
uint32_t i_timebase_den; /* Timebase denominator */
- int b_dts_compress; /* DTS compression: this algorithm eliminates negative DTS
- * by compressing them to be less than the second PTS.
- * Warning: this will change the timebase! */
int b_tff;