torchcodec 0.1.0

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.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +3 -0
  3. data/LICENSE.txt +29 -0
  4. data/README.md +69 -0
  5. data/ext/torchcodec/AVIOContextHolder.cpp +60 -0
  6. data/ext/torchcodec/AVIOContextHolder.h +64 -0
  7. data/ext/torchcodec/AVIOTensorContext.cpp +130 -0
  8. data/ext/torchcodec/AVIOTensorContext.h +44 -0
  9. data/ext/torchcodec/CpuDeviceInterface.cpp +509 -0
  10. data/ext/torchcodec/CpuDeviceInterface.h +141 -0
  11. data/ext/torchcodec/DeviceInterface.cpp +117 -0
  12. data/ext/torchcodec/DeviceInterface.h +191 -0
  13. data/ext/torchcodec/Encoder.cpp +1054 -0
  14. data/ext/torchcodec/Encoder.h +192 -0
  15. data/ext/torchcodec/FFMPEGCommon.cpp +684 -0
  16. data/ext/torchcodec/FFMPEGCommon.h +314 -0
  17. data/ext/torchcodec/FilterGraph.cpp +159 -0
  18. data/ext/torchcodec/FilterGraph.h +59 -0
  19. data/ext/torchcodec/Frame.cpp +47 -0
  20. data/ext/torchcodec/Frame.h +72 -0
  21. data/ext/torchcodec/Metadata.cpp +124 -0
  22. data/ext/torchcodec/Metadata.h +92 -0
  23. data/ext/torchcodec/SingleStreamDecoder.cpp +1586 -0
  24. data/ext/torchcodec/SingleStreamDecoder.h +391 -0
  25. data/ext/torchcodec/StableABICompat.h +185 -0
  26. data/ext/torchcodec/StreamOptions.h +70 -0
  27. data/ext/torchcodec/Transform.cpp +128 -0
  28. data/ext/torchcodec/Transform.h +86 -0
  29. data/ext/torchcodec/ValidationUtils.cpp +35 -0
  30. data/ext/torchcodec/ValidationUtils.h +21 -0
  31. data/ext/torchcodec/custom_ops.cpp +913 -0
  32. data/ext/torchcodec/ext.cpp +12 -0
  33. data/ext/torchcodec/extconf.rb +73 -0
  34. data/lib/torchcodec/core/metadata.rb +41 -0
  35. data/lib/torchcodec/decoders/audio_decoder.rb +88 -0
  36. data/lib/torchcodec/decoders/decoder_utils.rb +11 -0
  37. data/lib/torchcodec/version.rb +3 -0
  38. data/lib/torchcodec.rb +28 -0
  39. metadata +90 -0
@@ -0,0 +1,12 @@
1
+ #include <rice/rice.hpp>
2
+
3
+ void init_core(Rice::Module m);
4
+
5
+ extern "C"
6
+ void Init_ext() {
7
+ auto rb_mTorchCodec = Rice::define_module("TorchCodec");
8
+
9
+ auto rb_mCore = Rice::define_module_under(rb_mTorchCodec, "Core");
10
+
11
+ init_core(rb_mCore);
12
+ }
@@ -0,0 +1,73 @@
1
+ require "mkmf-rice"
2
+
3
+ $CXXFLAGS += " -std=c++17 $(optflags)"
4
+
5
+ paths = []
6
+ if RbConfig::CONFIG["host_os"] =~ /darwin/i
7
+ if RbConfig::CONFIG["host_cpu"] =~ /arm|aarch64/i
8
+ paths << "/opt/homebrew"
9
+ else
10
+ paths << "/usr/local"
11
+ end
12
+ elsif !Gem.win_platform?
13
+ paths << "/home/linuxbrew/.linuxbrew"
14
+ end
15
+
16
+ inc, lib = dir_config("torch")
17
+ inc ||= paths.map { |v| "#{v}/opt/pytorch/include" }.find { |v| Dir.exist?("#{v}/torch") }
18
+ lib ||= paths.map { |v| "#{v}/opt/pytorch/lib" }.find { |v| Dir["#{v}/*torch_cpu*"].any? }
19
+
20
+ unless inc && lib
21
+ abort "LibTorch not found"
22
+ end
23
+
24
+ cuda_inc, cuda_lib = dir_config("cuda")
25
+ cuda_lib ||= "/usr/local/cuda/lib64"
26
+
27
+ cudnn_inc, cudnn_lib = dir_config("cudnn")
28
+ cudnn_lib ||= "/usr/local/cuda/lib"
29
+
30
+ $LDFLAGS += " -L#{lib}" if Dir.exist?(lib)
31
+ abort "LibTorch not found" unless have_library("torch")
32
+
33
+ have_library("mkldnn")
34
+ have_library("nnpack")
35
+
36
+ with_cuda = false
37
+ if Dir["#{lib}/*torch_cuda*"].any?
38
+ $LDFLAGS += " -L#{cuda_lib}" if Dir.exist?(cuda_lib)
39
+ $LDFLAGS += " -L#{cudnn_lib}" if Dir.exist?(cudnn_lib) && cudnn_lib != cuda_lib
40
+ with_cuda = have_library("cuda") && have_library("cudnn")
41
+ end
42
+
43
+ $INCFLAGS += " -I#{inc}"
44
+ $INCFLAGS += " -I#{inc}/torch/csrc/api/include"
45
+
46
+ $LDFLAGS += " -Wl,-rpath,#{lib}"
47
+ $LDFLAGS += ":#{cuda_lib}/stubs:#{cuda_lib}" if with_cuda
48
+
49
+ # https://github.com/pytorch/pytorch/blob/v2.9.0/torch/utils/cpp_extension.py#L1351-L1364
50
+ $LDFLAGS += " -lc10 -ltorch_cpu -ltorch"
51
+ if with_cuda
52
+ $LDFLAGS += " -lcuda -lnvrtc"
53
+ $LDFLAGS += " -lnvToolsExt" if File.exist?("#{cuda_lib}/libnvToolsExt.so")
54
+ $LDFLAGS += " -lcudart -lc10_cuda -ltorch_cuda -lcufft -lcurand -lcublas -lcudnn"
55
+ # TODO figure out why this is needed
56
+ $LDFLAGS += " -Wl,--no-as-needed,#{lib}/libtorch.so"
57
+ end
58
+
59
+ ffmpeg_inc, ffmpeg_lib = dir_config("ffmpeg")
60
+ ffmpeg_inc ||= paths.map { |v| "#{v}/opt/ffmpeg/include" }.find { |v| File.exist?("#{v}/libavcodec/avcodec.h") }
61
+ ffmpeg_lib ||= paths.map { |v| "#{v}/opt/ffmpeg/lib" }.find { |v| Dir["#{v}/*libavcodec*"].any? }
62
+
63
+ ffmpeg_incs = ffmpeg_inc ? [ffmpeg_inc] : []
64
+ ffmpeg_libs = ffmpeg_lib ? [ffmpeg_lib] : []
65
+
66
+ abort "libavcodec not found" unless find_header("libavcodec/avcodec.h", *ffmpeg_incs)
67
+ abort "libavcodec not found" unless find_library("avcodec", nil, *ffmpeg_libs)
68
+ abort "libavdevice not found" unless find_library("avdevice", nil, *ffmpeg_libs)
69
+ abort "libavfilter not found" unless find_library("avfilter", nil, *ffmpeg_libs)
70
+ abort "libavutil not found" unless find_library("avutil", nil, *ffmpeg_libs)
71
+
72
+ # create makefile
73
+ create_makefile("torchcodec/ext")
@@ -0,0 +1,41 @@
1
+ module TorchCodec
2
+ module Core
3
+ def self.get_container_metadata(decoder)
4
+ container_dict = JSON.parse(_get_container_json_metadata(decoder))
5
+ streams_metadata = []
6
+
7
+ container_dict["numStreams"].times do |stream_index|
8
+ stream_dict = JSON.parse(_get_stream_json_metadata(decoder, stream_index))
9
+ common_meta = {
10
+ duration_seconds_from_header: stream_dict["durationSecondsFromHeader"],
11
+ duration_seconds: stream_dict["durationSeconds"],
12
+ bit_rate: stream_dict["bitRate"],
13
+ begin_stream_seconds_from_header: stream_dict["beginStreamSecondsFromHeader"],
14
+ begin_stream_seconds: stream_dict["beginStreamSeconds"],
15
+ codec: stream_dict["codec"],
16
+ stream_index: stream_index
17
+ }
18
+ if stream_dict["mediaType"] == "video"
19
+ raise Todo
20
+ elsif stream_dict["mediaType"] == "audio"
21
+ streams_metadata << {
22
+ sample_rate: stream_dict["sampleRate"],
23
+ num_channels: stream_dict["numChannels"],
24
+ sample_format: stream_dict["sampleFormat"],
25
+ **common_meta
26
+ }
27
+ else
28
+ raise Todo
29
+ end
30
+ end
31
+
32
+ {
33
+ duration_seconds_from_header: container_dict["durationSecondsFromHeader"],
34
+ bit_rate_from_header: container_dict["bitRate"],
35
+ best_video_stream_index: container_dict["bestVideoStreamIndex"],
36
+ best_audio_stream_index: container_dict["bestAudioStreamIndex"],
37
+ streams: streams_metadata
38
+ }
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,88 @@
1
+ module TorchCodec
2
+ module Decoders
3
+ class AudioDecoder
4
+ attr_reader :metadata
5
+
6
+ def initialize(
7
+ source,
8
+ stream_index: nil,
9
+ sample_rate: nil,
10
+ num_channels: nil
11
+ )
12
+ @decoder = Decoders.create_decoder(source, "approximate")
13
+
14
+ container_metadata = Core.get_container_metadata(@decoder)
15
+ @stream_index =
16
+ stream_index.nil? ? container_metadata[:best_audio_stream_index] : stream_index
17
+ if @stream_index.nil?
18
+ raise ArgumentError, "The best audio stream is unknown and there is no specified stream."
19
+ end
20
+ if @stream_index >= container_metadata[:streams].length
21
+ raise ArgumentError, "The stream at index #{@stream_index} is not a valid stream."
22
+ end
23
+
24
+ @metadata = container_metadata[:streams][@stream_index]
25
+ if !@metadata.key?(:sample_rate)
26
+ raise ArgumentError, "The stream at index #{@stream_index} is not an audio stream."
27
+ end
28
+
29
+ @desired_sample_rate =
30
+ !sample_rate.nil? ? sample_rate : @metadata[:sample_rate]
31
+
32
+ Core.add_audio_stream(
33
+ @decoder,
34
+ stream_index,
35
+ sample_rate,
36
+ num_channels
37
+ )
38
+ end
39
+
40
+ def get_all_samples
41
+ get_samples_played_in_range
42
+ end
43
+
44
+ def get_samples_played_in_range(start_seconds: 0.0, stop_seconds: nil)
45
+ if !stop_seconds.nil? && !(start_seconds <= stop_seconds)
46
+ raise ArgumentError, "Invalid start seconds: #{start_seconds}. It must be less than or equal to stop seconds (#{stop_seconds})."
47
+ end
48
+ frames, first_pts = Core.get_frames_by_pts_in_range_audio(
49
+ @decoder,
50
+ start_seconds,
51
+ stop_seconds
52
+ )
53
+ first_pts = first_pts.item
54
+
55
+ sample_rate = @desired_sample_rate
56
+ # TODO: metadata's sample_rate should probably not be Optional
57
+ raise if sample_rate.nil?
58
+
59
+ if first_pts < start_seconds
60
+ offset_beginning = ((start_seconds - first_pts) * sample_rate).round
61
+ output_pts_seconds = start_seconds
62
+ else
63
+ # In normal cases we'll have first_pts <= start_pts, but in some
64
+ # edge cases it's possible to have first_pts > start_seconds,
65
+ # typically if the stream's first frame's pts isn't exactly 0.
66
+ offset_beginning = 0
67
+ output_pts_seconds = first_pts
68
+ end
69
+
70
+ num_samples = frames.shape[1]
71
+ last_pts = first_pts + num_samples / sample_rate
72
+ if !stop_seconds.nil? && stop_seconds < last_pts
73
+ offset_end = num_samples - ((last_pts - stop_seconds) * sample_rate).round
74
+ else
75
+ offset_end = num_samples
76
+ end
77
+
78
+ data = frames[0.., offset_beginning...offset_end]
79
+ {
80
+ data: data,
81
+ pts_seconds: output_pts_seconds,
82
+ duration_seconds: data.shape[1] / sample_rate.to_f,
83
+ sample_rate: sample_rate
84
+ }
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,11 @@
1
+ module TorchCodec
2
+ module Decoders
3
+ def self.create_decoder(source, seek_mode)
4
+ if source.is_a?(String)
5
+ Core.create_from_file(source, seek_mode)
6
+ else
7
+ raise TypeError, "Unknown source type: #{source.class.name}"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module TorchCodec
2
+ VERSION = "0.1.0"
3
+ end
data/lib/torchcodec.rb ADDED
@@ -0,0 +1,28 @@
1
+ # dependencies
2
+ require "torch"
3
+
4
+ # ext
5
+ require "torchcodec/ext"
6
+
7
+ # stdlib
8
+ require "json"
9
+
10
+ # modules
11
+ require_relative "torchcodec/version"
12
+
13
+ # core
14
+ require_relative "torchcodec/core/metadata"
15
+
16
+ # decoders
17
+ require_relative "torchcodec/decoders/audio_decoder"
18
+ require_relative "torchcodec/decoders/decoder_utils"
19
+
20
+ module TorchCodec
21
+ class Error < StandardError; end
22
+
23
+ class Todo < Error
24
+ def message
25
+ "not implemented yet"
26
+ end
27
+ end
28
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: torchcodec
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Andrew Kane
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: torch-rb
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0.23'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0.23'
26
+ email: andrew@ankane.org
27
+ executables: []
28
+ extensions:
29
+ - ext/torchcodec/extconf.rb
30
+ extra_rdoc_files: []
31
+ files:
32
+ - CHANGELOG.md
33
+ - LICENSE.txt
34
+ - README.md
35
+ - ext/torchcodec/AVIOContextHolder.cpp
36
+ - ext/torchcodec/AVIOContextHolder.h
37
+ - ext/torchcodec/AVIOTensorContext.cpp
38
+ - ext/torchcodec/AVIOTensorContext.h
39
+ - ext/torchcodec/CpuDeviceInterface.cpp
40
+ - ext/torchcodec/CpuDeviceInterface.h
41
+ - ext/torchcodec/DeviceInterface.cpp
42
+ - ext/torchcodec/DeviceInterface.h
43
+ - ext/torchcodec/Encoder.cpp
44
+ - ext/torchcodec/Encoder.h
45
+ - ext/torchcodec/FFMPEGCommon.cpp
46
+ - ext/torchcodec/FFMPEGCommon.h
47
+ - ext/torchcodec/FilterGraph.cpp
48
+ - ext/torchcodec/FilterGraph.h
49
+ - ext/torchcodec/Frame.cpp
50
+ - ext/torchcodec/Frame.h
51
+ - ext/torchcodec/Metadata.cpp
52
+ - ext/torchcodec/Metadata.h
53
+ - ext/torchcodec/SingleStreamDecoder.cpp
54
+ - ext/torchcodec/SingleStreamDecoder.h
55
+ - ext/torchcodec/StableABICompat.h
56
+ - ext/torchcodec/StreamOptions.h
57
+ - ext/torchcodec/Transform.cpp
58
+ - ext/torchcodec/Transform.h
59
+ - ext/torchcodec/ValidationUtils.cpp
60
+ - ext/torchcodec/ValidationUtils.h
61
+ - ext/torchcodec/custom_ops.cpp
62
+ - ext/torchcodec/ext.cpp
63
+ - ext/torchcodec/extconf.rb
64
+ - lib/torchcodec.rb
65
+ - lib/torchcodec/core/metadata.rb
66
+ - lib/torchcodec/decoders/audio_decoder.rb
67
+ - lib/torchcodec/decoders/decoder_utils.rb
68
+ - lib/torchcodec/version.rb
69
+ homepage: https://github.com/ankane/torchcodec-ruby
70
+ licenses:
71
+ - BSD-3-Clause
72
+ metadata: {}
73
+ rdoc_options: []
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '3.2'
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ requirements: []
87
+ rubygems_version: 4.0.3
88
+ specification_version: 4
89
+ summary: Media encoding and decoding for Torch.rb
90
+ test_files: []