diff --git a/src/torchcodec/_core/CMakeLists.txt b/src/torchcodec/_core/CMakeLists.txt index ada56bdc8..ab6583526 100644 --- a/src/torchcodec/_core/CMakeLists.txt +++ b/src/torchcodec/_core/CMakeLists.txt @@ -304,6 +304,8 @@ else() set(ffmpeg_major_version "6") elseif (${libavcodec_major_version} STREQUAL "61") set(ffmpeg_major_version "7") + elseif (${libavcodec_major_version} STREQUAL "62") + set(ffmpeg_major_version "8") else() message( FATAL_ERROR diff --git a/src/torchcodec/_core/FFMPEGCommon.cpp b/src/torchcodec/_core/FFMPEGCommon.cpp index 94c666d79..172bfeb76 100644 --- a/src/torchcodec/_core/FFMPEGCommon.cpp +++ b/src/torchcodec/_core/FFMPEGCommon.cpp @@ -8,6 +8,11 @@ #include +extern "C" { +#include +#include +} + namespace facebook::torchcodec { AutoAVPacket::AutoAVPacket() : avPacket_(av_packet_alloc()) { @@ -374,6 +379,70 @@ SwrContext* createSwrContext( return swrContext; } +AVFilterContext* createBuffersinkFilter( + AVFilterGraph* filterGraph, + enum AVPixelFormat outputFormat) { + const AVFilter* buffersink = avfilter_get_by_name("buffersink"); + TORCH_CHECK(buffersink != nullptr, "Failed to get buffersink filter."); + + AVFilterContext* sinkContext = nullptr; + int status; + const char* filterName = "out"; + + enum AVPixelFormat pix_fmts[] = {outputFormat, AV_PIX_FMT_NONE}; + +// av_opt_set_int_list was replaced by av_opt_set_array() in FFmpeg 8. +#if LIBAVUTIL_VERSION_MAJOR >= 60 // FFmpeg >= 8 + // Output options like pixel_formats must be set before filter init + sinkContext = + avfilter_graph_alloc_filter(filterGraph, buffersink, filterName); + TORCH_CHECK( + sinkContext != nullptr, "Failed to allocate buffersink filter context."); + + // When setting pix_fmts, only the first element is used, so nb_elems = 1 + // AV_PIX_FMT_NONE acts as a terminator for the array in av_opt_set_int_list + status = av_opt_set_array( + sinkContext, + "pixel_formats", + AV_OPT_SEARCH_CHILDREN, + 0, // start_elem + 1, // nb_elems + AV_OPT_TYPE_PIXEL_FMT, + pix_fmts); + TORCH_CHECK( + status >= 0, + "Failed to set pixel format for buffersink filter: ", + getFFMPEGErrorStringFromErrorCode(status)); + + status = avfilter_init_str(sinkContext, nullptr); + TORCH_CHECK( + status >= 0, + "Failed to initialize buffersink filter: ", + getFFMPEGErrorStringFromErrorCode(status)); +#else // FFmpeg <= 7 + // For older FFmpeg versions, create filter and then set options + status = avfilter_graph_create_filter( + &sinkContext, buffersink, filterName, nullptr, nullptr, filterGraph); + TORCH_CHECK( + status >= 0, + "Failed to create buffersink filter: ", + getFFMPEGErrorStringFromErrorCode(status)); + + status = av_opt_set_int_list( + sinkContext, + "pix_fmts", + pix_fmts, + AV_PIX_FMT_NONE, + AV_OPT_SEARCH_CHILDREN); + TORCH_CHECK( + status >= 0, + "Failed to set pixel formats for buffersink filter: ", + getFFMPEGErrorStringFromErrorCode(status)); +#endif + + return sinkContext; +} + UniqueAVFrame convertAudioAVFrameSamples( const UniqueSwrContext& swrContext, const UniqueAVFrame& srcAVFrame, diff --git a/src/torchcodec/_core/FFMPEGCommon.h b/src/torchcodec/_core/FFMPEGCommon.h index 58589fe60..92a262d26 100644 --- a/src/torchcodec/_core/FFMPEGCommon.h +++ b/src/torchcodec/_core/FFMPEGCommon.h @@ -237,4 +237,8 @@ int64_t computeSafeDuration( const AVRational& frameRate, const AVRational& timeBase); +AVFilterContext* createBuffersinkFilter( + AVFilterGraph* filterGraph, + enum AVPixelFormat outputFormat); + } // namespace facebook::torchcodec diff --git a/src/torchcodec/_core/FilterGraph.cpp b/src/torchcodec/_core/FilterGraph.cpp index 70bc2b5dd..afc44d96d 100644 --- a/src/torchcodec/_core/FilterGraph.cpp +++ b/src/torchcodec/_core/FilterGraph.cpp @@ -5,6 +5,7 @@ // LICENSE file in the root directory of this source tree. #include "src/torchcodec/_core/FilterGraph.h" +#include "src/torchcodec/_core/FFMPEGCommon.h" extern "C" { #include @@ -63,7 +64,6 @@ FilterGraph::FilterGraph( } const AVFilter* buffersrc = avfilter_get_by_name("buffer"); - const AVFilter* buffersink = avfilter_get_by_name("buffersink"); UniqueAVBufferSrcParameters srcParams(av_buffersrc_parameters_alloc()); TORCH_CHECK(srcParams, "Failed to allocate buffersrc params"); @@ -93,26 +93,10 @@ FilterGraph::FilterGraph( "Failed to create filter graph : ", getFFMPEGErrorStringFromErrorCode(status)); - status = avfilter_graph_create_filter( - &sinkContext_, buffersink, "out", nullptr, nullptr, filterGraph_.get()); + sinkContext_ = + createBuffersinkFilter(filterGraph_.get(), filtersContext.outputFormat); TORCH_CHECK( - status >= 0, - "Failed to create filter graph: ", - getFFMPEGErrorStringFromErrorCode(status)); - - enum AVPixelFormat pix_fmts[] = { - filtersContext.outputFormat, AV_PIX_FMT_NONE}; - - status = av_opt_set_int_list( - sinkContext_, - "pix_fmts", - pix_fmts, - AV_PIX_FMT_NONE, - AV_OPT_SEARCH_CHILDREN); - TORCH_CHECK( - status >= 0, - "Failed to set output pixel formats: ", - getFFMPEGErrorStringFromErrorCode(status)); + sinkContext_ != nullptr, "Failed to create and configure buffersink"); UniqueAVFilterInOut outputs(avfilter_inout_alloc()); UniqueAVFilterInOut inputs(avfilter_inout_alloc()); diff --git a/src/torchcodec/_core/ops.py b/src/torchcodec/_core/ops.py index 10059da88..b7fff11fc 100644 --- a/src/torchcodec/_core/ops.py +++ b/src/torchcodec/_core/ops.py @@ -41,7 +41,7 @@ def load_torchcodec_shared_libraries(): # libraries do not meet those conditions. exceptions = [] - for ffmpeg_major_version in (7, 6, 5, 4): + for ffmpeg_major_version in (8, 7, 6, 5, 4): pybind_ops_module_name = _get_pybind_ops_module_name(ffmpeg_major_version) decoder_library_name = f"libtorchcodec_core{ffmpeg_major_version}" custom_ops_library_name = f"libtorchcodec_custom_ops{ffmpeg_major_version}"