torchcodec 0.10.0__cp312-cp312-manylinux_2_28_x86_64.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.
- torchcodec/__init__.py +27 -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 +130 -0
- torchcodec/_core/AVIOTensorContext.h +44 -0
- torchcodec/_core/BetaCudaDeviceInterface.cpp +849 -0
- torchcodec/_core/BetaCudaDeviceInterface.h +196 -0
- torchcodec/_core/CMakeLists.txt +295 -0
- torchcodec/_core/CUDACommon.cpp +330 -0
- torchcodec/_core/CUDACommon.h +51 -0
- torchcodec/_core/Cache.h +124 -0
- torchcodec/_core/CpuDeviceInterface.cpp +509 -0
- torchcodec/_core/CpuDeviceInterface.h +141 -0
- torchcodec/_core/CudaDeviceInterface.cpp +602 -0
- torchcodec/_core/CudaDeviceInterface.h +79 -0
- torchcodec/_core/DeviceInterface.cpp +117 -0
- torchcodec/_core/DeviceInterface.h +191 -0
- torchcodec/_core/Encoder.cpp +1054 -0
- torchcodec/_core/Encoder.h +192 -0
- torchcodec/_core/FFMPEGCommon.cpp +684 -0
- torchcodec/_core/FFMPEGCommon.h +314 -0
- torchcodec/_core/FilterGraph.cpp +159 -0
- torchcodec/_core/FilterGraph.h +59 -0
- torchcodec/_core/Frame.cpp +47 -0
- torchcodec/_core/Frame.h +72 -0
- torchcodec/_core/Metadata.cpp +124 -0
- torchcodec/_core/Metadata.h +92 -0
- torchcodec/_core/NVCUVIDRuntimeLoader.cpp +320 -0
- torchcodec/_core/NVCUVIDRuntimeLoader.h +14 -0
- torchcodec/_core/NVDECCache.cpp +60 -0
- torchcodec/_core/NVDECCache.h +102 -0
- torchcodec/_core/SingleStreamDecoder.cpp +1586 -0
- torchcodec/_core/SingleStreamDecoder.h +391 -0
- torchcodec/_core/StreamOptions.h +70 -0
- torchcodec/_core/Transform.cpp +128 -0
- torchcodec/_core/Transform.h +86 -0
- torchcodec/_core/ValidationUtils.cpp +35 -0
- torchcodec/_core/ValidationUtils.h +21 -0
- torchcodec/_core/__init__.py +46 -0
- torchcodec/_core/_metadata.py +262 -0
- torchcodec/_core/custom_ops.cpp +1090 -0
- torchcodec/_core/fetch_and_expose_non_gpl_ffmpeg_libs.cmake +169 -0
- torchcodec/_core/nvcuvid_include/cuviddec.h +1374 -0
- torchcodec/_core/nvcuvid_include/nvcuvid.h +610 -0
- torchcodec/_core/ops.py +605 -0
- torchcodec/_core/pybind_ops.cpp +50 -0
- torchcodec/_frame.py +146 -0
- torchcodec/_internally_replaced_utils.py +68 -0
- torchcodec/_samplers/__init__.py +7 -0
- torchcodec/_samplers/video_clip_sampler.py +419 -0
- torchcodec/decoders/__init__.py +12 -0
- torchcodec/decoders/_audio_decoder.py +185 -0
- torchcodec/decoders/_decoder_utils.py +113 -0
- torchcodec/decoders/_video_decoder.py +601 -0
- torchcodec/encoders/__init__.py +2 -0
- torchcodec/encoders/_audio_encoder.py +149 -0
- torchcodec/encoders/_video_encoder.py +196 -0
- torchcodec/libtorchcodec_core4.so +0 -0
- torchcodec/libtorchcodec_core5.so +0 -0
- torchcodec/libtorchcodec_core6.so +0 -0
- torchcodec/libtorchcodec_core7.so +0 -0
- torchcodec/libtorchcodec_core8.so +0 -0
- torchcodec/libtorchcodec_custom_ops4.so +0 -0
- torchcodec/libtorchcodec_custom_ops5.so +0 -0
- torchcodec/libtorchcodec_custom_ops6.so +0 -0
- torchcodec/libtorchcodec_custom_ops7.so +0 -0
- torchcodec/libtorchcodec_custom_ops8.so +0 -0
- torchcodec/libtorchcodec_pybind_ops4.so +0 -0
- torchcodec/libtorchcodec_pybind_ops5.so +0 -0
- torchcodec/libtorchcodec_pybind_ops6.so +0 -0
- torchcodec/libtorchcodec_pybind_ops7.so +0 -0
- torchcodec/libtorchcodec_pybind_ops8.so +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 +358 -0
- torchcodec/share/cmake/TorchCodec/TorchCodecConfig.cmake +76 -0
- torchcodec/share/cmake/TorchCodec/ffmpeg_versions.cmake +122 -0
- torchcodec/transforms/__init__.py +12 -0
- torchcodec/transforms/_decoder_transforms.py +375 -0
- torchcodec/version.py +2 -0
- torchcodec-0.10.0.dist-info/METADATA +286 -0
- torchcodec-0.10.0.dist-info/RECORD +88 -0
- torchcodec-0.10.0.dist-info/WHEEL +5 -0
- torchcodec-0.10.0.dist-info/licenses/LICENSE +28 -0
- torchcodec-0.10.0.dist-info/top_level.txt +2 -0
torchcodec/__init__.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
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
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
# Note: usort wants to put Frame and FrameBatch after decoders and samplers,
|
|
10
|
+
# but that results in circular import.
|
|
11
|
+
from ._frame import AudioSamples, Frame, FrameBatch # usort:skip # noqa
|
|
12
|
+
from . import decoders, encoders, samplers, transforms # noqa
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
# Note that version.py is generated during install.
|
|
16
|
+
from .version import __version__ # noqa: F401
|
|
17
|
+
except Exception:
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
# cmake_prefix_path is needed for downstream cmake-based builds that use
|
|
21
|
+
# torchcodec as a dependency to tell cmake where torchcodec is installed and where to find its
|
|
22
|
+
# CMake configuration files.
|
|
23
|
+
# Pytorch itself has a similar mechanism which we use in our setup.py!
|
|
24
|
+
cmake_prefix_path = Path(__file__).parent / "share" / "cmake"
|
|
25
|
+
# Similarly, these are exposed for downstream builds that use torchcodec as a
|
|
26
|
+
# dependency.
|
|
27
|
+
from ._core import core_library_path, ffmpeg_major_version # usort:skip
|
|
@@ -0,0 +1,60 @@
|
|
|
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 "AVIOContextHolder.h"
|
|
8
|
+
#include <torch/types.h>
|
|
9
|
+
|
|
10
|
+
namespace facebook::torchcodec {
|
|
11
|
+
|
|
12
|
+
void AVIOContextHolder::createAVIOContext(
|
|
13
|
+
AVIOReadFunction read,
|
|
14
|
+
AVIOWriteFunction write,
|
|
15
|
+
AVIOSeekFunction seek,
|
|
16
|
+
void* heldData,
|
|
17
|
+
bool isForWriting,
|
|
18
|
+
int bufferSize) {
|
|
19
|
+
TORCH_CHECK(
|
|
20
|
+
bufferSize > 0,
|
|
21
|
+
"Buffer size must be greater than 0; is " + std::to_string(bufferSize));
|
|
22
|
+
auto buffer = static_cast<uint8_t*>(av_malloc(bufferSize));
|
|
23
|
+
TORCH_CHECK(
|
|
24
|
+
buffer != nullptr,
|
|
25
|
+
"Failed to allocate buffer of size " + std::to_string(bufferSize));
|
|
26
|
+
|
|
27
|
+
TORCH_CHECK(seek != nullptr, "seek method must be defined");
|
|
28
|
+
|
|
29
|
+
if (isForWriting) {
|
|
30
|
+
TORCH_CHECK(write != nullptr, "write method must be defined for writing");
|
|
31
|
+
} else {
|
|
32
|
+
TORCH_CHECK(read != nullptr, "read method must be defined for reading");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
avioContext_.reset(avioAllocContext(
|
|
36
|
+
buffer,
|
|
37
|
+
bufferSize,
|
|
38
|
+
/*write_flag=*/isForWriting,
|
|
39
|
+
heldData,
|
|
40
|
+
read,
|
|
41
|
+
write,
|
|
42
|
+
seek));
|
|
43
|
+
|
|
44
|
+
if (!avioContext_) {
|
|
45
|
+
av_freep(&buffer);
|
|
46
|
+
TORCH_CHECK(false, "Failed to allocate AVIOContext");
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
AVIOContextHolder::~AVIOContextHolder() {
|
|
51
|
+
if (avioContext_) {
|
|
52
|
+
av_freep(&avioContext_->buffer);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
AVIOContext* AVIOContextHolder::getAVIOContext() {
|
|
57
|
+
return avioContext_.get();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
} // namespace facebook::torchcodec
|
|
@@ -0,0 +1,64 @@
|
|
|
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 "FFMPEGCommon.h"
|
|
10
|
+
|
|
11
|
+
namespace facebook::torchcodec {
|
|
12
|
+
|
|
13
|
+
// The AVIOContextHolder serves several purposes:
|
|
14
|
+
//
|
|
15
|
+
// 1. It is a smart pointer for the AVIOContext. It has the logic to create
|
|
16
|
+
// a new AVIOContext and will appropriately free the AVIOContext when it
|
|
17
|
+
// goes out of scope. Note that this requires more than just having a
|
|
18
|
+
// UniqueAVIOContext, as the AVIOContext points to a buffer which must be
|
|
19
|
+
// freed.
|
|
20
|
+
// 2. It is a base class for AVIOContext specializations. When specializing a
|
|
21
|
+
// AVIOContext, we need to provide four things:
|
|
22
|
+
// 1. A read callback function, for decoding.
|
|
23
|
+
// 2. A seek callback function, for decoding and encoding.
|
|
24
|
+
// 3. A write callback function, for encoding.
|
|
25
|
+
// 4. A pointer to some context object that has the same lifetime as the
|
|
26
|
+
// AVIOContext itself. This context object holds the custom state that
|
|
27
|
+
// tracks the custom behavior of reading, seeking and writing. It is
|
|
28
|
+
// provided upon AVIOContext creation and to the read, seek and
|
|
29
|
+
// write callback functions.
|
|
30
|
+
// The callback functions do not need to be members of the derived class,
|
|
31
|
+
// but the derived class must have access to them. The context object must
|
|
32
|
+
// be a member of the derived class. Derived classes need to call
|
|
33
|
+
// createAVIOContext(), ideally in their constructor.
|
|
34
|
+
// 3. A generic handle for those that just need to manage having access to an
|
|
35
|
+
// AVIOContext, but aren't necessarily concerned with how it was customized:
|
|
36
|
+
// typically, the SingleStreamDecoder.
|
|
37
|
+
class AVIOContextHolder {
|
|
38
|
+
public:
|
|
39
|
+
virtual ~AVIOContextHolder();
|
|
40
|
+
AVIOContext* getAVIOContext();
|
|
41
|
+
|
|
42
|
+
protected:
|
|
43
|
+
// Make constructor protected to prevent anyone from constructing
|
|
44
|
+
// an AVIOContextHolder without deriving it. (Ordinarily this would be
|
|
45
|
+
// enforced by having a pure virtual methods, but we don't have any.)
|
|
46
|
+
AVIOContextHolder() = default;
|
|
47
|
+
|
|
48
|
+
// Deriving classes should call this function in their constructor.
|
|
49
|
+
void createAVIOContext(
|
|
50
|
+
AVIOReadFunction read,
|
|
51
|
+
AVIOWriteFunction write,
|
|
52
|
+
AVIOSeekFunction seek,
|
|
53
|
+
void* heldData,
|
|
54
|
+
bool isForWriting,
|
|
55
|
+
int bufferSize = defaultBufferSize);
|
|
56
|
+
|
|
57
|
+
private:
|
|
58
|
+
UniqueAVIOContext avioContext_;
|
|
59
|
+
|
|
60
|
+
// Defaults to 64 KB
|
|
61
|
+
static const int defaultBufferSize = 64 * 1024;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
} // namespace facebook::torchcodec
|
|
@@ -0,0 +1,98 @@
|
|
|
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 "AVIOFileLikeContext.h"
|
|
8
|
+
#include <torch/types.h>
|
|
9
|
+
|
|
10
|
+
namespace facebook::torchcodec {
|
|
11
|
+
|
|
12
|
+
AVIOFileLikeContext::AVIOFileLikeContext(
|
|
13
|
+
const py::object& fileLike,
|
|
14
|
+
bool isForWriting)
|
|
15
|
+
: fileLike_{UniquePyObject(new py::object(fileLike))} {
|
|
16
|
+
{
|
|
17
|
+
// TODO: Is it necessary to acquire the GIL here? Is it maybe even
|
|
18
|
+
// harmful? At the moment, this is only called from within a pybind
|
|
19
|
+
// function, and pybind guarantees we have the GIL.
|
|
20
|
+
py::gil_scoped_acquire gil;
|
|
21
|
+
|
|
22
|
+
if (isForWriting) {
|
|
23
|
+
TORCH_CHECK(
|
|
24
|
+
py::hasattr(fileLike, "write"),
|
|
25
|
+
"File like object must implement a write method for writing.");
|
|
26
|
+
} else {
|
|
27
|
+
TORCH_CHECK(
|
|
28
|
+
py::hasattr(fileLike, "read"),
|
|
29
|
+
"File like object must implement a read method for reading.");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
TORCH_CHECK(
|
|
33
|
+
py::hasattr(fileLike, "seek"),
|
|
34
|
+
"File like object must implement a seek method.");
|
|
35
|
+
}
|
|
36
|
+
createAVIOContext(&read, &write, &seek, &fileLike_, isForWriting);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
int AVIOFileLikeContext::read(void* opaque, uint8_t* buf, int buf_size) {
|
|
40
|
+
auto fileLike = static_cast<UniquePyObject*>(opaque);
|
|
41
|
+
|
|
42
|
+
// Note that we acquire the GIL outside of the loop. This is likely more
|
|
43
|
+
// efficient than releasing and acquiring it each loop iteration.
|
|
44
|
+
py::gil_scoped_acquire gil;
|
|
45
|
+
|
|
46
|
+
int totalNumRead = 0;
|
|
47
|
+
while (totalNumRead < buf_size) {
|
|
48
|
+
int request = buf_size - totalNumRead;
|
|
49
|
+
|
|
50
|
+
// The Python method returns the actual bytes, which we access through the
|
|
51
|
+
// py::bytes wrapper. That wrapper, however, does not provide us access to
|
|
52
|
+
// the underlying data pointer, which we need for the memcpy below. So we
|
|
53
|
+
// convert the bytes to a string_view to get access to the data pointer.
|
|
54
|
+
// Becauase it's a view and not a copy, it should be cheap.
|
|
55
|
+
auto bytesRead = static_cast<py::bytes>((*fileLike)->attr("read")(request));
|
|
56
|
+
auto bytesView = static_cast<std::string_view>(bytesRead);
|
|
57
|
+
|
|
58
|
+
int numBytesRead = static_cast<int>(bytesView.size());
|
|
59
|
+
if (numBytesRead == 0) {
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
TORCH_CHECK(
|
|
64
|
+
numBytesRead <= request,
|
|
65
|
+
"Requested up to ",
|
|
66
|
+
request,
|
|
67
|
+
" bytes but, received ",
|
|
68
|
+
numBytesRead,
|
|
69
|
+
" bytes. The given object does not conform to read protocol of file object.");
|
|
70
|
+
|
|
71
|
+
std::memcpy(buf, bytesView.data(), numBytesRead);
|
|
72
|
+
buf += numBytesRead;
|
|
73
|
+
totalNumRead += numBytesRead;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return totalNumRead == 0 ? AVERROR_EOF : totalNumRead;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
int64_t AVIOFileLikeContext::seek(void* opaque, int64_t offset, int whence) {
|
|
80
|
+
// We do not know the file size.
|
|
81
|
+
if (whence == AVSEEK_SIZE) {
|
|
82
|
+
return AVERROR(EIO);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
auto fileLike = static_cast<UniquePyObject*>(opaque);
|
|
86
|
+
py::gil_scoped_acquire gil;
|
|
87
|
+
return py::cast<int64_t>((*fileLike)->attr("seek")(offset, whence));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
int AVIOFileLikeContext::write(void* opaque, const uint8_t* buf, int buf_size) {
|
|
91
|
+
auto fileLike = static_cast<UniquePyObject*>(opaque);
|
|
92
|
+
py::gil_scoped_acquire gil;
|
|
93
|
+
py::bytes bytes_obj(reinterpret_cast<const char*>(buf), buf_size);
|
|
94
|
+
|
|
95
|
+
return py::cast<int>((*fileLike)->attr("write")(bytes_obj));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
} // namespace facebook::torchcodec
|
|
@@ -0,0 +1,55 @@
|
|
|
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 <pybind11/pybind11.h>
|
|
10
|
+
#include <pybind11/stl.h>
|
|
11
|
+
|
|
12
|
+
#include "AVIOContextHolder.h"
|
|
13
|
+
|
|
14
|
+
namespace py = pybind11;
|
|
15
|
+
|
|
16
|
+
namespace facebook::torchcodec {
|
|
17
|
+
|
|
18
|
+
// Enables uers to pass in a Python file-like object. We then forward all read
|
|
19
|
+
// and seek calls back up to the methods on the Python object.
|
|
20
|
+
class AVIOFileLikeContext : public AVIOContextHolder {
|
|
21
|
+
public:
|
|
22
|
+
explicit AVIOFileLikeContext(const py::object& fileLike, bool isForWriting);
|
|
23
|
+
|
|
24
|
+
private:
|
|
25
|
+
static int read(void* opaque, uint8_t* buf, int buf_size);
|
|
26
|
+
static int64_t seek(void* opaque, int64_t offset, int whence);
|
|
27
|
+
static int write(void* opaque, const uint8_t* buf, int buf_size);
|
|
28
|
+
|
|
29
|
+
// Note that we dynamically allocate the Python object because we need to
|
|
30
|
+
// strictly control when its destructor is called. We must hold the GIL
|
|
31
|
+
// when its destructor gets called, as it needs to update the reference
|
|
32
|
+
// count. It's easiest to control that when it's dynamic memory. Otherwise,
|
|
33
|
+
// we'd have to ensure whatever enclosing scope holds the object has the GIL,
|
|
34
|
+
// and that's, at least, hard. For all of the common pitfalls, see:
|
|
35
|
+
//
|
|
36
|
+
// https://pybind11.readthedocs.io/en/stable/advanced/misc.html#common-sources-of-global-interpreter-lock-errors
|
|
37
|
+
//
|
|
38
|
+
// We maintain a reference to the file-like object because the file-like
|
|
39
|
+
// object that was created on the Python side must live as long as our
|
|
40
|
+
// potential use. That is, even if there are no more references to the object
|
|
41
|
+
// on the Python side, we require that the object is still live.
|
|
42
|
+
struct PyObjectDeleter {
|
|
43
|
+
inline void operator()(py::object* obj) const {
|
|
44
|
+
if (obj) {
|
|
45
|
+
py::gil_scoped_acquire gil;
|
|
46
|
+
delete obj;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
using UniquePyObject = std::unique_ptr<py::object, PyObjectDeleter>;
|
|
52
|
+
UniquePyObject fileLike_;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
} // namespace facebook::torchcodec
|
|
@@ -0,0 +1,130 @@
|
|
|
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 "AVIOTensorContext.h"
|
|
8
|
+
#include <torch/types.h>
|
|
9
|
+
|
|
10
|
+
namespace facebook::torchcodec {
|
|
11
|
+
|
|
12
|
+
namespace {
|
|
13
|
+
|
|
14
|
+
constexpr int64_t INITIAL_TENSOR_SIZE = 10'000'000; // 10 MB
|
|
15
|
+
constexpr int64_t MAX_TENSOR_SIZE = 320'000'000; // 320 MB
|
|
16
|
+
|
|
17
|
+
// The signature of this function is defined by FFMPEG.
|
|
18
|
+
int read(void* opaque, uint8_t* buf, int buf_size) {
|
|
19
|
+
auto tensorContext = static_cast<detail::TensorContext*>(opaque);
|
|
20
|
+
TORCH_CHECK(
|
|
21
|
+
tensorContext->current_pos <= tensorContext->data.numel(),
|
|
22
|
+
"Tried to read outside of the buffer: current_pos=",
|
|
23
|
+
tensorContext->current_pos,
|
|
24
|
+
", size=",
|
|
25
|
+
tensorContext->data.numel());
|
|
26
|
+
|
|
27
|
+
int64_t numBytesRead = std::min(
|
|
28
|
+
static_cast<int64_t>(buf_size),
|
|
29
|
+
tensorContext->data.numel() - tensorContext->current_pos);
|
|
30
|
+
|
|
31
|
+
TORCH_CHECK(
|
|
32
|
+
numBytesRead >= 0,
|
|
33
|
+
"Tried to read negative bytes: numBytesRead=",
|
|
34
|
+
numBytesRead,
|
|
35
|
+
", size=",
|
|
36
|
+
tensorContext->data.numel(),
|
|
37
|
+
", current_pos=",
|
|
38
|
+
tensorContext->current_pos);
|
|
39
|
+
|
|
40
|
+
if (numBytesRead == 0) {
|
|
41
|
+
return AVERROR_EOF;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
std::memcpy(
|
|
45
|
+
buf,
|
|
46
|
+
tensorContext->data.data_ptr<uint8_t>() + tensorContext->current_pos,
|
|
47
|
+
numBytesRead);
|
|
48
|
+
tensorContext->current_pos += numBytesRead;
|
|
49
|
+
return numBytesRead;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// The signature of this function is defined by FFMPEG.
|
|
53
|
+
int write(void* opaque, const uint8_t* buf, int buf_size) {
|
|
54
|
+
auto tensorContext = static_cast<detail::TensorContext*>(opaque);
|
|
55
|
+
|
|
56
|
+
int64_t bufSize = static_cast<int64_t>(buf_size);
|
|
57
|
+
if (tensorContext->current_pos + bufSize > tensorContext->data.numel()) {
|
|
58
|
+
TORCH_CHECK(
|
|
59
|
+
tensorContext->data.numel() * 2 <= MAX_TENSOR_SIZE,
|
|
60
|
+
"We tried to allocate an output encoded tensor larger than ",
|
|
61
|
+
MAX_TENSOR_SIZE,
|
|
62
|
+
" bytes. If you think this should be supported, please report.");
|
|
63
|
+
|
|
64
|
+
// We double the size of the outpout tensor. Calling cat() may not be the
|
|
65
|
+
// most efficient, but it's simple.
|
|
66
|
+
tensorContext->data =
|
|
67
|
+
torch::cat({tensorContext->data, tensorContext->data});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
TORCH_CHECK(
|
|
71
|
+
tensorContext->current_pos + bufSize <= tensorContext->data.numel(),
|
|
72
|
+
"Re-allocation of the output tensor didn't work. ",
|
|
73
|
+
"This should not happen, please report on TorchCodec bug tracker");
|
|
74
|
+
|
|
75
|
+
uint8_t* outputTensorData = tensorContext->data.data_ptr<uint8_t>();
|
|
76
|
+
std::memcpy(outputTensorData + tensorContext->current_pos, buf, bufSize);
|
|
77
|
+
tensorContext->current_pos += bufSize;
|
|
78
|
+
// Track the maximum position written so getOutputTensor's narrow() does not
|
|
79
|
+
// truncate the file if final seek was backwards
|
|
80
|
+
tensorContext->max_pos =
|
|
81
|
+
std::max(tensorContext->current_pos, tensorContext->max_pos);
|
|
82
|
+
return buf_size;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// The signature of this function is defined by FFMPEG.
|
|
86
|
+
int64_t seek(void* opaque, int64_t offset, int whence) {
|
|
87
|
+
auto tensorContext = static_cast<detail::TensorContext*>(opaque);
|
|
88
|
+
int64_t ret = -1;
|
|
89
|
+
|
|
90
|
+
switch (whence) {
|
|
91
|
+
case AVSEEK_SIZE:
|
|
92
|
+
ret = tensorContext->data.numel();
|
|
93
|
+
break;
|
|
94
|
+
case SEEK_SET:
|
|
95
|
+
tensorContext->current_pos = offset;
|
|
96
|
+
ret = offset;
|
|
97
|
+
break;
|
|
98
|
+
default:
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return ret;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
} // namespace
|
|
106
|
+
|
|
107
|
+
AVIOFromTensorContext::AVIOFromTensorContext(torch::Tensor data)
|
|
108
|
+
: tensorContext_{data, 0, 0} {
|
|
109
|
+
TORCH_CHECK(data.numel() > 0, "data must not be empty");
|
|
110
|
+
TORCH_CHECK(data.is_contiguous(), "data must be contiguous");
|
|
111
|
+
TORCH_CHECK(data.scalar_type() == torch::kUInt8, "data must be kUInt8");
|
|
112
|
+
createAVIOContext(
|
|
113
|
+
&read, nullptr, &seek, &tensorContext_, /*isForWriting=*/false);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
AVIOToTensorContext::AVIOToTensorContext()
|
|
117
|
+
: tensorContext_{
|
|
118
|
+
torch::empty({INITIAL_TENSOR_SIZE}, {torch::kUInt8}),
|
|
119
|
+
0,
|
|
120
|
+
0} {
|
|
121
|
+
createAVIOContext(
|
|
122
|
+
nullptr, &write, &seek, &tensorContext_, /*isForWriting=*/true);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
torch::Tensor AVIOToTensorContext::getOutputTensor() {
|
|
126
|
+
return tensorContext_.data.narrow(
|
|
127
|
+
/*dim=*/0, /*start=*/0, /*length=*/tensorContext_.max_pos);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
} // namespace facebook::torchcodec
|
|
@@ -0,0 +1,44 @@
|
|
|
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 "AVIOContextHolder.h"
|
|
11
|
+
|
|
12
|
+
namespace facebook::torchcodec {
|
|
13
|
+
|
|
14
|
+
namespace detail {
|
|
15
|
+
|
|
16
|
+
struct TensorContext {
|
|
17
|
+
torch::Tensor data;
|
|
18
|
+
int64_t current_pos;
|
|
19
|
+
int64_t max_pos;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
} // namespace detail
|
|
23
|
+
|
|
24
|
+
// For Decoding: enables users to pass in the entire video or audio as bytes.
|
|
25
|
+
// Our read and seek functions then traverse the bytes in memory.
|
|
26
|
+
class AVIOFromTensorContext : public AVIOContextHolder {
|
|
27
|
+
public:
|
|
28
|
+
explicit AVIOFromTensorContext(torch::Tensor data);
|
|
29
|
+
|
|
30
|
+
private:
|
|
31
|
+
detail::TensorContext tensorContext_;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// For Encoding: used to encode into an output uint8 (bytes) tensor.
|
|
35
|
+
class AVIOToTensorContext : public AVIOContextHolder {
|
|
36
|
+
public:
|
|
37
|
+
explicit AVIOToTensorContext();
|
|
38
|
+
torch::Tensor getOutputTensor();
|
|
39
|
+
|
|
40
|
+
private:
|
|
41
|
+
detail::TensorContext tensorContext_;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
} // namespace facebook::torchcodec
|