Skip to content

Commit 7f1016b

Browse files
authored
Use Encoder as backend of AudioEncoder and VideoEncoder (meta-pytorch#1478)
1 parent 8f41f1a commit 7f1016b

8 files changed

Lines changed: 92 additions & 1340 deletions

File tree

src/torchcodec/_core/Encoder.cpp

Lines changed: 0 additions & 747 deletions
Large diffs are not rendered by default.

src/torchcodec/_core/Encoder.h

Lines changed: 8 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -14,69 +14,18 @@ extern "C" {
1414
}
1515

1616
namespace facebook::torchcodec {
17-
class FORCE_PUBLIC_VISIBILITY AudioEncoder {
18-
public:
19-
~AudioEncoder();
20-
21-
AudioEncoder(
22-
const torch::stable::Tensor& samples,
23-
int sampleRate,
24-
std::string_view fileName,
25-
const AudioStreamOptions& audioStreamOptions);
26-
27-
AudioEncoder(
28-
const torch::stable::Tensor& samples,
29-
int sampleRate,
30-
std::string_view formatName,
31-
std::unique_ptr<AVIOContextHolder> avioContextHolder,
32-
const AudioStreamOptions& audioStreamOptions);
33-
34-
void encode();
35-
36-
torch::stable::Tensor encodeToTensor();
37-
38-
private:
39-
void initializeEncoder(const AudioStreamOptions& audioStreamOptions);
40-
UniqueAVFrame maybeConvertAVFrame(const UniqueAVFrame& avFrame);
41-
void encodeFrameThroughFifo(
42-
AutoAVPacket& autoAVPacket,
43-
const UniqueAVFrame& avFrame,
44-
bool flushFifo = false);
45-
void encodeFrame(AutoAVPacket& autoAVPacket, const UniqueAVFrame& avFrame);
46-
void maybeFlushSwrBuffers(AutoAVPacket& autoAVPacket);
47-
void flushBuffers();
48-
49-
UniqueEncodingAVFormatContext avFormatContext_;
50-
UniqueAVCodecContext avCodecContext_;
51-
int streamIndex_;
52-
UniqueSwrContext swrContext_;
53-
AudioStreamOptions audioStreamOptions;
54-
55-
const torch::stable::Tensor samples_;
56-
57-
int outNumChannels_ = -1;
58-
int outSampleRate_ = -1;
59-
int inSampleRate_ = -1;
60-
61-
UniqueAVAudioFifo avAudioFifo_;
62-
63-
std::unique_ptr<AVIOContextHolder> avioContextHolder_;
64-
65-
bool encodeWasCalled_ = false;
66-
int64_t lastEncodedAVFramePts_ = 0;
67-
};
6817

6918
/* clang-format off */
7019
//
7120
// Note: [Encoding loop, sample rate conversion and FIFO]
7221
//
7322
// The input samples are in a given format, sample rate, and number of channels.
7423
// We may want to change these properties before encoding. The conversion is
75-
// done in maybeConvertAVFrame() and we rely on libswresample. When sample rate
76-
// conversion is needed, this means two things:
24+
// done in maybeConvertAudioAVFrame() and we rely on libswresample. When sample
25+
// rate conversion is needed, this means two things:
7726
// - swr will be storing samples in its internal buffers, which we'll need to
7827
// flush at the very end of the encoding process.
79-
// - the converted AVFrame we get back from maybeConvertAVFrame() typically
28+
// - the converted AVFrame we get back from maybeConvertAudioAVFrame() typically
8029
// won't have the same number of samples as the original AVFrame. And that's
8130
// a problem, because some encoders expect AVFrames with a specific and
8231
// constant number of samples. If we were to send it as-is, we'd get an error
@@ -85,100 +34,14 @@ class FORCE_PUBLIC_VISIBILITY AudioEncoder {
8534
// from which we can pull the exact number of samples that we need. Note that
8635
// this involves at least 2 additional copies.
8736
//
88-
// To be clear, the FIFO is only used if BOTH the following conditions are met:
89-
// - sample rate conversion is needed (inSampleRate_ != outSampleRate_)
90-
// - the encoder expects a specific number of samples per AVFrame (fixed frame size)
91-
// This is not the case for all encoders, e.g. WAV doesn't care about frame size.
92-
//
93-
// Also, the FIFO is either:
94-
// - used for every single frame during the encoding process, or
95-
// - not used at all.
96-
// There is no scenario where a given Encoder() instance would sometimes use a
97-
// FIFO, sometimes not.
98-
//
99-
// Drawing made with https://asciiflow.com/, can be copy/pasted there if it
100-
// needs editing:
101-
//
102-
// ┌─One─iteration─of─main─encoding─loop─(encode())───────────────────────────────────────────┐
103-
// │ │
104-
// │ Converts: │
105-
// │ - num channels │
106-
// │ - format │
107-
// │ - sample rate │
108-
// │ If sample rate is converted, stores data in swr buffers, │
109-
// │ which will need to be flushed by maybeFlushSwrBuffers() │
110-
// │ │
111-
// │ ▲ │
112-
// │ │ ┌─EncodeFrameThroughFifo()──────────────┐│
113-
// │ │ │ ││
114-
// │ AVFrame ──────► MaybeConvertAVFrame()───▲──│─┬──► NO FIFO ─►┬──▲────►encodeFrame() ││
115-
// │ with │ │ │ │ │ ││
116-
// │ input │ │ │ │ │ ││
117-
// │ samples │ │ │ │ │ ││
118-
// │ │ │ │ │ │ ││
119-
// │ │ │ └────► FIFO ─►─┘ │ ││
120-
// │ │ └───────────────────┼───────────────────┘│
121-
// └──────────────────────────────────────────────┼──────────────────────┼────────────────────┘
122-
// │ │
123-
// AVFrame from maybeFlushSwrBuffers() ───┘ │
124-
// Only if sample rate conversion was needed nullptr, to flush
125-
// The call to maybeFlushSwrBuffers() will FFmpeg buffers
126-
// also instruct to flush the FIFO, if it exists.
127-
//
128-
//
37+
// Unlike the old single-stream AudioEncoder where the FIFO was only used when
38+
// sample rate conversion was needed with a fixed-frame-size encoder, the
39+
// MultiStreamEncoder always creates a FIFO. This is because addSamples() can
40+
// be called multiple times with arbitrary chunk sizes, so we always need to
41+
// buffer and re-chunk samples into frame_size-sized batches.
12942
//
13043
/* clang-format on */
13144

132-
class FORCE_PUBLIC_VISIBILITY VideoEncoder {
133-
public:
134-
~VideoEncoder();
135-
136-
// Rule of Five requires that we define copy and move
137-
// constructors and assignment operators.
138-
// Both are deleted because we have unique_ptr members
139-
VideoEncoder(const VideoEncoder&) = delete;
140-
VideoEncoder& operator=(const VideoEncoder&) = delete;
141-
142-
// Move operators deleted since UniqueAVDictionary member is not movable
143-
VideoEncoder(VideoEncoder&&) = delete;
144-
VideoEncoder& operator=(VideoEncoder&&) = delete;
145-
146-
VideoEncoder(
147-
const torch::stable::Tensor& frames,
148-
double frameRate,
149-
std::string_view fileName,
150-
const VideoStreamOptions& videoStreamOptions);
151-
152-
VideoEncoder(
153-
const torch::stable::Tensor& frames,
154-
double frameRate,
155-
std::string_view formatName,
156-
std::unique_ptr<AVIOContextHolder> avioContextHolder,
157-
const VideoStreamOptions& videoStreamOptions);
158-
159-
void encode();
160-
161-
torch::stable::Tensor encodeToTensor();
162-
163-
private:
164-
void initializeEncoder(const VideoStreamOptions& videoStreamOptions);
165-
void encodeFrame(AutoAVPacket& autoAVPacket, const UniqueAVFrame& avFrame);
166-
void flushBuffers();
167-
168-
UniqueEncodingAVFormatContext avFormatContext_;
169-
UniqueAVCodecContext avCodecContext_;
170-
AVStream* avStream_ = nullptr;
171-
172-
const torch::stable::Tensor frames_;
173-
double inFrameRate_;
174-
175-
std::unique_ptr<AVIOContextHolder> avioContextHolder_;
176-
std::unique_ptr<DeviceInterface> deviceInterface_;
177-
178-
bool encodeWasCalled_ = false;
179-
UniqueAVDictionary avFormatOptions_;
180-
};
181-
18245
class FORCE_PUBLIC_VISIBILITY MultiStreamEncoder {
18346
public:
18447
~MultiStreamEncoder();

src/torchcodec/_core/__init__.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,6 @@
2020
core_library_path,
2121
create_streaming_encoder,
2222
create_wav_decoder_from_file,
23-
encode_audio_to_file,
24-
encode_audio_to_file_like,
25-
encode_audio_to_tensor,
26-
encode_video_to_file,
27-
encode_video_to_file_like,
28-
encode_video_to_tensor,
2923
ffmpeg_major_version,
3024
get_ffmpeg_library_versions,
3125
get_frame_at_index,

0 commit comments

Comments
 (0)