static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x264_param_t *param, char *opt_string ) { selvry_hnd_t *h = malloc( sizeof(selvry_hnd_t) ); if( !h ) return -1; h->pattern_len = 0; h->step_size = 0; int offsets[MAX_PATTERN_SIZE]; for( char *tok, *p = opt_string; (tok = strtok( p, "," )); p = NULL ) { int val = x264_otoi( tok, -1 ); if( p ) { FAIL_IF_ERROR( val <= 0, "invalid step `%s'\n", tok ) h->step_size = val; continue; } FAIL_IF_ERROR( val < 0 || val >= h->step_size, "invalid offset `%s'\n", tok ) FAIL_IF_ERROR( h->pattern_len >= MAX_PATTERN_SIZE, "max pattern size %d reached\n", MAX_PATTERN_SIZE ) offsets[h->pattern_len++] = val; } FAIL_IF_ERROR( !h->step_size, "no step size provided\n" ) FAIL_IF_ERROR( !h->pattern_len, "no offsets supplied\n" ) h->pattern = malloc( h->pattern_len * sizeof(int) ); if( !h->pattern ) return -1; memcpy( h->pattern, offsets, h->pattern_len * sizeof(int) ); /* determine required cache size to maintain pattern. */ intptr_t max_rewind = 0; int min = h->step_size; for( int i = h->pattern_len-1; i >= 0; i-- ) { min = X264_MIN( min, offsets[i] ); if( i ) max_rewind = X264_MAX( max_rewind, offsets[i-1] - min + 1 ); /* reached maximum rewind size */ if( max_rewind == h->step_size ) break; } if( x264_init_vid_filter( "cache", handle, filter, info, param, (void*)max_rewind ) ) return -1; /* done initing, overwrite properties */ if( h->step_size != h->pattern_len ) { info->num_frames = (uint64_t)info->num_frames * h->pattern_len / h->step_size; info->fps_den *= h->step_size; info->fps_num *= h->pattern_len; x264_reduce_fraction( &info->fps_num, &info->fps_den ); if( info->vfr ) { info->timebase_den *= h->pattern_len; info->timebase_num *= h->step_size; x264_reduce_fraction( &info->timebase_num, &info->timebase_den ); } } h->pts = 0; h->vfr = info->vfr; h->prev_filter = *filter; h->prev_hnd = *handle; *filter = select_every_filter; *handle = h; return 0; }
static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, cli_input_opt_t *opt ) { y4m_hnd_t *h = malloc( sizeof(y4m_hnd_t) ); int i; uint32_t n, d; char header[MAX_YUV4_HEADER+10]; char *tokend, *header_end; int colorspace = X264_CSP_NONE; int alt_colorspace = X264_CSP_NONE; int alt_bit_depth = 8; if( !h ) return -1; h->next_frame = 0; info->vfr = 0; if( !strcmp( psz_filename, "-" ) ) h->fh = stdin; else h->fh = fopen(psz_filename, "rb"); if( h->fh == NULL ) return -1; h->frame_header_len = strlen( Y4M_FRAME_MAGIC )+1; /* Read header */ for( i = 0; i < MAX_YUV4_HEADER; i++ ) { header[i] = fgetc( h->fh ); if( header[i] == '\n' ) { /* Add a space after last option. Makes parsing "444" vs "444alpha" easier. */ header[i+1] = 0x20; header[i+2] = 0; break; } } if( i == MAX_YUV4_HEADER || strncmp( header, Y4M_MAGIC, strlen( Y4M_MAGIC ) ) ) return -1; /* Scan properties */ header_end = &header[i+1]; /* Include space */ h->seq_header_len = i+1; for( char *tokstart = &header[strlen( Y4M_MAGIC )+1]; tokstart < header_end; tokstart++ ) { if( *tokstart == 0x20 ) continue; switch( *tokstart++ ) { case 'W': /* Width. Required. */ info->width = strtol( tokstart, &tokend, 10 ); tokstart=tokend; break; case 'H': /* Height. Required. */ info->height = strtol( tokstart, &tokend, 10 ); tokstart=tokend; break; case 'C': /* Color space */ colorspace = parse_csp_and_depth( tokstart, &h->bit_depth ); tokstart = strchr( tokstart, 0x20 ); break; case 'I': /* Interlace type */ switch( *tokstart++ ) { case 't': info->interlaced = 1; info->tff = 1; break; case 'b': info->interlaced = 1; info->tff = 0; break; case 'm': info->interlaced = 1; break; //case '?': //case 'p': default: break; } break; case 'F': /* Frame rate - 0:0 if unknown */ if( sscanf( tokstart, "%u:%u", &n, &d ) == 2 && n && d ) { x264_reduce_fraction( &n, &d ); info->fps_num = n; info->fps_den = d; } tokstart = strchr( tokstart, 0x20 ); break; case 'A': /* Pixel aspect - 0:0 if unknown */ /* Don't override the aspect ratio if sar has been explicitly set on the commandline. */ if( sscanf( tokstart, "%u:%u", &n, &d ) == 2 && n && d ) { x264_reduce_fraction( &n, &d ); info->sar_width = n; info->sar_height = d; } tokstart = strchr( tokstart, 0x20 ); break; case 'X': /* Vendor extensions */ if( !strncmp( "YSCSS=", tokstart, 6 ) ) { /* Older nonstandard pixel format representation */ tokstart += 6; alt_colorspace = parse_csp_and_depth( tokstart, &alt_bit_depth ); } tokstart = strchr( tokstart, 0x20 ); break; } } if( colorspace == X264_CSP_NONE ) { colorspace = alt_colorspace; h->bit_depth = alt_bit_depth; } // default to 8bit 4:2:0 if nothing is specified if( colorspace == X264_CSP_NONE ) { colorspace = X264_CSP_I420; h->bit_depth = 8; } FAIL_IF_ERROR( colorspace <= X264_CSP_NONE || colorspace >= X264_CSP_MAX, "colorspace unhandled\n" ) FAIL_IF_ERROR( h->bit_depth < 8 || h->bit_depth > 16, "unsupported bit depth `%d'\n", h->bit_depth ); info->thread_safe = 1; info->num_frames = 0; info->csp = colorspace; h->frame_size = h->frame_header_len; if( h->bit_depth > 8 ) info->csp |= X264_CSP_HIGH_DEPTH; const x264_cli_csp_t *csp = x264_cli_get_csp( info->csp ); for( i = 0; i < csp->planes; i++ ) { h->plane_size[i] = x264_cli_pic_plane_size( info->csp, info->width, info->height, i ); h->frame_size += h->plane_size[i]; /* x264_cli_pic_plane_size returns the size in bytes, we need the value in pixels from here on */ h->plane_size[i] /= x264_cli_csp_depth_factor( info->csp ); } /* Most common case: frame_header = "FRAME" */ if( x264_is_regular_file( h->fh ) ) { uint64_t init_pos = ftell( h->fh ); fseek( h->fh, 0, SEEK_END ); uint64_t i_size = ftell( h->fh ); fseek( h->fh, init_pos, SEEK_SET ); info->num_frames = (i_size - h->seq_header_len) / h->frame_size; } *p_handle = h; return 0; }
static int parse_tcfile( FILE *tcfile_in, timecode_hnd_t *h, video_info_t *info ) { char buff[256]; int ret, tcfv, num, seq_num, timecodes_num; int64_t pts_seek_offset; double *timecodes = NULL; double *fpss = NULL; ret = fscanf( tcfile_in, "# timecode format v%d", &tcfv ); if( ret != 1 || (tcfv != 1 && tcfv != 2) ) { fprintf( stderr, "timecode [error]: unsupported timecode format\n" ); return -1; } if( tcfv == 1 ) { uint64_t file_pos; double assume_fps, seq_fps; int start, end = h->seek; int prev_start = -1, prev_end = -1; h->assume_fps = 0; for( num = 2; fgets( buff, sizeof(buff), tcfile_in ) != NULL; num++ ) { if( buff[0] == '#' || buff[0] == '\n' || buff[0] == '\r' ) continue; if( sscanf( buff, "assume %lf", &h->assume_fps ) != 1 && sscanf( buff, "Assume %lf", &h->assume_fps ) != 1 ) { fprintf( stderr, "timecode [error]: tcfile parsing error: assumed fps not found\n" ); return -1; } break; } if( h->assume_fps <= 0 ) { fprintf( stderr, "timecode [error]: invalid assumed fps %.6f\n", h->assume_fps ); return -1; } file_pos = ftell( tcfile_in ); h->stored_pts_num = 0; for( seq_num = 0; fgets( buff, sizeof(buff), tcfile_in ) != NULL; num++ ) { if( buff[0] == '#' || buff[0] == '\n' || buff[0] == '\r' ) { if( sscanf( buff, "# TDecimate Mode 3: Last Frame = %d", &end ) == 1 ) h->stored_pts_num = end + 1 - h->seek; continue; } ret = sscanf( buff, "%d,%d,%lf", &start, &end, &seq_fps ); if( ret != 3 && ret != EOF ) { fprintf( stderr, "timecode [error]: invalid input tcfile\n" ); return -1; } if( start > end || start <= prev_start || end <= prev_end || seq_fps <= 0 ) { fprintf( stderr, "timecode [error]: invalid input tcfile at line %d: %s\n", num, buff ); return -1; } prev_start = start; prev_end = end; if( h->auto_timebase_den || h->auto_timebase_num ) ++seq_num; } if( !h->stored_pts_num ) h->stored_pts_num = end + 1 - h->seek; timecodes_num = h->stored_pts_num + h->seek; fseek( tcfile_in, file_pos, SEEK_SET ); timecodes = malloc( timecodes_num * sizeof(double) ); if( !timecodes ) return -1; if( h->auto_timebase_den || h->auto_timebase_num ) { fpss = malloc( (seq_num + 1) * sizeof(double) ); if( !fpss ) goto fail; } assume_fps = correct_fps( h->assume_fps, h ); if( assume_fps < 0 ) goto fail; timecodes[0] = 0; for( num = seq_num = 0; num < timecodes_num - 1; ) { fgets( buff, sizeof(buff), tcfile_in ); if( buff[0] == '#' || buff[0] == '\n' || buff[0] == '\r' ) continue; ret = sscanf( buff, "%d,%d,%lf", &start, &end, &seq_fps ); if( ret != 3 ) start = end = timecodes_num - 1; if( h->auto_timebase_den || h->auto_timebase_num ) fpss[seq_num++] = seq_fps; seq_fps = correct_fps( seq_fps, h ); if( seq_fps < 0 ) goto fail; for( ; num < start && num < timecodes_num - 1; num++ ) timecodes[num + 1] = timecodes[num] + 1 / assume_fps; for( num = start; num <= end && num < timecodes_num - 1; num++ ) timecodes[num + 1] = timecodes[num] + 1 / seq_fps; } if( h->auto_timebase_den || h->auto_timebase_num ) fpss[seq_num] = h->assume_fps; if( h->auto_timebase_num && !h->auto_timebase_den ) { double exponent; double assume_fps_sig, seq_fps_sig; if( try_mkv_timebase_den( fpss, h, seq_num + 1 ) < 0 ) goto fail; fseek( tcfile_in, file_pos, SEEK_SET ); assume_fps_sig = sigexp10( h->assume_fps, &exponent ); assume_fps = MKV_TIMEBASE_DEN / ( round( MKV_TIMEBASE_DEN / assume_fps_sig ) / exponent ); for( num = 0; num < timecodes_num - 1; ) { fgets( buff, sizeof(buff), tcfile_in ); if( buff[0] == '#' || buff[0] == '\n' || buff[0] == '\r' ) continue; ret = sscanf( buff, "%d,%d,%lf", &start, &end, &seq_fps ); if( ret != 3 ) start = end = timecodes_num - 1; seq_fps_sig = sigexp10( seq_fps, &exponent ); seq_fps = MKV_TIMEBASE_DEN / ( round( MKV_TIMEBASE_DEN / seq_fps_sig ) / exponent ); for( ; num < start && num < timecodes_num - 1; num++ ) timecodes[num + 1] = timecodes[num] + 1 / assume_fps; for( num = start; num <= end && num < timecodes_num - 1; num++ ) timecodes[num + 1] = timecodes[num] + 1 / seq_fps; } } if( fpss ) free( fpss ); h->assume_fps = assume_fps; h->last_timecode = timecodes[timecodes_num - 1]; } else /* tcfv == 2 */ { uint64_t file_pos = ftell( tcfile_in ); num = h->stored_pts_num = 0; while( fgets( buff, sizeof(buff), tcfile_in ) != NULL ) { if( buff[0] == '#' || buff[0] == '\n' || buff[0] == '\r' ) { if( !num ) file_pos = ftell( tcfile_in ); continue; } if( num >= h->seek ) ++h->stored_pts_num; ++num; } timecodes_num = h->stored_pts_num + h->seek; if( !timecodes_num ) { fprintf( stderr, "timecode [error]: input tcfile doesn't have any timecodes!\n" ); return -1; } fseek( tcfile_in, file_pos, SEEK_SET ); timecodes = malloc( timecodes_num * sizeof(double) ); if( !timecodes ) return -1; fgets( buff, sizeof(buff), tcfile_in ); ret = sscanf( buff, "%lf", &timecodes[0] ); if( ret != 1 ) { fprintf( stderr, "timecode [error]: invalid input tcfile for frame 0\n" ); goto fail; } for( num = 1; num < timecodes_num; ) { fgets( buff, sizeof(buff), tcfile_in ); if( buff[0] == '#' || buff[0] == '\n' || buff[0] == '\r' ) continue; ret = sscanf( buff, "%lf", &timecodes[num] ); timecodes[num] *= 1e-3; /* Timecode format v2 is expressed in milliseconds. */ if( ret != 1 || timecodes[num] <= timecodes[num - 1] ) { fprintf( stderr, "timecode [error]: invalid input tcfile for frame %d\n", num ); goto fail; } ++num; } if( timecodes_num == 1 ) h->timebase_den = info->fps_num; else if( h->auto_timebase_den ) { fpss = malloc( (timecodes_num - 1) * sizeof(double) ); if( !fpss ) goto fail; for( num = 0; num < timecodes_num - 1; num++ ) { fpss[num] = 1 / (timecodes[num + 1] - timecodes[num]); if( h->timebase_den >= 0 ) { int i = 1; int fps_num, fps_den; double exponent; double fps_sig = sigexp10( fpss[num], &exponent ); while( 1 ) { fps_den = i * h->timebase_num; fps_num = round( fps_den * fps_sig ) * exponent; if( fps_num < 0 || fabs( ((double)fps_num / fps_den) / exponent - fps_sig ) < DOUBLE_EPSILON ) break; ++i; } h->timebase_den = fps_num > 0 && h->timebase_den ? lcm( h->timebase_den, fps_num ) : fps_num; if( h->timebase_den < 0 ) { h->auto_timebase_den = 0; continue; } } } if( h->auto_timebase_num && !h->auto_timebase_den ) if( try_mkv_timebase_den( fpss, h, timecodes_num - 1 ) < 0 ) goto fail; free( fpss ); } if( timecodes_num > 1 ) h->assume_fps = 1 / (timecodes[timecodes_num - 1] - timecodes[timecodes_num - 2]); else h->assume_fps = (double)info->fps_num / info->fps_den; h->last_timecode = timecodes[timecodes_num - 1]; } if( h->auto_timebase_den || h->auto_timebase_num ) { x264_reduce_fraction( &h->timebase_num, &h->timebase_den ); fprintf( stderr, "timecode [info]: automatic timebase generation %d/%d\n", h->timebase_num, h->timebase_den ); } else if( h->timebase_den <= 0 ) { fprintf( stderr, "timecode [error]: automatic timebase generation failed.\n" " Specify an appropriate timebase manually.\n" ); goto fail; } h->pts = malloc( h->stored_pts_num * sizeof(int64_t) ); if( !h->pts ) goto fail; pts_seek_offset = (int64_t)( timecodes[h->seek] * ((double)h->timebase_den / h->timebase_num) + 0.5 ); h->pts[0] = 0; for( num = 1; num < h->stored_pts_num; num++ ) { h->pts[num] = (int64_t)( timecodes[h->seek + num] * ((double)h->timebase_den / h->timebase_num) + 0.5 ); h->pts[num] -= pts_seek_offset; if( h->pts[num] <= h->pts[num - 1] ) { fprintf( stderr, "timecode [error]: invalid timebase or timecode for frame %d\n", num ); goto fail; } } free( timecodes ); return 0; fail: if( timecodes ) free( timecodes ); if( fpss ) free( fpss ); return -1; }