From 7a090610b9da791dffb80891be5d085e29be56b3 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 20 Feb 2023 13:45:37 +0400 Subject: [PATCH] Upgrade FFmpeg to 5.1 on macOS. In this version videos play fine with hardware acceleration enabled. --- Telegram/SourceFiles/ffmpeg/ffmpeg_utility.h | 3 + .../media/audio/media_audio_capture.cpp | 69 +++++++---- .../media/audio/media_audio_ffmpeg_loader.cpp | 117 +++++++++++++----- .../media/audio/media_audio_ffmpeg_loader.h | 10 +- Telegram/build/prepare/prepare.py | 3 +- Telegram/lib_ui | 2 +- 6 files changed, 150 insertions(+), 54 deletions(-) diff --git a/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.h b/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.h index 6e5e2ad50f..5292c82729 100644 --- a/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.h +++ b/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.h @@ -19,8 +19,11 @@ extern "C" { #include #include #include +#include } // extern "C" +#define DA_FFMPEG_NEW_CHANNEL_LAYOUT (LIBAVUTIL_VERSION_MAJOR >= 59) + class QImage; namespace FFmpeg { diff --git a/Telegram/SourceFiles/media/audio/media_audio_capture.cpp b/Telegram/SourceFiles/media/audio/media_audio_capture.cpp index 2eff9c98da..2a2960dd78 100644 --- a/Telegram/SourceFiles/media/audio/media_audio_capture.cpp +++ b/Telegram/SourceFiles/media/audio/media_audio_capture.cpp @@ -153,6 +153,7 @@ struct Instance::Inner::Private { AVStream *stream = nullptr; const AVCodec *codec = nullptr; AVCodecContext *codecContext = nullptr; + int channels = 0; bool opened = false; bool processing = false; @@ -310,9 +311,14 @@ void Instance::Inner::start(Fn updated, Fn error) { d->codecContext->sample_fmt = AV_SAMPLE_FMT_FLTP; d->codecContext->bit_rate = 32000; +#if DA_FFMPEG_NEW_CHANNEL_LAYOUT + d->codecContext->ch_layout = AV_CHANNEL_LAYOUT_MONO; + d->channels = d->codecContext->ch_layout.nb_channels; +#else // DA_FFMPEG_NEW_CHANNEL_LAYOUT d->codecContext->channel_layout = AV_CH_LAYOUT_MONO; + d->channels = d->codecContext->channels = 1; +#endif // DA_FFMPEG_NEW_CHANNEL_LAYOUT d->codecContext->sample_rate = kCaptureFrequency; - d->codecContext->channels = 1; if (d->fmtContext->oformat->flags & AVFMT_GLOBALHEADER) { d->codecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; @@ -337,32 +343,47 @@ void Instance::Inner::start(Fn updated, Fn error) { // Using _captured directly // Prepare resampling - d->swrContext = swr_alloc(); - if (!d->swrContext) { - fprintf(stderr, "Could not allocate resampler context\n"); - exit(1); - } - - av_opt_set_int(d->swrContext, "in_channel_count", d->codecContext->channels, 0); - av_opt_set_int(d->swrContext, "in_sample_rate", d->codecContext->sample_rate, 0); - av_opt_set_sample_fmt(d->swrContext, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0); - av_opt_set_int(d->swrContext, "out_channel_count", d->codecContext->channels, 0); - av_opt_set_int(d->swrContext, "out_sample_rate", d->codecContext->sample_rate, 0); - av_opt_set_sample_fmt(d->swrContext, "out_sample_fmt", d->codecContext->sample_fmt, 0); - - if ((res = swr_init(d->swrContext)) < 0) { +#if DA_FFMPEG_NEW_CHANNEL_LAYOUT + res = swr_alloc_set_opts2( + &d->swrContext, + &d->codecContext->ch_layout, + d->codecContext->sample_fmt, + d->codecContext->sample_rate, + &d->codecContext->ch_layout, + AV_SAMPLE_FMT_S16, + d->codecContext->sample_rate, + 0, + nullptr); +#else // DA_FFMPEG_NEW_CHANNEL_LAYOUT + d->swrContext = swr_alloc_set_opts( + d->swrContext, + d->codecContext->channel_layout, + d->codecContext->sample_fmt, + d->codecContext->sample_rate, + d->codecContext->channel_layout, + AV_SAMPLE_FMT_S16, + d->codecContext->sample_rate, + 0, + nullptr); + res = 0; +#endif // DA_FFMPEG_NEW_CHANNEL_LAYOUT + if (res < 0 || !d->swrContext) { + LOG(("Audio Error: Unable to swr_alloc_set_opts2 for capture, error %1, %2").arg(res).arg(av_make_error_string(err, sizeof(err), res))); + fail(); + return; + } else if ((res = swr_init(d->swrContext)) < 0) { LOG(("Audio Error: Unable to swr_init for capture, error %1, %2").arg(res).arg(av_make_error_string(err, sizeof(err), res))); fail(); return; } d->maxDstSamples = d->srcSamples; - if ((res = av_samples_alloc_array_and_samples(&d->dstSamplesData, 0, d->codecContext->channels, d->maxDstSamples, d->codecContext->sample_fmt, 0)) < 0) { + if ((res = av_samples_alloc_array_and_samples(&d->dstSamplesData, 0, d->channels, d->maxDstSamples, d->codecContext->sample_fmt, 0)) < 0) { LOG(("Audio Error: Unable to av_samples_alloc_array_and_samples for capture, error %1, %2").arg(res).arg(av_make_error_string(err, sizeof(err), res))); fail(); return; } - d->dstSamplesSize = av_samples_get_buffer_size(0, d->codecContext->channels, d->maxDstSamples, d->codecContext->sample_fmt, 0); + d->dstSamplesSize = av_samples_get_buffer_size(0, d->channels, d->maxDstSamples, d->codecContext->sample_fmt, 0); if ((res = avcodec_parameters_from_context(d->stream->codecpar, d->codecContext)) < 0) { LOG(("Audio Error: Unable to avcodec_parameters_from_context for capture, error %1, %2").arg(res).arg(av_make_error_string(err, sizeof(err), res))); @@ -425,7 +446,7 @@ void Instance::Inner::stop(Fn callback) { memset(_captured.data() + s, 0, _captured.size() - s); } - int32 framesize = d->srcSamples * d->codecContext->channels * sizeof(short), encoded = 0; + int32 framesize = d->srcSamples * d->channels * sizeof(short), encoded = 0; while (_captured.size() >= encoded + framesize) { if (!processFrame(encoded, framesize)) { break; @@ -596,7 +617,7 @@ void Instance::Inner::process() { d->levelMax = 0; } // Write frames - int32 framesize = d->srcSamples * d->codecContext->channels * sizeof(short), encoded = 0; + int32 framesize = d->srcSamples * d->channels * sizeof(short), encoded = 0; while (uint32(_captured.size()) >= encoded + framesize + fadeSamples * sizeof(short)) { if (!processFrame(encoded, framesize)) { return; @@ -665,12 +686,12 @@ bool Instance::Inner::processFrame(int32 offset, int32 framesize) { if (d->dstSamples > d->maxDstSamples) { d->maxDstSamples = d->dstSamples; av_freep(&d->dstSamplesData[0]); - if ((res = av_samples_alloc(d->dstSamplesData, 0, d->codecContext->channels, d->dstSamples, d->codecContext->sample_fmt, 1)) < 0) { + if ((res = av_samples_alloc(d->dstSamplesData, 0, d->channels, d->dstSamples, d->codecContext->sample_fmt, 1)) < 0) { LOG(("Audio Error: Unable to av_samples_alloc for capture, error %1, %2").arg(res).arg(av_make_error_string(err, sizeof(err), res))); fail(); return false; } - d->dstSamplesSize = av_samples_get_buffer_size(0, d->codecContext->channels, d->maxDstSamples, d->codecContext->sample_fmt, 0); + d->dstSamplesSize = av_samples_get_buffer_size(0, d->channels, d->maxDstSamples, d->codecContext->sample_fmt, 0); } if ((res = swr_convert(d->swrContext, d->dstSamplesData, d->dstSamples, (const uint8_t **)srcSamplesData, d->srcSamples)) < 0) { @@ -684,13 +705,17 @@ bool Instance::Inner::processFrame(int32 offset, int32 framesize) { AVFrame *frame = av_frame_alloc(); frame->format = d->codecContext->sample_fmt; +#if DA_FFMPEG_NEW_CHANNEL_LAYOUT + av_channel_layout_copy(&frame->ch_layout, &d->codecContext->ch_layout); +#else // DA_FFMPEG_NEW_CHANNEL_LAYOUT frame->channels = d->codecContext->channels; frame->channel_layout = d->codecContext->channel_layout; +#endif // DA_FFMPEG_NEW_CHANNEL_LAYOUT frame->sample_rate = d->codecContext->sample_rate; frame->nb_samples = d->dstSamples; frame->pts = av_rescale_q(d->fullSamples, AVRational { 1, d->codecContext->sample_rate }, d->codecContext->time_base); - avcodec_fill_audio_frame(frame, d->codecContext->channels, d->codecContext->sample_fmt, d->dstSamplesData[0], d->dstSamplesSize, 0); + avcodec_fill_audio_frame(frame, d->channels, d->codecContext->sample_fmt, d->dstSamplesData[0], d->dstSamplesSize, 0); if (!writeFrame(frame)) { return false; diff --git a/Telegram/SourceFiles/media/audio/media_audio_ffmpeg_loader.cpp b/Telegram/SourceFiles/media/audio/media_audio_ffmpeg_loader.cpp index 7788421ab7..03dfc230a0 100644 --- a/Telegram/SourceFiles/media/audio/media_audio_ffmpeg_loader.cpp +++ b/Telegram/SourceFiles/media/audio/media_audio_ffmpeg_loader.cpp @@ -13,9 +13,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Media { +#if !DA_FFMPEG_NEW_CHANNEL_LAYOUT uint64_t AbstractFFMpegLoader::ComputeChannelLayout( - uint64_t channel_layout, - int channels) { + uint64_t channel_layout, + int channels) { if (channel_layout) { if (av_get_channel_layout_nb_channels(channel_layout) == channels) { return channel_layout; @@ -23,6 +24,7 @@ uint64_t AbstractFFMpegLoader::ComputeChannelLayout( } return av_get_default_channel_layout(channels); } +#endif // !DA_FFMPEG_NEW_CHANNEL_LAYOUT int64 AbstractFFMpegLoader::Mul(int64 value, AVRational rational) { return value * rational.num / rational.den; @@ -205,6 +207,20 @@ bool AbstractAudioFFMpegLoader::initUsingContext( not_null context, int64 initialCount, int initialFrequency) { + _swrSrcSampleFormat = context->sample_fmt; +#if DA_FFMPEG_NEW_CHANNEL_LAYOUT + const AVChannelLayout mono = AV_CHANNEL_LAYOUT_MONO; + const AVChannelLayout stereo = AV_CHANNEL_LAYOUT_STEREO; + const auto useMono = !av_channel_layout_compare( + &context->ch_layout, + &mono); + const auto useStereo = !av_channel_layout_compare( + &context->ch_layout, + &stereo); + const auto copyDstChannelLayout = [&] { + av_channel_layout_copy(&_swrDstChannelLayout, &context->ch_layout); + }; +#else // DA_FFMPEG_NEW_CHANNEL_LAYOUT const auto layout = ComputeChannelLayout( context->channel_layout, context->channels); @@ -215,15 +231,18 @@ bool AbstractAudioFFMpegLoader::initUsingContext( )); return false; } - - _swrSrcSampleFormat = context->sample_fmt; - switch (layout) { - case AV_CH_LAYOUT_MONO: + const auto useMono = (layout == AV_CH_LAYOUT_MONO); + const auto useStereo = (layout == AV_CH_LAYOUT_STEREO); + const auto copyDstChannelLayout = [&] { + _swrDstChannelLayout = layout; + }; +#endif // DA_FFMPEG_NEW_CHANNEL_LAYOUT + if (useMono) { switch (_swrSrcSampleFormat) { case AV_SAMPLE_FMT_U8: case AV_SAMPLE_FMT_U8P: _swrDstSampleFormat = _swrSrcSampleFormat; - _swrDstChannelLayout = layout; + copyDstChannelLayout(); _outputChannels = 1; _outputSampleSize = 1; _outputFormat = AL_FORMAT_MONO8; @@ -231,31 +250,29 @@ bool AbstractAudioFFMpegLoader::initUsingContext( case AV_SAMPLE_FMT_S16: case AV_SAMPLE_FMT_S16P: _swrDstSampleFormat = _swrSrcSampleFormat; - _swrDstChannelLayout = layout; + copyDstChannelLayout(); _outputChannels = 1; _outputSampleSize = sizeof(uint16); _outputFormat = AL_FORMAT_MONO16; break; } - break; - case AV_CH_LAYOUT_STEREO: + } else if (useStereo) { switch (_swrSrcSampleFormat) { case AV_SAMPLE_FMT_U8: _swrDstSampleFormat = _swrSrcSampleFormat; - _swrDstChannelLayout = layout; + copyDstChannelLayout(); _outputChannels = 2; _outputSampleSize = 2; _outputFormat = AL_FORMAT_STEREO8; break; case AV_SAMPLE_FMT_S16: _swrDstSampleFormat = _swrSrcSampleFormat; - _swrDstChannelLayout = layout; + copyDstChannelLayout(); _outputChannels = 2; _outputSampleSize = 2 * sizeof(uint16); _outputFormat = AL_FORMAT_STEREO16; break; } - break; } if (_swrDstRate == initialFrequency) { @@ -307,27 +324,39 @@ auto AbstractAudioFFMpegLoader::readFromReadyContext( } bool AbstractAudioFFMpegLoader::frameHasDesiredFormat() const { - const auto frameChannelLayout = ComputeChannelLayout( - _frame->channel_layout, - _frame->channels); + const auto sameChannelLayout = [&] { +#if DA_FFMPEG_NEW_CHANNEL_LAYOUT + return !av_channel_layout_compare( + &_frame->ch_layout, + &_swrDstChannelLayout); +#else // DA_FFMPEG_NEW_CHANNEL_LAYOUT + const auto frameChannelLayout = ComputeChannelLayout( + _frame->channel_layout, + _frame->channels); + return (frameChannelLayout == _swrDstChannelLayout); +#endif // DA_FFMPEG_NEW_CHANNEL_LAYOUT + }; return true && (_frame->format == _swrDstSampleFormat) - && (frameChannelLayout == _swrDstChannelLayout) - && (_frame->sample_rate == _swrDstRate); + && (_frame->sample_rate == _swrDstRate) + && sameChannelLayout(); } bool AbstractAudioFFMpegLoader::initResampleForFrame() { +#if DA_FFMPEG_NEW_CHANNEL_LAYOUT + const auto bad = !_frame->ch_layout.nb_channels; +#else // DA_FFMPEG_NEW_CHANNEL_LAYOUT const auto frameChannelLayout = ComputeChannelLayout( _frame->channel_layout, _frame->channels); - if (!frameChannelLayout) { + const auto bad = !frameChannelLayout; +#endif // DA_FFMPEG_NEW_CHANNEL_LAYOUT + if (bad) { LOG(("Audio Error: " - "Unable to compute channel layout for frame in file '%1', " - "data size '%2', channel_layout %3, channels %4" + "Unknown channel layout for frame in file '%1', " + "data size '%2'" ).arg(_file.name() ).arg(_data.size() - ).arg(_frame->channel_layout - ).arg(_frame->channels )); return false; } else if (_frame->format == -1) { @@ -338,24 +367,48 @@ bool AbstractAudioFFMpegLoader::initResampleForFrame() { )); return false; } else if (_swrContext) { + const auto sameChannelLayout = [&] { +#if DA_FFMPEG_NEW_CHANNEL_LAYOUT + return !av_channel_layout_compare( + &_frame->ch_layout, + &_swrSrcChannelLayout); +#else // DA_FFMPEG_NEW_CHANNEL_LAYOUT + return (frameChannelLayout == _swrSrcChannelLayout); +#endif // DA_FFMPEG_NEW_CHANNEL_LAYOUT + }; if (true && (_frame->format == _swrSrcSampleFormat) - && (frameChannelLayout == _swrSrcChannelLayout) - && (_frame->sample_rate == _swrSrcRate)) { + && (_frame->sample_rate == _swrSrcRate) + && sameChannelLayout()) { return true; } swr_close(_swrContext); } _swrSrcSampleFormat = static_cast(_frame->format); +#if DA_FFMPEG_NEW_CHANNEL_LAYOUT + av_channel_layout_copy(&_swrSrcChannelLayout, &_frame->ch_layout); +#else // DA_FFMPEG_NEW_CHANNEL_LAYOUT _swrSrcChannelLayout = frameChannelLayout; +#endif // DA_FFMPEG_NEW_CHANNEL_LAYOUT _swrSrcRate = _frame->sample_rate; return initResampleUsingFormat(); } bool AbstractAudioFFMpegLoader::initResampleUsingFormat() { int res = 0; - +#if DA_FFMPEG_NEW_CHANNEL_LAYOUT + res = swr_alloc_set_opts2( + &_swrContext, + &_swrDstChannelLayout, + _swrDstSampleFormat, + _swrDstRate, + &_swrSrcChannelLayout, + _swrSrcSampleFormat, + _swrSrcRate, + 0, + nullptr); +#else // DA_FFMPEG_NEW_CHANNEL_LAYOUT _swrContext = swr_alloc_set_opts( _swrContext, _swrDstChannelLayout, @@ -366,11 +419,17 @@ bool AbstractAudioFFMpegLoader::initResampleUsingFormat() { _swrSrcRate, 0, nullptr); - if (!_swrContext) { +#endif // DA_FFMPEG_NEW_CHANNEL_LAYOUT + if (res < 0 || !_swrContext) { + char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; LOG(("Audio Error: " - "Unable to swr_alloc for file '%1', data size '%2'" + "Unable to swr_alloc_set_opts2 for file '%1', data size '%2', " + "error %3, %4" ).arg(_file.name() - ).arg(_data.size())); + ).arg(_data.size() + ).arg(res + ).arg(av_make_error_string(err, sizeof(err), res) + )); return false; } else if ((res = swr_init(_swrContext)) < 0) { char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; diff --git a/Telegram/SourceFiles/media/audio/media_audio_ffmpeg_loader.h b/Telegram/SourceFiles/media/audio/media_audio_ffmpeg_loader.h index 2dae6b4734..f74e4a5bb4 100644 --- a/Telegram/SourceFiles/media/audio/media_audio_ffmpeg_loader.h +++ b/Telegram/SourceFiles/media/audio/media_audio_ffmpeg_loader.h @@ -45,9 +45,11 @@ public: return _samplesFrequency; } +#if !DA_FFMPEG_NEW_CHANNEL_LAYOUT static uint64_t ComputeChannelLayout( uint64_t channel_layout, int channels); +#endif // !DA_FFMPEG_NEW_CHANNEL_LAYOUT ~AbstractFFMpegLoader(); @@ -144,11 +146,17 @@ private: int _swrSrcRate = 0; AVSampleFormat _swrSrcSampleFormat = AV_SAMPLE_FMT_NONE; - uint64_t _swrSrcChannelLayout = 0; const int _swrDstRate = Media::Player::kDefaultFrequency; AVSampleFormat _swrDstSampleFormat = AV_SAMPLE_FMT_S16; + +#if DA_FFMPEG_NEW_CHANNEL_LAYOUT + AVChannelLayout _swrSrcChannelLayout = AV_CHANNEL_LAYOUT_STEREO; + AVChannelLayout _swrDstChannelLayout = AV_CHANNEL_LAYOUT_STEREO; +#else // DA_FFMPEG_NEW_CHANNEL_LAYOUT + uint64_t _swrSrcChannelLayout = 0; uint64_t _swrDstChannelLayout = AV_CH_LAYOUT_STEREO; +#endif // DA_FFMPEG_NEW_CHANNEL_LAYOUT uint8_t **_swrDstData = nullptr; int _swrDstDataCapacity = 0; diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py index 0ba4a61f15..b51840ba30 100644 --- a/Telegram/build/prepare/prepare.py +++ b/Telegram/build/prepare/prepare.py @@ -873,8 +873,8 @@ win: stage('ffmpeg', """ git clone https://github.com/FFmpeg/FFmpeg.git ffmpeg cd ffmpeg - git checkout cc33e73618 win: + git checkout cc33e73618 SET PATH_BACKUP_=%PATH% SET PATH=%ROOT_DIR%\\ThirdParty\\msys64\\usr\\bin;%PATH% @@ -886,6 +886,7 @@ depends:patches/build_ffmpeg_win.sh SET PATH=%PATH_BACKUP_% mac: + git checkout 7268323193 export PKG_CONFIG_PATH=$USED_PREFIX/lib/pkgconfig depends:yasm/yasm diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 74ab66cfa9..c3b5f2e62d 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 74ab66cfa9c05745ca513504940e59f6fd68eff3 +Subproject commit c3b5f2e62dbe54bca50c14b977fac626bab98138