Merge branch 'olive-editor:master' into av1

This commit is contained in:
jazztickets
2022-09-07 17:26:09 -06:00
committed by GitHub
314 changed files with 11070 additions and 3363 deletions
+90 -24
View File
@@ -63,7 +63,6 @@ FFmpegDecoder::FFmpegDecoder() :
native_output_pix_fmt_(VideoParams::kFormatInvalid),
working_frame_(nullptr),
working_packet_(nullptr),
is_working_(false),
cache_at_zero_(false),
cache_at_eof_(false)
{
@@ -80,6 +79,7 @@ bool FFmpegDecoder::OpenInternal()
working_frame_ = av_frame_alloc();
working_packet_ = av_packet_alloc();
frame_rate_tb_ = rational::NaN;
return true;
}
@@ -142,28 +142,32 @@ bool FFmpegDecoder::OpenInternal()
return output_frame;
}*/
TexturePtr FFmpegDecoder::RetrieveVideoInternal(Renderer *renderer, const rational &timecode, const RetrieveVideoParams &params, const QAtomicInt *cancelled)
TexturePtr FFmpegDecoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
{
if (AVFramePtr f = RetrieveFrame(timecode, cancelled)) {
if (cancelled && *cancelled) {
if (AVFramePtr f = RetrieveFrame(p.time, p.cancelled)) {
if (p.cancelled && p.cancelled->IsCancelled()) {
return nullptr;
}
if (InitScaler(f.get(), params)) {
int &src_fmt = f.get()->format;
src_fmt = FFmpegUtils::ConvertJPEGSpaceToRegularSpace(static_cast<AVPixelFormat>(src_fmt));
f->color_range = p.force_range == VideoParams::kColorRangeFull ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG;
if (InitScaler(f.get(), p)) {
VideoParams vp(instance_.avstream()->codecpar->width,
instance_.avstream()->codecpar->height,
native_output_pix_fmt_,
native_channel_count_,
av_guess_sample_aspect_ratio(instance_.fmt_ctx(), instance_.avstream(), nullptr),
VideoParams::kInterlaceNone,
params.divider);
p.divider);
TexturePtr tex = nullptr;
const bool hwscale = true;
bool hwscale = true;
// Attempt to use GLSL shader for faster YUV to RGB conversion
if (hwscale) {
AVPixelFormat src_fmt = AVPixelFormat(f.get()->format);
if (src_fmt == AV_PIX_FMT_YUV420P
|| src_fmt == AV_PIX_FMT_YUV422P
|| src_fmt == AV_PIX_FMT_YUV444P
@@ -175,7 +179,7 @@ TexturePtr FFmpegDecoder::RetrieveVideoInternal(Renderer *renderer, const ration
|| src_fmt == AV_PIX_FMT_YUV444P12LE) {
if (Yuv2RgbShader.isNull()) {
// Compile shader
Yuv2RgbShader = renderer->CreateNativeShader(ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/yuv2rgb.frag"))));
Yuv2RgbShader = p.renderer->CreateNativeShader(ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/yuv2rgb.frag"))));
}
if (!Yuv2RgbShader.isNull()) {
@@ -207,7 +211,7 @@ TexturePtr FFmpegDecoder::RetrieveVideoInternal(Renderer *renderer, const ration
plane_params.set_channel_count(1);
plane_params.set_divider(1);
plane_params.set_format(native_internal_pix_fmt_);
TexturePtr y_plane = renderer->CreateTexture(plane_params, f->data[0], f->linesize[0] / px_size);
TexturePtr y_plane = p.renderer->CreateTexture(plane_params, f->data[0], f->linesize[0] / px_size);
if (src_fmt == AV_PIX_FMT_YUV420P
|| src_fmt == AV_PIX_FMT_YUV422P
@@ -224,17 +228,47 @@ TexturePtr FFmpegDecoder::RetrieveVideoInternal(Renderer *renderer, const ration
plane_params.set_height(plane_params.height()/2);
}
TexturePtr u_plane = renderer->CreateTexture(plane_params, f->data[1], f->linesize[1] / px_size);
TexturePtr v_plane = renderer->CreateTexture(plane_params, f->data[2], f->linesize[2] / px_size);
TexturePtr u_plane = p.renderer->CreateTexture(plane_params, f->data[1], f->linesize[1] / px_size);
TexturePtr v_plane = p.renderer->CreateTexture(plane_params, f->data[2], f->linesize[2] / px_size);
ShaderJob job;
job.Insert(QStringLiteral("y_channel"), NodeValue(NodeValue::kTexture, QVariant::fromValue(y_plane)));
job.Insert(QStringLiteral("u_channel"), NodeValue(NodeValue::kTexture, QVariant::fromValue(u_plane)));
job.Insert(QStringLiteral("v_channel"), NodeValue(NodeValue::kTexture, QVariant::fromValue(v_plane)));
job.Insert(QStringLiteral("bits_per_pixel"), NodeValue(NodeValue::kInt, bits_per_pixel));
job.Insert(QStringLiteral("full_range"), NodeValue(NodeValue::kBoolean, f->color_range == AVCOL_RANGE_JPEG));
tex = renderer->CreateTexture(vp);
renderer->BlitToTexture(Yuv2RgbShader, job, tex.get(), false);
const int *yuv_coeffs = sws_getCoefficients(FFmpegUtils::GetSwsColorspaceFromAVColorSpace(f.get()->colorspace));
job.Insert(QStringLiteral("yuv_crv"), NodeValue(NodeValue::kInt, yuv_coeffs[0]));
job.Insert(QStringLiteral("yuv_cgu"), NodeValue(NodeValue::kInt, yuv_coeffs[2]));
job.Insert(QStringLiteral("yuv_cgv"), NodeValue(NodeValue::kInt, yuv_coeffs[3]));
job.Insert(QStringLiteral("yuv_cbu"), NodeValue(NodeValue::kInt, yuv_coeffs[1]));
int interlacing = 0;
if (p.src_interlacing != VideoParams::kInterlaceNone) {
if (frame_rate_tb_.isNull()) {
frame_rate_tb_ = av_guess_frame_rate(instance_.fmt_ctx(), instance_.avstream(), f.get());
// Double frame rate for interlaced fields
frame_rate_tb_ *= 2;
// Flip frame rate so it can be used as a timebase
frame_rate_tb_.flip();
}
int64_t req = Timecode::time_to_timestamp(p.time, frame_rate_tb_);
int64_t frm = Timecode::rescale_timestamp(f->pts - instance_.avstream()->start_time, instance_.avstream()->time_base, frame_rate_tb_);
bool first = (req == frm);
bool top_first = (p.src_interlacing == VideoParams::kInterlacedTopFirst);
interlacing = (first == top_first) ? 1 : 2;
}
job.Insert(QStringLiteral("interlacing"), NodeValue(NodeValue::kInt, interlacing));
job.Insert(QStringLiteral("pixel_height"), NodeValue(NodeValue::kInt, f->height));
tex = p.renderer->CreateTexture(vp);
p.renderer->BlitToTexture(Yuv2RgbShader, job, tex.get(), false);
}
}
}
@@ -242,6 +276,7 @@ TexturePtr FFmpegDecoder::RetrieveVideoInternal(Renderer *renderer, const ration
if (!tex) {
// Fallback to software pixel format conversion
int r;
r = av_buffersrc_add_frame_flags(buffersrc_ctx_, f.get(), AV_BUFFERSRC_FLAG_KEEP_REF);
if (r < 0) {
return nullptr;
@@ -251,7 +286,7 @@ TexturePtr FFmpegDecoder::RetrieveVideoInternal(Renderer *renderer, const ration
return nullptr;
}
tex = renderer->CreateTexture(vp, working_frame_->data[0], working_frame_->linesize[0] / vp.GetBytesPerPixel());
tex = p.renderer->CreateTexture(vp, working_frame_->data[0], working_frame_->linesize[0] / vp.GetBytesPerPixel());
av_frame_unref(working_frame_);
}
@@ -290,7 +325,7 @@ QString FFmpegDecoder::id() const
return QStringLiteral("ffmpeg");
}
FootageDescription FFmpegDecoder::Probe(const QString &filename, const QAtomicInt *cancelled) const
FootageDescription FFmpegDecoder::Probe(const QString &filename, CancelAtom *cancelled) const
{
// Return value
FootageDescription desc(id());
@@ -416,6 +451,7 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, const QAtomicIn
stream.set_start_time(avstream->start_time);
stream.set_time_base(avstream->time_base);
stream.set_duration(avstream->duration);
stream.set_color_range(avstream->codecpar->color_range == AVCOL_RANGE_JPEG ? VideoParams::kColorRangeFull : VideoParams::kColorRangeLimited);
// Defaults to false, requires user intervention if incorrect
stream.set_premultiplied_alpha(false);
@@ -500,6 +536,8 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, const QAtomicIn
}
desc.SetStreamCount(fmt_ctx->nb_streams);
}
// Free all memory
@@ -515,7 +553,7 @@ QString FFmpegDecoder::FFmpegError(int error_code)
return QStringLiteral("%1 %2").arg(QString::number(error_code), err);
}
bool FFmpegDecoder::ConformAudioInternal(const QVector<QString> &filenames, const AudioParams &params, const QAtomicInt *cancelled)
bool FFmpegDecoder::ConformAudioInternal(const QVector<QString> &filenames, const AudioParams &params, CancelAtom *cancelled)
{
// Iterate through each audio frame and extract the PCM data
@@ -565,7 +603,7 @@ bool FFmpegDecoder::ConformAudioInternal(const QVector<QString> &filenames, cons
while (true) {
// Check if we have a `cancelled` ptr and its value
if (cancelled && *cancelled) {
if (cancelled && cancelled->IsCancelled()) {
break;
}
@@ -745,12 +783,12 @@ void FFmpegDecoder::ClearFrameCache()
}
}
AVFramePtr FFmpegDecoder::RetrieveFrame(const rational& time, const QAtomicInt *cancelled)
AVFramePtr FFmpegDecoder::RetrieveFrame(const rational& time, CancelAtom *cancelled)
{
int64_t target_ts = GetTimeInTimebaseUnits(time, instance_.avstream()->time_base, instance_.avstream()->start_time);
const int64_t min_seek = -instance_.avstream()->start_time;
int64_t seek_ts = target_ts;
int64_t seek_ts = std::max(min_seek, target_ts - MaximumQueueSize());
bool still_seeking = false;
if (time != kAnyTimecode) {
@@ -783,7 +821,7 @@ AVFramePtr FFmpegDecoder::RetrieveFrame(const rational& time, const QAtomicInt *
while (true) {
// Break out of loop if we've cancelled
if (cancelled && *cancelled) {
if (cancelled && cancelled->IsCancelled()) {
break;
}
@@ -794,7 +832,7 @@ AVFramePtr FFmpegDecoder::RetrieveFrame(const rational& time, const QAtomicInt *
// Pull from the decoder
ret = instance_.GetFrame(working_packet_, filtered.get());
if (cancelled && *cancelled) {
if (cancelled && cancelled->IsCancelled()) {
break;
}
@@ -839,7 +877,7 @@ AVFramePtr FFmpegDecoder::RetrieveFrame(const rational& time, const QAtomicInt *
} else {
// Cut down to thread count - 1 before we acquire a new frame
if (cached_frames_.size() == size_t(QThread::idealThreadCount())) {
if (cached_frames_.size() > size_t(MaximumQueueSize())) {
RemoveFirstFrame();
}
@@ -879,7 +917,12 @@ AVFramePtr FFmpegDecoder::RetrieveFrame(const rational& time, const QAtomicInt *
bool FFmpegDecoder::InitScaler(AVFrame *input, const RetrieveVideoParams& params)
{
if (params == filter_params_ && filter_graph_ && input_fmt_ == input->format) {
if (params.divider == filter_params_.divider
&& params.force_range == filter_params_.force_range
&& params.maximum_format == filter_params_.maximum_format
&& params.src_interlacing == filter_params_.src_interlacing
&& filter_graph_
&& input_fmt_ == input->format) {
// We have an appropriate filter for these parameters, just return true
return true;
}
@@ -944,6 +987,20 @@ bool FFmpegDecoder::InitScaler(AVFrame *input, const RetrieveVideoParams& params
// Link filters as necessary
AVFilterContext *last_filter = buffersrc_ctx_;
// Add deinterlace filter if necessary
if (filter_params_.src_interlacing != VideoParams::kInterlaceNone) {
AVFilterContext* deint_filter;
snprintf(filter_args, kFilterArgSz, "mode=1:parity=%s",
filter_params_.src_interlacing == VideoParams::kInterlacedTopFirst ? "0" : "1");
avfilter_graph_create_filter(&deint_filter, avfilter_get_by_name("yadif"), "deint", filter_args, nullptr, filter_graph_);
avfilter_link(last_filter, 0, deint_filter, 0);
last_filter = deint_filter;
}
// Add scale filter if necessary
int dst_width, dst_height;
if (filter_params_.divider > 1) {
@@ -1040,6 +1097,15 @@ void FFmpegDecoder::RemoveFirstFrame()
cache_at_zero_ = false;
}
int FFmpegDecoder::MaximumQueueSize()
{
// Fairly arbitrary size. This used to need to be the number of current threads to ensure any
// thread that arrived would have its frame available, but if we only have one render thread,
// that's no longer a concern. Now, this value could technically be 1, but some memory cache
// may be useful for reversing. This value may be tweaked over time.
return 2;
}
FFmpegDecoder::Instance::Instance() :
fmt_ctx_(nullptr),
codec_ctx_(nullptr),
+9 -14
View File
@@ -25,27 +25,22 @@
#include <inttypes.h>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavfilter/avfilter.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
}
#include <QAtomicInt>
#include <QTimer>
#include <QVector>
#include <QWaitCondition>
#include "codec/decoder.h"
#include "common/ffmpegutils.h"
namespace olive {
using AVFramePtr = std::shared_ptr<AVFrame>;
inline AVFramePtr CreateAVFramePtr(AVFrame *f)
{
return std::shared_ptr<AVFrame>(f, [](AVFrame *g){ av_frame_free(&g); });
}
/**
* @brief A Decoder derivative that wraps FFmpeg functions as on Olive decoder
*/
@@ -64,12 +59,12 @@ public:
virtual bool SupportsVideo() override{return true;}
virtual bool SupportsAudio() override{return true;}
virtual FootageDescription Probe(const QString &filename, const QAtomicInt *cancelled) const override;
virtual FootageDescription Probe(const QString &filename, CancelAtom *cancelled) const override;
protected:
virtual bool OpenInternal() override;
virtual TexturePtr RetrieveVideoInternal(Renderer *renderer, const rational& timecode, const RetrieveVideoParams& params, const QAtomicInt *cancelled) override;
virtual bool ConformAudioInternal(const QVector<QString>& filenames, const AudioParams &params, const QAtomicInt* cancelled) override;
virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams& p) override;
virtual bool ConformAudioInternal(const QVector<QString>& filenames, const AudioParams &params, CancelAtom *cancelled) override;
virtual void CloseInternal() override;
private:
@@ -151,10 +146,12 @@ private:
void ClearFrameCache();
AVFramePtr RetrieveFrame(const rational &time, const QAtomicInt *cancelled);
AVFramePtr RetrieveFrame(const rational &time, CancelAtom *cancelled);
void RemoveFirstFrame();
static int MaximumQueueSize();
RetrieveVideoParams filter_params_;
AVFilterGraph* filter_graph_;
AVFilterContext* buffersrc_ctx_;
@@ -163,6 +160,7 @@ private:
VideoParams::Format native_internal_pix_fmt_;
VideoParams::Format native_output_pix_fmt_;
int native_channel_count_;
rational frame_rate_tb_;
AVFrame *working_frame_;
AVPacket *working_packet_;
@@ -171,9 +169,6 @@ private:
std::list<AVFramePtr> cached_frames_;
bool is_working_;
QMutex is_working_mutex_;
bool cache_at_zero_;
bool cache_at_eof_;
+105 -81
View File
@@ -21,6 +21,8 @@
#include "ffmpegencoder.h"
extern "C" {
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavutil/pixdesc.h>
}
@@ -36,8 +38,9 @@ FFmpegEncoder::FFmpegEncoder(const EncodingParams &params) :
fmt_ctx_(nullptr),
video_stream_(nullptr),
video_codec_ctx_(nullptr),
video_alpha_scale_ctx_(nullptr),
video_noalpha_scale_ctx_(nullptr),
video_scale_ctx_(nullptr),
video_buffersrc_ctx_(nullptr),
video_buffersink_ctx_(nullptr),
audio_stream_(nullptr),
audio_codec_ctx_(nullptr),
audio_resample_ctx_(nullptr),
@@ -54,6 +57,11 @@ QStringList FFmpegEncoder::GetPixelFormatsForCodec(ExportCodec::Codec c) const
if (codec_info) {
for (int i=0; codec_info->pix_fmts[i]!=-1; i++) {
if (FFmpegUtils::ConvertJPEGSpaceToRegularSpace(codec_info->pix_fmts[i]) != codec_info->pix_fmts[i]) {
// This is a deprecated "JPEG" space, skip it
continue;
}
const char* pix_fmt_name = av_get_pix_fmt_name(codec_info->pix_fmts[i]);
pix_fmts.append(pix_fmt_name);
}
@@ -142,29 +150,59 @@ bool FFmpegEncoder::Open()
// This is the pixel format the encoder wants to encode to
AVPixelFormat encoder_pix_fmt = video_codec_ctx_->pix_fmt;
// Set up a scaling context - if the native pixel format is not equal to the encoder's, we'll need to convert it
// before encoding. Even if we don't, this may be useful for converting between linesizes, etc.
video_alpha_scale_ctx_ = sws_getContext(params().video_params().width(),
params().video_params().height(),
src_alpha_pix_fmt,
params().video_params().width(),
params().video_params().height(),
encoder_pix_fmt,
0,
nullptr,
nullptr,
nullptr);
video_scale_ctx_ = avfilter_graph_alloc();
if (!video_scale_ctx_) {
return false;
}
video_noalpha_scale_ctx_ = sws_getContext(params().video_params().width(),
params().video_params().height(),
src_noalpha_pix_fmt,
params().video_params().width(),
params().video_params().height(),
encoder_pix_fmt,
0,
nullptr,
nullptr,
nullptr);
static const int FILTER_ARG_SZ = 1024;
char filter_args[FILTER_ARG_SZ];
snprintf(filter_args, FILTER_ARG_SZ, "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
params().video_params().effective_width(),
params().video_params().effective_height(),
src_alpha_pix_fmt,
params().video_params().time_base().numerator(),
params().video_params().time_base().denominator(),
params().video_params().pixel_aspect_ratio().numerator(),
params().video_params().pixel_aspect_ratio().denominator());
avfilter_graph_create_filter(&video_buffersrc_ctx_, avfilter_get_by_name("buffer"), "in", filter_args, nullptr, video_scale_ctx_);
avfilter_graph_create_filter(&video_buffersink_ctx_, avfilter_get_by_name("buffersink"), "out", nullptr, nullptr, video_scale_ctx_);
AVFilterContext *last_filter = video_buffersrc_ctx_;
{
// Set color range
AVFilterContext* range_filter;
snprintf(filter_args, FILTER_ARG_SZ, "in_range=full:out_range=%s",
params().video_params().color_range() == VideoParams::kColorRangeFull ? "full" : "limited");
avfilter_graph_create_filter(&range_filter, avfilter_get_by_name("scale"), "range", filter_args, nullptr, video_scale_ctx_);
avfilter_link(last_filter, 0, range_filter, 0);
last_filter = range_filter;
}
if (src_alpha_pix_fmt != encoder_pix_fmt) {
// Transform pixel format
AVFilterContext* format_filter;
snprintf(filter_args, FILTER_ARG_SZ, "pix_fmts=%u", encoder_pix_fmt);
avfilter_graph_create_filter(&format_filter, avfilter_get_by_name("format"), "format", filter_args, nullptr, video_scale_ctx_);
avfilter_link(last_filter, 0, format_filter, 0);
last_filter = format_filter;
}
avfilter_link(last_filter, 0, video_buffersink_ctx_, 0);
if (avfilter_graph_config(video_scale_ctx_, nullptr) < 0) {
SetError(tr("Failed to configure filter graph"));
return false;
}
}
// Initialize an audio stream if it's enabled
@@ -203,67 +241,41 @@ bool FFmpegEncoder::Open()
bool FFmpegEncoder::WriteFrame(FramePtr frame, rational time)
{
bool success = false;
AVFrame* encoded_frame = av_frame_alloc();
int error_code;
const char* input_data;
int input_linesize;
// Frame must be video
encoded_frame->width = frame->width();
encoded_frame->height = frame->height();
encoded_frame->format = video_codec_ctx_->pix_fmt;
// Set interlacing
if (frame->video_params().interlacing() != VideoParams::kInterlaceNone) {
encoded_frame->interlaced_frame = 1;
if (frame->video_params().interlacing() == VideoParams::kInterlacedTopFirst) {
encoded_frame->top_field_first = 1;
} else {
encoded_frame->top_field_first = 0;
}
}
error_code = av_frame_get_buffer(encoded_frame, 0);
if (error_code < 0) {
FFmpegError(tr("Failed to create AVFrame buffer"), error_code);
goto fail;
}
// We may need to convert this frame to a frame that swscale will understand
if (frame->format() != video_conversion_fmt_) {
frame = frame->convert(video_conversion_fmt_);
}
// Use swscale context to convert formats/linesizes
input_data = frame->const_data();
input_linesize = frame->linesize_bytes();
AVFramePtr input_frame = CreateAVFramePtr(av_frame_alloc());
input_frame->width = frame->width();
input_frame->height = frame->height();
input_frame->format = FFmpegUtils::GetFFmpegPixelFormat(frame->format(), frame->channel_count());
input_frame->data[0] = reinterpret_cast<uint8_t*>(frame->data());
input_frame->linesize[0] = frame->linesize_bytes();
error_code = sws_scale((frame->channel_count() == VideoParams::kRGBAChannelCount) ? video_alpha_scale_ctx_ : video_noalpha_scale_ctx_,
reinterpret_cast<const uint8_t**>(&input_data),
&input_linesize,
0,
frame->height(),
encoded_frame->data,
encoded_frame->linesize);
input_frame->color_primaries = video_codec_ctx_->color_primaries;
input_frame->color_trc = video_codec_ctx_->color_trc;
input_frame->colorspace = video_codec_ctx_->colorspace;
input_frame->color_range = video_codec_ctx_->color_range;
int r;
r = av_buffersrc_add_frame_flags(video_buffersrc_ctx_, input_frame.get(), AV_BUFFERSRC_FLAG_KEEP_REF);
if (r < 0) {
FFmpegError(tr("Failed to add frame to filter graph"), r);
return false;
}
if (error_code < 0) {
FFmpegError(tr("Failed to scale frame"), error_code);
goto fail;
AVFramePtr encoded_frame = CreateAVFramePtr(av_frame_alloc());
r = av_buffersink_get_frame(video_buffersink_ctx_, encoded_frame.get());
if (r < 0) {
FFmpegError(tr("Failed to retrieve frame from buffer sink"), r);
return false;
}
encoded_frame->pts = qRound64(time.toDouble() / av_q2d(video_codec_ctx_->time_base));
success = WriteAVFrame(encoded_frame, video_codec_ctx_, video_stream_);
fail:
av_frame_free(&encoded_frame);
return success;
return WriteAVFrame(encoded_frame.get(), video_codec_ctx_, video_stream_);
}
bool FFmpegEncoder::WriteAudio(const SampleBuffer &audio)
@@ -484,14 +496,11 @@ void FFmpegEncoder::Close()
audio_frame_ = nullptr;
}
if (video_alpha_scale_ctx_) {
sws_freeContext(video_alpha_scale_ctx_);
video_alpha_scale_ctx_ = nullptr;
}
if (video_noalpha_scale_ctx_) {
sws_freeContext(video_noalpha_scale_ctx_);
video_noalpha_scale_ctx_ = nullptr;
if (video_scale_ctx_) {
avfilter_graph_free(&video_scale_ctx_);
video_scale_ctx_ = nullptr;
video_buffersrc_ctx_ = nullptr;
video_buffersink_ctx_ = nullptr;
}
if (video_codec_ctx_) {
@@ -606,6 +615,7 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV
codec_ctx->time_base = params().video_params().frame_rate_as_time_base().toAVRational();
codec_ctx->framerate = params().video_params().frame_rate().toAVRational();
codec_ctx->pix_fmt = av_get_pix_fmt(params().video_pix_fmt().toUtf8());
codec_ctx->color_range = params().video_params().color_range() == VideoParams::kColorRangeFull ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG;
if (params().video_params().interlacing() != VideoParams::kInterlaceNone) {
// FIXME: I actually don't know what these flags do, the documentation helpfully doesn't
@@ -628,7 +638,9 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV
// Set custom options
{
for (auto i=params().video_opts().begin();i!=params().video_opts().end();i++) {
av_opt_set(codec_ctx->priv_data, i.key().toUtf8(), i.value().toUtf8(), AV_OPT_SEARCH_CHILDREN);
if (!i.key().startsWith(QStringLiteral("ove_"))) {
av_opt_set(codec_ctx->priv_data, i.key().toUtf8(), i.value().toUtf8(), AV_OPT_SEARCH_CHILDREN);
}
}
if (params().video_bit_rate() > 0) {
@@ -646,6 +658,18 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV
if (params().video_buffer_size() > 0) {
codec_ctx->rc_buffer_size = static_cast<int>(params().video_buffer_size());
}
// nclc tags. See https://ffmpeg.org/doxygen/4.0/pixfmt_8h.html#ad384ee5a840bafd73daef08e6d9cafe7
// ffprobe -v error -show_format -show_streams "C:\Users\Tom\Documents\srgb correct tags.mov"
if (params().color_transform().output().contains(QStringLiteral("sRGB"), Qt::CaseInsensitive)) {
codec_ctx->color_primaries = AVCOL_PRI_BT709;
codec_ctx->color_trc = AVCOL_TRC_IEC61966_2_1;
codec_ctx->colorspace = AVCOL_SPC_BT709;
} else { // Assume Rec.709
codec_ctx->color_primaries = AVCOL_PRI_BT709;
codec_ctx->color_trc = AVCOL_TRC_BT709;
codec_ctx->colorspace = AVCOL_SPC_BT709;
}
}
} else if (type == AVMEDIA_TYPE_AUDIO) {
+4 -3
View File
@@ -23,8 +23,8 @@
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavfilter/avfilter.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
#include <libavutil/opt.h>
}
@@ -88,8 +88,9 @@ private:
AVStream* video_stream_;
AVCodecContext* video_codec_ctx_;
SwsContext* video_alpha_scale_ctx_;
SwsContext* video_noalpha_scale_ctx_;
AVFilterGraph *video_scale_ctx_;
AVFilterContext *video_buffersrc_ctx_;
AVFilterContext *video_buffersink_ctx_;
VideoParams::Format video_conversion_fmt_;
AVStream* audio_stream_;