Hi destal,

On Wed, 27 May 2026 06:20:36 +0000
destal takul wrote:
> Thank you for the clarification. Is there any way to fix the files that were 
> remuxed with those warnings, or do I have to remux from the original? I have 
> been using the Cygwin version of ffmpeg to cut the end of videos muxed with 
> mkvmerge on non-key-frames since mkvmerge itself doesn't support this, and I 
> probably have hundreds of files that have been remuxed with such warnings 
> over the course of the past few years.

Sorry for inconvinience.
Could you please try attached tool? This should fix the broken
timestamp.

Usage: fix_ts broken.mkv output.mkv

To compile this, you need libavformat-devel, libavcodec-devel,
and libavutil-deve packages.

-- 
Takashi Yano <[email protected]>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
	int index;
	int display_rank;
} FrameMap;

static int cmp_frame(const void *a, const void *b) {
	return ((FrameMap*)a)->display_rank - ((FrameMap*)b)->display_rank;
}

#define MAX_REORDER_DELAY 16

void cleanup(AVFormatContext *ifmt, AVFormatContext *ofmt,
		AVCodecParserContext *parser, AVCodecContext *codec_ctx,
		FrameMap *map, FrameMap *sorted) {
	if (parser) av_parser_close(parser);
	if (codec_ctx) avcodec_free_context(&codec_ctx);
	if (ifmt) avformat_close_input(&ifmt);
	if (ofmt) {
		if (!(ofmt->oformat->flags & AVFMT_NOFILE)) avio_closep(&ofmt->pb);
		avformat_free_context(ofmt);
	}
	if (map) free(map);
	if (sorted) free(sorted);
}

int main(int argc, char **argv) {
	int ret = 1;
	if (argc < 3) {
		fprintf(stderr, "Usage: %s in.mkv out.mkv\n", argv[0]);
		return 1;
	}

	AVFormatContext *ifmt = NULL, *ofmt = NULL;
	AVCodecParserContext *parser = NULL;
	AVCodecContext *codec_ctx = NULL;
	FrameMap *map = NULL, *sorted = NULL;
	int frame_count = 0, frame_cap = 10000;

	if (avformat_open_input(&ifmt, argv[1], NULL, NULL) < 0) {
		fprintf(stderr, "avformat_open_input() failed.\n");
		goto end;
	}
	if (avformat_find_stream_info(ifmt, NULL) < 0) {
		fprintf(stderr, "avformat_find_stream_info() failed.\n");
		goto end;
	}

	int video_index = -1;
	for (unsigned i = 0; i < ifmt->nb_streams; i++)
		if (ifmt->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
			video_index = i;

	parser = av_parser_init(AV_CODEC_ID_H264);
	codec_ctx = avcodec_alloc_context3(NULL);
	avcodec_parameters_to_context(codec_ctx,
			ifmt->streams[video_index]->codecpar);

	map = malloc(frame_cap * sizeof(FrameMap));
	AVPacket pkt;
	uint64_t pict_num_msb = 0;
	int prev_pict_num = -1;

	while (av_read_frame(ifmt, &pkt) >= 0) {
		if (pkt.stream_index == video_index) {
			if (frame_count >= frame_cap) {
				frame_cap *= 2;
				map = realloc(map, frame_cap * sizeof(FrameMap));
			}
			uint8_t *data; int size;
			av_parser_parse2(parser, codec_ctx, &data, &size,
					pkt.data, pkt.size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
			if (prev_pict_num >= 0 &&
					parser->output_picture_number - prev_pict_num
					< -MAX_REORDER_DELAY)
				pict_num_msb++;
			map[frame_count] =
				(FrameMap){frame_count, parser->output_picture_number
					+ (int)(pict_num_msb * 1024)};
			prev_pict_num = parser->output_picture_number;
			frame_count++;
		}
		av_packet_unref(&pkt);
	}

	sorted = malloc(frame_count * sizeof(FrameMap));
	memcpy(sorted, map, frame_count * sizeof(FrameMap));
	qsort(sorted, frame_count, sizeof(FrameMap), cmp_frame);
	for (int i = 0; i < frame_count; i++)
		map[sorted[i].index].display_rank = i;

	if (avformat_alloc_output_context2(&ofmt, NULL, NULL, argv[2]) != 0) {
		fprintf(stderr, "avformat_alloc_output_context2() failed.\n");
		goto end;
	}
	for (unsigned i = 0; i < ifmt->nb_streams; i++) {
		AVStream *out_st = avformat_new_stream(ofmt, NULL);
		avcodec_parameters_copy(out_st->codecpar, ifmt->streams[i]->codecpar);
	}

	AVStream *in_st = ifmt->streams[video_index];

	if (!(ofmt->oformat->flags & AVFMT_NOFILE))
		avio_open(&ofmt->pb, argv[2], AVIO_FLAG_WRITE);
	if (avformat_write_header(ofmt, NULL) < 0) {
		fprintf(stderr, "avformat_write_header() failed.\n");
		goto end;
	}

	AVRational out_tb = ofmt->streams[video_index]->time_base;

	AVRational frame_rate = in_st->avg_frame_rate;
	if (frame_rate.num == 0)
		frame_rate = in_st->r_frame_rate;

	av_seek_frame(ifmt, video_index, 0, AVSEEK_FLAG_BACKWARD);
	int current = 0;
	while (av_read_frame(ifmt, &pkt) >= 0) {
		if (pkt.stream_index == video_index) {
			pkt.pts = av_rescale_q(map[current].display_rank,
					av_inv_q(frame_rate), out_tb);
			pkt.dts = av_rescale_q(current - MAX_REORDER_DELAY,
					av_inv_q(frame_rate), out_tb);
			pkt.duration = av_rescale_q(1, av_inv_q(frame_rate), out_tb);
			current++;
		}
		if (ifmt->streams[pkt.stream_index]->codecpar->codec_type
				== AVMEDIA_TYPE_AUDIO)
			av_packet_rescale_ts(&pkt,
					ifmt->streams[pkt.stream_index]->time_base,
					ofmt->streams[pkt.stream_index]->time_base);
		av_interleaved_write_frame(ofmt, &pkt);
		av_packet_unref(&pkt);
	}
	av_write_trailer(ofmt);
	ret = 0;

end:
	cleanup(ifmt, ofmt, parser, codec_ctx, map, sorted);
	return ret;
}
fix_ts: fix_ts.c
        $(CC) $< -o $@ -lavformat -lavcodec -lavutil

clean:
        $(RM) fix_ts
-- 
Problem reports:      https://cygwin.com/problems.html
FAQ:                  https://cygwin.com/faq/
Documentation:        https://cygwin.com/docs.html
Unsubscribe info:     https://cygwin.com/ml/#unsubscribe-simple

Reply via email to