Skip to content

Commit

Permalink
Merge pull request #10862 from uklotzde/ffmpeg-5.1
Browse files Browse the repository at this point in the history
[2.3.4] Fix compatibility with FFmpeg 5.1
  • Loading branch information
Holzhaus authored Sep 3, 2022
2 parents 6218f7c + 7d094ce commit 6926695
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 4 deletions.
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

0 comments on commit 6926695

Please sign in to comment.