From 61cb5b4da638d520c41a9d99b5850384b2e22679 Mon Sep 17 00:00:00 2001 From: Sam Hellawell Date: Sat, 3 Aug 2024 21:05:00 +0100 Subject: [PATCH] Support compiling with ffmpeg 5 and greater --- apps/openmw/mwsound/ffmpeg_decoder.cpp | 130 ++++++++++++++---- apps/openmw/mwsound/ffmpeg_decoder.hpp | 11 ++ apps/openmw/mwsound/movieaudiofactory.cpp | 9 +- .../osg-ffmpeg-videoplayer/audiodecoder.cpp | 58 ++++++++ .../osg-ffmpeg-videoplayer/audiodecoder.hpp | 6 + extern/osg-ffmpeg-videoplayer/videostate.cpp | 4 + extern/osg-ffmpeg-videoplayer/videostate.hpp | 11 +- 7 files changed, 204 insertions(+), 25 deletions(-) diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index e9a5a1eb94..88bcdcebdd 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -8,6 +8,10 @@ #include #include +#if FFMPEG_5_OR_GREATER +#include +#endif + namespace MWSound { void AVIOContextDeleter::operator()(AVIOContext* ptr) const @@ -57,7 +61,11 @@ namespace MWSound } } +#if FFMPEG_CONST_WRITEPACKET + int FFmpeg_Decoder::writePacket(void*, const uint8_t*, int) +#else int FFmpeg_Decoder::writePacket(void*, uint8_t*, int) +#endif { Log(Debug::Error) << "can't write to read-only stream"; return -1; @@ -152,7 +160,11 @@ namespace MWSound if (!mDataBuf || mDataBufLen < mFrame->nb_samples) { av_freep(&mDataBuf); +#if FFMPEG_5_OR_GREATER + if (av_samples_alloc(&mDataBuf, nullptr, mOutputChannelLayout.nb_channels, +#else if (av_samples_alloc(&mDataBuf, nullptr, av_get_channel_layout_nb_channels(mOutputChannelLayout), +#endif mFrame->nb_samples, mOutputSampleFormat, 0) < 0) return false; @@ -189,7 +201,11 @@ namespace MWSound if (!getAVAudioData()) break; mFramePos = 0; +#if FFMPEG_5_OR_GREATER + mFrameSize = mFrame->nb_samples * mOutputChannelLayout.nb_channels +#else mFrameSize = mFrame->nb_samples * av_get_channel_layout_nb_channels(mOutputChannelLayout) +#endif * av_get_bytes_per_sample(mOutputSampleFormat); } @@ -277,11 +293,19 @@ namespace MWSound else mOutputSampleFormat = AV_SAMPLE_FMT_S16; +#if FFMPEG_5_OR_GREATER + mOutputChannelLayout = (*stream)->codecpar->ch_layout; // sefault + if (mOutputChannelLayout.u.mask == 0) + av_channel_layout_default(&mOutputChannelLayout, codecCtxPtr->ch_layout.nb_channels); + + codecCtxPtr->ch_layout = mOutputChannelLayout; +#else mOutputChannelLayout = (*stream)->codecpar->channel_layout; if (mOutputChannelLayout == 0) mOutputChannelLayout = av_get_default_channel_layout(codecCtxPtr->channels); codecCtxPtr->channel_layout = mOutputChannelLayout; +#endif mIoCtx = std::move(ioCtx); mFrame = std::move(frame); @@ -332,41 +356,88 @@ namespace MWSound *type = SampleType_Int16; } - if (mOutputChannelLayout == AV_CH_LAYOUT_MONO) - *chans = ChannelConfig_Mono; - else if (mOutputChannelLayout == AV_CH_LAYOUT_STEREO) - *chans = ChannelConfig_Stereo; - else if (mOutputChannelLayout == AV_CH_LAYOUT_QUAD) - *chans = ChannelConfig_Quad; - else if (mOutputChannelLayout == AV_CH_LAYOUT_5POINT1) - *chans = ChannelConfig_5point1; - else if (mOutputChannelLayout == AV_CH_LAYOUT_7POINT1) - *chans = ChannelConfig_7point1; - else +#if FFMPEG_5_OR_GREATER + switch (mOutputChannelLayout.u.mask) +#else + switch (mOutputChannelLayout) +#endif { - char str[1024]; - av_get_channel_layout_string(str, sizeof(str), mCodecCtx->channels, mCodecCtx->channel_layout); - Log(Debug::Error) << "Unsupported channel layout: " << str; - - if (mCodecCtx->channels == 1) - { - mOutputChannelLayout = AV_CH_LAYOUT_MONO; + case AV_CH_LAYOUT_MONO: *chans = ChannelConfig_Mono; - } - else - { - mOutputChannelLayout = AV_CH_LAYOUT_STEREO; + break; + case AV_CH_LAYOUT_STEREO: *chans = ChannelConfig_Stereo; - } + break; + case AV_CH_LAYOUT_QUAD: + *chans = ChannelConfig_Quad; + break; + case AV_CH_LAYOUT_5POINT1: + *chans = ChannelConfig_5point1; + break; + case AV_CH_LAYOUT_7POINT1: + *chans = ChannelConfig_7point1; + break; + default: + char str[1024]; +#if FFMPEG_5_OR_GREATER + av_channel_layout_describe(&mCodecCtx->ch_layout, str, sizeof(str)); + Log(Debug::Error) << "Unsupported channel layout: " << str; + + if (mCodecCtx->ch_layout.nb_channels == 1) + { + mOutputChannelLayout = AV_CHANNEL_LAYOUT_MONO; + *chans = ChannelConfig_Mono; + } + else + { + mOutputChannelLayout = AV_CHANNEL_LAYOUT_STEREO; + *chans = ChannelConfig_Stereo; + } +#else + av_get_channel_layout_string(str, sizeof(str), mCodecCtx->channels, mCodecCtx->channel_layout); + Log(Debug::Error) << "Unsupported channel layout: " << str; + + if (mCodecCtx->channels == 1) + { + mOutputChannelLayout = AV_CH_LAYOUT_MONO; + *chans = ChannelConfig_Mono; + } + else + { + mOutputChannelLayout = AV_CH_LAYOUT_STEREO; + *chans = ChannelConfig_Stereo; + } +#endif + break; } *samplerate = mCodecCtx->sample_rate; +#if FFMPEG_5_OR_GREATER + AVChannelLayout ch_layout = mCodecCtx->ch_layout; + if (ch_layout.u.mask == 0) + av_channel_layout_default(&ch_layout, mCodecCtx->ch_layout.nb_channels); + + if (mOutputSampleFormat != mCodecCtx->sample_fmt || mOutputChannelLayout.u.mask != ch_layout.u.mask) +#else int64_t ch_layout = mCodecCtx->channel_layout; if (ch_layout == 0) ch_layout = av_get_default_channel_layout(mCodecCtx->channels); if (mOutputSampleFormat != mCodecCtx->sample_fmt || mOutputChannelLayout != ch_layout) +#endif + { +#if FFMPEG_5_OR_GREATER + swr_alloc_set_opts2(&mSwr, // SwrContext + &mOutputChannelLayout, // output ch layout + mOutputSampleFormat, // output sample format + mCodecCtx->sample_rate, // output sample rate + &ch_layout, // input ch layout + mCodecCtx->sample_fmt, // input sample format + mCodecCtx->sample_rate, // input sample rate + 0, // logging level offset + nullptr); // log context +#else mSwr = swr_alloc_set_opts(mSwr, // SwrContext mOutputChannelLayout, // output ch layout mOutputSampleFormat, // output sample format @@ -376,6 +447,7 @@ namespace MWSound mCodecCtx->sample_rate, // input sample rate 0, // logging level offset nullptr); // log context +#endif if (!mSwr) throw std::runtime_error("Couldn't allocate SwrContext"); int init = swr_init(mSwr); @@ -404,7 +476,11 @@ namespace MWSound while (getAVAudioData()) { +#if FFMPEG_5_OR_GREATER + size_t got = mFrame->nb_samples * mOutputChannelLayout.nb_channels +#else size_t got = mFrame->nb_samples * av_get_channel_layout_nb_channels(mOutputChannelLayout) +#endif * av_get_bytes_per_sample(mOutputSampleFormat); const char* inbuf = reinterpret_cast(mFrameData[0]); output.insert(output.end(), inbuf, inbuf + got); @@ -413,7 +489,11 @@ namespace MWSound size_t FFmpeg_Decoder::getSampleOffset() { +#if FFMPEG_5_OR_GREATER + std::size_t delay = (mFrameSize - mFramePos) / mOutputChannelLayout.nb_channels +#else std::size_t delay = (mFrameSize - mFramePos) / av_get_channel_layout_nb_channels(mOutputChannelLayout) +#endif / av_get_bytes_per_sample(mOutputSampleFormat); return static_cast(mNextPts * mCodecCtx->sample_rate) - delay; } @@ -426,7 +506,11 @@ namespace MWSound , mNextPts(0.0) , mSwr(nullptr) , mOutputSampleFormat(AV_SAMPLE_FMT_NONE) +#if FFMPEG_5_OR_GREATER + , mOutputChannelLayout({}) +#else , mOutputChannelLayout(0) +#endif , mDataBuf(nullptr) , mFrameData(nullptr) , mDataBufLen(0) diff --git a/apps/openmw/mwsound/ffmpeg_decoder.hpp b/apps/openmw/mwsound/ffmpeg_decoder.hpp index 264ff8fab7..92c4478d17 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.hpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.hpp @@ -30,6 +30,9 @@ extern "C" #include "sound_decoder.hpp" +#define FFMPEG_5_OR_GREATER (LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100)) +#define FFMPEG_CONST_WRITEPACKET (LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(60, 12, 100)) + namespace MWSound { struct AVIOContextDeleter @@ -77,7 +80,11 @@ namespace MWSound SwrContext* mSwr; enum AVSampleFormat mOutputSampleFormat; +#if FFMPEG_5_OR_GREATER + AVChannelLayout mOutputChannelLayout; +#else int64_t mOutputChannelLayout; +#endif uint8_t* mDataBuf; uint8_t** mFrameData; int mDataBufLen; @@ -87,7 +94,11 @@ namespace MWSound Files::IStreamPtr mDataStream; static int readPacket(void* user_data, uint8_t* buf, int buf_size); +#if FFMPEG_CONST_WRITEPACKET + static int writePacket(void* user_data, const uint8_t* buf, int buf_size); +#else static int writePacket(void* user_data, uint8_t* buf, int buf_size); +#endif static int64_t seek(void* user_data, int64_t offset, int whence); bool getAVAudioData(); diff --git a/apps/openmw/mwsound/movieaudiofactory.cpp b/apps/openmw/mwsound/movieaudiofactory.cpp index 68ea221321..7d21b8c5af 100644 --- a/apps/openmw/mwsound/movieaudiofactory.cpp +++ b/apps/openmw/mwsound/movieaudiofactory.cpp @@ -46,12 +46,19 @@ namespace MWSound size_t getSampleOffset() { +#if FFMPEG_5_OR_GREATER + ssize_t clock_delay = (mFrameSize - mFramePos) / mOutputChannelLayout.nb_channels +#else ssize_t clock_delay = (mFrameSize - mFramePos) / av_get_channel_layout_nb_channels(mOutputChannelLayout) +#endif / av_get_bytes_per_sample(mOutputSampleFormat); return (size_t)(mAudioClock * mAudioContext->sample_rate) - clock_delay; } - std::string getStreamName() { return std::string(); } + std::string getStreamName() + { + return std::string(); + } private: // MovieAudioDecoder overrides diff --git a/extern/osg-ffmpeg-videoplayer/audiodecoder.cpp b/extern/osg-ffmpeg-videoplayer/audiodecoder.cpp index decf57e4df..841535f823 100644 --- a/extern/osg-ffmpeg-videoplayer/audiodecoder.cpp +++ b/extern/osg-ffmpeg-videoplayer/audiodecoder.cpp @@ -18,6 +18,10 @@ extern "C" #pragma warning (pop) #endif +#if FFMPEG_5_OR_GREATER + #include +#endif + #include "videostate.hpp" namespace @@ -53,7 +57,11 @@ MovieAudioDecoder::MovieAudioDecoder(VideoState* videoState) : mVideoState(videoState) , mAVStream(*videoState->audio_st) , mOutputSampleFormat(AV_SAMPLE_FMT_NONE) + #if FFMPEG_5_OR_GREATER + , mOutputChannelLayout({}) + #else , mOutputChannelLayout(0) + #endif , mOutputSampleRate(0) , mFramePos(0) , mFrameSize(0) @@ -109,21 +117,49 @@ void MovieAudioDecoder::setupFormat() AVSampleFormat inputSampleFormat = mAudioContext->sample_fmt; +#if FFMPEG_5_OR_GREATER + AVChannelLayout inputChannelLayout = mAudioContext->ch_layout; + if (inputChannelLayout.u.mask != 0) + mOutputChannelLayout = inputChannelLayout; + else + av_channel_layout_default(&mOutputChannelLayout, mAudioContext->ch_layout.nb_channels); +#else uint64_t inputChannelLayout = mAudioContext->channel_layout; if (inputChannelLayout == 0) inputChannelLayout = av_get_default_channel_layout(mAudioContext->channels); +#endif int inputSampleRate = mAudioContext->sample_rate; mOutputSampleRate = inputSampleRate; mOutputSampleFormat = inputSampleFormat; + +#if FFMPEG_5_OR_GREATER + adjustAudioSettings(mOutputSampleFormat, mOutputChannelLayout.u.mask, mOutputSampleRate); +#else mOutputChannelLayout = inputChannelLayout; adjustAudioSettings(mOutputSampleFormat, mOutputChannelLayout, mOutputSampleRate); +#endif if (inputSampleFormat != mOutputSampleFormat + #if FFMPEG_5_OR_GREATER + || inputChannelLayout.u.mask != mOutputChannelLayout.u.mask + #else || inputChannelLayout != mOutputChannelLayout + #endif || inputSampleRate != mOutputSampleRate) { + #if FFMPEG_5_OR_GREATER + swr_alloc_set_opts2(&mAudioResampler->mSwr, + &mOutputChannelLayout, + mOutputSampleFormat, + mOutputSampleRate, + &inputChannelLayout, + inputSampleFormat, + inputSampleRate, + 0, // logging level offset + nullptr); // log context + #else mAudioResampler->mSwr = swr_alloc_set_opts(mAudioResampler->mSwr, mOutputChannelLayout, mOutputSampleFormat, @@ -133,6 +169,8 @@ void MovieAudioDecoder::setupFormat() inputSampleRate, 0, // logging level offset nullptr); // log context + #endif + if(!mAudioResampler->mSwr) fail(std::string("Couldn't allocate SwrContext")); if(swr_init(mAudioResampler->mSwr) < 0) @@ -158,7 +196,11 @@ int MovieAudioDecoder::synchronize_audio() if(fabs(avg_diff) >= mAudioDiffThreshold) { int n = av_get_bytes_per_sample(mOutputSampleFormat) * + #if FFMPEG_5_OR_GREATER + mOutputChannelLayout.nb_channels; + #else av_get_channel_layout_nb_channels(mOutputChannelLayout); + #endif sample_skip = ((int)(diff * mAudioContext->sample_rate) * n); } } @@ -204,7 +246,11 @@ int MovieAudioDecoder::audio_decode_frame(AVFrame *frame, int &sample_skip) if(!mDataBuf || mDataBufLen < frame->nb_samples) { av_freep(&mDataBuf); + #if FFMPEG_5_OR_GREATER + if(av_samples_alloc(&mDataBuf, nullptr, mOutputChannelLayout.nb_channels, + #else if(av_samples_alloc(&mDataBuf, nullptr, av_get_channel_layout_nb_channels(mOutputChannelLayout), + #endif frame->nb_samples, mOutputSampleFormat, 0) < 0) break; else @@ -221,7 +267,11 @@ int MovieAudioDecoder::audio_decode_frame(AVFrame *frame, int &sample_skip) else mFrameData = &frame->data[0]; + #if FFMPEG_5_OR_GREATER + int result = frame->nb_samples * mOutputChannelLayout.nb_channels * + #else int result = frame->nb_samples * av_get_channel_layout_nb_channels(mOutputChannelLayout) * + #endif av_get_bytes_per_sample(mOutputSampleFormat); /* We have data, return it and come back for more later */ @@ -298,7 +348,11 @@ size_t MovieAudioDecoder::read(char *stream, size_t len) len1 = std::min(len1, -mFramePos); int n = av_get_bytes_per_sample(mOutputSampleFormat) + #if FFMPEG_5_OR_GREATER + * mOutputChannelLayout.nb_channels; + #else * av_get_channel_layout_nb_channels(mOutputChannelLayout); + #endif /* add samples by copying the first sample*/ if(n == 1) @@ -348,7 +402,11 @@ int MovieAudioDecoder::getOutputSampleRate() const uint64_t MovieAudioDecoder::getOutputChannelLayout() const { + #if FFMPEG_5_OR_GREATER + return mOutputChannelLayout.u.mask; + #else return mOutputChannelLayout; + #endif } AVSampleFormat MovieAudioDecoder::getOutputSampleFormat() const diff --git a/extern/osg-ffmpeg-videoplayer/audiodecoder.hpp b/extern/osg-ffmpeg-videoplayer/audiodecoder.hpp index bbb5a27838..7163b30459 100644 --- a/extern/osg-ffmpeg-videoplayer/audiodecoder.hpp +++ b/extern/osg-ffmpeg-videoplayer/audiodecoder.hpp @@ -29,6 +29,8 @@ extern "C" typedef SSIZE_T ssize_t; #endif +#define FFMPEG_5_OR_GREATER (LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100)) + namespace Video { @@ -43,7 +45,11 @@ protected: AVCodecContext* mAudioContext; AVStream *mAVStream; enum AVSampleFormat mOutputSampleFormat; + #if FFMPEG_5_OR_GREATER + AVChannelLayout mOutputChannelLayout; + #else uint64_t mOutputChannelLayout; + #endif int mOutputSampleRate; ssize_t mFramePos; ssize_t mFrameSize; diff --git a/extern/osg-ffmpeg-videoplayer/videostate.cpp b/extern/osg-ffmpeg-videoplayer/videostate.cpp index c062c99b65..2b192209a0 100644 --- a/extern/osg-ffmpeg-videoplayer/videostate.cpp +++ b/extern/osg-ffmpeg-videoplayer/videostate.cpp @@ -243,7 +243,11 @@ int VideoState::istream_read(void *user_data, uint8_t *buf, int buf_size) } } +#if FFMPEG_CONST_WRITEPACKET +int VideoState::istream_write(void *, const unsigned char *, int) +#else int VideoState::istream_write(void *, uint8_t *, int) +#endif { throw std::runtime_error("can't write to read-only stream"); } diff --git a/extern/osg-ffmpeg-videoplayer/videostate.hpp b/extern/osg-ffmpeg-videoplayer/videostate.hpp index 3681a64976..09ea44cfd0 100644 --- a/extern/osg-ffmpeg-videoplayer/videostate.hpp +++ b/extern/osg-ffmpeg-videoplayer/videostate.hpp @@ -42,6 +42,9 @@ extern "C" #define VIDEO_PICTURE_QUEUE_SIZE 50 +#define FFMPEG_5_OR_GREATER (LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 28, 100)) +#define FFMPEG_CONST_WRITEPACKET (LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(60, 12, 100)) + extern "C" { struct SwsContext; @@ -155,7 +158,13 @@ struct VideoState { double get_master_clock(); static int istream_read(void *user_data, uint8_t *buf, int buf_size); - static int istream_write(void *user_data, uint8_t *buf, int buf_size); + +#if FFMPEG_CONST_WRITEPACKET + static int istream_write(void *, const unsigned char *, int); +#else + static int istream_write(void *, uint8_t *, int); +#endif + static int64_t istream_seek(void *user_data, int64_t offset, int whence); osg::ref_ptr mTexture;