From bc9f0507df17c42746ef5acc06313a7a058391d4 Mon Sep 17 00:00:00 2001 From: Mladen Milinkovic Date: Fri, 18 Nov 2022 15:59:10 +0100 Subject: [PATCH 01/11] Replaced deprecated FFmpeg channel layout code --- src/CMakeLists.txt | 8 ++++ src/streamprocessor/streamprocessor.cpp | 48 ++++++++++++------- src/streamprocessor/streamprocessor.h | 17 +++---- src/videoplayer/backend/audiodecoder.cpp | 57 ++++++++++++----------- src/videoplayer/backend/audiodecoder.h | 5 +- src/videoplayer/backend/streamdemuxer.cpp | 22 +++++---- src/videoplayer/backend/videostate.cpp | 2 +- 7 files changed, 92 insertions(+), 67 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bf0043ea..bacf025f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,6 +5,14 @@ endif() if(CMAKE_COMPILER_IS_GNUCC) message(STATUS "GNU C compiler detected") + if(SC_NO_DEPRECATED) + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wno-deprecated-declarations") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wno-deprecated-declarations") + endif() + if(SC_WARN_ERRORS) + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Werror") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Werror") + endif() set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -Og -g") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -Og -g") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wall -O3") diff --git a/src/streamprocessor/streamprocessor.cpp b/src/streamprocessor/streamprocessor.cpp index 1e883780..aa27a66e 100644 --- a/src/streamprocessor/streamprocessor.cpp +++ b/src/streamprocessor/streamprocessor.cpp @@ -35,13 +35,15 @@ StreamProcessor::StreamProcessor(QObject *parent) m_avFormat(nullptr), m_avStream(nullptr), m_codecCtx(nullptr), - m_swResample(nullptr) + m_swResample(nullptr), + m_audioChLayout(new AVChannelLayout{}) { } StreamProcessor::~StreamProcessor() { close(); + delete m_audioChLayout; } bool @@ -81,7 +83,7 @@ StreamProcessor::open(const QString &filename) m_opened = true; - return true; + return true; } void @@ -92,6 +94,7 @@ StreamProcessor::close() wait(); } + av_channel_layout_uninit(m_audioChLayout); if(m_swResample) swr_free(&m_swResample); if(m_codecCtx) @@ -273,28 +276,40 @@ StreamProcessor::initAudio(int streamIndex, const WaveFormat &waveFormat) return false; } + av_channel_layout_uninit(m_audioChLayout); + // figure channel layout or update stream format - if(!m_codecCtx->channel_layout) - m_codecCtx->channel_layout = av_get_default_channel_layout(m_codecCtx->channels);; + if(m_codecCtx->ch_layout.order != AV_CHANNEL_ORDER_NATIVE) { + const int cc = m_codecCtx->ch_layout.nb_channels; + av_channel_layout_uninit(&m_codecCtx->ch_layout); + av_channel_layout_default(&m_codecCtx->ch_layout, cc); + } if(m_audioStreamFormat.channels() == 0) { - m_audioStreamFormat.setChannels(m_codecCtx->channels); - m_audioChannelLayout = m_codecCtx->channel_layout; + m_audioStreamFormat.setChannels(m_codecCtx->ch_layout.nb_channels); + av_channel_layout_copy(m_audioChLayout, &m_codecCtx->ch_layout); } else { - m_audioChannelLayout = av_get_default_channel_layout(m_audioStreamFormat.channels()); + av_channel_layout_default(m_audioChLayout, m_audioStreamFormat.channels()); } // setup resampler if needed - const bool convChannels = m_codecCtx->channel_layout != m_audioChannelLayout; + const bool convChannels = av_channel_layout_compare(&m_codecCtx->ch_layout, m_audioChLayout) != 0; const bool convSampleRate = m_codecCtx->sample_rate != m_audioStreamFormat.sampleRate(); const bool convSampleFormat = m_codecCtx->sample_fmt != m_audioSampleFormat; if(convChannels || convSampleRate || convSampleFormat) { - m_swResample = swr_alloc_set_opts(nullptr, - m_audioChannelLayout, static_cast(m_audioSampleFormat), m_audioStreamFormat.sampleRate(), - m_codecCtx->channel_layout, m_codecCtx->sample_fmt, m_codecCtx->sample_rate, - 0, nullptr); + swr_alloc_set_opts2(&m_swResample, + m_audioChLayout, AVSampleFormat(m_audioSampleFormat), m_audioStreamFormat.sampleRate(), + &m_codecCtx->ch_layout, m_codecCtx->sample_fmt, m_codecCtx->sample_rate, + 0, nullptr); // NOTE: swr_convert_frame() will call swr_init() and swr_config_frame() which is better as it seems m_codecCtx can - // end up with different config that what is actually in the stream + // end up with different config than what is actually in the stream + if(!m_swResample) { + av_log(nullptr, AV_LOG_ERROR, + "Cannot create sample rate converter for conversion of %d Hz %s %d channels to %d Hz %s %d channels!\n", + m_codecCtx->sample_rate, av_get_sample_fmt_name(m_codecCtx->sample_fmt), m_codecCtx->ch_layout.nb_channels, + m_audioStreamFormat.sampleRate(), av_get_sample_fmt_name(AVSampleFormat(m_audioSampleFormat)), m_audioChLayout->nb_channels); + return false; + } } return true; @@ -363,7 +378,8 @@ StreamProcessor::processAudio() if(m_swResample) { frameResampled = av_frame_alloc(); Q_ASSERT(frameResampled != nullptr); - frameResampled->channel_layout = m_audioChannelLayout; + av_channel_layout_uninit(&frameResampled->ch_layout); + av_channel_layout_copy(&frameResampled->ch_layout, m_audioChLayout); frameResampled->sample_rate = m_audioStreamFormat.sampleRate(); frameResampled->format = m_audioSampleFormat; } @@ -448,12 +464,12 @@ StreamProcessor::processAudio() if(m_swResample) { Q_ASSERT(frameResampled != nullptr); - emit audioDataAvailable(frameResampled->data[0], qint32(frameSize * frameResampled->channels), + emit audioDataAvailable(frameResampled->data[0], qint32(frameSize * frameResampled->ch_layout.nb_channels), &m_audioStreamFormat, qint64(timeFrameStart + timeResampleDelay), qint64(timeFrameDuration)); drainSampleBuffer = swr_get_out_samples(m_swResample, 0) > 1000; } else { - emit audioDataAvailable(frame->data[0], qint32(frameSize * frame->channels), + emit audioDataAvailable(frame->data[0], qint32(frameSize * frame->ch_layout.nb_channels), &m_audioStreamFormat, qint64(timeFrameStart), qint64(timeFrameDuration)); } } while(!conversionComplete && !isInterruptionRequested() && drainSampleBuffer); diff --git a/src/streamprocessor/streamprocessor.h b/src/streamprocessor/streamprocessor.h index 152a985a..02e7f3b1 100644 --- a/src/streamprocessor/streamprocessor.h +++ b/src/streamprocessor/streamprocessor.h @@ -16,14 +16,11 @@ QT_FORWARD_DECLARE_CLASS(QTimer) -QT_FORWARD_DECLARE_STRUCT(AVFormatContext) -typedef struct AVFormatContext AVFormatContext; -QT_FORWARD_DECLARE_STRUCT(AVCodecContext) -typedef struct AVCodecContext AVCodecContext; -QT_FORWARD_DECLARE_STRUCT(AVStream) -typedef struct AVStream AVStream; -QT_FORWARD_DECLARE_STRUCT(SwrContext) -typedef struct SwrContext SwrContext; +struct AVFormatContext; +struct AVCodecContext; +struct AVStream; +struct SwrContext; +struct AVChannelLayout; namespace SubtitleComposer { @@ -59,7 +56,7 @@ protected: int findStream(int streamType, int streamIndex, bool imageSub); void processAudio(); void processText(); - virtual void run() override; + virtual void run() override; private: bool m_opened; @@ -86,7 +83,7 @@ private: AVCodecContext *m_codecCtx; SwrContext *m_swResample; int m_audioSampleFormat; - uint64_t m_audioChannelLayout; + AVChannelLayout *m_audioChLayout; }; } diff --git a/src/videoplayer/backend/audiodecoder.cpp b/src/videoplayer/backend/audiodecoder.cpp index c0c99bb3..8c0b943e 100644 --- a/src/videoplayer/backend/audiodecoder.cpp +++ b/src/videoplayer/backend/audiodecoder.cpp @@ -37,6 +37,8 @@ using namespace SubtitleComposer; AudioDecoder::AudioDecoder(VideoState *state, QObject *parent) : Decoder(parent), m_vs(state), + m_fmtSrc({}), + m_fmtTgt({}), m_swrCtx(nullptr), m_audioBuf(nullptr), m_bufSize(0), @@ -147,7 +149,7 @@ AudioDecoder::close() } bool -AudioDecoder::open(int64_t wantChLayout, int wantNbChan, int wantSampleRate) +AudioDecoder::open(AVChannelLayout *wantChLayout, int wantSampleRate) { const static QMap bufFmtMap = { { 4, "AL_FORMAT_QUAD16" }, @@ -158,12 +160,12 @@ AudioDecoder::open(int64_t wantChLayout, int wantNbChan, int wantSampleRate) int err; - if(wantSampleRate <= 0 || wantNbChan <= 0) { + if(wantSampleRate <= 0 || !wantChLayout || wantChLayout->nb_channels <= 0) { av_log(nullptr, AV_LOG_ERROR, "openal: invalid sample rate or channel count!\n"); return false; } - int availNbChan = wantNbChan; + int availNbChan = wantChLayout->nb_channels; for(;;) { while(availNbChan > 2 && !bufFmtMap.contains(availNbChan)) availNbChan--; @@ -171,15 +173,15 @@ AudioDecoder::open(int64_t wantChLayout, int wantNbChan, int wantSampleRate) m_bufFmt = availNbChan == 2 ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16; break; } - m_bufFmt = alGetEnumValue(bufFmtMap[wantNbChan]); + m_bufFmt = alGetEnumValue(bufFmtMap[wantChLayout->nb_channels]); if(m_bufFmt) break; availNbChan--; } - if(!wantChLayout || wantNbChan != availNbChan || wantNbChan != av_get_channel_layout_nb_channels(wantChLayout)) { - wantChLayout = av_get_default_channel_layout(availNbChan); - wantChLayout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX; + if(wantChLayout->nb_channels != availNbChan || wantChLayout->order != AV_CHANNEL_ORDER_NATIVE) { + av_channel_layout_uninit(wantChLayout); + av_channel_layout_default(wantChLayout, availNbChan); } m_alDev = alcOpenDevice(nullptr); @@ -212,10 +214,14 @@ AudioDecoder::open(int64_t wantChLayout, int wantNbChan, int wantSampleRate) m_fmtTgt.fmt = AV_SAMPLE_FMT_S16; m_fmtTgt.freq = wantSampleRate; - m_fmtTgt.channelLayout = wantChLayout; - m_fmtTgt.channels = availNbChan; - m_fmtTgt.frameSize = av_samples_get_buffer_size(nullptr, m_fmtTgt.channels, 1, m_fmtTgt.fmt, 1); - m_fmtTgt.bytesPerSec = av_samples_get_buffer_size(nullptr, m_fmtTgt.channels, m_fmtTgt.freq, m_fmtTgt.fmt, 1); + if((err = av_channel_layout_copy(&m_fmtTgt.chLayout, wantChLayout)) < 0) { + av_log(nullptr, AV_LOG_ERROR, "av_channel_layout_copy() failed (errL %d).\n", err); + close(); + return false; + } + + m_fmtTgt.frameSize = av_samples_get_buffer_size(nullptr, m_fmtTgt.chLayout.nb_channels, 1, m_fmtTgt.fmt, 1); + m_fmtTgt.bytesPerSec = av_samples_get_buffer_size(nullptr, m_fmtTgt.chLayout.nb_channels, m_fmtTgt.freq, m_fmtTgt.fmt, 1); if(m_fmtTgt.bytesPerSec <= 0 || m_fmtTgt.frameSize <= 0) { av_log(nullptr, AV_LOG_ERROR, "av_samples_get_buffer_size failed\n"); close(); @@ -349,47 +355,44 @@ AudioDecoder::syncAudio(int nbSamples) int AudioDecoder::decodeFrame(Frame *af) { + // CONVERTED maxrd2 if(af->serial != m_queue->serial()) return -1; - int dataSize = av_samples_get_buffer_size(nullptr, af->frame->channels, + int dataSize = av_samples_get_buffer_size(nullptr, af->frame->ch_layout.nb_channels, af->frame->nb_samples, (AVSampleFormat)af->frame->format, 1); int resampledDataSize; - uint64_t decChannelLayout = - (af->frame->channel_layout && - af->frame->channels == av_get_channel_layout_nb_channels(af->frame->channel_layout)) ? - af->frame->channel_layout : av_get_default_channel_layout(af->frame->channels); int wantedNbSamples = syncAudio(af->frame->nb_samples); if(af->frame->format != m_fmtSrc.fmt - || decChannelLayout != m_fmtSrc.channelLayout + || av_channel_layout_compare(&af->frame->ch_layout, &m_fmtSrc.chLayout) || af->frame->sample_rate != m_fmtSrc.freq || (wantedNbSamples != af->frame->nb_samples && !m_swrCtx)) { swr_free(&m_swrCtx); - m_swrCtx = swr_alloc_set_opts(nullptr, - m_fmtTgt.channelLayout, m_fmtTgt.fmt, m_fmtTgt.freq, - decChannelLayout, (AVSampleFormat)af->frame->format, af->frame->sample_rate, - 0, nullptr); + swr_alloc_set_opts2(&m_swrCtx, + &m_fmtTgt.chLayout, m_fmtTgt.fmt, m_fmtTgt.freq, + &af->frame->ch_layout, AVSampleFormat(af->frame->format), af->frame->sample_rate, + 0, nullptr); if(!m_swrCtx || swr_init(m_swrCtx) < 0) { av_log(nullptr, AV_LOG_ERROR, "Cannot create sample rate converter for conversion of %d Hz %s %d channels to %d Hz %s %d channels!\n", af->frame->sample_rate, av_get_sample_fmt_name((AVSampleFormat)af->frame->format), - af->frame->channels, - m_fmtTgt.freq, av_get_sample_fmt_name(m_fmtTgt.fmt), m_fmtTgt.channels); + af->frame->ch_layout.nb_channels, + m_fmtTgt.freq, av_get_sample_fmt_name(m_fmtTgt.fmt), m_fmtTgt.chLayout.nb_channels); swr_free(&m_swrCtx); return -1; } - m_fmtSrc.channelLayout = decChannelLayout; - m_fmtSrc.channels = af->frame->channels; + if(av_channel_layout_copy(&m_fmtSrc.chLayout, &af->frame->ch_layout) < 0) + return -1; m_fmtSrc.freq = af->frame->sample_rate; m_fmtSrc.fmt = (AVSampleFormat)af->frame->format; } if(m_swrCtx) { const int outCount = (int64_t)wantedNbSamples * m_fmtTgt.freq / af->frame->sample_rate + 256; - const int outSize = av_samples_get_buffer_size(nullptr, m_fmtTgt.channels, outCount, m_fmtTgt.fmt, 0); + const int outSize = av_samples_get_buffer_size(nullptr, m_fmtTgt.chLayout.nb_channels, outCount, m_fmtTgt.fmt, 0); if(outSize < 0) { av_log(nullptr, AV_LOG_ERROR, "av_samples_get_buffer_size() failed\n"); return -1; @@ -417,7 +420,7 @@ AudioDecoder::decodeFrame(Frame *af) swr_free(&m_swrCtx); } m_audioBuf = m_audioBuf1; - resampledDataSize = outSamplesPerChannel * m_fmtTgt.channels * av_get_bytes_per_sample(m_fmtTgt.fmt); + resampledDataSize = outSamplesPerChannel * m_fmtTgt.chLayout.nb_channels * av_get_bytes_per_sample(m_fmtTgt.fmt); } else { m_audioBuf = af->frame->data[0]; resampledDataSize = dataSize; diff --git a/src/videoplayer/backend/audiodecoder.h b/src/videoplayer/backend/audiodecoder.h index ae579078..5b32b9a3 100644 --- a/src/videoplayer/backend/audiodecoder.h +++ b/src/videoplayer/backend/audiodecoder.h @@ -37,8 +37,7 @@ private: struct Params { int freq; - int channels; - uint64_t channelLayout; + AVChannelLayout chLayout; AVSampleFormat fmt; int frameSize; int bytesPerSec; @@ -50,7 +49,7 @@ private: void queueBuffer(uint8_t *data, int len); int syncAudio(int nbSamples); - bool open(int64_t wanted_channel_layout, int wanted_nb_channels, int wanted_sample_rate); + bool open(AVChannelLayout *wantChLayout, int wantSampleRate); void close(); void flush(); void play(); diff --git a/src/videoplayer/backend/streamdemuxer.cpp b/src/videoplayer/backend/streamdemuxer.cpp index 72d21896..2eb59af1 100644 --- a/src/videoplayer/backend/streamdemuxer.cpp +++ b/src/videoplayer/backend/streamdemuxer.cpp @@ -233,9 +233,9 @@ StreamDemuxer::componentOpen(int streamIndex) AVCodecContext *avCtx; const AVCodec *codec; AVDictionary *opts = nullptr; - AVDictionaryEntry *t = nullptr; - int sampleRate, nbChannels; - int64_t channelLayout; + const AVDictionaryEntry *t = nullptr; + int sampleRate; + AVChannelLayout chLayout = {}; int ret = 0; int stream_lowres = m_vs->lowres; @@ -301,19 +301,21 @@ StreamDemuxer::componentOpen(int streamIndex) switch(avCtx->codec_type) { case AVMEDIA_TYPE_AUDIO: sampleRate = avCtx->sample_rate; - nbChannels = avCtx->channels; - channelLayout = avCtx->channel_layout; + if((ret = av_channel_layout_copy(&chLayout, &avCtx->ch_layout)) < 0) { + av_log(nullptr, AV_LOG_ERROR, "av_channel_layout_copy() failed (errL %d).\n", ret); + goto fail; + } // prepare audio output - if(!m_vs->audDec.open(channelLayout, nbChannels, sampleRate)) + if(!m_vs->audDec.open(&chLayout, sampleRate)) goto fail; m_vs->audStreamIdx = streamIndex; m_vs->audStream = ic->streams[streamIndex]; m_vs->audDec.init(avCtx, &m_vs->audPQ, nullptr, m_vs->continueReadThread); - if((m_vs->fmtContext->iformat->flags & (AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH | AVFMT_NO_BYTE_SEEK)) && - !m_vs->fmtContext->iformat->read_seek) { + if((m_vs->fmtContext->iformat->flags & (AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH | AVFMT_NO_BYTE_SEEK)) + && !m_vs->fmtContext->iformat->read_seek) { m_vs->audDec.startPts(m_vs->audStream->start_time, m_vs->audStream->time_base); } m_vs->audDec.start(); @@ -342,6 +344,7 @@ StreamDemuxer::componentOpen(int streamIndex) fail: avcodec_free_context(&avCtx); out: + av_channel_layout_uninit(&chLayout); av_dict_free(&opts); return ret; @@ -397,8 +400,7 @@ StreamDemuxer::cycleStream(int codecType) /* check that parameters are OK */ switch(codecType) { case AVMEDIA_TYPE_AUDIO: - if(st->codecpar->sample_rate != 0 && - st->codecpar->channels != 0) + if(st->codecpar->sample_rate != 0 && st->codecpar->ch_layout.nb_channels != 0) goto the_end; break; case AVMEDIA_TYPE_VIDEO: diff --git a/src/videoplayer/backend/videostate.cpp b/src/videoplayer/backend/videostate.cpp index e34399df..a09f2466 100644 --- a/src/videoplayer/backend/videostate.cpp +++ b/src/videoplayer/backend/videostate.cpp @@ -131,7 +131,7 @@ VideoState::notifyLoaded() continue; *streamName += QStringLiteral(": "); - AVDictionaryEntry *tag = av_dict_get(stream->metadata, "lang", nullptr, AV_DICT_IGNORE_SUFFIX); + const AVDictionaryEntry *tag = av_dict_get(stream->metadata, "lang", nullptr, AV_DICT_IGNORE_SUFFIX); *streamName += tag ? QString("%2 (%3)").arg(LanguageCode::nameFromIso(tag->value)).arg(tag->value) : QStringLiteral("Unknown"); if((tag = av_dict_get(stream->metadata, "title", nullptr, 0)) != nullptr) -- 2.46.0