Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions ffmpeg/decoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ struct input_ctx {
// In HW transcoding, demuxer is opened once and used,
// so it is necessary to check whether the input pixel format does not change in the middle.
enum AVPixelFormat last_format;

// per-segment tracking (reset for each segment)
int64_t segment_last_pts; // best-effort pts of most recent decoded video frame
};

// Exported methods
Expand Down
56 changes: 30 additions & 26 deletions ffmpeg/ffmpeg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2504,8 +2504,8 @@ func TestTranscoder_LargeOutputs(t *testing.T) {
close(closeCh)
assert.Nil(err)
assert.Equal(120, res.Decoded.Frames)
assert.Equal(116, res.Encoded[0].Frames) // ffmpeg probably drops missing timestamp frames
assert.Equal(56, res.Encoded[1].Frames)
assert.Equal(120, res.Encoded[0].Frames)
assert.Equal(60, res.Encoded[1].Frames)
cmd := `
# check input properties to ensure they still have the weird timestamps
ffprobe -of csv -hide_banner -show_entries frame=pts_time,pkt_dts_time,media_type,pict_type $1/../data/missing-dts.ts 2>&1 | grep video > input.out
Expand Down Expand Up @@ -2642,25 +2642,26 @@ func TestTranscoder_LargeOutputs(t *testing.T) {
cat <<- 'EOF2' > expected-output.out
frame,video,25994.033333,25994.033333,I,
frame,video,25994.066667,25994.066667,P
frame,video,25994.100000,25994.100000,B
frame,video,25994.133333,25994.133333,P
frame,video,25994.100000,25994.100000,P
frame,video,25994.133333,25994.133333,B
frame,video,25994.166667,25994.166667,B
frame,video,25994.200000,25994.200000,B
frame,video,25994.233333,25994.233333,B
frame,video,25994.266667,25994.266667,P
frame,video,25994.233333,25994.233333,P
frame,video,25994.266667,25994.266667,B
frame,video,25994.300000,25994.300000,B
frame,video,25994.333333,25994.333333,P
frame,video,25994.366667,25994.366667,B
frame,video,25994.333333,25994.333333,B
frame,video,25994.366667,25994.366667,P
frame,video,25994.400000,25994.400000,B
frame,video,25994.433333,25994.433333,B
frame,video,25994.466667,25994.466667,P
frame,video,25994.500000,25994.500000,B
frame,video,25994.466667,25994.466667,B
frame,video,25994.500000,25994.500000,P
frame,video,25994.533333,25994.533333,B
frame,video,25994.566667,25994.566667,B
frame,video,25994.600000,25994.600000,P
frame,video,25994.666667,25994.666667,P,
frame,video,25994.700000,25994.700000,B,
frame,video,25994.733333,25994.733333,B,
frame,video,25994.600000,25994.600000,B
frame,video,25994.633333,25994.633333,P,
frame,video,25994.666667,25994.666667,B,
frame,video,25994.700000,25994.700000,P,
frame,video,25994.733333,25994.733333,P,
frame,video,25994.766667,25994.766667,B,
frame,video,25994.800000,25994.800000,P,
frame,video,25994.833333,25994.833333,B,
Expand All @@ -2669,32 +2670,35 @@ func TestTranscoder_LargeOutputs(t *testing.T) {
frame,video,25994.933333,25994.933333,P,
frame,video,25994.966667,25994.966667,B,
frame,video,25995.000000,25995.000000,P,
frame,video,25995.033333,25995.033333,B,
frame,video,25995.066667,25995.066667,B,
frame,video,25995.033333,25995.033333,P,
frame,video,25995.066667,25995.066667,P,
frame,video,25995.100000,25995.100000,P,
frame,video,25995.133333,25995.133333,B,
frame,video,25995.166667,25995.166667,P,
frame,video,25995.200000,25995.200000,B,
frame,video,25995.233333,25995.233333,B,
frame,video,25995.266667,25995.266667,B,
frame,video,25995.300000,25995.300000,B,
frame,video,25995.333333,25995.333333,P,
frame,video,25995.300000,25995.300000,P,
frame,video,25995.333333,25995.333333,B,
frame,video,25995.366667,25995.366667,B,
frame,video,25995.400000,25995.400000,B,
frame,video,25995.433333,25995.433333,B,
frame,video,25995.466667,25995.466667,P,
frame,video,25995.433333,25995.433333,P,
frame,video,25995.466667,25995.466667,B,
frame,video,25995.500000,25995.500000,B,
frame,video,25995.533333,25995.533333,B,
frame,video,25995.566667,25995.566667,B,
frame,video,25995.600000,25995.600000,P,
frame,video,25995.566667,25995.566667,P,
frame,video,25995.600000,25995.600000,B,
frame,video,25995.633333,25995.633333,B,
frame,video,25995.666667,25995.666667,B,
frame,video,25995.700000,25995.700000,P,
frame,video,25995.733333,25995.733333,B,
frame,video,25995.766667,25995.766667,P,
frame,video,25995.766667,25995.766667,B,
frame,video,25995.800000,25995.800000,B,
frame,video,25995.833333,25995.833333,B,
frame,video,25995.833333,25995.833333,P,
frame,video,25995.866667,25995.866667,B,
frame,video,25995.900000,25995.900000,P,
frame,video,25995.900000,25995.900000,B,
frame,video,25995.933333,25995.933333,B,
frame,video,25995.966667,N/A,B,
frame,video,25995.966667,N/A,P,
frame,video,25996.000000,N/A,P,
EOF2
diff -u expected-output.out output.out
Expand Down
23 changes: 22 additions & 1 deletion ffmpeg/filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,28 @@ int filtergraph_write(AVFrame *inf, struct input_ctx *ictx, struct output_ctx *o
// Timestamp handling code
AVStream *vst = ictx->ic->streams[ictx->vi];
if (inf) { // Non-Flush Frame
inf->opaque = (void *) inf->pts; // Store original PTS for calc later
if (is_video) {
int64_t pts = inf->pts;
if (pts == AV_NOPTS_VALUE) {
av_log(NULL, AV_LOG_WARNING, "Filter frame pts is AV_NOPTS_VALUE. Falling back to best_effort_timestamp\n");
pts = inf->best_effort_timestamp;
}
if (pts == AV_NOPTS_VALUE && ictx->segment_last_pts != AV_NOPTS_VALUE) {
av_log(NULL, AV_LOG_WARNING, "Filter frame pts is still AV_NOPTS_VALUE. Falling back to segment_last_pts + step\n");
int64_t step = inf->duration;
if (!step && vst->r_frame_rate.den){
step = av_rescale_q(1, av_inv_q(vst->r_frame_rate), vst->time_base);
}
if (step){
pts = ictx->segment_last_pts + step;
}
}
inf->pts = pts;
}
inf->opaque = (void *) inf->pts;
if (inf->pts == AV_NOPTS_VALUE) {
av_log(NULL, AV_LOG_ERROR, "Filter frame pts remains AV_NOPTS_VALUE\n");
}
if (is_video && octx->fps.den) {
// Custom PTS set when FPS filter is used
int64_t ts_step = inf->pts - filter->prev_frame_pts;
Expand Down
14 changes: 13 additions & 1 deletion ffmpeg/transcoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,8 @@ int transcode(struct transcode_thread *h,
int nb_outputs = h->nb_outputs;
int outputs_ready = 0, hit_eof = 0;

ictx->segment_last_pts = AV_NOPTS_VALUE;

ipkt = av_packet_alloc();
if (!ipkt) LPMS_ERR(transcode_cleanup, "Unable to allocated packet");
dframe = av_frame_alloc();
Expand Down Expand Up @@ -426,7 +428,17 @@ int transcode(struct transcode_thread *h,
decoded_results->frames += dframe->width && dframe->height;
decoded_results->pixels += dframe->width * dframe->height;
has_frame = has_frame && dframe->width && dframe->height;
if (has_frame) last_frame = ictx->last_frame_v;
if (has_frame) {
last_frame = ictx->last_frame_v;
int64_t pts = dframe->pts;
// Try best effort timestamp if pts is not available
if (pts == AV_NOPTS_VALUE) pts = dframe->best_effort_timestamp;
// If best effort timestamp is not available, try to use segment last pts + duration
if (pts == AV_NOPTS_VALUE && ictx->segment_last_pts != AV_NOPTS_VALUE && dframe->duration)
pts = ictx->segment_last_pts + dframe->duration;
// Only update segment last pts if pts is valid
if (pts != AV_NOPTS_VALUE) ictx->segment_last_pts = pts;
}
} else if (AVMEDIA_TYPE_AUDIO == ist->codecpar->codec_type) {
has_frame = has_frame && dframe->nb_samples;
if (has_frame) last_frame = ictx->last_frame_a;
Expand Down
Loading