From a17b466bf63c50a036845e8cff5e014131d5795f Mon Sep 17 00:00:00 2001 From: Daniel Flores Date: Thu, 11 Sep 2025 17:36:51 -0400 Subject: [PATCH 1/6] use avcodec_get_supported_config --- src/torchcodec/_core/Encoder.cpp | 53 ++++++++++++++++++++++----- src/torchcodec/_core/FFMPEGCommon.cpp | 29 ++++++++++++++- 2 files changed, 72 insertions(+), 10 deletions(-) diff --git a/src/torchcodec/_core/Encoder.cpp b/src/torchcodec/_core/Encoder.cpp index b4d2c5609..af7a4a034 100644 --- a/src/torchcodec/_core/Encoder.cpp +++ b/src/torchcodec/_core/Encoder.cpp @@ -33,21 +33,39 @@ torch::Tensor validateSamples(const torch::Tensor& samples) { } void validateSampleRate(const AVCodec& avCodec, int sampleRate) { - if (avCodec.supported_samplerates == nullptr) { + const int* supportedSampleRates = nullptr; +#if LIBAVFILTER_VERSION_MAJOR >= 10 // FFmpeg >= 7 + int numSampleRates = 0; + int ret = avcodec_get_supported_config( + nullptr, + &avCodec, + AV_CODEC_CONFIG_SAMPLE_RATE, + 0, + (const void**)&supportedSampleRates, + &numSampleRates); + if (ret < 0 || supportedSampleRates == nullptr) { + // If we can't validate, assume it'll be fine?? + return; + } +#else + supportedSampleRates = avCodec.supported_samplerates; +#endif + + if (supportedSampleRates == nullptr) { return; } - for (auto i = 0; avCodec.supported_samplerates[i] != 0; ++i) { - if (sampleRate == avCodec.supported_samplerates[i]) { + for (auto i = 0; supportedSampleRates[i] != 0; ++i) { + if (sampleRate == supportedSampleRates[i]) { return; } } std::stringstream supportedRates; - for (auto i = 0; avCodec.supported_samplerates[i] != 0; ++i) { + for (auto i = 0; supportedSampleRates[i] != 0; ++i) { if (i > 0) { supportedRates << ", "; } - supportedRates << avCodec.supported_samplerates[i]; + supportedRates << supportedSampleRates[i]; } TORCH_CHECK( @@ -73,19 +91,36 @@ static const std::vector preferredFormatsOrder = { AV_SAMPLE_FMT_U8}; AVSampleFormat findBestOutputSampleFormat(const AVCodec& avCodec) { + const AVSampleFormat* supportedSampleFormats = nullptr; +#if LIBAVFILTER_VERSION_MAJOR >= 10 // FFmpeg >= 7 + int numSampleFormats = 0; + int ret = avcodec_get_supported_config( + nullptr, + &avCodec, + AV_CODEC_CONFIG_SAMPLE_FORMAT, + 0, + (const void**)&supportedSampleFormats, + &numSampleFormats); + if (ret < 0 || supportedSampleFormats == nullptr) { + TORCH_CHECK(false, "Couldn't get supported sample formats from encoder."); + } +#else + supportedSampleFormats = avCodec.sample_fmts; +#endif + // Find a sample format that the encoder supports. We prefer using FLT[P], // since this is the format of the input samples. If FLTP isn't supported // then we'll need to convert the AVFrame's format. Our heuristic is to encode // into the format with the highest resolution. - if (avCodec.sample_fmts == nullptr) { + if (supportedSampleFormats == nullptr) { // Can't really validate anything in this case, best we can do is hope that // FLTP is supported by the encoder. If not, FFmpeg will raise. return AV_SAMPLE_FMT_FLTP; } for (AVSampleFormat preferredFormat : preferredFormatsOrder) { - for (int i = 0; avCodec.sample_fmts[i] != -1; ++i) { - if (avCodec.sample_fmts[i] == preferredFormat) { + for (int i = 0; supportedSampleFormats[i] != -1; ++i) { + if (supportedSampleFormats[i] == preferredFormat) { return preferredFormat; } } @@ -93,7 +128,7 @@ AVSampleFormat findBestOutputSampleFormat(const AVCodec& avCodec) { // We should always find a match in preferredFormatsOrder, so we should always // return earlier. But in the event that a future FFmpeg version defines an // additional sample format that isn't in preferredFormatsOrder, we fallback: - return avCodec.sample_fmts[0]; + return supportedSampleFormats[0]; } } // namespace diff --git a/src/torchcodec/_core/FFMPEGCommon.cpp b/src/torchcodec/_core/FFMPEGCommon.cpp index 094a1be0a..a7111778b 100644 --- a/src/torchcodec/_core/FFMPEGCommon.cpp +++ b/src/torchcodec/_core/FFMPEGCommon.cpp @@ -7,6 +7,7 @@ #include "src/torchcodec/_core/FFMPEGCommon.h" #include +#include namespace facebook::torchcodec { @@ -109,7 +110,33 @@ void setDefaultChannelLayout(UniqueAVFrame& avFrame, int numChannels) { } void validateNumChannels(const AVCodec& avCodec, int numChannels) { -#if LIBAVFILTER_VERSION_MAJOR > 7 // FFmpeg > 4 +#if LIBAVFILTER_VERSION_MAJOR >= 10 // FFmpeg >= 7 + std::stringstream supportedNumChannels; + std::cout << "ffmpeg7? " << std::endl; + const AVChannelLayout* supported_layouts = nullptr; + int num_layouts = 0; + int ret = avcodec_get_supported_config( + nullptr, + &avCodec, + AV_CODEC_CONFIG_CHANNEL_LAYOUT, + 0, + (const void**)&supported_layouts, // use this instead of building + // supportedNumChannels? + &num_layouts); + if (ret < 0 || supported_layouts == nullptr) { + // If we can't validate, assume it'll be fine?? + return; + } + for (int i = 0; supported_layouts[i].nb_channels != 0; ++i) { + if (i > 0) { + supportedNumChannels << ", "; + } + supportedNumChannels << supported_layouts[i].nb_channels; + if (numChannels == supported_layouts[i].nb_channels) { + return; + } + } +#elif LIBAVFILTER_VERSION_MAJOR > 7 // FFmpeg > 4 if (avCodec.ch_layouts == nullptr) { // If we can't validate, we must assume it'll be fine. If not, FFmpeg will // eventually raise. From a7faca05fe89d14497b9dd1c4ae56df527a2ec90 Mon Sep 17 00:00:00 2001 From: Daniel Flores Date: Mon, 15 Sep 2025 15:46:42 -0400 Subject: [PATCH 2/6] check LIBAVCODEC version --- src/torchcodec/_core/Encoder.cpp | 4 ++-- src/torchcodec/_core/FFMPEGCommon.cpp | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/torchcodec/_core/Encoder.cpp b/src/torchcodec/_core/Encoder.cpp index af7a4a034..228161a84 100644 --- a/src/torchcodec/_core/Encoder.cpp +++ b/src/torchcodec/_core/Encoder.cpp @@ -34,7 +34,7 @@ torch::Tensor validateSamples(const torch::Tensor& samples) { void validateSampleRate(const AVCodec& avCodec, int sampleRate) { const int* supportedSampleRates = nullptr; -#if LIBAVFILTER_VERSION_MAJOR >= 10 // FFmpeg >= 7 +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61, 13, 100) int numSampleRates = 0; int ret = avcodec_get_supported_config( nullptr, @@ -92,7 +92,7 @@ static const std::vector preferredFormatsOrder = { AVSampleFormat findBestOutputSampleFormat(const AVCodec& avCodec) { const AVSampleFormat* supportedSampleFormats = nullptr; -#if LIBAVFILTER_VERSION_MAJOR >= 10 // FFmpeg >= 7 +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61, 13, 100) // FFmpeg >= 7 int numSampleFormats = 0; int ret = avcodec_get_supported_config( nullptr, diff --git a/src/torchcodec/_core/FFMPEGCommon.cpp b/src/torchcodec/_core/FFMPEGCommon.cpp index a7111778b..dd62b2d46 100644 --- a/src/torchcodec/_core/FFMPEGCommon.cpp +++ b/src/torchcodec/_core/FFMPEGCommon.cpp @@ -7,7 +7,6 @@ #include "src/torchcodec/_core/FFMPEGCommon.h" #include -#include namespace facebook::torchcodec { @@ -110,9 +109,8 @@ void setDefaultChannelLayout(UniqueAVFrame& avFrame, int numChannels) { } void validateNumChannels(const AVCodec& avCodec, int numChannels) { -#if LIBAVFILTER_VERSION_MAJOR >= 10 // FFmpeg >= 7 +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61, 13, 100) // FFmpeg >= 7 std::stringstream supportedNumChannels; - std::cout << "ffmpeg7? " << std::endl; const AVChannelLayout* supported_layouts = nullptr; int num_layouts = 0; int ret = avcodec_get_supported_config( @@ -158,7 +156,7 @@ void validateNumChannels(const AVCodec& avCodec, int numChannels) { } supportedNumChannels << avCodec.ch_layouts[i].nb_channels; } -#else +#else // FFmpeg <= 4 if (avCodec.channel_layouts == nullptr) { // can't validate, same as above. return; From 8f20a8f368f85958fcfed14c0cffcea559501d50 Mon Sep 17 00:00:00 2001 From: Daniel Flores Date: Mon, 15 Sep 2025 15:57:12 -0400 Subject: [PATCH 3/6] add torch check when avcodec_get_supported_config fails --- src/torchcodec/_core/Encoder.cpp | 2 +- src/torchcodec/_core/FFMPEGCommon.cpp | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/torchcodec/_core/Encoder.cpp b/src/torchcodec/_core/Encoder.cpp index 228161a84..e3b4a1ee0 100644 --- a/src/torchcodec/_core/Encoder.cpp +++ b/src/torchcodec/_core/Encoder.cpp @@ -44,7 +44,7 @@ void validateSampleRate(const AVCodec& avCodec, int sampleRate) { (const void**)&supportedSampleRates, &numSampleRates); if (ret < 0 || supportedSampleRates == nullptr) { - // If we can't validate, assume it'll be fine?? + TORCH_CHECK(false, "Couldn't get supported sample rates from encoder."); return; } #else diff --git a/src/torchcodec/_core/FFMPEGCommon.cpp b/src/torchcodec/_core/FFMPEGCommon.cpp index dd62b2d46..0e99c314c 100644 --- a/src/torchcodec/_core/FFMPEGCommon.cpp +++ b/src/torchcodec/_core/FFMPEGCommon.cpp @@ -118,11 +118,10 @@ void validateNumChannels(const AVCodec& avCodec, int numChannels) { &avCodec, AV_CODEC_CONFIG_CHANNEL_LAYOUT, 0, - (const void**)&supported_layouts, // use this instead of building - // supportedNumChannels? + (const void**)&supported_layouts, &num_layouts); if (ret < 0 || supported_layouts == nullptr) { - // If we can't validate, assume it'll be fine?? + TORCH_CHECK(false, "Couldn't get supported channel layouts from encoder."); return; } for (int i = 0; supported_layouts[i].nb_channels != 0; ++i) { From 599556da7049b5fe8914dc9b2480ed2ab619c1f9 Mon Sep 17 00:00:00 2001 From: Daniel Flores Date: Tue, 16 Sep 2025 10:04:11 -0400 Subject: [PATCH 4/6] remove unused return --- src/torchcodec/_core/Encoder.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/torchcodec/_core/Encoder.cpp b/src/torchcodec/_core/Encoder.cpp index e3b4a1ee0..6a2708d07 100644 --- a/src/torchcodec/_core/Encoder.cpp +++ b/src/torchcodec/_core/Encoder.cpp @@ -45,7 +45,6 @@ void validateSampleRate(const AVCodec& avCodec, int sampleRate) { &numSampleRates); if (ret < 0 || supportedSampleRates == nullptr) { TORCH_CHECK(false, "Couldn't get supported sample rates from encoder."); - return; } #else supportedSampleRates = avCodec.supported_samplerates; From 2c4863c22a91f8f3911fec7d3e66ffb83b4ba324 Mon Sep 17 00:00:00 2001 From: Daniel Flores Date: Thu, 18 Sep 2025 09:59:25 -0400 Subject: [PATCH 5/6] move ifdef into ffmpegcommon --- src/torchcodec/_core/Encoder.cpp | 36 ++---------------------- src/torchcodec/_core/FFMPEGCommon.cpp | 40 +++++++++++++++++++++++++++ src/torchcodec/_core/FFMPEGCommon.h | 3 ++ 3 files changed, 46 insertions(+), 33 deletions(-) diff --git a/src/torchcodec/_core/Encoder.cpp b/src/torchcodec/_core/Encoder.cpp index 6a2708d07..8c2ce85e1 100644 --- a/src/torchcodec/_core/Encoder.cpp +++ b/src/torchcodec/_core/Encoder.cpp @@ -33,23 +33,7 @@ torch::Tensor validateSamples(const torch::Tensor& samples) { } void validateSampleRate(const AVCodec& avCodec, int sampleRate) { - const int* supportedSampleRates = nullptr; -#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61, 13, 100) - int numSampleRates = 0; - int ret = avcodec_get_supported_config( - nullptr, - &avCodec, - AV_CODEC_CONFIG_SAMPLE_RATE, - 0, - (const void**)&supportedSampleRates, - &numSampleRates); - if (ret < 0 || supportedSampleRates == nullptr) { - TORCH_CHECK(false, "Couldn't get supported sample rates from encoder."); - } -#else - supportedSampleRates = avCodec.supported_samplerates; -#endif - + const int* supportedSampleRates = getSupportedSampleRates(avCodec); if (supportedSampleRates == nullptr) { return; } @@ -90,22 +74,8 @@ static const std::vector preferredFormatsOrder = { AV_SAMPLE_FMT_U8}; AVSampleFormat findBestOutputSampleFormat(const AVCodec& avCodec) { - const AVSampleFormat* supportedSampleFormats = nullptr; -#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61, 13, 100) // FFmpeg >= 7 - int numSampleFormats = 0; - int ret = avcodec_get_supported_config( - nullptr, - &avCodec, - AV_CODEC_CONFIG_SAMPLE_FORMAT, - 0, - (const void**)&supportedSampleFormats, - &numSampleFormats); - if (ret < 0 || supportedSampleFormats == nullptr) { - TORCH_CHECK(false, "Couldn't get supported sample formats from encoder."); - } -#else - supportedSampleFormats = avCodec.sample_fmts; -#endif + const AVSampleFormat* supportedSampleFormats = + getSupportedOutputSampleFormats(avCodec); // Find a sample format that the encoder supports. We prefer using FLT[P], // since this is the format of the input samples. If FLTP isn't supported diff --git a/src/torchcodec/_core/FFMPEGCommon.cpp b/src/torchcodec/_core/FFMPEGCommon.cpp index 0e99c314c..e21fcd691 100644 --- a/src/torchcodec/_core/FFMPEGCommon.cpp +++ b/src/torchcodec/_core/FFMPEGCommon.cpp @@ -56,6 +56,46 @@ int64_t getDuration(const UniqueAVFrame& avFrame) { #endif } +const int* getSupportedSampleRates(const AVCodec& avCodec) { + const int* supportedSampleRates = nullptr; +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61, 13, 100) + int numSampleRates = 0; + int ret = avcodec_get_supported_config( + nullptr, + &avCodec, + AV_CODEC_CONFIG_SAMPLE_RATE, + 0, + (const void**)&supportedSampleRates, + &numSampleRates); + if (ret < 0 || supportedSampleRates == nullptr) { + TORCH_CHECK(false, "Couldn't get supported sample rates from encoder."); + } +#else + supportedSampleRates = avCodec.supported_samplerates; +#endif + return supportedSampleRates; +} + +const AVSampleFormat* getSupportedOutputSampleFormats(const AVCodec& avCodec) { + const AVSampleFormat* supportedSampleFormats = nullptr; +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61, 13, 100) // FFmpeg >= 7 + int numSampleFormats = 0; + int ret = avcodec_get_supported_config( + nullptr, + &avCodec, + AV_CODEC_CONFIG_SAMPLE_FORMAT, + 0, + (const void**)&supportedSampleFormats, + &numSampleFormats); + if (ret < 0 || supportedSampleFormats == nullptr) { + TORCH_CHECK(false, "Couldn't get supported sample formats from encoder."); + } +#else + supportedSampleFormats = avCodec.sample_fmts; +#endif + return supportedSampleFormats; +} + int getNumChannels(const UniqueAVFrame& avFrame) { #if LIBAVFILTER_VERSION_MAJOR > 8 || \ (LIBAVFILTER_VERSION_MAJOR == 8 && LIBAVFILTER_VERSION_MINOR >= 44) diff --git a/src/torchcodec/_core/FFMPEGCommon.h b/src/torchcodec/_core/FFMPEGCommon.h index b8c9e621c..179c7464b 100644 --- a/src/torchcodec/_core/FFMPEGCommon.h +++ b/src/torchcodec/_core/FFMPEGCommon.h @@ -162,6 +162,9 @@ std::string getFFMPEGErrorStringFromErrorCode(int errorCode); // support. int64_t getDuration(const UniqueAVFrame& frame); +const int* getSupportedSampleRates(const AVCodec& avCodec); +const AVSampleFormat* getSupportedOutputSampleFormats(const AVCodec& avCodec); + int getNumChannels(const UniqueAVFrame& avFrame); int getNumChannels(const UniqueAVCodecContext& avCodecContext); From 8c63eceee8154dd6ffd8863bf5dbfdd5ddd1e516 Mon Sep 17 00:00:00 2001 From: Daniel Flores Date: Thu, 18 Sep 2025 14:49:54 -0400 Subject: [PATCH 6/6] use reinterpret_cast --- src/torchcodec/_core/FFMPEGCommon.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/torchcodec/_core/FFMPEGCommon.cpp b/src/torchcodec/_core/FFMPEGCommon.cpp index e21fcd691..f0143ddd3 100644 --- a/src/torchcodec/_core/FFMPEGCommon.cpp +++ b/src/torchcodec/_core/FFMPEGCommon.cpp @@ -65,7 +65,7 @@ const int* getSupportedSampleRates(const AVCodec& avCodec) { &avCodec, AV_CODEC_CONFIG_SAMPLE_RATE, 0, - (const void**)&supportedSampleRates, + reinterpret_cast & supportedSampleRates, &numSampleRates); if (ret < 0 || supportedSampleRates == nullptr) { TORCH_CHECK(false, "Couldn't get supported sample rates from encoder."); @@ -85,7 +85,7 @@ const AVSampleFormat* getSupportedOutputSampleFormats(const AVCodec& avCodec) { &avCodec, AV_CODEC_CONFIG_SAMPLE_FORMAT, 0, - (const void**)&supportedSampleFormats, + reinterpret_cast & supportedSampleFormats, &numSampleFormats); if (ret < 0 || supportedSampleFormats == nullptr) { TORCH_CHECK(false, "Couldn't get supported sample formats from encoder."); @@ -158,7 +158,7 @@ void validateNumChannels(const AVCodec& avCodec, int numChannels) { &avCodec, AV_CODEC_CONFIG_CHANNEL_LAYOUT, 0, - (const void**)&supported_layouts, + reinterpret_cast & supported_layouts, &num_layouts); if (ret < 0 || supported_layouts == nullptr) { TORCH_CHECK(false, "Couldn't get supported channel layouts from encoder.");