You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

105 lines
2.6 KiB
C++

// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright The Music Player Daemon Project
#include "FfmpegFilter.hxx"
#include "lib/ffmpeg/Interleave.hxx"
#include "lib/ffmpeg/SampleFormat.hxx"
extern "C" {
#include <libavfilter/buffersrc.h>
#include <libavfilter/buffersink.h>
}
#include <string.h>
FfmpegFilter::FfmpegFilter(const AudioFormat &in_audio_format,
const AudioFormat &_out_audio_format,
Ffmpeg::FilterGraph &&_graph,
AVFilterContext &_buffer_src,
AVFilterContext &_buffer_sink) noexcept
:Filter(_out_audio_format),
graph(std::move(_graph)),
buffer_src(_buffer_src),
buffer_sink(_buffer_sink),
in_format(Ffmpeg::ToFfmpegSampleFormat(in_audio_format.format)),
in_sample_rate(in_audio_format.sample_rate),
#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(57, 25, 100)
in_channels(in_audio_format.channels),
#endif
in_audio_frame_size(in_audio_format.GetFrameSize()),
out_audio_frame_size(_out_audio_format.GetFrameSize())
{
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 25, 100)
av_channel_layout_default(&in_ch_layout, in_audio_format.channels);
#endif
}
inline std::span<const std::byte>
FfmpegFilter::ReadOutput()
{
frame.Unref();
if (int err = av_buffersink_get_frame(&buffer_sink, frame.get()); err < 0) {
if (err == AVERROR(EAGAIN) || err == AVERROR_EOF)
return {};
throw MakeFfmpegError(err, "av_buffersink_get_frame() failed");
}
return Ffmpeg::InterleaveFrame(*frame, interleave_buffer);
}
std::span<const std::byte>
FfmpegFilter::FilterPCM(std::span<const std::byte> src)
{
assert(!flushed);
/* submit source data into the FFmpeg audio buffer source */
frame.Unref();
frame->format = in_format;
frame->sample_rate = in_sample_rate;
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 25, 100)
frame->ch_layout = in_ch_layout;
#else
frame->channels = in_channels;
#endif
frame->nb_samples = src.size() / in_audio_frame_size;
frame->pts = pts;
pts += frame->nb_samples;
frame.GetBuffer();
memcpy(frame.GetData(0), src.data(), src.size());
if (int err = av_buffersrc_add_frame(&buffer_src, frame.get()); err < 0)
throw MakeFfmpegError(err, "av_buffersrc_write_frame() failed");
/* collect filtered data from the FFmpeg audio buffer sink */
/* TODO: call av_buffersink_get_frame() repeatedly? Not
possible with MPD's current Filter API */
return ReadOutput();
}
std::span<const std::byte>
FfmpegFilter::ReadMore()
{
return ReadOutput();
}
std::span<const std::byte>
FfmpegFilter::Flush()
{
if (!flushed) {
if (int err = av_buffersrc_add_frame(&buffer_src, nullptr); err < 0)
throw MakeFfmpegError(err, "av_buffersrc_write_frame() failed");
flushed = true;
}
return ReadOutput();
}