Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ak/skip flush frames #368

Closed
wants to merge 2 commits into from
Closed
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
Binary file added data/stopwatch_8frames.ts
Binary file not shown.
3 changes: 2 additions & 1 deletion ffmpeg/decoder.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "transcoder.h"
#include "decoder.h"
#include "logging.h"
#include "flushing.h"

#include <libavutil/pixfmt.h>

Expand Down Expand Up @@ -70,7 +71,7 @@ int decode_in(struct input_ctx *ictx, AVPacket *pkt, AVFrame *frame, int *stream

if (!ictx->first_pkt && pkt->flags & AV_PKT_FLAG_KEY && decoder == ictx->vc) {
ictx->first_pkt = av_packet_clone(pkt);
ictx->first_pkt->pts = -1;
ictx->first_pkt->pts = FLUSH_FRAME_PTS;
}

ret = lpms_send_packet(ictx, decoder, pkt);
Expand Down
5 changes: 5 additions & 0 deletions ffmpeg/encoder.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "encoder.h"
#include "logging.h"
#include "flushing.h"

#include <libavcodec/avcodec.h>
#include <libavfilter/buffersrc.h>
Expand Down Expand Up @@ -371,6 +372,10 @@ static int encode(AVCodecContext* encoder, AVFrame *frame, struct output_ctx* oc
ret = avcodec_receive_packet(encoder, pkt);
if (AVERROR(EAGAIN) == ret || AVERROR_EOF == ret) goto encode_cleanup;
if (ret < 0) LPMS_ERR(encode_cleanup, "Error receiving packet from encoder");
if(pkt->pts == FLUSH_FRAME_PTS) {
// This is flush-sentinel frame, do not leak into output
return ret;
}
ret = mux(pkt, encoder->time_base, octx, ost);
if (ret < 0) goto encode_cleanup;
}
Expand Down
16 changes: 10 additions & 6 deletions ffmpeg/filter.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "filter.h"
#include "logging.h"
#include "flushing.h"

#include <libavfilter/buffersrc.h>
#include <libavfilter/buffersink.h>
Expand Down Expand Up @@ -314,7 +315,7 @@ int filtergraph_write(AVFrame *inf, struct input_ctx *ictx, struct output_ctx *o
} else if (!filter->flushed) { // Flush Frame
int ts_step;
inf = (is_video) ? ictx->last_frame_v : ictx->last_frame_a;
inf->opaque = (void *) (INT64_MIN); // Store INT64_MIN as pts for flush frames
inf->opaque = (void *) (FLUSH_FRAME_PTS);
filter->flushing = 1;
if (is_video) {
ts_step = av_rescale_q(1, av_inv_q(vst->r_frame_rate), vst->time_base);
Expand Down Expand Up @@ -344,6 +345,7 @@ int filtergraph_write(AVFrame *inf, struct input_ctx *ictx, struct output_ctx *o

int filtergraph_read(struct input_ctx *ictx, struct output_ctx *octx, struct filter_ctx *filter, int is_video)
{
int64_t original_input_pts;
AVFrame *frame = filter->frame;
av_frame_unref(frame);

Expand All @@ -353,22 +355,24 @@ int filtergraph_read(struct input_ctx *ictx, struct output_ctx *octx, struct fil
if (AVERROR(EAGAIN) == ret || AVERROR_EOF == ret) return ret;
else if (ret < 0) LPMS_ERR(fg_read_cleanup, "Error consuming the filtergraph");

if (frame && ((int64_t) frame->opaque == INT64_MIN)) {
// opaque being INT64_MIN means it's a flush packet
// don't set flushed flag in case this is a flush from a previous segment
original_input_pts = (int64_t)frame->opaque;

if (frame && ((int64_t) frame->opaque == FLUSH_FRAME_PTS)) {
// it's a flush packet. don't set flushed flag in case this is a flush from a previous segment
if (filter->flushing) filter->flushed = 1;
ret = lpms_ERR_FILTER_FLUSHED;
} else if (frame && is_video && octx->fps.den) {
// We set custom PTS as an input of the filtergraph so we need to
// re-calculate our output PTS before passing it on to the encoder
if (filter->pts_diff == INT64_MIN) {
int64_t pts = (int64_t)frame->opaque; // original input PTS
pts = av_rescale_q_rnd(pts, ictx->ic->streams[ictx->vi]->time_base, av_buffersink_get_time_base(filter->sink_ctx), AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
int64_t pts = av_rescale_q_rnd(original_input_pts, ictx->ic->streams[ictx->vi]->time_base, av_buffersink_get_time_base(filter->sink_ctx), AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
// difference between rescaled input PTS and the segment's first frame PTS of the filtergraph output
filter->pts_diff = pts - frame->pts;
}
frame->pts += filter->pts_diff; // Re-calculate by adding back this segment's difference calculated at start
}

if(original_input_pts == FLUSH_FRAME_PTS) frame->pts = FLUSH_FRAME_PTS;
fg_read_cleanup:
return ret;
}
Expand Down
9 changes: 9 additions & 0 deletions ffmpeg/flushing.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

#ifndef _LPMS_FLUSHING_H_
#define _LPMS_FLUSHING_H_

#include <stdint.h>

#define FLUSH_FRAME_PTS INT64_MIN

#endif // _LPMS_FLUSHING_H_
10 changes: 10 additions & 0 deletions ffmpeg/nvidia_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -793,3 +793,13 @@ func TestTranscoder_Portrait(t *testing.T) {
func TestNvidia_DiscontinuityAudioSegment(t *testing.T) {
discontinuityAudioSegment(t, Nvidia)
}

func TestTranscoder_FlushFrameLeak(t *testing.T) {
in := &TranscodeOptionsIn{Fname: "../data/stopwatch_8frames.ts", Accel: Nvidia}
profile := VideoProfile{Name: "p720", Bitrate: "3000000", Framerate: 5994, FramerateDen: 100, AspectRatio: "16:9", Resolution: "1280x720"}
out := []TranscodeOptions{{Profile: profile, Oname: "../data/stopwatch_8frames_720p.ts", Accel: Nvidia}}
result, err := Transcode3(in, out)
require.NoError(t, err)
require.Equal(t, 8, result.Decoded.Frames)
require.Equal(t, 8, result.Encoded[0].Frames)
}
5 changes: 3 additions & 2 deletions ffmpeg/transcoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "filter.h"
#include "encoder.h"
#include "logging.h"
#include "flushing.h"

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
Expand Down Expand Up @@ -463,7 +464,7 @@ int handle_video_packet(struct transcode_thread *h, output_results *decoded_resu
// very first video packet, keep it
// TODO: this should be called first_video_pkt
ictx->first_pkt = av_packet_clone(pkt);
ictx->first_pkt->pts = -1;
ictx->first_pkt->pts = FLUSH_FRAME_PTS;
}

// TODO: this could probably be done always, because it is a no-op if
Expand Down Expand Up @@ -983,7 +984,7 @@ struct transcode_thread* lpms_transcode_new() {
// keep track of last dts in each stream.
// used while transmuxing, to skip packets with invalid dts.
for (int i = 0; i < MAX_OUTPUT_SIZE; i++) {
h->ictx.last_dts[i] = -1;
h->ictx.last_dts[i] = FLUSH_FRAME_PTS;
}
return h;
}
Expand Down