torchcodec 0.7.0__cp312-cp312-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of torchcodec might be problematic. Click here for more details.
- torchcodec/__init__.py +16 -0
- torchcodec/_core/AVIOContextHolder.cpp +60 -0
- torchcodec/_core/AVIOContextHolder.h +64 -0
- torchcodec/_core/AVIOFileLikeContext.cpp +98 -0
- torchcodec/_core/AVIOFileLikeContext.h +55 -0
- torchcodec/_core/AVIOTensorContext.cpp +123 -0
- torchcodec/_core/AVIOTensorContext.h +43 -0
- torchcodec/_core/CMakeLists.txt +292 -0
- torchcodec/_core/Cache.h +138 -0
- torchcodec/_core/CpuDeviceInterface.cpp +266 -0
- torchcodec/_core/CpuDeviceInterface.h +70 -0
- torchcodec/_core/CudaDeviceInterface.cpp +514 -0
- torchcodec/_core/CudaDeviceInterface.h +37 -0
- torchcodec/_core/DeviceInterface.cpp +79 -0
- torchcodec/_core/DeviceInterface.h +67 -0
- torchcodec/_core/Encoder.cpp +514 -0
- torchcodec/_core/Encoder.h +123 -0
- torchcodec/_core/FFMPEGCommon.cpp +421 -0
- torchcodec/_core/FFMPEGCommon.h +227 -0
- torchcodec/_core/FilterGraph.cpp +142 -0
- torchcodec/_core/FilterGraph.h +45 -0
- torchcodec/_core/Frame.cpp +32 -0
- torchcodec/_core/Frame.h +118 -0
- torchcodec/_core/Metadata.h +72 -0
- torchcodec/_core/SingleStreamDecoder.cpp +1715 -0
- torchcodec/_core/SingleStreamDecoder.h +380 -0
- torchcodec/_core/StreamOptions.h +53 -0
- torchcodec/_core/ValidationUtils.cpp +35 -0
- torchcodec/_core/ValidationUtils.h +21 -0
- torchcodec/_core/__init__.py +40 -0
- torchcodec/_core/_metadata.py +317 -0
- torchcodec/_core/custom_ops.cpp +727 -0
- torchcodec/_core/fetch_and_expose_non_gpl_ffmpeg_libs.cmake +300 -0
- torchcodec/_core/ops.py +455 -0
- torchcodec/_core/pybind_ops.cpp +87 -0
- torchcodec/_frame.py +145 -0
- torchcodec/_internally_replaced_utils.py +67 -0
- torchcodec/_samplers/__init__.py +7 -0
- torchcodec/_samplers/video_clip_sampler.py +430 -0
- torchcodec/decoders/__init__.py +11 -0
- torchcodec/decoders/_audio_decoder.py +177 -0
- torchcodec/decoders/_decoder_utils.py +52 -0
- torchcodec/decoders/_video_decoder.py +464 -0
- torchcodec/encoders/__init__.py +1 -0
- torchcodec/encoders/_audio_encoder.py +150 -0
- torchcodec/libtorchcodec_core4.dll +0 -0
- torchcodec/libtorchcodec_core5.dll +0 -0
- torchcodec/libtorchcodec_core6.dll +0 -0
- torchcodec/libtorchcodec_core7.dll +0 -0
- torchcodec/libtorchcodec_custom_ops4.dll +0 -0
- torchcodec/libtorchcodec_custom_ops5.dll +0 -0
- torchcodec/libtorchcodec_custom_ops6.dll +0 -0
- torchcodec/libtorchcodec_custom_ops7.dll +0 -0
- torchcodec/libtorchcodec_pybind_ops4.pyd +0 -0
- torchcodec/libtorchcodec_pybind_ops5.pyd +0 -0
- torchcodec/libtorchcodec_pybind_ops6.pyd +0 -0
- torchcodec/libtorchcodec_pybind_ops7.pyd +0 -0
- torchcodec/samplers/__init__.py +2 -0
- torchcodec/samplers/_common.py +84 -0
- torchcodec/samplers/_index_based.py +287 -0
- torchcodec/samplers/_time_based.py +350 -0
- torchcodec/version.py +2 -0
- torchcodec-0.7.0.dist-info/METADATA +242 -0
- torchcodec-0.7.0.dist-info/RECORD +67 -0
- torchcodec-0.7.0.dist-info/WHEEL +5 -0
- torchcodec-0.7.0.dist-info/licenses/LICENSE +28 -0
- torchcodec-0.7.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
// All rights reserved.
|
|
3
|
+
//
|
|
4
|
+
// This source code is licensed under the BSD-style license found in the
|
|
5
|
+
// LICENSE file in the root directory of this source tree.
|
|
6
|
+
|
|
7
|
+
#pragma once
|
|
8
|
+
|
|
9
|
+
#include <memory>
|
|
10
|
+
#include <stdexcept>
|
|
11
|
+
#include <string>
|
|
12
|
+
|
|
13
|
+
extern "C" {
|
|
14
|
+
#include <libavcodec/avcodec.h>
|
|
15
|
+
#include <libavfilter/avfilter.h>
|
|
16
|
+
#include <libavfilter/buffersrc.h>
|
|
17
|
+
#include <libavformat/avformat.h>
|
|
18
|
+
#include <libavformat/avio.h>
|
|
19
|
+
#include <libavutil/audio_fifo.h>
|
|
20
|
+
#include <libavutil/avutil.h>
|
|
21
|
+
#include <libavutil/dict.h>
|
|
22
|
+
#include <libavutil/display.h>
|
|
23
|
+
#include <libavutil/file.h>
|
|
24
|
+
#include <libavutil/opt.h>
|
|
25
|
+
#include <libavutil/pixfmt.h>
|
|
26
|
+
#include <libavutil/version.h>
|
|
27
|
+
#include <libswresample/swresample.h>
|
|
28
|
+
#include <libswscale/swscale.h>
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
namespace facebook::torchcodec {
|
|
32
|
+
|
|
33
|
+
// FFMPEG uses special delete functions for some structures. These template
|
|
34
|
+
// functions are used to pass into unique_ptr as custom deleters so we can
|
|
35
|
+
// wrap FFMPEG structs with unique_ptrs for ease of use.
|
|
36
|
+
template <typename T, typename R, R (*Fn)(T**)>
|
|
37
|
+
struct Deleterp {
|
|
38
|
+
inline void operator()(T* p) const {
|
|
39
|
+
if (p) {
|
|
40
|
+
Fn(&p);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
template <typename T, typename R, R (*Fn)(void*)>
|
|
46
|
+
struct Deleterv {
|
|
47
|
+
inline void operator()(T* p) const {
|
|
48
|
+
if (p) {
|
|
49
|
+
Fn(&p);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
template <typename T, typename R, R (*Fn)(T*)>
|
|
55
|
+
struct Deleter {
|
|
56
|
+
inline void operator()(T* p) const {
|
|
57
|
+
if (p) {
|
|
58
|
+
Fn(p);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Unique pointers for FFMPEG structures.
|
|
64
|
+
using UniqueDecodingAVFormatContext = std::unique_ptr<
|
|
65
|
+
AVFormatContext,
|
|
66
|
+
Deleterp<AVFormatContext, void, avformat_close_input>>;
|
|
67
|
+
using UniqueEncodingAVFormatContext = std::unique_ptr<
|
|
68
|
+
AVFormatContext,
|
|
69
|
+
Deleter<AVFormatContext, void, avformat_free_context>>;
|
|
70
|
+
using UniqueAVCodecContext = std::unique_ptr<
|
|
71
|
+
AVCodecContext,
|
|
72
|
+
Deleterp<AVCodecContext, void, avcodec_free_context>>;
|
|
73
|
+
using UniqueAVFrame =
|
|
74
|
+
std::unique_ptr<AVFrame, Deleterp<AVFrame, void, av_frame_free>>;
|
|
75
|
+
using UniqueAVFilterGraph = std::unique_ptr<
|
|
76
|
+
AVFilterGraph,
|
|
77
|
+
Deleterp<AVFilterGraph, void, avfilter_graph_free>>;
|
|
78
|
+
using UniqueAVFilterInOut = std::unique_ptr<
|
|
79
|
+
AVFilterInOut,
|
|
80
|
+
Deleterp<AVFilterInOut, void, avfilter_inout_free>>;
|
|
81
|
+
using UniqueAVIOContext = std::
|
|
82
|
+
unique_ptr<AVIOContext, Deleterp<AVIOContext, void, avio_context_free>>;
|
|
83
|
+
using UniqueSwsContext =
|
|
84
|
+
std::unique_ptr<SwsContext, Deleter<SwsContext, void, sws_freeContext>>;
|
|
85
|
+
using UniqueSwrContext =
|
|
86
|
+
std::unique_ptr<SwrContext, Deleterp<SwrContext, void, swr_free>>;
|
|
87
|
+
using UniqueAVAudioFifo = std::
|
|
88
|
+
unique_ptr<AVAudioFifo, Deleter<AVAudioFifo, void, av_audio_fifo_free>>;
|
|
89
|
+
using UniqueAVBufferRef =
|
|
90
|
+
std::unique_ptr<AVBufferRef, Deleterp<AVBufferRef, void, av_buffer_unref>>;
|
|
91
|
+
using UniqueAVBufferSrcParameters = std::unique_ptr<
|
|
92
|
+
AVBufferSrcParameters,
|
|
93
|
+
Deleterv<AVBufferSrcParameters, void, av_freep>>;
|
|
94
|
+
|
|
95
|
+
// These 2 classes share the same underlying AVPacket object. They are meant to
|
|
96
|
+
// be used in tandem, like so:
|
|
97
|
+
//
|
|
98
|
+
// AutoAVPacket autoAVPacket; // <-- malloc for AVPacket happens here
|
|
99
|
+
// while(...){
|
|
100
|
+
// ReferenceAVPacket packet(autoAVPacket);
|
|
101
|
+
// av_read_frame(..., packet.get()); <-- av_packet_ref() called by FFmpeg
|
|
102
|
+
// } <-- av_packet_unref() called here
|
|
103
|
+
//
|
|
104
|
+
// This achieves a few desirable things:
|
|
105
|
+
// - Memory allocation of the underlying AVPacket happens only once, when
|
|
106
|
+
// autoAVPacket is created.
|
|
107
|
+
// - av_packet_free() is called when autoAVPacket gets out of scope
|
|
108
|
+
// - av_packet_unref() is automatically called when needed, i.e. at the end of
|
|
109
|
+
// each loop iteration (or when hitting break / continue). This prevents the
|
|
110
|
+
// risk of us forgetting to call it.
|
|
111
|
+
class AutoAVPacket {
|
|
112
|
+
friend class ReferenceAVPacket;
|
|
113
|
+
|
|
114
|
+
private:
|
|
115
|
+
AVPacket* avPacket_;
|
|
116
|
+
|
|
117
|
+
public:
|
|
118
|
+
AutoAVPacket();
|
|
119
|
+
AutoAVPacket(const AutoAVPacket& other) = delete;
|
|
120
|
+
AutoAVPacket& operator=(const AutoAVPacket& other) = delete;
|
|
121
|
+
~AutoAVPacket();
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
class ReferenceAVPacket {
|
|
125
|
+
private:
|
|
126
|
+
AVPacket* avPacket_;
|
|
127
|
+
|
|
128
|
+
public:
|
|
129
|
+
explicit ReferenceAVPacket(AutoAVPacket& shared);
|
|
130
|
+
ReferenceAVPacket(const ReferenceAVPacket& other) = delete;
|
|
131
|
+
ReferenceAVPacket& operator=(const ReferenceAVPacket& other) = delete;
|
|
132
|
+
~ReferenceAVPacket();
|
|
133
|
+
AVPacket* get();
|
|
134
|
+
AVPacket* operator->();
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// av_find_best_stream is not const-correct before commit:
|
|
138
|
+
// https://github.com/FFmpeg/FFmpeg/commit/46dac8cf3d250184ab4247809bc03f60e14f4c0c
|
|
139
|
+
// which was released in FFMPEG version=5.0.3
|
|
140
|
+
// with libavcodec's version=59.18.100
|
|
141
|
+
// (https://www.ffmpeg.org/olddownload.html).
|
|
142
|
+
// Note that the alias is so-named so that it is only used when interacting with
|
|
143
|
+
// av_find_best_stream(). It is not needed elsewhere.
|
|
144
|
+
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 18, 100)
|
|
145
|
+
using AVCodecOnlyUseForCallingAVFindBestStream = AVCodec*;
|
|
146
|
+
#else
|
|
147
|
+
using AVCodecOnlyUseForCallingAVFindBestStream = const AVCodec*;
|
|
148
|
+
#endif
|
|
149
|
+
|
|
150
|
+
AVCodecOnlyUseForCallingAVFindBestStream
|
|
151
|
+
makeAVCodecOnlyUseForCallingAVFindBestStream(const AVCodec* codec);
|
|
152
|
+
|
|
153
|
+
// Success code from FFMPEG is just a 0. We define it to make the code more
|
|
154
|
+
// readable.
|
|
155
|
+
const int AVSUCCESS = 0;
|
|
156
|
+
|
|
157
|
+
// Returns the FFMPEG error as a string using the provided `errorCode`.
|
|
158
|
+
std::string getFFMPEGErrorStringFromErrorCode(int errorCode);
|
|
159
|
+
|
|
160
|
+
// Returns duration from the frame. Abstracted into a function because the
|
|
161
|
+
// struct member representing duration has changed across the versions we
|
|
162
|
+
// support.
|
|
163
|
+
int64_t getDuration(const UniqueAVFrame& frame);
|
|
164
|
+
|
|
165
|
+
int getNumChannels(const UniqueAVFrame& avFrame);
|
|
166
|
+
int getNumChannels(const UniqueAVCodecContext& avCodecContext);
|
|
167
|
+
|
|
168
|
+
void setDefaultChannelLayout(
|
|
169
|
+
UniqueAVCodecContext& avCodecContext,
|
|
170
|
+
int numChannels);
|
|
171
|
+
|
|
172
|
+
void setDefaultChannelLayout(UniqueAVFrame& avFrame, int numChannels);
|
|
173
|
+
|
|
174
|
+
void validateNumChannels(const AVCodec& avCodec, int numChannels);
|
|
175
|
+
|
|
176
|
+
void setChannelLayout(
|
|
177
|
+
UniqueAVFrame& dstAVFrame,
|
|
178
|
+
const UniqueAVFrame& srcAVFrame,
|
|
179
|
+
int desiredNumChannels);
|
|
180
|
+
|
|
181
|
+
UniqueAVFrame allocateAVFrame(
|
|
182
|
+
int numSamples,
|
|
183
|
+
int sampleRate,
|
|
184
|
+
int numChannels,
|
|
185
|
+
AVSampleFormat sampleFormat);
|
|
186
|
+
|
|
187
|
+
SwrContext* createSwrContext(
|
|
188
|
+
AVSampleFormat srcSampleFormat,
|
|
189
|
+
AVSampleFormat desiredSampleFormat,
|
|
190
|
+
int srcSampleRate,
|
|
191
|
+
int desiredSampleRate,
|
|
192
|
+
const UniqueAVFrame& srcAVFrame,
|
|
193
|
+
int desiredNumChannels);
|
|
194
|
+
|
|
195
|
+
// Converts, if needed:
|
|
196
|
+
// - sample format
|
|
197
|
+
// - sample rate
|
|
198
|
+
// - number of channels.
|
|
199
|
+
// createSwrContext must have been previously called with matching parameters.
|
|
200
|
+
UniqueAVFrame convertAudioAVFrameSamples(
|
|
201
|
+
const UniqueSwrContext& swrContext,
|
|
202
|
+
const UniqueAVFrame& srcAVFrame,
|
|
203
|
+
AVSampleFormat desiredSampleFormat,
|
|
204
|
+
int desiredSampleRate,
|
|
205
|
+
int desiredNumChannels);
|
|
206
|
+
|
|
207
|
+
// Returns true if sws_scale can handle unaligned data.
|
|
208
|
+
bool canSwsScaleHandleUnalignedData();
|
|
209
|
+
|
|
210
|
+
void setFFmpegLogLevel();
|
|
211
|
+
|
|
212
|
+
// These signatures are defined by FFmpeg.
|
|
213
|
+
using AVIOReadFunction = int (*)(void*, uint8_t*, int);
|
|
214
|
+
using AVIOWriteFunction = int (*)(void*, const uint8_t*, int); // FFmpeg >= 7
|
|
215
|
+
using AVIOWriteFunctionOld = int (*)(void*, uint8_t*, int); // FFmpeg < 7
|
|
216
|
+
using AVIOSeekFunction = int64_t (*)(void*, int64_t, int);
|
|
217
|
+
|
|
218
|
+
AVIOContext* avioAllocContext(
|
|
219
|
+
uint8_t* buffer,
|
|
220
|
+
int buffer_size,
|
|
221
|
+
int write_flag,
|
|
222
|
+
void* opaque,
|
|
223
|
+
AVIOReadFunction read_packet,
|
|
224
|
+
AVIOWriteFunction write_packet,
|
|
225
|
+
AVIOSeekFunction seek);
|
|
226
|
+
|
|
227
|
+
} // namespace facebook::torchcodec
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
// All rights reserved.
|
|
3
|
+
//
|
|
4
|
+
// This source code is licensed under the BSD-style license found in the
|
|
5
|
+
// LICENSE file in the root directory of this source tree.
|
|
6
|
+
|
|
7
|
+
#include "src/torchcodec/_core/FilterGraph.h"
|
|
8
|
+
|
|
9
|
+
extern "C" {
|
|
10
|
+
#include <libavfilter/buffersink.h>
|
|
11
|
+
#include <libavfilter/buffersrc.h>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
namespace facebook::torchcodec {
|
|
15
|
+
|
|
16
|
+
bool operator==(const AVRational& lhs, const AVRational& rhs) {
|
|
17
|
+
return lhs.num == rhs.num && lhs.den == rhs.den;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
bool FiltersContext::operator==(const FiltersContext& other) const {
|
|
21
|
+
return inputWidth == other.inputWidth && inputHeight == other.inputHeight &&
|
|
22
|
+
inputFormat == other.inputFormat && outputWidth == other.outputWidth &&
|
|
23
|
+
outputHeight == other.outputHeight &&
|
|
24
|
+
outputFormat == other.outputFormat &&
|
|
25
|
+
filtergraphStr == other.filtergraphStr && timeBase == other.timeBase &&
|
|
26
|
+
hwFramesCtx.get() == other.hwFramesCtx.get();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
bool FiltersContext::operator!=(const FiltersContext& other) const {
|
|
30
|
+
return !(*this == other);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
FilterGraph::FilterGraph(
|
|
34
|
+
const FiltersContext& filtersContext,
|
|
35
|
+
const VideoStreamOptions& videoStreamOptions) {
|
|
36
|
+
filterGraph_.reset(avfilter_graph_alloc());
|
|
37
|
+
TORCH_CHECK(filterGraph_.get() != nullptr);
|
|
38
|
+
|
|
39
|
+
if (videoStreamOptions.ffmpegThreadCount.has_value()) {
|
|
40
|
+
filterGraph_->nb_threads = videoStreamOptions.ffmpegThreadCount.value();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const AVFilter* buffersrc = avfilter_get_by_name("buffer");
|
|
44
|
+
const AVFilter* buffersink = avfilter_get_by_name("buffersink");
|
|
45
|
+
|
|
46
|
+
UniqueAVBufferSrcParameters srcParams(av_buffersrc_parameters_alloc());
|
|
47
|
+
TORCH_CHECK(srcParams, "Failed to allocate buffersrc params");
|
|
48
|
+
|
|
49
|
+
srcParams->format = filtersContext.inputFormat;
|
|
50
|
+
srcParams->width = filtersContext.inputWidth;
|
|
51
|
+
srcParams->height = filtersContext.inputHeight;
|
|
52
|
+
srcParams->sample_aspect_ratio = filtersContext.inputAspectRatio;
|
|
53
|
+
srcParams->time_base = filtersContext.timeBase;
|
|
54
|
+
if (filtersContext.hwFramesCtx) {
|
|
55
|
+
srcParams->hw_frames_ctx = av_buffer_ref(filtersContext.hwFramesCtx.get());
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
sourceContext_ =
|
|
59
|
+
avfilter_graph_alloc_filter(filterGraph_.get(), buffersrc, "in");
|
|
60
|
+
TORCH_CHECK(sourceContext_, "Failed to allocate filter graph");
|
|
61
|
+
|
|
62
|
+
int status = av_buffersrc_parameters_set(sourceContext_, srcParams.get());
|
|
63
|
+
TORCH_CHECK(
|
|
64
|
+
status >= 0,
|
|
65
|
+
"Failed to create filter graph: ",
|
|
66
|
+
getFFMPEGErrorStringFromErrorCode(status));
|
|
67
|
+
|
|
68
|
+
status = avfilter_init_str(sourceContext_, nullptr);
|
|
69
|
+
TORCH_CHECK(
|
|
70
|
+
status >= 0,
|
|
71
|
+
"Failed to create filter graph : ",
|
|
72
|
+
getFFMPEGErrorStringFromErrorCode(status));
|
|
73
|
+
|
|
74
|
+
status = avfilter_graph_create_filter(
|
|
75
|
+
&sinkContext_, buffersink, "out", nullptr, nullptr, filterGraph_.get());
|
|
76
|
+
TORCH_CHECK(
|
|
77
|
+
status >= 0,
|
|
78
|
+
"Failed to create filter graph: ",
|
|
79
|
+
getFFMPEGErrorStringFromErrorCode(status));
|
|
80
|
+
|
|
81
|
+
enum AVPixelFormat pix_fmts[] = {
|
|
82
|
+
filtersContext.outputFormat, AV_PIX_FMT_NONE};
|
|
83
|
+
|
|
84
|
+
status = av_opt_set_int_list(
|
|
85
|
+
sinkContext_,
|
|
86
|
+
"pix_fmts",
|
|
87
|
+
pix_fmts,
|
|
88
|
+
AV_PIX_FMT_NONE,
|
|
89
|
+
AV_OPT_SEARCH_CHILDREN);
|
|
90
|
+
TORCH_CHECK(
|
|
91
|
+
status >= 0,
|
|
92
|
+
"Failed to set output pixel formats: ",
|
|
93
|
+
getFFMPEGErrorStringFromErrorCode(status));
|
|
94
|
+
|
|
95
|
+
UniqueAVFilterInOut outputs(avfilter_inout_alloc());
|
|
96
|
+
UniqueAVFilterInOut inputs(avfilter_inout_alloc());
|
|
97
|
+
|
|
98
|
+
outputs->name = av_strdup("in");
|
|
99
|
+
outputs->filter_ctx = sourceContext_;
|
|
100
|
+
outputs->pad_idx = 0;
|
|
101
|
+
outputs->next = nullptr;
|
|
102
|
+
inputs->name = av_strdup("out");
|
|
103
|
+
inputs->filter_ctx = sinkContext_;
|
|
104
|
+
inputs->pad_idx = 0;
|
|
105
|
+
inputs->next = nullptr;
|
|
106
|
+
|
|
107
|
+
AVFilterInOut* outputsTmp = outputs.release();
|
|
108
|
+
AVFilterInOut* inputsTmp = inputs.release();
|
|
109
|
+
status = avfilter_graph_parse_ptr(
|
|
110
|
+
filterGraph_.get(),
|
|
111
|
+
filtersContext.filtergraphStr.c_str(),
|
|
112
|
+
&inputsTmp,
|
|
113
|
+
&outputsTmp,
|
|
114
|
+
nullptr);
|
|
115
|
+
outputs.reset(outputsTmp);
|
|
116
|
+
inputs.reset(inputsTmp);
|
|
117
|
+
TORCH_CHECK(
|
|
118
|
+
status >= 0,
|
|
119
|
+
"Failed to parse filter description: ",
|
|
120
|
+
getFFMPEGErrorStringFromErrorCode(status));
|
|
121
|
+
|
|
122
|
+
status = avfilter_graph_config(filterGraph_.get(), nullptr);
|
|
123
|
+
TORCH_CHECK(
|
|
124
|
+
status >= 0,
|
|
125
|
+
"Failed to configure filter graph: ",
|
|
126
|
+
getFFMPEGErrorStringFromErrorCode(status));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
UniqueAVFrame FilterGraph::convert(const UniqueAVFrame& avFrame) {
|
|
130
|
+
int status = av_buffersrc_write_frame(sourceContext_, avFrame.get());
|
|
131
|
+
TORCH_CHECK(
|
|
132
|
+
status >= AVSUCCESS, "Failed to add frame to buffer source context");
|
|
133
|
+
|
|
134
|
+
UniqueAVFrame filteredAVFrame(av_frame_alloc());
|
|
135
|
+
status = av_buffersink_get_frame(sinkContext_, filteredAVFrame.get());
|
|
136
|
+
TORCH_CHECK(
|
|
137
|
+
status >= AVSUCCESS, "Failed to get frame from buffer sink context");
|
|
138
|
+
|
|
139
|
+
return filteredAVFrame;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
} // namespace facebook::torchcodec
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
// All rights reserved.
|
|
3
|
+
//
|
|
4
|
+
// This source code is licensed under the BSD-style license found in the
|
|
5
|
+
// LICENSE file in the root directory of this source tree.
|
|
6
|
+
|
|
7
|
+
#pragma once
|
|
8
|
+
|
|
9
|
+
#include "src/torchcodec/_core/FFMPEGCommon.h"
|
|
10
|
+
#include "src/torchcodec/_core/StreamOptions.h"
|
|
11
|
+
|
|
12
|
+
namespace facebook::torchcodec {
|
|
13
|
+
|
|
14
|
+
struct FiltersContext {
|
|
15
|
+
int inputWidth = 0;
|
|
16
|
+
int inputHeight = 0;
|
|
17
|
+
AVPixelFormat inputFormat = AV_PIX_FMT_NONE;
|
|
18
|
+
AVRational inputAspectRatio = {0, 0};
|
|
19
|
+
int outputWidth = 0;
|
|
20
|
+
int outputHeight = 0;
|
|
21
|
+
AVPixelFormat outputFormat = AV_PIX_FMT_NONE;
|
|
22
|
+
|
|
23
|
+
std::string filtergraphStr;
|
|
24
|
+
AVRational timeBase = {0, 0};
|
|
25
|
+
UniqueAVBufferRef hwFramesCtx;
|
|
26
|
+
|
|
27
|
+
bool operator==(const FiltersContext&) const;
|
|
28
|
+
bool operator!=(const FiltersContext&) const;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
class FilterGraph {
|
|
32
|
+
public:
|
|
33
|
+
FilterGraph(
|
|
34
|
+
const FiltersContext& filtersContext,
|
|
35
|
+
const VideoStreamOptions& videoStreamOptions);
|
|
36
|
+
|
|
37
|
+
UniqueAVFrame convert(const UniqueAVFrame& avFrame);
|
|
38
|
+
|
|
39
|
+
private:
|
|
40
|
+
UniqueAVFilterGraph filterGraph_;
|
|
41
|
+
AVFilterContext* sourceContext_ = nullptr;
|
|
42
|
+
AVFilterContext* sinkContext_ = nullptr;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
} // namespace facebook::torchcodec
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
// All rights reserved.
|
|
3
|
+
//
|
|
4
|
+
// This source code is licensed under the BSD-style license found in the
|
|
5
|
+
// LICENSE file in the root directory of this source tree.
|
|
6
|
+
|
|
7
|
+
#include "src/torchcodec/_core/Frame.h"
|
|
8
|
+
|
|
9
|
+
namespace facebook::torchcodec {
|
|
10
|
+
|
|
11
|
+
torch::Tensor allocateEmptyHWCTensor(
|
|
12
|
+
int height,
|
|
13
|
+
int width,
|
|
14
|
+
torch::Device device,
|
|
15
|
+
std::optional<int> numFrames) {
|
|
16
|
+
auto tensorOptions = torch::TensorOptions()
|
|
17
|
+
.dtype(torch::kUInt8)
|
|
18
|
+
.layout(torch::kStrided)
|
|
19
|
+
.device(device);
|
|
20
|
+
TORCH_CHECK(height > 0, "height must be > 0, got: ", height);
|
|
21
|
+
TORCH_CHECK(width > 0, "width must be > 0, got: ", width);
|
|
22
|
+
if (numFrames.has_value()) {
|
|
23
|
+
auto numFramesValue = numFrames.value();
|
|
24
|
+
TORCH_CHECK(
|
|
25
|
+
numFramesValue >= 0, "numFrames must be >= 0, got: ", numFramesValue);
|
|
26
|
+
return torch::empty({numFramesValue, height, width, 3}, tensorOptions);
|
|
27
|
+
} else {
|
|
28
|
+
return torch::empty({height, width, 3}, tensorOptions);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
} // namespace facebook::torchcodec
|
torchcodec/_core/Frame.h
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
// All rights reserved.
|
|
3
|
+
//
|
|
4
|
+
// This source code is licensed under the BSD-style license found in the
|
|
5
|
+
// LICENSE file in the root directory of this source tree.
|
|
6
|
+
|
|
7
|
+
#pragma once
|
|
8
|
+
|
|
9
|
+
#include <torch/types.h>
|
|
10
|
+
#include "src/torchcodec/_core/FFMPEGCommon.h"
|
|
11
|
+
#include "src/torchcodec/_core/Metadata.h"
|
|
12
|
+
#include "src/torchcodec/_core/StreamOptions.h"
|
|
13
|
+
|
|
14
|
+
namespace facebook::torchcodec {
|
|
15
|
+
|
|
16
|
+
// All public video decoding entry points return either a FrameOutput or a
|
|
17
|
+
// FrameBatchOutput.
|
|
18
|
+
// They are the equivalent of the user-facing Frame and FrameBatch classes in
|
|
19
|
+
// Python. They contain RGB decoded frames along with some associated data
|
|
20
|
+
// like PTS and duration.
|
|
21
|
+
// FrameOutput is also relevant for audio decoding, typically as the output of
|
|
22
|
+
// getNextFrame(), or as a temporary output variable.
|
|
23
|
+
struct FrameOutput {
|
|
24
|
+
// data shape is:
|
|
25
|
+
// - 3D (C, H, W) or (H, W, C) for videos
|
|
26
|
+
// - 2D (numChannels, numSamples) for audio
|
|
27
|
+
torch::Tensor data;
|
|
28
|
+
double ptsSeconds;
|
|
29
|
+
double durationSeconds;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
struct FrameBatchOutput {
|
|
33
|
+
torch::Tensor data; // 4D: of shape NCHW or NHWC.
|
|
34
|
+
torch::Tensor ptsSeconds; // 1D of shape (N,)
|
|
35
|
+
torch::Tensor durationSeconds; // 1D of shape (N,)
|
|
36
|
+
|
|
37
|
+
explicit FrameBatchOutput(
|
|
38
|
+
int64_t numFrames,
|
|
39
|
+
const VideoStreamOptions& videoStreamOptions,
|
|
40
|
+
const StreamMetadata& streamMetadata);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
struct AudioFramesOutput {
|
|
44
|
+
torch::Tensor data; // shape is (numChannels, numSamples)
|
|
45
|
+
double ptsSeconds;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// --------------------------------------------------------------------------
|
|
49
|
+
// FRAME TENSOR ALLOCATION APIs
|
|
50
|
+
// --------------------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
// Note [Frame Tensor allocation and height and width]
|
|
53
|
+
//
|
|
54
|
+
// We always allocate [N]HWC tensors. The low-level decoding functions all
|
|
55
|
+
// assume HWC tensors, since this is what FFmpeg natively handles. It's up to
|
|
56
|
+
// the high-level decoding entry-points to permute that back to CHW, by calling
|
|
57
|
+
// maybePermuteHWC2CHW().
|
|
58
|
+
//
|
|
59
|
+
// Also, importantly, the way we figure out the the height and width of the
|
|
60
|
+
// output frame tensor varies, and depends on the decoding entry-point. In
|
|
61
|
+
// *decreasing order of accuracy*, we use the following sources for determining
|
|
62
|
+
// height and width:
|
|
63
|
+
// - getHeightAndWidthFromResizedAVFrame(). This is the height and width of the
|
|
64
|
+
// AVframe, *post*-resizing. This is only used for single-frame decoding APIs,
|
|
65
|
+
// on CPU, with filtergraph.
|
|
66
|
+
// - getHeightAndWidthFromOptionsOrAVFrame(). This is the height and width from
|
|
67
|
+
// the user-specified options if they exist, or the height and width of the
|
|
68
|
+
// AVFrame *before* it is resized. In theory, i.e. if there are no bugs within
|
|
69
|
+
// our code or within FFmpeg code, this should be exactly the same as
|
|
70
|
+
// getHeightAndWidthFromResizedAVFrame(). This is used by single-frame
|
|
71
|
+
// decoding APIs, on CPU with swscale, and on GPU.
|
|
72
|
+
// - getHeightAndWidthFromOptionsOrMetadata(). This is the height and width from
|
|
73
|
+
// the user-specified options if they exist, or the height and width form the
|
|
74
|
+
// stream metadata, which itself got its value from the CodecContext, when the
|
|
75
|
+
// stream was added. This is used by batch decoding APIs, for both GPU and
|
|
76
|
+
// CPU.
|
|
77
|
+
//
|
|
78
|
+
// The source of truth for height and width really is the (resized) AVFrame: it
|
|
79
|
+
// comes from the decoded ouptut of FFmpeg. The info from the metadata (i.e.
|
|
80
|
+
// from the CodecContext) may not be as accurate. However, the AVFrame is only
|
|
81
|
+
// available late in the call stack, when the frame is decoded, while the
|
|
82
|
+
// CodecContext is available early when a stream is added. This is why we use
|
|
83
|
+
// the CodecContext for pre-allocating batched output tensors (we could
|
|
84
|
+
// pre-allocate those only once we decode the first frame to get the info frame
|
|
85
|
+
// the AVFrame, but that's a more complex logic).
|
|
86
|
+
//
|
|
87
|
+
// Because the sources for height and width may disagree, we may end up with
|
|
88
|
+
// conflicts: e.g. if we pre-allocate a batch output tensor based on the
|
|
89
|
+
// metadata info, but the decoded AVFrame has a different height and width.
|
|
90
|
+
// it is very important to check the height and width assumptions where the
|
|
91
|
+
// tensors memory is used/filled in order to avoid segfaults.
|
|
92
|
+
|
|
93
|
+
struct FrameDims {
|
|
94
|
+
int height;
|
|
95
|
+
int width;
|
|
96
|
+
|
|
97
|
+
FrameDims(int h, int w) : height(h), width(w) {}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// There's nothing preventing you from calling this on a non-resized frame, but
|
|
101
|
+
// please don't.
|
|
102
|
+
FrameDims getHeightAndWidthFromResizedAVFrame(const AVFrame& resizedAVFrame);
|
|
103
|
+
|
|
104
|
+
FrameDims getHeightAndWidthFromOptionsOrMetadata(
|
|
105
|
+
const VideoStreamOptions& videoStreamOptions,
|
|
106
|
+
const StreamMetadata& streamMetadata);
|
|
107
|
+
|
|
108
|
+
FrameDims getHeightAndWidthFromOptionsOrAVFrame(
|
|
109
|
+
const VideoStreamOptions& videoStreamOptions,
|
|
110
|
+
const UniqueAVFrame& avFrame);
|
|
111
|
+
|
|
112
|
+
torch::Tensor allocateEmptyHWCTensor(
|
|
113
|
+
int height,
|
|
114
|
+
int width,
|
|
115
|
+
torch::Device device,
|
|
116
|
+
std::optional<int> numFrames = std::nullopt);
|
|
117
|
+
|
|
118
|
+
} // namespace facebook::torchcodec
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
// All rights reserved.
|
|
3
|
+
//
|
|
4
|
+
// This source code is licensed under the BSD-style license found in the
|
|
5
|
+
// LICENSE file in the root directory of this source tree.
|
|
6
|
+
|
|
7
|
+
#pragma once
|
|
8
|
+
|
|
9
|
+
#include <optional>
|
|
10
|
+
#include <string>
|
|
11
|
+
#include <vector>
|
|
12
|
+
|
|
13
|
+
extern "C" {
|
|
14
|
+
#include <libavcodec/avcodec.h>
|
|
15
|
+
#include <libavutil/avutil.h>
|
|
16
|
+
#include <libavutil/rational.h>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
namespace facebook::torchcodec {
|
|
20
|
+
|
|
21
|
+
struct StreamMetadata {
|
|
22
|
+
// Common (video and audio) fields derived from the AVStream.
|
|
23
|
+
int streamIndex;
|
|
24
|
+
// See this link for what various values are available:
|
|
25
|
+
// https://ffmpeg.org/doxygen/trunk/group__lavu__misc.html#ga9a84bba4713dfced21a1a56163be1f48
|
|
26
|
+
AVMediaType mediaType;
|
|
27
|
+
std::optional<AVCodecID> codecId;
|
|
28
|
+
std::optional<std::string> codecName;
|
|
29
|
+
std::optional<double> durationSecondsFromHeader;
|
|
30
|
+
std::optional<double> beginStreamSecondsFromHeader;
|
|
31
|
+
std::optional<int64_t> numFramesFromHeader;
|
|
32
|
+
std::optional<int64_t> numKeyFrames;
|
|
33
|
+
std::optional<double> averageFpsFromHeader;
|
|
34
|
+
std::optional<double> bitRate;
|
|
35
|
+
|
|
36
|
+
// More accurate duration, obtained by scanning the file.
|
|
37
|
+
// These presentation timestamps are in time base.
|
|
38
|
+
std::optional<int64_t> beginStreamPtsFromContent;
|
|
39
|
+
std::optional<int64_t> endStreamPtsFromContent;
|
|
40
|
+
// These presentation timestamps are in seconds.
|
|
41
|
+
std::optional<double> beginStreamPtsSecondsFromContent;
|
|
42
|
+
std::optional<double> endStreamPtsSecondsFromContent;
|
|
43
|
+
// This can be useful for index-based seeking.
|
|
44
|
+
std::optional<int64_t> numFramesFromContent;
|
|
45
|
+
|
|
46
|
+
// Video-only fields derived from the AVCodecContext.
|
|
47
|
+
std::optional<int64_t> width;
|
|
48
|
+
std::optional<int64_t> height;
|
|
49
|
+
std::optional<AVRational> sampleAspectRatio;
|
|
50
|
+
|
|
51
|
+
// Audio-only fields
|
|
52
|
+
std::optional<int64_t> sampleRate;
|
|
53
|
+
std::optional<int64_t> numChannels;
|
|
54
|
+
std::optional<std::string> sampleFormat;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
struct ContainerMetadata {
|
|
58
|
+
std::vector<StreamMetadata> allStreamMetadata;
|
|
59
|
+
int numAudioStreams = 0;
|
|
60
|
+
int numVideoStreams = 0;
|
|
61
|
+
// Note that this is the container-level duration, which is usually the max
|
|
62
|
+
// of all stream durations available in the container.
|
|
63
|
+
std::optional<double> durationSecondsFromHeader;
|
|
64
|
+
// Total BitRate level information at the container level in bit/s
|
|
65
|
+
std::optional<double> bitRate;
|
|
66
|
+
// If set, this is the index to the default audio stream.
|
|
67
|
+
std::optional<int> bestAudioStreamIndex;
|
|
68
|
+
// If set, this is the index to the default video stream.
|
|
69
|
+
std::optional<int> bestVideoStreamIndex;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
} // namespace facebook::torchcodec
|