torchcodec 0.4.0__cp39-cp39-macosx_11_0_arm64.whl → 0.5__cp39-cp39-macosx_11_0_arm64.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.

Files changed (52) hide show
  1. torchcodec/.dylibs/libc++.1.0.dylib +0 -0
  2. torchcodec/.dylibs/libpython3.9.dylib +0 -0
  3. torchcodec/_core/AVIOContextHolder.h +3 -2
  4. torchcodec/_core/AVIOTensorContext.cpp +121 -0
  5. torchcodec/_core/AVIOTensorContext.h +43 -0
  6. torchcodec/_core/CMakeLists.txt +27 -20
  7. torchcodec/_core/CpuDeviceInterface.cpp +30 -34
  8. torchcodec/_core/CudaDeviceInterface.cpp +2 -10
  9. torchcodec/_core/Encoder.cpp +233 -93
  10. torchcodec/_core/Encoder.h +86 -20
  11. torchcodec/_core/FFMPEGCommon.cpp +132 -55
  12. torchcodec/_core/FFMPEGCommon.h +12 -3
  13. torchcodec/_core/Metadata.h +12 -10
  14. torchcodec/_core/SingleStreamDecoder.cpp +256 -165
  15. torchcodec/_core/SingleStreamDecoder.h +30 -5
  16. torchcodec/_core/StreamOptions.h +4 -1
  17. torchcodec/_core/_metadata.py +56 -19
  18. torchcodec/_core/custom_ops.cpp +109 -60
  19. torchcodec/_core/ops.py +25 -11
  20. torchcodec/_core/pybind_ops.cpp +5 -1
  21. torchcodec/_frame.py +2 -2
  22. torchcodec/_internally_replaced_utils.py +11 -0
  23. torchcodec/_samplers/video_clip_sampler.py +11 -11
  24. torchcodec/decoders/_audio_decoder.py +3 -2
  25. torchcodec/decoders/_video_decoder.py +7 -2
  26. torchcodec/encoders/__init__.py +1 -0
  27. torchcodec/encoders/_audio_encoder.py +110 -0
  28. torchcodec/libtorchcodec_core4.dylib +0 -0
  29. torchcodec/libtorchcodec_core5.dylib +0 -0
  30. torchcodec/libtorchcodec_core6.dylib +0 -0
  31. torchcodec/libtorchcodec_core7.dylib +0 -0
  32. torchcodec/libtorchcodec_custom_ops4.dylib +0 -0
  33. torchcodec/libtorchcodec_custom_ops5.dylib +0 -0
  34. torchcodec/libtorchcodec_custom_ops6.dylib +0 -0
  35. torchcodec/libtorchcodec_custom_ops7.dylib +0 -0
  36. torchcodec/libtorchcodec_pybind_ops4.so +0 -0
  37. torchcodec/libtorchcodec_pybind_ops5.so +0 -0
  38. torchcodec/libtorchcodec_pybind_ops6.so +0 -0
  39. torchcodec/libtorchcodec_pybind_ops7.so +0 -0
  40. torchcodec/version.py +1 -1
  41. {torchcodec-0.4.0.dist-info → torchcodec-0.5.dist-info}/METADATA +10 -8
  42. torchcodec-0.5.dist-info/RECORD +64 -0
  43. torchcodec/_core/AVIOBytesContext.cpp +0 -137
  44. torchcodec/_core/AVIOBytesContext.h +0 -54
  45. torchcodec/libtorchcodec_decoder4.dylib +0 -0
  46. torchcodec/libtorchcodec_decoder5.dylib +0 -0
  47. torchcodec/libtorchcodec_decoder6.dylib +0 -0
  48. torchcodec/libtorchcodec_decoder7.dylib +0 -0
  49. torchcodec-0.4.0.dist-info/RECORD +0 -62
  50. {torchcodec-0.4.0.dist-info → torchcodec-0.5.dist-info}/WHEEL +0 -0
  51. {torchcodec-0.4.0.dist-info → torchcodec-0.5.dist-info}/licenses/LICENSE +0 -0
  52. {torchcodec-0.4.0.dist-info → torchcodec-0.5.dist-info}/top_level.txt +0 -0
Binary file
Binary file
@@ -27,8 +27,9 @@ namespace facebook::torchcodec {
27
27
  // tracks the custom behavior of reading, seeking and writing. It is
28
28
  // provided upon AVIOContext creation and to the read, seek and
29
29
  // write callback functions.
30
- // While it's not required, it is natural for the derived classes to make
31
- // all of the above members. Base classes need to call
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
32
33
  // createAVIOContext(), ideally in their constructor.
33
34
  // 3. A generic handle for those that just need to manage having access to an
34
35
  // AVIOContext, but aren't necessarily concerned with how it was customized:
@@ -0,0 +1,121 @@
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/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 <= tensorContext->data.numel(),
22
+ "Tried to read outside of the buffer: current=",
23
+ tensorContext->current,
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);
30
+
31
+ TORCH_CHECK(
32
+ numBytesRead >= 0,
33
+ "Tried to read negative bytes: numBytesRead=",
34
+ numBytesRead,
35
+ ", size=",
36
+ tensorContext->data.numel(),
37
+ ", current=",
38
+ tensorContext->current);
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,
47
+ numBytesRead);
48
+ tensorContext->current += 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 + 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 + 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, buf, bufSize);
77
+ tensorContext->current += bufSize;
78
+ return buf_size;
79
+ }
80
+
81
+ // The signature of this function is defined by FFMPEG.
82
+ int64_t seek(void* opaque, int64_t offset, int whence) {
83
+ auto tensorContext = static_cast<detail::TensorContext*>(opaque);
84
+ int64_t ret = -1;
85
+
86
+ switch (whence) {
87
+ case AVSEEK_SIZE:
88
+ ret = tensorContext->data.numel();
89
+ break;
90
+ case SEEK_SET:
91
+ tensorContext->current = offset;
92
+ ret = offset;
93
+ break;
94
+ default:
95
+ break;
96
+ }
97
+
98
+ return ret;
99
+ }
100
+
101
+ } // namespace
102
+
103
+ AVIOFromTensorContext::AVIOFromTensorContext(torch::Tensor data)
104
+ : tensorContext_{data, 0} {
105
+ TORCH_CHECK(data.numel() > 0, "data must not be empty");
106
+ TORCH_CHECK(data.is_contiguous(), "data must be contiguous");
107
+ TORCH_CHECK(data.scalar_type() == torch::kUInt8, "data must be kUInt8");
108
+ createAVIOContext(&read, nullptr, &seek, &tensorContext_);
109
+ }
110
+
111
+ AVIOToTensorContext::AVIOToTensorContext()
112
+ : tensorContext_{torch::empty({INITIAL_TENSOR_SIZE}, {torch::kUInt8}), 0} {
113
+ createAVIOContext(nullptr, &write, &seek, &tensorContext_);
114
+ }
115
+
116
+ torch::Tensor AVIOToTensorContext::getOutputTensor() {
117
+ return tensorContext_.data.narrow(
118
+ /*dim=*/0, /*start=*/0, /*length=*/tensorContext_.current);
119
+ }
120
+
121
+ } // namespace facebook::torchcodec
@@ -0,0 +1,43 @@
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/AVIOContextHolder.h"
11
+
12
+ namespace facebook::torchcodec {
13
+
14
+ namespace detail {
15
+
16
+ struct TensorContext {
17
+ torch::Tensor data;
18
+ int64_t current;
19
+ };
20
+
21
+ } // namespace detail
22
+
23
+ // For Decoding: enables users to pass in the entire video or audio as bytes.
24
+ // Our read and seek functions then traverse the bytes in memory.
25
+ class AVIOFromTensorContext : public AVIOContextHolder {
26
+ public:
27
+ explicit AVIOFromTensorContext(torch::Tensor data);
28
+
29
+ private:
30
+ detail::TensorContext tensorContext_;
31
+ };
32
+
33
+ // For Encoding: used to encode into an output uint8 (bytes) tensor.
34
+ class AVIOToTensorContext : public AVIOContextHolder {
35
+ public:
36
+ explicit AVIOToTensorContext();
37
+ torch::Tensor getOutputTensor();
38
+
39
+ private:
40
+ detail::TensorContext tensorContext_;
41
+ };
42
+
43
+ } // namespace facebook::torchcodec
@@ -48,65 +48,63 @@ function(make_torchcodec_libraries
48
48
  # We create three shared libraries per version of FFmpeg, where the version
49
49
  # is denoted by N:
50
50
  #
51
- # 1. libtorchcodec_decoderN.{ext}: Base library which contains the
51
+ # 1. libtorchcodec_coreN.{ext}: Base library which contains the
52
52
  # implementation of VideoDecoder and everything VideoDecoder needs. On
53
53
  # Linux, {ext} is so. On Mac, it is dylib.
54
54
  #
55
55
  # 2. libtorchcodec_custom_opsN.{ext}: Implementation of the PyTorch custom
56
- # ops. Depends on libtorchcodec_decoderN.{ext}. On Linux, {ext} is so.
56
+ # ops. Depends on libtorchcodec_coreN.{ext}. On Linux, {ext} is so.
57
57
  # On Mac, it is dylib.
58
58
  #
59
59
  # 3. libtorchcodec_pybind_opsN.{ext}: Implementation of the pybind11 ops. We
60
60
  # keep these separate from the PyTorch custom ops because we have to
61
61
  # load these libraries separately on the Python side. Depends on
62
- # libtorchcodec_decoderN.{ext}. On BOTH Linux and Mac {ext} is so.
62
+ # libtorchcodec_coreN.{ext}. On BOTH Linux and Mac {ext} is so.
63
63
 
64
- # 1. Create libtorchcodec_decoderN.{ext}.
65
- set(decoder_library_name "libtorchcodec_decoder${ffmpeg_major_version}")
66
- set(decoder_sources
64
+ # 1. Create libtorchcodec_coreN.{ext}.
65
+ set(core_library_name "libtorchcodec_core${ffmpeg_major_version}")
66
+ set(core_sources
67
67
  AVIOContextHolder.cpp
68
- AVIOBytesContext.cpp
68
+ AVIOTensorContext.cpp
69
69
  FFMPEGCommon.cpp
70
70
  Frame.cpp
71
71
  DeviceInterface.cpp
72
72
  CpuDeviceInterface.cpp
73
73
  SingleStreamDecoder.cpp
74
- # TODO: lib name should probably not be "*_decoder*" now that it also
75
- # contains an encoder
76
74
  Encoder.cpp
77
75
  )
78
76
 
79
77
  if(ENABLE_CUDA)
80
- list(APPEND decoder_sources CudaDeviceInterface.cpp)
78
+ list(APPEND core_sources CudaDeviceInterface.cpp)
81
79
  endif()
82
80
 
83
- set(decoder_library_dependencies
81
+ set(core_library_dependencies
84
82
  ${ffmpeg_target}
85
83
  ${TORCH_LIBRARIES}
86
84
  )
87
85
 
88
86
  if(ENABLE_CUDA)
89
- list(APPEND decoder_library_dependencies
87
+ list(APPEND core_library_dependencies
90
88
  ${CUDA_nppi_LIBRARY}
91
89
  ${CUDA_nppicc_LIBRARY}
92
90
  )
93
91
  endif()
94
92
 
95
93
  make_torchcodec_sublibrary(
96
- "${decoder_library_name}"
94
+ "${core_library_name}"
97
95
  SHARED
98
- "${decoder_sources}"
99
- "${decoder_library_dependencies}"
96
+ "${core_sources}"
97
+ "${core_library_dependencies}"
100
98
  )
101
99
 
102
100
  # 2. Create libtorchcodec_custom_opsN.{ext}.
103
101
  set(custom_ops_library_name "libtorchcodec_custom_ops${ffmpeg_major_version}")
104
102
  set(custom_ops_sources
105
- AVIOBytesContext.cpp
103
+ AVIOTensorContext.cpp
106
104
  custom_ops.cpp
107
105
  )
108
106
  set(custom_ops_dependencies
109
- ${decoder_library_name}
107
+ ${core_library_name}
110
108
  ${Python3_LIBRARIES}
111
109
  )
112
110
  make_torchcodec_sublibrary(
@@ -123,7 +121,7 @@ function(make_torchcodec_libraries
123
121
  pybind_ops.cpp
124
122
  )
125
123
  set(pybind_ops_dependencies
126
- ${decoder_library_name}
124
+ ${core_library_name}
127
125
  pybind11::module # This library dependency makes sure we have the right
128
126
  # Python libraries included as well as all of the right
129
127
  # settings so that we can successfully load the shared
@@ -151,6 +149,15 @@ function(make_torchcodec_libraries
151
149
  PUBLIC
152
150
  "-fvisibility=hidden"
153
151
  )
152
+ # The value we use here must match the value we return from
153
+ # _get_pybind_ops_module_name() on the Python side. If the values do not
154
+ # match, then we will be unable to import the C++ shared library as a
155
+ # Python module at runtime.
156
+ target_compile_definitions(
157
+ ${pybind_ops_library_name}
158
+ PRIVATE
159
+ PYBIND_OPS_MODULE_NAME=core_pybind_ops
160
+ )
154
161
  # If we don't make sure this flag is set, we run into segfauls at import
155
162
  # time on Mac. See:
156
163
  # https://github.com/pybind/pybind11/issues/3907#issuecomment-1170412764
@@ -163,7 +170,7 @@ function(make_torchcodec_libraries
163
170
  # Install all libraries.
164
171
  set(
165
172
  all_libraries
166
- ${decoder_library_name}
173
+ ${core_library_name}
167
174
  ${custom_ops_library_name}
168
175
  ${pybind_ops_library_name}
169
176
  )
@@ -240,7 +247,7 @@ else()
240
247
  # Expose these values updwards so that the test compilation does not need
241
248
  # to re-figure it out. FIXME: it's not great that we just copy-paste the
242
249
  # library names.
243
- set(libtorchcodec_library_name "libtorchcodec_decoder${ffmpeg_major_version}" PARENT_SCOPE)
250
+ set(libtorchcodec_library_name "libtorchcodec_core${ffmpeg_major_version}" PARENT_SCOPE)
244
251
  set(libtorchcodec_custom_ops_name "libtorchcodec_custom_ops${ffmpeg_major_version}" PARENT_SCOPE)
245
252
  set(libav_include_dirs ${LIBAV_INCLUDE_DIRS} PARENT_SCOPE)
246
253
  endif()
@@ -37,9 +37,8 @@ bool CpuDeviceInterface::DecodedFrameContext::operator!=(
37
37
  CpuDeviceInterface::CpuDeviceInterface(const torch::Device& device)
38
38
  : DeviceInterface(device) {
39
39
  TORCH_CHECK(g_cpu, "CpuDeviceInterface was not registered!");
40
- if (device_.type() != torch::kCPU) {
41
- throw std::runtime_error("Unsupported device: " + device_.str());
42
- }
40
+ TORCH_CHECK(
41
+ device_.type() == torch::kCPU, "Unsupported device: ", device_.str());
43
42
  }
44
43
 
45
44
  // Note [preAllocatedOutputTensor with swscale and filtergraph]:
@@ -161,9 +160,10 @@ void CpuDeviceInterface::convertAVFrameToFrameOutput(
161
160
  frameOutput.data = outputTensor;
162
161
  }
163
162
  } else {
164
- throw std::runtime_error(
165
- "Invalid color conversion library: " +
166
- std::to_string(static_cast<int>(colorConversionLibrary)));
163
+ TORCH_CHECK(
164
+ false,
165
+ "Invalid color conversion library: ",
166
+ static_cast<int>(colorConversionLibrary));
167
167
  }
168
168
  }
169
169
 
@@ -189,9 +189,8 @@ torch::Tensor CpuDeviceInterface::convertAVFrameToTensorUsingFilterGraph(
189
189
  const UniqueAVFrame& avFrame) {
190
190
  int status = av_buffersrc_write_frame(
191
191
  filterGraphContext_.sourceContext, avFrame.get());
192
- if (status < AVSUCCESS) {
193
- throw std::runtime_error("Failed to add frame to buffer source context");
194
- }
192
+ TORCH_CHECK(
193
+ status >= AVSUCCESS, "Failed to add frame to buffer source context");
195
194
 
196
195
  UniqueAVFrame filteredAVFrame(av_frame_alloc());
197
196
  status = av_buffersink_get_frame(
@@ -241,11 +240,12 @@ void CpuDeviceInterface::createFilterGraph(
241
240
  filterArgs.str().c_str(),
242
241
  nullptr,
243
242
  filterGraphContext_.filterGraph.get());
244
- if (status < 0) {
245
- throw std::runtime_error(
246
- std::string("Failed to create filter graph: ") + filterArgs.str() +
247
- ": " + getFFMPEGErrorStringFromErrorCode(status));
248
- }
243
+ TORCH_CHECK(
244
+ status >= 0,
245
+ "Failed to create filter graph: ",
246
+ filterArgs.str(),
247
+ ": ",
248
+ getFFMPEGErrorStringFromErrorCode(status));
249
249
 
250
250
  status = avfilter_graph_create_filter(
251
251
  &filterGraphContext_.sinkContext,
@@ -254,11 +254,10 @@ void CpuDeviceInterface::createFilterGraph(
254
254
  nullptr,
255
255
  nullptr,
256
256
  filterGraphContext_.filterGraph.get());
257
- if (status < 0) {
258
- throw std::runtime_error(
259
- "Failed to create filter graph: " +
260
- getFFMPEGErrorStringFromErrorCode(status));
261
- }
257
+ TORCH_CHECK(
258
+ status >= 0,
259
+ "Failed to create filter graph: ",
260
+ getFFMPEGErrorStringFromErrorCode(status));
262
261
 
263
262
  enum AVPixelFormat pix_fmts[] = {AV_PIX_FMT_RGB24, AV_PIX_FMT_NONE};
264
263
 
@@ -268,11 +267,10 @@ void CpuDeviceInterface::createFilterGraph(
268
267
  pix_fmts,
269
268
  AV_PIX_FMT_NONE,
270
269
  AV_OPT_SEARCH_CHILDREN);
271
- if (status < 0) {
272
- throw std::runtime_error(
273
- "Failed to set output pixel formats: " +
274
- getFFMPEGErrorStringFromErrorCode(status));
275
- }
270
+ TORCH_CHECK(
271
+ status >= 0,
272
+ "Failed to set output pixel formats: ",
273
+ getFFMPEGErrorStringFromErrorCode(status));
276
274
 
277
275
  UniqueAVFilterInOut outputs(avfilter_inout_alloc());
278
276
  UniqueAVFilterInOut inputs(avfilter_inout_alloc());
@@ -301,19 +299,17 @@ void CpuDeviceInterface::createFilterGraph(
301
299
  nullptr);
302
300
  outputs.reset(outputsTmp);
303
301
  inputs.reset(inputsTmp);
304
- if (status < 0) {
305
- throw std::runtime_error(
306
- "Failed to parse filter description: " +
307
- getFFMPEGErrorStringFromErrorCode(status));
308
- }
302
+ TORCH_CHECK(
303
+ status >= 0,
304
+ "Failed to parse filter description: ",
305
+ getFFMPEGErrorStringFromErrorCode(status));
309
306
 
310
307
  status =
311
308
  avfilter_graph_config(filterGraphContext_.filterGraph.get(), nullptr);
312
- if (status < 0) {
313
- throw std::runtime_error(
314
- "Failed to configure filter graph: " +
315
- getFFMPEGErrorStringFromErrorCode(status));
316
- }
309
+ TORCH_CHECK(
310
+ status >= 0,
311
+ "Failed to configure filter graph: ",
312
+ getFFMPEGErrorStringFromErrorCode(status));
317
313
  }
318
314
 
319
315
  void CpuDeviceInterface::createSwsContext(
@@ -166,9 +166,8 @@ AVBufferRef* getCudaContext(const torch::Device& device) {
166
166
  CudaDeviceInterface::CudaDeviceInterface(const torch::Device& device)
167
167
  : DeviceInterface(device) {
168
168
  TORCH_CHECK(g_cuda, "CudaDeviceInterface was not registered!");
169
- if (device_.type() != torch::kCUDA) {
170
- throw std::runtime_error("Unsupported device: " + device_.str());
171
- }
169
+ TORCH_CHECK(
170
+ device_.type() == torch::kCUDA, "Unsupported device: ", device_.str());
172
171
  }
173
172
 
174
173
  CudaDeviceInterface::~CudaDeviceInterface() {
@@ -228,7 +227,6 @@ void CudaDeviceInterface::convertAVFrameToFrameOutput(
228
227
  NppiSize oSizeROI = {width, height};
229
228
  Npp8u* input[2] = {avFrame->data[0], avFrame->data[1]};
230
229
 
231
- auto start = std::chrono::high_resolution_clock::now();
232
230
  NppStatus status;
233
231
  if (avFrame->colorspace == AVColorSpace::AVCOL_SPC_BT709) {
234
232
  status = nppiNV12ToRGB_709CSC_8u_P2C3R(
@@ -254,12 +252,6 @@ void CudaDeviceInterface::convertAVFrameToFrameOutput(
254
252
  c10::cuda::getStreamFromExternal(nppGetStream(), device_.index());
255
253
  nppDoneEvent.record(nppStreamWrapper);
256
254
  nppDoneEvent.block(at::cuda::getCurrentCUDAStream());
257
-
258
- auto end = std::chrono::high_resolution_clock::now();
259
-
260
- std::chrono::duration<double, std::micro> duration = end - start;
261
- VLOG(9) << "NPP Conversion of frame height=" << height << " width=" << width
262
- << " took: " << duration.count() << "us" << std::endl;
263
255
  }
264
256
 
265
257
  // inspired by https://github.com/FFmpeg/FFmpeg/commit/ad67ea9