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++
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();
|
|
}
|