Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[2.3.4] Fix compatibility with FFmpeg 5.1 #10862

Merged
merged 1 commit into from
Sep 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 119 additions & 4 deletions src/sources/soundsourceffmpeg.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#include "sources/soundsourceffmpeg.h"

#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100) // FFmpeg 5.1
#include <libavutil/channel_layout.h>
#endif

#include <mutex>

#include "util/logger.h"
Expand Down Expand Up @@ -79,6 +83,23 @@ void initFFmpegLib() {
#endif
}

#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100) // FFmpeg 5.1
inline void getStreamChannelLayout(AVChannelLayout* pChannelLayout, const AVStream& avStream) {
if (avStream.codecpar->ch_layout.order == AV_CHANNEL_ORDER_UNSPEC) {
// Workaround: FFmpeg sometimes fails to determine the channel
// layout, e.g. for a mono WAV files with a single channel!
av_channel_layout_default(pChannelLayout, avStream.codecpar->ch_layout.nb_channels);
kLogger.info()
<< "Unknown channel layout -> using default layout"
<< pChannelLayout->order
<< "for"
<< avStream.codecpar->ch_layout.nb_channels
<< "channel(s)";
} else {
av_channel_layout_copy(pChannelLayout, &avStream.codecpar->ch_layout);
}
}
#else
inline int64_t getStreamChannelLayout(const AVStream& avStream) {
auto channel_layout = avStream.codecpar->channel_layout;
if (channel_layout == kavChannelLayoutUndefined) {
Expand All @@ -94,6 +115,7 @@ inline int64_t getStreamChannelLayout(const AVStream& avStream) {
}
return channel_layout;
}
#endif

inline int64_t getStreamStartTime(const AVStream& avStream) {
auto start_time = avStream.start_time;
Expand Down Expand Up @@ -184,9 +206,14 @@ SINT getStreamSeekPrerollFrameCount(const AVStream& avStream) {
// the risk for drop outs when jumping to a new position within
// the file. Audible drop outs are considered more harmful than
// slight deviations from the exact signal!
DEBUG_ASSERT(avStream.codecpar->channels <= 2);
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100) // FFmpeg 5.1
auto numChannels = avStream.codecpar->ch_layout.nb_channels;
#else
auto numChannels = avStream.codecpar->channels;
#endif
DEBUG_ASSERT(numChannels <= 2);
const SINT mp3SeekPrerollFrameCount =
9 * (kMaxSamplesPerMP3Frame / avStream.codecpar->channels);
9 * (kMaxSamplesPerMP3Frame / numChannels);
return math_max(mp3SeekPrerollFrameCount, defaultSeekPrerollFrameCount);
}
case AV_CODEC_ID_AAC:
Expand Down Expand Up @@ -470,12 +497,20 @@ SoundSourceFFmpeg::SoundSourceFFmpeg(const QUrl& url)
m_pavResampledFrame(nullptr),
m_seekPrerollFrameCount(0) {
DEBUG_ASSERT(m_pavPacket);
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100) // FFmpeg 5.1
av_channel_layout_default(&m_avStreamChannelLayout, 0);
av_channel_layout_default(&m_avResampledChannelLayout, 0);
#endif
}

SoundSourceFFmpeg::~SoundSourceFFmpeg() {
close();
av_packet_free(&m_pavPacket);
DEBUG_ASSERT(!m_pavPacket);
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100) // FFmpeg 5.1
av_channel_layout_uninit(&m_avStreamChannelLayout);
av_channel_layout_uninit(&m_avResampledChannelLayout);
#endif
}

SoundSource::OpenResult SoundSourceFFmpeg::tryOpen(
Expand Down Expand Up @@ -583,8 +618,13 @@ SoundSource::OpenResult SoundSourceFFmpeg::tryOpen(
// A dedicated number of channels for the output signal
// has been requested. Forward this to FFmpeg to avoid
// manual resampling or post-processing after decoding.
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100) // FFmpeg 5.1
av_channel_layout_default(&pavCodecContext->ch_layout,
params.getSignalInfo().getChannelCount());
#else
pavCodecContext->request_channel_layout =
av_get_default_channel_layout(params.getSignalInfo().getChannelCount());
#endif
}

// Open decoding context
Expand All @@ -598,6 +638,10 @@ SoundSource::OpenResult SoundSourceFFmpeg::tryOpen(
m_pavStream = pavStream;

if (kLogger.debugEnabled()) {
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100) // FFmpeg 5.1
AVChannelLayout fixedChannelLayout;
getStreamChannelLayout(&fixedChannelLayout, *m_pavStream);
#endif
kLogger.debug()
<< "AVStream"
<< "{ index" << m_pavStream->index
Expand All @@ -608,9 +652,15 @@ SoundSource::OpenResult SoundSourceFFmpeg::tryOpen(
<< "| nb_frames" << m_pavStream->nb_frames
<< "| codec_type" << m_pavStream->codecpar->codec_type
<< "| codec_id" << m_pavStream->codecpar->codec_id
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100) // FFmpeg 5.1
<< "| ch_layout.nb_channels" << m_pavStream->codecpar->ch_layout.nb_channels
<< "| ch_layout.order" << m_pavStream->codecpar->ch_layout.order
<< "| ch_layout.order (fixed)" << fixedChannelLayout.order
#else
<< "| channels" << m_pavStream->codecpar->channels
<< "| channel_layout" << m_pavStream->codecpar->channel_layout
<< "| channel_layout (fixed)" << getStreamChannelLayout(*m_pavStream)
#endif
<< "| format" << m_pavStream->codecpar->format
<< "| sample_rate" << m_pavStream->codecpar->sample_rate
<< "| bit_rate" << m_pavStream->codecpar->bit_rate
Expand All @@ -619,6 +669,9 @@ SoundSource::OpenResult SoundSourceFFmpeg::tryOpen(
<< "| initial_padding" << m_pavStream->codecpar->initial_padding
<< "| trailing_padding" << m_pavStream->codecpar->trailing_padding
<< '}';
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100) // FFmpeg 5.1
av_channel_layout_uninit(&fixedChannelLayout);
#endif
}

audio::ChannelCount channelCount;
Expand Down Expand Up @@ -703,10 +756,17 @@ SoundSource::OpenResult SoundSourceFFmpeg::tryOpen(
bool SoundSourceFFmpeg::initResampling(
audio::ChannelCount* pResampledChannelCount,
audio::SampleRate* pResampledSampleRate) {
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100) // FFmpeg 5.1
AVChannelLayout avStreamChannelLayout;
getStreamChannelLayout(&avStreamChannelLayout, *m_pavStream);
const auto streamChannelCount =
audio::ChannelCount(m_pavStream->codecpar->ch_layout.nb_channels);
#else
const auto avStreamChannelLayout =
getStreamChannelLayout(*m_pavStream);
const auto streamChannelCount =
audio::ChannelCount(m_pavStream->codecpar->channels);
#endif
// NOTE(uklotzde, 2017-09-26): Resampling to a different number of
// channels like upsampling a mono to stereo signal breaks various
// tests in the EngineBufferE2ETest suite!! SoundSource decoding tests
Expand All @@ -716,8 +776,13 @@ bool SoundSourceFFmpeg::initResampling(
// how to handle this later.
const auto resampledChannelCount =
/*config.getSignalInfo().getChannelCount().isValid() ? config.getSignalInfo().getChannelCount() :*/ streamChannelCount;
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100) // FFmpeg 5.1
AVChannelLayout avResampledChannelLayout;
av_channel_layout_default(&avResampledChannelLayout, resampledChannelCount);
#else
const auto avResampledChannelLayout =
av_get_default_channel_layout(resampledChannelCount);
#endif
const auto avStreamSampleFormat =
m_pavCodecContext->sample_fmt;
const auto avResampledSampleFormat =
Expand All @@ -730,16 +795,46 @@ bool SoundSourceFFmpeg::initResampling(
const auto streamSampleRate =
audio::SampleRate(m_pavStream->codecpar->sample_rate);
const auto resampledSampleRate = streamSampleRate;
if ((resampledChannelCount != streamChannelCount) ||
if (
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100) // FFmpeg 5.1
av_channel_layout_compare(&avResampledChannelLayout, &avStreamChannelLayout) != 0 ||
#else
(resampledChannelCount != streamChannelCount) ||
(avResampledChannelLayout != avStreamChannelLayout) ||
#endif
(avResampledSampleFormat != avStreamSampleFormat)) {
#if VERBOSE_DEBUG_LOG
kLogger.debug()
<< "Decoded stream needs to be resampled"
<< ": channel count =" << resampledChannelCount
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100) // FFmpeg 5.1
<< "| channel layout order =" << avResampledChannelLayout.order
#else
<< "| channel layout =" << avResampledChannelLayout
#endif
<< "| sample format =" << av_get_sample_fmt_name(avResampledSampleFormat);
#endif
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100) // FFmpeg 5.1
SwrContext* pSwrContext = nullptr;
if (swr_alloc_set_opts2(
&pSwrContext,
&avResampledChannelLayout,
avResampledSampleFormat,
resampledSampleRate,
&avStreamChannelLayout,
avStreamSampleFormat,
streamSampleRate,
0,
nullptr) != 0) {
kLogger.warning()
<< "Failed to allocate resampling context";
m_pSwrContext = SwrContextPtr();
av_channel_layout_uninit(&avStreamChannelLayout);
av_channel_layout_uninit(&avResampledChannelLayout);
return false;
}
m_pSwrContext = SwrContextPtr(pSwrContext);
#else
m_pSwrContext = SwrContextPtr(swr_alloc_set_opts(
nullptr,
avResampledChannelLayout,
Expand All @@ -755,20 +850,32 @@ bool SoundSourceFFmpeg::initResampling(
<< "Failed to allocate resampling context";
return false;
}
#endif
const auto swr_init_result =
swr_init(m_pSwrContext);
if (swr_init_result < 0) {
kLogger.warning().noquote()
<< "swr_init() failed:"
<< formatErrorString(swr_init_result);
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100) // FFmpeg 5.1
av_channel_layout_uninit(&avStreamChannelLayout);
av_channel_layout_uninit(&avResampledChannelLayout);
#endif
return false;
}
DEBUG_ASSERT(!m_pavResampledFrame);
m_pavResampledFrame = av_frame_alloc();
}
// Finish initialization
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100) // FFmpeg 5.1
av_channel_layout_copy(&m_avStreamChannelLayout, &avStreamChannelLayout);
av_channel_layout_uninit(&avStreamChannelLayout);
av_channel_layout_copy(&m_avResampledChannelLayout, &avResampledChannelLayout);
av_channel_layout_uninit(&avResampledChannelLayout);
#else
m_avStreamChannelLayout = avStreamChannelLayout;
m_avResampledChannelLayout = avResampledChannelLayout;
#endif
// Write output parameters
DEBUG_ASSERT(pResampledChannelCount);
*pResampledChannelCount = resampledChannelCount;
Expand Down Expand Up @@ -951,13 +1058,21 @@ bool SoundSourceFFmpeg::consumeNextAVPacket(
const CSAMPLE* SoundSourceFFmpeg::resampleDecodedAVFrame() {
if (m_pSwrContext) {
// Decoded frame must be resampled before reading
m_pavResampledFrame->channel_layout = m_avResampledChannelLayout;
m_pavResampledFrame->sample_rate = getSignalInfo().getSampleRate();
m_pavResampledFrame->format = kavSampleFormat;
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100) // FFmpeg 5.1
av_channel_layout_copy(&m_pavResampledFrame->ch_layout, &m_avResampledChannelLayout);
if (m_pavDecodedFrame->ch_layout.order == AV_CHANNEL_ORDER_UNSPEC) {
// Sometimes the channel layout is undefined.
av_channel_layout_copy(&m_pavDecodedFrame->ch_layout, &m_avStreamChannelLayout);
}
#else
m_pavResampledFrame->channel_layout = m_avResampledChannelLayout;
if (m_pavDecodedFrame->channel_layout == kavChannelLayoutUndefined) {
// Sometimes the channel layout is undefined.
m_pavDecodedFrame->channel_layout = m_avStreamChannelLayout;
}
#endif
#if VERBOSE_DEBUG_LOG
avTrace("Resampling decoded frame", *m_pavDecodedFrame);
#endif
Expand Down
5 changes: 5 additions & 0 deletions src/sources/soundsourceffmpeg.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,13 @@ class SoundSourceFFmpeg : public SoundSource {
};
SwrContextPtr m_pSwrContext;

#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100) // FFmpeg 5.1
AVChannelLayout m_avStreamChannelLayout;
AVChannelLayout m_avResampledChannelLayout;
#else
uint64_t m_avStreamChannelLayout;
uint64_t m_avResampledChannelLayout;
#endif

AVPacket* m_pavPacket;

Expand Down