mirror of
https://github.com/livepeer/lpms
synced 2026-04-22 15:57:25 +08:00
699 lines
27 KiB
C
Executable File
699 lines
27 KiB
C
Executable File
#include "encoder.h"
|
|
#include "logging.h"
|
|
|
|
#include <libavcodec/avcodec.h>
|
|
#include <libavfilter/buffersrc.h>
|
|
#include <libavfilter/buffersink.h>
|
|
#include <string.h>
|
|
|
|
static int add_video_stream(struct output_ctx *octx, struct input_ctx *ictx)
|
|
{
|
|
// video stream to muxer
|
|
int ret = 0;
|
|
AVStream *st = avformat_new_stream(octx->oc, NULL);
|
|
if (!st) LPMS_ERR(add_video_err, "Unable to alloc video stream");
|
|
octx->vi = st->index;
|
|
if (octx->fps.den) st->avg_frame_rate = octx->fps;
|
|
else st->avg_frame_rate = ictx->ic->streams[ictx->vi]->r_frame_rate;
|
|
if (is_copy(octx->video->name)) {
|
|
AVStream *ist = ictx->ic->streams[ictx->vi];
|
|
if (ictx->vi < 0 || !ist) LPMS_ERR(add_video_err, "Input video stream does not exist");
|
|
st->time_base = ist->time_base;
|
|
ret = avcodec_parameters_copy(st->codecpar, ist->codecpar);
|
|
if (ret < 0) LPMS_ERR(add_video_err, "Error copying video params from input stream");
|
|
// Sometimes the codec tag is wonky for some reason, so correct it
|
|
ret = av_codec_get_tag2(octx->oc->oformat->codec_tag, st->codecpar->codec_id, &st->codecpar->codec_tag);
|
|
avformat_transfer_internal_stream_timing_info(octx->oc->oformat, st, ist, AVFMT_TBCF_DEMUXER);
|
|
} else if (octx->vc) {
|
|
st->time_base = octx->vc->time_base;
|
|
ret = avcodec_parameters_from_context(st->codecpar, octx->vc);
|
|
// Rescale the gop/clip time to the expected timebase after filtering.
|
|
// The FPS filter outputs pts incrementing by 1 at a rate of 1/framerate
|
|
// while non-fps will retain the input timebase.
|
|
AVRational ms_tb = {1, 1000};
|
|
AVRational dest_tb;
|
|
if (octx->fps.den) dest_tb = av_inv_q(octx->fps);
|
|
else dest_tb = ictx->ic->streams[ictx->vi]->time_base;
|
|
if (octx->gop_time) {
|
|
octx->gop_pts_len = av_rescale_q(octx->gop_time, ms_tb, dest_tb);
|
|
octx->next_kf_pts = 0; // force for first frame
|
|
}
|
|
if (octx->clip_from) {
|
|
octx->clip_from_pts = av_rescale_q(octx->clip_from, ms_tb, dest_tb);
|
|
}
|
|
if (octx->clip_to) {
|
|
octx->clip_to_pts = av_rescale_q(octx->clip_to, ms_tb, dest_tb);
|
|
}
|
|
if (ret < 0) LPMS_ERR(add_video_err, "Error setting video params from encoder");
|
|
} else LPMS_ERR(add_video_err, "No video encoder, not a copy; what is this?");
|
|
|
|
octx->last_video_dts = AV_NOPTS_VALUE;
|
|
octx->last_enc_pts = AV_NOPTS_VALUE;
|
|
return 0;
|
|
|
|
add_video_err:
|
|
// XXX free anything here?
|
|
return ret;
|
|
}
|
|
|
|
static int add_audio_stream(struct input_ctx *ictx, struct output_ctx *octx)
|
|
{
|
|
if (ictx->ai < 0 || octx->da) {
|
|
// Don't need to add an audio stream if no input audio exists,
|
|
// or we're dropping the output audio stream
|
|
return 0;
|
|
}
|
|
|
|
// audio stream to muxer
|
|
int ret = 0;
|
|
AVStream *st = avformat_new_stream(octx->oc, NULL);
|
|
if (!st) LPMS_ERR(add_audio_err, "Unable to alloc audio stream");
|
|
if (is_copy(octx->audio->name)) {
|
|
AVStream *ist = ictx->ic->streams[ictx->ai];
|
|
if (ictx->ai < 0 || !ist) LPMS_ERR(add_audio_err, "Input audio stream does not exist");
|
|
st->time_base = ist->time_base;
|
|
ret = avcodec_parameters_copy(st->codecpar, ist->codecpar);
|
|
if (ret < 0) LPMS_ERR(add_audio_err, "Error copying audio params from input stream");
|
|
// Sometimes the codec tag is wonky for some reason, so correct it
|
|
ret = av_codec_get_tag2(octx->oc->oformat->codec_tag, st->codecpar->codec_id, &st->codecpar->codec_tag);
|
|
avformat_transfer_internal_stream_timing_info(octx->oc->oformat, st, ist, AVFMT_TBCF_DEMUXER);
|
|
} else if (octx->ac) {
|
|
st->time_base = octx->ac->time_base;
|
|
ret = avcodec_parameters_from_context(st->codecpar, octx->ac);
|
|
if (ret < 0) LPMS_ERR(add_audio_err, "Error setting audio params from encoder");
|
|
} else if (is_drop(octx->audio->name)) {
|
|
// Supposed to exit this function early if there's a drop
|
|
LPMS_ERR(add_audio_err, "Shouldn't ever happen here");
|
|
} else {
|
|
LPMS_ERR(add_audio_err, "No audio encoder; not a copy; what is this?");
|
|
}
|
|
octx->ai = st->index;
|
|
|
|
AVRational ms_tb = {1, 1000};
|
|
AVRational dest_tb = ictx->ic->streams[ictx->ai]->time_base;
|
|
if (octx->clip_from) {
|
|
octx->clip_audio_from_pts = av_rescale_q(octx->clip_from, ms_tb, dest_tb);
|
|
}
|
|
if (octx->clip_to) {
|
|
octx->clip_audio_to_pts = av_rescale_q(octx->clip_to, ms_tb, dest_tb);
|
|
}
|
|
|
|
// signal whether to drop preroll audio
|
|
if (st->codecpar->initial_padding) octx->drop_ts = AV_NOPTS_VALUE;
|
|
|
|
octx->last_audio_dts = AV_NOPTS_VALUE;
|
|
|
|
return 0;
|
|
|
|
add_audio_err:
|
|
// XXX free anything here?
|
|
return ret;
|
|
}
|
|
|
|
static int open_audio_output(struct input_ctx *ictx, struct output_ctx *octx,
|
|
const AVOutputFormat *fmt)
|
|
{
|
|
int ret = 0;
|
|
const AVCodec *codec = NULL;
|
|
AVCodecContext *ac = NULL;
|
|
|
|
// add audio encoder if a decoder exists and this output requires one
|
|
if (ictx->ac && needs_decoder(octx->audio->name)) {
|
|
|
|
// initialize audio filters
|
|
ret = init_audio_filters(ictx, octx);
|
|
if (ret < 0) LPMS_ERR(audio_output_err, "Unable to open audio filter")
|
|
|
|
// open encoder
|
|
codec = avcodec_find_encoder_by_name(octx->audio->name);
|
|
if (!codec) LPMS_ERR(audio_output_err, "Unable to find audio encoder");
|
|
// open audio encoder
|
|
ac = avcodec_alloc_context3(codec);
|
|
if (!ac) LPMS_ERR(audio_output_err, "Unable to alloc audio encoder");
|
|
octx->ac = ac;
|
|
ac->sample_fmt = av_buffersink_get_format(octx->af.sink_ctx);
|
|
ret = av_buffersink_get_ch_layout(octx->af.sink_ctx, &ac->ch_layout);
|
|
if (ret < 0) LPMS_ERR(audio_output_err, "Unable to initialize channel layout");
|
|
ac->sample_rate = av_buffersink_get_sample_rate(octx->af.sink_ctx);
|
|
ac->time_base = av_buffersink_get_time_base(octx->af.sink_ctx);
|
|
if (fmt->flags & AVFMT_GLOBALHEADER) ac->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
|
|
ret = avcodec_open2(ac, codec, &octx->audio->opts);
|
|
if (ret < 0) LPMS_ERR(audio_output_err, "Error opening audio encoder");
|
|
av_buffersink_set_frame_size(octx->af.sink_ctx, ac->frame_size);
|
|
}
|
|
|
|
ret = add_audio_stream(ictx, octx);
|
|
if (ret < 0) LPMS_ERR(audio_output_err, "Error adding audio stream")
|
|
|
|
audio_output_err:
|
|
// TODO clean up anything here?
|
|
return ret;
|
|
}
|
|
|
|
void close_output(struct output_ctx *octx)
|
|
{
|
|
if (octx->oc) {
|
|
if (!(octx->oc->oformat->flags & AVFMT_NOFILE) && octx->oc->pb) {
|
|
avio_closep(&octx->oc->pb);
|
|
}
|
|
avformat_free_context(octx->oc);
|
|
octx->oc = NULL;
|
|
}
|
|
if (octx->vc && octx->hw_type == AV_HWDEVICE_TYPE_NONE) avcodec_free_context(&octx->vc);
|
|
if (octx->ac) avcodec_free_context(&octx->ac);
|
|
free_filter(&octx->vf);
|
|
octx->af.flushed = octx->vf.flushed = 0;
|
|
octx->af.flushing = octx->vf.flushing = 0;
|
|
}
|
|
|
|
void free_output(struct output_ctx *octx)
|
|
{
|
|
close_output(octx);
|
|
if (octx->vc) avcodec_free_context(&octx->vc);
|
|
free_filter(&octx->vf);
|
|
free_filter(&octx->af);
|
|
free_filter(&octx->sf);
|
|
}
|
|
|
|
int open_remux_output(struct input_ctx *ictx, struct output_ctx *octx)
|
|
{
|
|
int ret = 0;
|
|
octx->oc->flags |= AVFMT_FLAG_FLUSH_PACKETS;
|
|
octx->oc->flush_packets = 1;
|
|
for (int i = 0; i < ictx->ic->nb_streams; i++) {
|
|
ret = 0;
|
|
AVStream *st = avformat_new_stream(octx->oc, NULL);
|
|
if (!st) LPMS_ERR(open_output_err, "Unable to alloc stream");
|
|
if (octx->fps.den)
|
|
st->avg_frame_rate = octx->fps;
|
|
else
|
|
st->avg_frame_rate = ictx->ic->streams[i]->r_frame_rate;
|
|
|
|
AVStream *ist = ictx->ic->streams[i];
|
|
st->time_base = ist->time_base;
|
|
ret = avcodec_parameters_copy(st->codecpar, ist->codecpar);
|
|
if (ret < 0)
|
|
LPMS_ERR(open_output_err, "Error copying params from input stream");
|
|
// Sometimes the codec tag is wonky for some reason, so correct it
|
|
ret = av_codec_get_tag2(octx->oc->oformat->codec_tag,
|
|
st->codecpar->codec_id, &st->codecpar->codec_tag);
|
|
avformat_transfer_internal_stream_timing_info(octx->oc->oformat, st, ist,
|
|
AVFMT_TBCF_DEMUXER);
|
|
|
|
}
|
|
return 0;
|
|
open_output_err:
|
|
return ret;
|
|
}
|
|
|
|
int open_output(struct output_ctx *octx, struct input_ctx *ictx)
|
|
{
|
|
int ret = 0, inp_has_stream;
|
|
|
|
const AVOutputFormat *fmt = NULL;
|
|
const AVCodec *codec = NULL;
|
|
AVFormatContext *oc = NULL;
|
|
AVCodecContext *vc = NULL;
|
|
|
|
// open muxer
|
|
fmt = av_guess_format(octx->muxer->name, octx->fname, NULL);
|
|
if (!fmt) LPMS_ERR(open_output_err, "Unable to guess output format");
|
|
ret = avformat_alloc_output_context2(&oc, fmt, NULL, octx->fname);
|
|
if (ret < 0) LPMS_ERR(open_output_err, "Unable to alloc output context");
|
|
octx->oc = oc;
|
|
|
|
// add video encoder if a decoder exists and this output requires one
|
|
if (ictx->vc && needs_decoder(octx->video->name)) {
|
|
ret = init_video_filters(ictx, octx, NULL);
|
|
if (ret < 0) LPMS_ERR(open_output_err, "Unable to open video filter");
|
|
|
|
codec = avcodec_find_encoder_by_name(octx->video->name);
|
|
if (!codec) LPMS_ERR(open_output_err, "Unable to find encoder");
|
|
|
|
// open video encoder
|
|
// XXX use avoptions rather than manual enumeration
|
|
vc = avcodec_alloc_context3(codec);
|
|
if (!vc) LPMS_ERR(open_output_err, "Unable to alloc video encoder");
|
|
octx->vc = vc;
|
|
vc->width = av_buffersink_get_w(octx->vf.sink_ctx);
|
|
vc->height = av_buffersink_get_h(octx->vf.sink_ctx);
|
|
if (octx->fps.den) vc->framerate = av_buffersink_get_frame_rate(octx->vf.sink_ctx);
|
|
else if (ictx->vc->framerate.num && ictx->vc->framerate.den) vc->framerate = ictx->vc->framerate;
|
|
else vc->framerate = ictx->ic->streams[ictx->vi]->r_frame_rate;
|
|
if (octx->fps.den) vc->time_base = av_buffersink_get_time_base(octx->vf.sink_ctx);
|
|
else if (ictx->vc->framerate.num && ictx->vc->framerate.den) vc->time_base = av_inv_q(ictx->vc->framerate);
|
|
else vc->time_base = ictx->ic->streams[ictx->vi]->time_base;
|
|
vc->flags |= AV_CODEC_FLAG_COPY_OPAQUE;
|
|
if (octx->bitrate) vc->rc_min_rate = vc->bit_rate = vc->rc_max_rate = vc->rc_buffer_size = octx->bitrate;
|
|
if (av_buffersink_get_hw_frames_ctx(octx->vf.sink_ctx)) {
|
|
vc->hw_frames_ctx =
|
|
av_buffer_ref(av_buffersink_get_hw_frames_ctx(octx->vf.sink_ctx));
|
|
if (!vc->hw_frames_ctx) LPMS_ERR(open_output_err, "Unable to alloc hardware context");
|
|
}
|
|
vc->pix_fmt = av_buffersink_get_format(octx->vf.sink_ctx); // XXX select based on encoder + input support
|
|
if (fmt->flags & AVFMT_GLOBALHEADER) vc->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
|
|
if(strcmp(octx->xcoderParams,"")!=0){
|
|
av_opt_set(vc->priv_data, "xcoder-params", octx->xcoderParams, 0);
|
|
}
|
|
// copy codec options and open encoder
|
|
AVDictionary *opts = NULL;
|
|
if (octx->video->opts) av_dict_copy(&opts, octx->video->opts, 0);
|
|
ret = avcodec_open2(vc, codec, &opts);
|
|
if (opts) av_dict_free(&opts);
|
|
if (ret < 0) LPMS_ERR(open_output_err, "Error opening video encoder");
|
|
octx->hw_type = ictx->hw_type;
|
|
}
|
|
|
|
if (!ictx->transmuxing) {
|
|
// add video stream if input contains video
|
|
inp_has_stream = ictx->vi >= 0;
|
|
if (inp_has_stream && !octx->dv) {
|
|
ret = add_video_stream(octx, ictx);
|
|
if (ret < 0) LPMS_ERR(open_output_err, "Error adding video stream");
|
|
}
|
|
|
|
ret = open_audio_output(ictx, octx, fmt);
|
|
if (ret < 0) LPMS_ERR(open_output_err, "Error opening audio output");
|
|
} else {
|
|
ret = open_remux_output(ictx, octx);
|
|
if (ret < 0) {
|
|
goto open_output_err;
|
|
}
|
|
}
|
|
|
|
if (!(fmt->flags & AVFMT_NOFILE)) {
|
|
ret = avio_open(&octx->oc->pb, octx->fname, AVIO_FLAG_WRITE);
|
|
if (ret < 0) LPMS_ERR(open_output_err, "Error opening output file");
|
|
}
|
|
|
|
if (octx->metadata) av_dict_copy(&oc->metadata, octx->metadata, 0);
|
|
|
|
ret = avformat_write_header(oc, &octx->muxer->opts);
|
|
if (ret < 0) LPMS_ERR(open_output_err, "Error writing header");
|
|
|
|
if(octx->sfilters != NULL && needs_decoder(octx->video->name) && octx->sf.active == 0) {
|
|
ret = init_signature_filters(octx, NULL);
|
|
if (ret < 0) LPMS_ERR(open_output_err, "Unable to open signature filter");
|
|
}
|
|
|
|
octx->initialized = 1;
|
|
|
|
return 0;
|
|
|
|
open_output_err:
|
|
free_output(octx);
|
|
return ret;
|
|
}
|
|
|
|
int reopen_output(struct output_ctx *octx, struct input_ctx *ictx)
|
|
{
|
|
int ret = 0;
|
|
// re-open muxer for HW encoding
|
|
const AVOutputFormat *fmt = av_guess_format(octx->muxer->name, octx->fname, NULL);
|
|
if (!fmt) LPMS_ERR(reopen_out_err, "Unable to guess format for reopen");
|
|
ret = avformat_alloc_output_context2(&octx->oc, fmt, NULL, octx->fname);
|
|
if (ret < 0) LPMS_ERR(reopen_out_err, "Unable to alloc reopened out context");
|
|
|
|
// re-attach video encoder
|
|
if (octx->vc) {
|
|
ret = add_video_stream(octx, ictx);
|
|
if (ret < 0) LPMS_ERR(reopen_out_err, "Unable to re-add video stream");
|
|
} else LPMS_INFO("No video stream!?");
|
|
|
|
// re-attach audio encoder
|
|
ret = open_audio_output(ictx, octx, fmt);
|
|
if (ret < 0) LPMS_ERR(reopen_out_err, "Unable to re-add audio stream");
|
|
|
|
if (!(fmt->flags & AVFMT_NOFILE)) {
|
|
ret = avio_open(&octx->oc->pb, octx->fname, AVIO_FLAG_WRITE);
|
|
if (ret < 0) LPMS_ERR(reopen_out_err, "Error re-opening output file");
|
|
}
|
|
|
|
if (octx->metadata) av_dict_copy(&octx->oc->metadata, octx->metadata, 0);
|
|
ret = avformat_write_header(octx->oc, &octx->muxer->opts);
|
|
if (ret < 0) LPMS_ERR(reopen_out_err, "Error re-writing header");
|
|
|
|
if(octx->sfilters != NULL && needs_decoder(octx->video->name) && octx->sf.active == 0) {
|
|
ret = init_signature_filters(octx, NULL);
|
|
if (ret < 0) LPMS_ERR(reopen_out_err, "Unable to open signature filter");
|
|
}
|
|
|
|
reopen_out_err:
|
|
return ret;
|
|
}
|
|
|
|
int encode(AVCodecContext* encoder, AVFrame *frame, struct output_ctx* octx, AVStream* ost)
|
|
{
|
|
int ret = 0;
|
|
AVPacket *pkt = NULL;
|
|
|
|
if (AVMEDIA_TYPE_VIDEO == ost->codecpar->codec_type && frame) {
|
|
if (encoder->width != frame->width || encoder->height != frame->height) {
|
|
// Frame dimensions changed so need to re-init encoder
|
|
const AVCodec *codec = avcodec_find_encoder_by_name(octx->video->name);
|
|
if (!codec) LPMS_ERR(encode_cleanup, "Unable to find encoder");
|
|
AVCodecContext *vc = avcodec_alloc_context3(codec);
|
|
if (!vc) LPMS_ERR(encode_cleanup, "Unable to alloc video encoder");
|
|
// copy any additional params needed from AVCodecParameters
|
|
AVCodecParameters *codecpar = avcodec_parameters_alloc();
|
|
if (!codecpar) LPMS_ERR(encode_cleanup, "Unable to alloc codec params");
|
|
avcodec_parameters_from_context(codecpar, encoder);
|
|
avcodec_parameters_to_context(vc, codecpar);
|
|
avcodec_parameters_free(&codecpar);
|
|
// manually set some additional fields
|
|
vc->width = frame->width;
|
|
vc->height = frame->height;
|
|
vc->time_base = encoder->time_base;
|
|
vc->flags = encoder->flags;
|
|
vc->rc_min_rate = encoder->rc_min_rate;
|
|
vc->rc_max_rate = encoder->rc_max_rate;
|
|
vc->bit_rate = encoder->bit_rate;
|
|
vc->rc_buffer_size = encoder->rc_buffer_size;
|
|
if (encoder->hw_frames_ctx) {
|
|
if (octx->vf.active && av_buffersink_get_hw_frames_ctx(octx->vf.sink_ctx)) {
|
|
vc->hw_frames_ctx =
|
|
av_buffer_ref(av_buffersink_get_hw_frames_ctx(octx->vf.sink_ctx));
|
|
if (!vc->hw_frames_ctx) {
|
|
LPMS_ERR(encode_cleanup, "Unable to re-alloc encoder hwframes")
|
|
}
|
|
} else {
|
|
vc->hw_frames_ctx = av_buffer_ref(encoder->hw_frames_ctx);
|
|
}
|
|
}
|
|
|
|
// flush old encoder
|
|
AVPacket *pkt = av_packet_alloc();
|
|
if (!pkt) LPMS_ERR(encode_cleanup, "Unable to alloc flush packet");
|
|
avcodec_send_frame(encoder, NULL);
|
|
AVRational time_base = encoder->time_base;
|
|
while (!ret) {
|
|
av_packet_unref(pkt);
|
|
ret = avcodec_receive_packet(encoder, pkt);
|
|
// TODO error handling
|
|
if (!ret) {
|
|
if (!octx->fps.den && octx->vf.active) {
|
|
// adjust timestamps for filter passthrough
|
|
time_base = octx->vf.time_base;
|
|
int64_t pts_dts = pkt->pts - pkt->dts;
|
|
pkt->pts = (int64_t)pkt->opaque; // already in filter timebase
|
|
pkt->dts = pkt->pts - av_rescale_q(pts_dts, encoder->time_base, time_base);
|
|
}
|
|
mux(pkt, time_base, octx, ost);
|
|
} else if (AVERROR_EOF != ret) {
|
|
av_packet_free(&pkt);
|
|
LPMS_ERR(encode_cleanup, "did not get eof");
|
|
}
|
|
}
|
|
av_packet_free(&pkt);
|
|
avcodec_free_context(&octx->vc);
|
|
|
|
// copy codec options and open encoder
|
|
AVDictionary *opts = NULL;
|
|
if (octx->video->opts) av_dict_copy(&opts, octx->video->opts, 0);
|
|
ret = avcodec_open2(vc, codec, &opts);
|
|
if (opts) av_dict_free(&opts);
|
|
if (ret < 0) LPMS_ERR(encode_cleanup, "Error opening video encoder");
|
|
if (octx->gop_pts_len) octx->next_kf_pts = frame->pts + octx->gop_pts_len;
|
|
octx->vc = vc;
|
|
encoder = vc;
|
|
}
|
|
if (!octx->res->frames) {
|
|
frame->pict_type = AV_PICTURE_TYPE_I;
|
|
}
|
|
octx->res->frames++;
|
|
octx->res->pixels += encoder->width * encoder->height;
|
|
}
|
|
|
|
// We don't want to send NULL frames for HW encoding
|
|
// because that closes the encoder: not something we want
|
|
if (AV_HWDEVICE_TYPE_NONE == octx->hw_type || AV_HWDEVICE_TYPE_MEDIACODEC == octx->hw_type ||
|
|
AVMEDIA_TYPE_AUDIO == ost->codecpar->codec_type || frame) {
|
|
ret = avcodec_send_frame(encoder, frame);
|
|
if (AVERROR_EOF == ret) ; // continue ; drain encoder
|
|
else if (ret < 0) LPMS_ERR(encode_cleanup, "Error sending frame to encoder");
|
|
}
|
|
|
|
if (AVMEDIA_TYPE_VIDEO == ost->codecpar->codec_type &&
|
|
AV_HWDEVICE_TYPE_CUDA == octx->hw_type && !frame) {
|
|
avcodec_flush_buffers(encoder);
|
|
}
|
|
|
|
pkt = av_packet_alloc();
|
|
if (!pkt) {
|
|
ret = AVERROR(ENOMEM);
|
|
LPMS_ERR(encode_cleanup, "Error allocating packet for encode");
|
|
}
|
|
while (1) {
|
|
av_packet_unref(pkt);
|
|
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");
|
|
AVRational time_base = encoder->time_base;
|
|
if (AVMEDIA_TYPE_VIDEO == ost->codecpar->codec_type && !octx->fps.den && octx->vf.active) {
|
|
// try to preserve source timestamps for fps passthrough.
|
|
time_base = octx->vf.time_base;
|
|
int64_t pts_dts_diff = pkt->pts - pkt->dts;
|
|
pkt->pts = (int64_t)pkt->opaque; // already in filter timebase
|
|
pkt->dts = pkt->pts - av_rescale_q(pts_dts_diff, encoder->time_base, time_base);
|
|
}
|
|
ret = mux(pkt, time_base, octx, ost);
|
|
if (ret < 0) goto encode_cleanup;
|
|
}
|
|
|
|
encode_cleanup:
|
|
if (pkt) av_packet_free(&pkt);
|
|
return ret;
|
|
}
|
|
|
|
int mux(AVPacket *pkt, AVRational tb, struct output_ctx *octx, AVStream *ost)
|
|
{
|
|
pkt->stream_index = ost->index;
|
|
if (av_cmp_q(tb, ost->time_base)) {
|
|
av_packet_rescale_ts(pkt, tb, ost->time_base);
|
|
}
|
|
|
|
/* Enable this if it seems we have issues
|
|
with the first and second segments overlapping due to bframes
|
|
See TestTranscoder_API_DTSOverlap
|
|
|
|
int delay = av_rescale_q(10, (AVRational){1, 1}, ost->time_base);
|
|
if (pkt->dts != AV_NOPTS_VALUE) {
|
|
pkt->dts += delay;
|
|
}
|
|
if (pkt->pts != AV_NOPTS_VALUE) {
|
|
pkt->pts += delay;
|
|
}
|
|
*/
|
|
|
|
// drop any preroll audio. may need to drop multiple packets for multichannel
|
|
// XXX this breaks if preroll isn't exactly one AVPacket or drop_ts == 0
|
|
// hasn't been a problem in practice (so far)
|
|
if (AVMEDIA_TYPE_AUDIO == ost->codecpar->codec_type) {
|
|
if (octx->drop_ts == AV_NOPTS_VALUE) octx->drop_ts = pkt->pts;
|
|
if (pkt->pts && pkt->pts == octx->drop_ts) return 0;
|
|
|
|
if (pkt->dts != AV_NOPTS_VALUE && pkt->pts != AV_NOPTS_VALUE && pkt->dts > pkt->pts) {
|
|
pkt->pts = pkt->dts = pkt->pts + pkt->dts + octx->last_audio_dts + 1
|
|
- FFMIN3(pkt->pts, pkt->dts, octx->last_audio_dts + 1)
|
|
- FFMAX3(pkt->pts, pkt->dts, octx->last_audio_dts + 1);
|
|
}
|
|
/*https://github.com/livepeer/FFmpeg/blob/682c4189d8364867bcc49f9749e04b27dc37cded/fftools/ffmpeg.c#L824*/
|
|
if (pkt->dts != AV_NOPTS_VALUE && octx->last_audio_dts != AV_NOPTS_VALUE) {
|
|
/*If the out video format does not require strictly increasing timestamps,
|
|
but they must still be monotonic, then let set max timestamp as octx->last_audio_dts+1.*/
|
|
int64_t max = octx->last_audio_dts + !(octx->oc->oformat->flags & AVFMT_TS_NONSTRICT);
|
|
// check if dts is bigger than previous last dts or not, not then that's non-monotonic
|
|
if (pkt->dts < max) {
|
|
if (pkt->pts >= pkt->dts) pkt->pts = FFMAX(pkt->pts, max);
|
|
pkt->dts = max;
|
|
}
|
|
}
|
|
octx->last_audio_dts = pkt->dts;
|
|
}
|
|
if (AVMEDIA_TYPE_VIDEO == ost->codecpar->codec_type) {
|
|
//after a long time of transcoding on GPU, exactly 6.5 hours, sometimes here,
|
|
//got weird packets which DTS > PTS. But muxer doesn't agree with those packets.
|
|
//so we make DTS and PTS of these packets accept in muxer.
|
|
/*https://github.com/livepeer/FFmpeg/blob/dd7e5c34e75fcb8ed79e0798d190d523e11ce60b/libavformat/mux.c#L604*/
|
|
if (pkt->dts != AV_NOPTS_VALUE && pkt->pts != AV_NOPTS_VALUE && pkt->dts > pkt->pts) {
|
|
//picking middle value from (pkt->pts, pkt->dts and oct->last_video_dts + 1).
|
|
pkt->pts = pkt->dts = pkt->pts + pkt->dts + octx->last_video_dts + 1
|
|
- FFMIN3(pkt->pts, pkt->dts, octx->last_video_dts + 1)
|
|
- FFMAX3(pkt->pts, pkt->dts, octx->last_video_dts + 1);
|
|
}
|
|
// Match ffmpeg's mux behavior and clamp non-monotonic DTS separately,
|
|
// even when the packet did not trip the decoder's DTS > PTS repair path.
|
|
if (pkt->dts != AV_NOPTS_VALUE && octx->last_video_dts != AV_NOPTS_VALUE) {
|
|
int64_t max = octx->last_video_dts + !(octx->oc->oformat->flags & AVFMT_TS_NONSTRICT);
|
|
if (pkt->dts < max) {
|
|
if (pkt->pts >= pkt->dts) pkt->pts = FFMAX(pkt->pts, max);
|
|
pkt->dts = max;
|
|
}
|
|
}
|
|
octx->last_video_dts = pkt->dts;
|
|
}
|
|
|
|
return av_interleaved_write_frame(octx->oc, pkt);
|
|
}
|
|
|
|
static int calc_signature(AVFrame *inf, struct output_ctx *octx)
|
|
{
|
|
int ret = 0;
|
|
if (inf->hw_frames_ctx && octx->sf.hw_frames_ctx && inf->hw_frames_ctx->data != octx->sf.hw_frames_ctx->data) {
|
|
free_filter(&octx->sf);
|
|
ret = init_signature_filters(octx, inf);
|
|
if (ret < 0) return lpms_ERR_FILTERS;
|
|
}
|
|
ret = av_buffersrc_write_frame(octx->sf.src_ctx, inf);
|
|
if (ret < 0) return ret;
|
|
AVFrame *signframe = octx->sf.frame;
|
|
av_frame_unref(signframe);
|
|
ret = av_buffersink_get_frame(octx->sf.sink_ctx, signframe);
|
|
return ret;
|
|
}
|
|
|
|
int process_out(struct input_ctx *ictx, struct output_ctx *octx, AVCodecContext *encoder, AVStream *ost,
|
|
struct filter_ctx *filter, AVFrame *inf)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (!encoder) LPMS_ERR(proc_cleanup, "Trying to transmux; not supported")
|
|
|
|
int is_video = (AVMEDIA_TYPE_VIDEO == ost->codecpar->codec_type);
|
|
int is_audio = (AVMEDIA_TYPE_AUDIO == ost->codecpar->codec_type);
|
|
|
|
if (is_video && filter && !filter->active && inf) {
|
|
ret = init_video_filters(ictx, octx, inf);
|
|
if (ret < 0) LPMS_ERR(proc_cleanup, "Unable to initialize video filter");
|
|
}
|
|
|
|
if (!filter || !filter->active) {
|
|
// Don't call encode if nothing has been sent to CUDA yet (via filter
|
|
// lazy init) because it may cause odd interactions with flushing
|
|
if (is_video && !inf &&
|
|
octx->hw_type > AV_HWDEVICE_TYPE_NONE &&
|
|
AV_HWDEVICE_TYPE_MEDIACODEC != octx->hw_type) {
|
|
return AVERROR_EOF;
|
|
}
|
|
// No filter in between decoder and encoder, so use input frame directly
|
|
return encode(encoder, inf, octx, ost);
|
|
}
|
|
|
|
ret = filtergraph_write(inf, ictx, octx, filter, is_video);
|
|
if (ret < 0) goto proc_cleanup;
|
|
|
|
while (1) {
|
|
// Drain the filter. Each input frame may have multiple output frames
|
|
AVFrame *frame = filter->frame;
|
|
ret = filtergraph_read(ictx, octx, filter, is_video);
|
|
if (ret == lpms_ERR_FILTER_FLUSHED) continue;
|
|
else if (AVERROR(EAGAIN) == ret || AVERROR_EOF == ret) {
|
|
// no frame returned from filtergraph
|
|
// proceed only if the input frame is a flush (inf == null)
|
|
if (inf) return ret;
|
|
frame = NULL;
|
|
} else if (ret < 0) goto proc_cleanup;
|
|
|
|
if (is_video && !octx->clip_start_pts_found && frame) {
|
|
octx->clip_start_pts = frame->pts;
|
|
octx->clip_start_pts_found = 1;
|
|
}
|
|
if (is_audio && !octx->clip_audio_start_pts_found && frame) {
|
|
octx->clip_audio_start_pts = frame->pts;
|
|
octx->clip_audio_start_pts_found = 1;
|
|
}
|
|
|
|
|
|
if (is_video && octx->clip_to && octx->clip_start_pts_found && frame && frame->pts > octx->clip_to_pts + octx->clip_start_pts) goto skip;
|
|
if (is_audio && octx->clip_to && octx->clip_audio_start_pts_found && frame && frame->pts > octx->clip_audio_to_pts + octx->clip_audio_start_pts) {
|
|
goto skip;
|
|
}
|
|
|
|
if (is_video) {
|
|
if (octx->clip_from && frame) {
|
|
if (frame->pts < octx->clip_from_pts + octx->clip_start_pts) goto skip;
|
|
if (!octx->clip_started) {
|
|
octx->clip_started = 1;
|
|
frame->pict_type = AV_PICTURE_TYPE_I;
|
|
if (octx->gop_pts_len) {
|
|
octx->next_kf_pts = frame->pts + octx->gop_pts_len;
|
|
}
|
|
}
|
|
if (octx->clip_from && frame) {
|
|
frame->pts -= octx->clip_from_pts + octx->clip_start_pts;
|
|
}
|
|
}
|
|
} else if (octx->clip_from_pts && !octx->clip_started) {
|
|
// we want first frame to be video frame
|
|
goto skip;
|
|
}
|
|
if (is_audio && octx->clip_from && frame && frame->pts < octx->clip_audio_from_pts + octx->clip_audio_start_pts) {
|
|
goto skip;
|
|
}
|
|
if (is_audio && octx->clip_from && frame) {
|
|
frame->pts -= octx->clip_audio_from_pts + octx->clip_audio_start_pts;
|
|
}
|
|
|
|
// Set GOP interval if necessary
|
|
if (is_video && octx->gop_pts_len && frame && frame->pts >= octx->next_kf_pts) {
|
|
frame->pict_type = AV_PICTURE_TYPE_I;
|
|
octx->next_kf_pts = frame->pts + octx->gop_pts_len;
|
|
}
|
|
|
|
if(is_video && frame != NULL && octx->sfilters != NULL) {
|
|
ret = calc_signature(frame, octx);
|
|
if(ret < 0) LPMS_WARN("Could not calculate signature value for frame");
|
|
}
|
|
|
|
if (frame) {
|
|
// rescale pts to match encoder timebase if necessary (eg, fps passthrough)
|
|
AVRational filter_tb = av_buffersink_get_time_base(filter->sink_ctx);
|
|
int pts_rescaled = av_cmp_q(filter_tb, encoder->time_base);
|
|
if (pts_rescaled) {
|
|
frame->pts = av_rescale_q(frame->pts, filter_tb, encoder->time_base);
|
|
// TODO does frame->duration needs to be rescaled too?
|
|
}
|
|
// Handle timebase conversion collapsing adjacent PTS into the same encoder tick
|
|
if (is_video && pts_rescaled) {
|
|
if (octx->last_enc_pts != AV_NOPTS_VALUE && frame->pts <= octx->last_enc_pts) {
|
|
frame->pts = octx->last_enc_pts + 1;
|
|
AVRational ftb = av_buffersink_get_time_base(filter->sink_ctx);
|
|
frame->opaque = (void *)av_rescale_q(frame->pts, encoder->time_base, ftb);
|
|
}
|
|
octx->last_enc_pts = frame->pts;
|
|
}
|
|
}
|
|
|
|
// Check for runaway encodes where the FPS filter produces too many frames
|
|
// Unclear what causes these
|
|
if (is_video && frame && ictx->decoded_res && ictx->decoded_res->frames > 0) {
|
|
if (ictx->ic && ictx->ic->iformat &&
|
|
!strcmp(ictx->ic->iformat->name, "image2")) {
|
|
// Image sequence input can legitimately expand frame counts.
|
|
goto after_runaway_check;
|
|
}
|
|
int64_t decoded_frames = ictx->decoded_res->frames;
|
|
if ((int64_t)octx->res->frames + 1 > 25 * decoded_frames) {
|
|
av_frame_unref(frame);
|
|
ret = lpms_ERR_ENC_RUNAWAY;
|
|
goto proc_cleanup;
|
|
}
|
|
}
|
|
after_runaway_check:
|
|
|
|
ret = encode(encoder, frame, octx, ost);
|
|
skip:
|
|
av_frame_unref(frame);
|
|
// For HW we keep the encoder open so will only get EAGAIN.
|
|
// Return EOF in place of EAGAIN for to terminate the flush
|
|
if (frame == NULL && octx->hw_type > AV_HWDEVICE_TYPE_NONE &&
|
|
AV_HWDEVICE_TYPE_MEDIACODEC != octx->hw_type &&
|
|
AVERROR(EAGAIN) == ret && !inf) return AVERROR_EOF;
|
|
if (frame == NULL) return ret;
|
|
}
|
|
|
|
proc_cleanup:
|
|
return ret;
|
|
}
|
|
|