@@ -76,6 +76,7 @@ public:
|
||||
void set_video_pix_fmt(const QString& s) { video_pix_fmt_ = s; }
|
||||
void set_video_is_image_sequence(bool s) { video_is_image_sequence_ = s; }
|
||||
void set_video_color_range(YUVRange r) { video_color_range_ = r; }
|
||||
void set_color_transform(const ColorTransform& color_transform) { color_transform_ = color_transform; }
|
||||
|
||||
const QString& filename() const { return filename_; }
|
||||
|
||||
@@ -91,6 +92,7 @@ public:
|
||||
const QString& video_pix_fmt() const { return video_pix_fmt_; }
|
||||
bool video_is_image_sequence() const { return video_is_image_sequence_; }
|
||||
YUVRange video_color_range() const { return video_color_range_; }
|
||||
const ColorTransform& color_transform() const { return color_transform_; }
|
||||
|
||||
bool audio_enabled() const { return audio_enabled_; }
|
||||
const ExportCodec::Codec &audio_codec() const { return audio_codec_; }
|
||||
@@ -125,6 +127,7 @@ private:
|
||||
QString video_pix_fmt_;
|
||||
bool video_is_image_sequence_;
|
||||
YUVRange video_color_range_;
|
||||
ColorTransform color_transform_;
|
||||
|
||||
bool audio_enabled_;
|
||||
ExportCodec::Codec audio_codec_;
|
||||
|
||||
@@ -36,15 +36,10 @@ extern "C" {
|
||||
#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
|
||||
*/
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include "ffmpegencoder.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavfilter/buffersink.h>
|
||||
#include <libavfilter/buffersrc.h>
|
||||
#include <libavutil/pixdesc.h>
|
||||
}
|
||||
|
||||
@@ -37,6 +39,8 @@ FFmpegEncoder::FFmpegEncoder(const EncodingParams ¶ms) :
|
||||
video_stream_(nullptr),
|
||||
video_codec_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),
|
||||
@@ -141,34 +145,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_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;
|
||||
}
|
||||
|
||||
int *inv_table;
|
||||
int src_range;
|
||||
int *table;
|
||||
int dst_range;
|
||||
int brightness;
|
||||
int contrast;
|
||||
int saturation;
|
||||
static const int FILTER_ARG_SZ = 1024;
|
||||
char filter_args[FILTER_ARG_SZ];
|
||||
|
||||
sws_getColorspaceDetails(video_scale_ctx_, &inv_table, &src_range, &table, &dst_range, &brightness, &contrast, &saturation);
|
||||
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());
|
||||
|
||||
// Set swscale's dst range based on AVCodecContext's color_range. Here, 1 == JPEG range (0-255)
|
||||
// and 0 == MPEG range (16-235).
|
||||
dst_range = (video_codec_ctx_->color_range == AVCOL_RANGE_JPEG);
|
||||
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_);
|
||||
|
||||
sws_setColorspaceDetails(video_scale_ctx_, inv_table, src_range, table, dst_range, brightness, contrast, saturation);
|
||||
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_color_range() == EncodingParams::kYUVJPEG0_255 ? "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
|
||||
@@ -207,68 +236,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;
|
||||
encoded_frame->color_range = video_codec_ctx_->color_range;
|
||||
|
||||
// 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(video_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)
|
||||
@@ -490,8 +492,10 @@ void FFmpegEncoder::Close()
|
||||
}
|
||||
|
||||
if (video_scale_ctx_) {
|
||||
sws_freeContext(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_) {
|
||||
@@ -647,6 +651,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) {
|
||||
|
||||
@@ -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,7 +88,9 @@ private:
|
||||
|
||||
AVStream* video_stream_;
|
||||
AVCodecContext* video_codec_ctx_;
|
||||
SwsContext* video_scale_ctx_;
|
||||
AVFilterGraph *video_scale_ctx_;
|
||||
AVFilterContext *video_buffersrc_ctx_;
|
||||
AVFilterContext *video_buffersink_ctx_;
|
||||
VideoParams::Format video_conversion_fmt_;
|
||||
|
||||
AVStream* audio_stream_;
|
||||
|
||||
@@ -59,6 +59,12 @@ public:
|
||||
static AVSampleFormat GetFFmpegSampleFormat(const AudioParams::Format &smp_fmt);
|
||||
};
|
||||
|
||||
using AVFramePtr = std::shared_ptr<AVFrame>;
|
||||
inline AVFramePtr CreateAVFramePtr(AVFrame *f)
|
||||
{
|
||||
return std::shared_ptr<AVFrame>(f, [](AVFrame *g){ av_frame_free(&g); });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // FFMPEGABSTRACTION_H
|
||||
|
||||
@@ -281,6 +281,20 @@ colorspaces:
|
||||
- !<MatrixTransform> {matrix: [0.606530, 0.220408, 0.123479, 0, 0.267989, 0.832731, -0.100720, 0, -0.029442, -0.086611, 1.204861, 0, 0, 0, 0, 1]}
|
||||
- !<ColorSpaceTransform> {src: CIE-XYZ D65, dst: reference}
|
||||
|
||||
- !<ColorSpace>
|
||||
name: Rec.709 OETF
|
||||
family: Camera Footage
|
||||
equalitygroup: ""
|
||||
bitdepth: 32f
|
||||
description: |
|
||||
Rec.709 OETF
|
||||
isdata: false
|
||||
allocation: uniform
|
||||
allocationvars: [0, 1]
|
||||
to_reference: !<GroupTransform>
|
||||
children:
|
||||
- !<FileTransform> {src: rec709_to_linear.spi1d, interpolation: linear}
|
||||
|
||||
- !<ColorSpace>
|
||||
name: Non-Colour Data
|
||||
family:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -54,16 +54,6 @@ void ExportParams::set_video_scaling_method(const ExportParams::VideoScalingMeth
|
||||
video_scaling_method_ = video_scaling_method;
|
||||
}
|
||||
|
||||
const ColorTransform &ExportParams::color_transform() const
|
||||
{
|
||||
return color_transform_;
|
||||
}
|
||||
|
||||
void ExportParams::set_color_transform(const ColorTransform &color_transform)
|
||||
{
|
||||
color_transform_ = color_transform;
|
||||
}
|
||||
|
||||
QMatrix4x4 ExportParams::GenerateMatrix(ExportParams::VideoScalingMethod method,
|
||||
int source_width, int source_height,
|
||||
int dest_width, int dest_height)
|
||||
@@ -103,7 +93,7 @@ void ExportParams::Save(QXmlStreamWriter *writer) const
|
||||
writer->writeTextElement(QStringLiteral("customrangeout"), custom_range_.out().toString());
|
||||
|
||||
// FIXME: Change this when color chains are implemented
|
||||
writer->writeTextElement(QStringLiteral("color"), color_transform_.output());
|
||||
writer->writeTextElement(QStringLiteral("color"), color_transform().output());
|
||||
|
||||
EncodingParams::Save(writer);
|
||||
|
||||
|
||||
@@ -46,9 +46,6 @@ public:
|
||||
const VideoScalingMethod& video_scaling_method() const;
|
||||
void set_video_scaling_method(const VideoScalingMethod& video_scaling_method);
|
||||
|
||||
const ColorTransform& color_transform() const;
|
||||
void set_color_transform(const ColorTransform& color_transform);
|
||||
|
||||
static QMatrix4x4 GenerateMatrix(ExportParams::VideoScalingMethod method,
|
||||
int source_width, int source_height,
|
||||
int dest_width, int dest_height);
|
||||
@@ -61,8 +58,6 @@ private:
|
||||
bool has_custom_range_;
|
||||
TimeRange custom_range_;
|
||||
|
||||
ColorTransform color_transform_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user