gsvvcompressor 1.2.0__cp310-cp310-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.
Files changed (50) hide show
  1. gsvvcompressor/__init__.py +13 -0
  2. gsvvcompressor/__main__.py +243 -0
  3. gsvvcompressor/combinations/__init__.py +84 -0
  4. gsvvcompressor/combinations/registry.py +52 -0
  5. gsvvcompressor/combinations/vq_xyz_1mask.py +89 -0
  6. gsvvcompressor/combinations/vq_xyz_1mask_zstd.py +103 -0
  7. gsvvcompressor/combinations/vq_xyz_draco.py +468 -0
  8. gsvvcompressor/combinations/vq_xyz_draco_2pass.py +156 -0
  9. gsvvcompressor/combinations/vq_xyz_zstd.py +106 -0
  10. gsvvcompressor/compress/__init__.py +5 -0
  11. gsvvcompressor/compress/zstd.py +144 -0
  12. gsvvcompressor/decoder.py +155 -0
  13. gsvvcompressor/deserializer.py +42 -0
  14. gsvvcompressor/draco/__init__.py +34 -0
  15. gsvvcompressor/draco/draco_decoder.exe +0 -0
  16. gsvvcompressor/draco/draco_encoder.exe +0 -0
  17. gsvvcompressor/draco/dracoreduced3dgs.cp310-win_amd64.pyd +0 -0
  18. gsvvcompressor/draco/interface.py +339 -0
  19. gsvvcompressor/draco/serialize.py +235 -0
  20. gsvvcompressor/draco/twopass.py +359 -0
  21. gsvvcompressor/encoder.py +122 -0
  22. gsvvcompressor/interframe/__init__.py +11 -0
  23. gsvvcompressor/interframe/combine.py +271 -0
  24. gsvvcompressor/interframe/decoder.py +99 -0
  25. gsvvcompressor/interframe/encoder.py +92 -0
  26. gsvvcompressor/interframe/interface.py +221 -0
  27. gsvvcompressor/interframe/twopass.py +226 -0
  28. gsvvcompressor/io/__init__.py +31 -0
  29. gsvvcompressor/io/bytes.py +103 -0
  30. gsvvcompressor/io/config.py +78 -0
  31. gsvvcompressor/io/gaussian_model.py +127 -0
  32. gsvvcompressor/movecameras.py +33 -0
  33. gsvvcompressor/payload.py +34 -0
  34. gsvvcompressor/serializer.py +42 -0
  35. gsvvcompressor/vq/__init__.py +15 -0
  36. gsvvcompressor/vq/interface.py +324 -0
  37. gsvvcompressor/vq/singlemask.py +127 -0
  38. gsvvcompressor/vq/twopass.py +1 -0
  39. gsvvcompressor/xyz/__init__.py +26 -0
  40. gsvvcompressor/xyz/dense.py +39 -0
  41. gsvvcompressor/xyz/interface.py +382 -0
  42. gsvvcompressor/xyz/knn.py +141 -0
  43. gsvvcompressor/xyz/quant.py +143 -0
  44. gsvvcompressor/xyz/size.py +44 -0
  45. gsvvcompressor/xyz/twopass.py +1 -0
  46. gsvvcompressor-1.2.0.dist-info/METADATA +690 -0
  47. gsvvcompressor-1.2.0.dist-info/RECORD +50 -0
  48. gsvvcompressor-1.2.0.dist-info/WHEEL +5 -0
  49. gsvvcompressor-1.2.0.dist-info/licenses/LICENSE +21 -0
  50. gsvvcompressor-1.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,226 @@
1
+ from abc import abstractmethod
2
+ from dataclasses import dataclass
3
+ from typing import Iterator, List, Optional
4
+
5
+ from gaussian_splatting import GaussianModel
6
+
7
+ from ..encoder import AbstractEncoder
8
+ from ..payload import Payload
9
+ from ..serializer import AbstractSerializer
10
+ from .interface import (
11
+ InterframeCodecContext,
12
+ InterframeCodecInterface,
13
+ InterframeEncoderInitConfig,
14
+ )
15
+ from .encoder import InterframeEncoder
16
+
17
+
18
+ @dataclass
19
+ class PassOneContext:
20
+ """
21
+ Context data from the first pass of two-pass encoding.
22
+
23
+ This dataclass holds the information gathered during the first pass
24
+ over all frames, which is then used to optimize the encoding in the
25
+ second pass. For example, this could contain statistics about the
26
+ entire video sequence, codebook data, or other global information.
27
+
28
+ Subclasses should define specific fields for their encoding scheme.
29
+ """
30
+ pass
31
+
32
+
33
+ class TwoPassInterframeCodecInterface(InterframeCodecInterface):
34
+ """
35
+ Abstract interface for two-pass inter-frame encoding/decoding algorithms.
36
+
37
+ This interface extends InterframeCodecInterface to support two-pass encoding,
38
+ where the first pass gathers information about all frames, and the second
39
+ pass uses this information to optimize encoding.
40
+
41
+ Design Guidelines:
42
+ - PassOneContext: Store information gathered during the first pass
43
+ (e.g., global statistics, codebook data, quality parameters).
44
+ - The keyframe_to_context method receives PassOneContext instead of
45
+ InterframeEncoderInitConfig, allowing it to use first-pass data.
46
+ """
47
+
48
+ @abstractmethod
49
+ def keyframe_to_context_pass_one(
50
+ self,
51
+ frame: GaussianModel,
52
+ init_config: InterframeEncoderInitConfig,
53
+ ) -> PassOneContext:
54
+ """
55
+ Process the keyframe during the first pass to initialize PassOneContext.
56
+
57
+ This method is called by the encoder when processing the first frame
58
+ during the first pass. It initializes the PassOneContext with information
59
+ from the keyframe.
60
+
61
+ Args:
62
+ frame: The GaussianModel keyframe to process.
63
+ init_config: Encoder initialization configuration.
64
+
65
+ Returns:
66
+ PassOneContext initialized from the keyframe.
67
+ """
68
+ pass
69
+
70
+ @abstractmethod
71
+ def interframe_to_context_pass_one(
72
+ self,
73
+ frame: GaussianModel,
74
+ prev_pass_one_context: PassOneContext,
75
+ ) -> PassOneContext:
76
+ """
77
+ Process a frame during the first pass to update PassOneContext.
78
+
79
+ This method is called by the encoder when processing subsequent frames
80
+ during the first pass. It updates the PassOneContext with information
81
+ gathered from the current frame.
82
+
83
+ Args:
84
+ frame: The GaussianModel frame to process.
85
+ prev_pass_one_context: The PassOneContext from processing previous frames.
86
+
87
+ Returns:
88
+ Updated PassOneContext incorporating information from this frame.
89
+ """
90
+ pass
91
+
92
+ @abstractmethod
93
+ def keyframe_to_context(
94
+ self,
95
+ frame: GaussianModel,
96
+ pass_one_context: PassOneContext,
97
+ ) -> InterframeCodecContext:
98
+ """
99
+ Convert a keyframe to a Context using first-pass information.
100
+
101
+ This method is called by the encoder when processing the first frame
102
+ in the second pass. The pass_one_context provides information gathered
103
+ during the first pass for optimized encoding.
104
+
105
+ Args:
106
+ frame: The GaussianModel frame to convert.
107
+ pass_one_context: Context from the first pass containing
108
+ global encoding information.
109
+
110
+ Returns:
111
+ The corresponding Context representation.
112
+ """
113
+ pass
114
+
115
+
116
+ class TwoPassInterframeEncoder(InterframeEncoder):
117
+ """
118
+ Encoder that uses two-pass inter-frame compression.
119
+
120
+ This encoder collects frames during pack() calls (pass one), then
121
+ performs the actual encoding in flush_pack() (pass two).
122
+
123
+ Pass one: Gather information from all frames to build PassOneContext.
124
+ Pass two: Encode all frames using the gathered information.
125
+ """
126
+
127
+ def __init__(
128
+ self,
129
+ serializer: AbstractSerializer,
130
+ interface: TwoPassInterframeCodecInterface,
131
+ init_config: InterframeEncoderInitConfig,
132
+ payload_device=None,
133
+ ):
134
+ """
135
+ Initialize the two-pass inter-frame encoder.
136
+
137
+ Args:
138
+ serializer: The serializer to use for converting Payload to bytes.
139
+ interface: The TwoPassInterframeCodecInterface instance that
140
+ provides encoding methods.
141
+ init_config: Configuration parameters for encoder initialization.
142
+ """
143
+ super().__init__(serializer=serializer, interface=interface, init_config=init_config, payload_device=payload_device)
144
+ self._interface = interface
145
+ self._frames: List[GaussianModel] = []
146
+ self._pass_one_context: Optional[PassOneContext] = None
147
+
148
+ def pack(self, frame: GaussianModel) -> Iterator[Payload]:
149
+ """
150
+ Perform pass one on the frame and store it for pass two.
151
+
152
+ During pass one, this method processes each frame to gather encoding
153
+ information and stores the frame for later encoding in flush_pack().
154
+
155
+ Args:
156
+ frame: A GaussianModel instance to process.
157
+
158
+ Yields:
159
+ No payloads during pass one (empty iterator).
160
+ """
161
+ # Pass one: gather information
162
+ if self._pass_one_context is None:
163
+ # First frame: keyframe
164
+ self._pass_one_context = self._interface.keyframe_to_context_pass_one(
165
+ frame, self._init_config
166
+ )
167
+ else:
168
+ # Subsequent frames: interframe
169
+ self._pass_one_context = self._interface.interframe_to_context_pass_one(
170
+ frame, self._pass_one_context
171
+ )
172
+
173
+ # Store frame for pass two
174
+ self._frames.append(frame)
175
+
176
+ # No payloads during pass one
177
+ return
178
+ yield # Make this a generator
179
+
180
+ def flush_pack(self) -> Iterator[Payload]:
181
+ """
182
+ Perform pass two: encode all stored frames using pass one information.
183
+
184
+ This method encodes all frames that were collected during pack() calls,
185
+ using the PassOneContext gathered during pass one.
186
+
187
+ Yields:
188
+ Packed Payload instances for all frames.
189
+ """
190
+ if not self._frames or self._pass_one_context is None:
191
+ return
192
+ yield # Make this a generator
193
+
194
+ prev_context: Optional[InterframeCodecContext] = None
195
+
196
+ for frame in self._frames:
197
+ if prev_context is None:
198
+ # First frame: convert and encode as keyframe using pass_one_context
199
+ current_context = self._interface.keyframe_to_context(
200
+ frame, self._pass_one_context
201
+ )
202
+ payload = self._interface.encode_keyframe(current_context)
203
+ # Decode back to get reconstructed context (avoid error accumulation)
204
+ reconstructed_context = self._interface.decode_keyframe_for_encode(
205
+ payload, current_context
206
+ )
207
+ else:
208
+ # Subsequent frames: convert and encode as delta from previous
209
+ current_context = self._interface.interframe_to_context(
210
+ frame, prev_context
211
+ )
212
+ payload = self._interface.encode_interframe(
213
+ prev_context, current_context
214
+ )
215
+ # Decode back to get reconstructed context (avoid error accumulation)
216
+ reconstructed_context = self._interface.decode_interframe_for_encode(
217
+ payload, prev_context
218
+ )
219
+
220
+ # Use reconstructed context as previous for next frame
221
+ prev_context = reconstructed_context
222
+
223
+ yield payload
224
+
225
+ # Clear stored frames after encoding
226
+ self._frames.clear()
@@ -0,0 +1,31 @@
1
+ """
2
+ IO module for reading and writing GaussianModel frames and bytes data.
3
+ """
4
+
5
+ from .gaussian_model import FrameReader, FrameWriter
6
+ from .bytes import BytesReader, BytesWriter
7
+ from .config import (
8
+ FrameReaderConfig,
9
+ FrameWriterConfig,
10
+ build_frame_reader,
11
+ build_frame_writer,
12
+ BytesReaderConfig,
13
+ BytesWriterConfig,
14
+ build_bytes_reader,
15
+ build_bytes_writer,
16
+ )
17
+
18
+ __all__ = [
19
+ "FrameReader",
20
+ "FrameWriter",
21
+ "FrameReaderConfig",
22
+ "FrameWriterConfig",
23
+ "build_frame_reader",
24
+ "build_frame_writer",
25
+ "BytesReader",
26
+ "BytesWriter",
27
+ "BytesReaderConfig",
28
+ "BytesWriterConfig",
29
+ "build_bytes_reader",
30
+ "build_bytes_writer",
31
+ ]
@@ -0,0 +1,103 @@
1
+ """
2
+ Bytes data reader and writer classes.
3
+
4
+ This module provides classes for reading and writing bytes data from/to files.
5
+ """
6
+
7
+ import os
8
+ from typing import Iterator
9
+
10
+
11
+ class BytesReader:
12
+ """
13
+ Reader for bytes data from a file.
14
+
15
+ This class reads bytes data from a file and yields it as an iterator.
16
+ Each yielded item is a bytes chunk from the file.
17
+
18
+ Example:
19
+ reader = BytesReader("data/compressed.bin")
20
+ for chunk in reader.read():
21
+ process(chunk)
22
+ """
23
+
24
+ # Default chunk size for reading (64KB)
25
+ DEFAULT_CHUNK_SIZE = 64 * 1024
26
+
27
+ def __init__(self, path: str, chunk_size: int = DEFAULT_CHUNK_SIZE):
28
+ """
29
+ Initialize the bytes reader.
30
+
31
+ Args:
32
+ path: Path to the file to read.
33
+ chunk_size: Size of each chunk to yield in bytes.
34
+ Default is 64KB.
35
+ """
36
+ self._path = path
37
+ self._chunk_size = chunk_size
38
+
39
+ def read(self) -> Iterator[bytes]:
40
+ """
41
+ Read bytes data from the file.
42
+
43
+ Yields:
44
+ bytes chunks from the file.
45
+
46
+ Raises:
47
+ FileNotFoundError: If the file does not exist.
48
+ """
49
+ if not os.path.exists(self._path):
50
+ raise FileNotFoundError(f"File not found: {self._path}")
51
+
52
+ with open(self._path, "rb") as f:
53
+ while True:
54
+ chunk = f.read(self._chunk_size)
55
+ if not chunk:
56
+ break
57
+ yield chunk
58
+
59
+
60
+ class BytesWriter:
61
+ """
62
+ Writer for bytes data to a file.
63
+
64
+ This class writes bytes data from an iterator to a file.
65
+
66
+ Example:
67
+ writer = BytesWriter("output/compressed.bin")
68
+ writer.write(bytes_iterator)
69
+ """
70
+
71
+ def __init__(self, path: str, overwrite: bool = False):
72
+ """
73
+ Initialize the bytes writer.
74
+
75
+ Args:
76
+ path: Path to the file to write.
77
+ overwrite: If True, overwrite existing file. Default is False.
78
+ """
79
+ self._path = path
80
+ self._overwrite = overwrite
81
+
82
+ def write(self, data: Iterator[bytes]) -> None:
83
+ """
84
+ Write bytes data to the file.
85
+
86
+ Args:
87
+ data: An iterator yielding bytes chunks to write.
88
+
89
+ Raises:
90
+ FileExistsError: If the target file already exists and overwrite is False.
91
+ """
92
+ # Check if file exists before writing
93
+ if not self._overwrite and os.path.exists(self._path):
94
+ raise FileExistsError(f"File already exists: {self._path}")
95
+
96
+ # Create parent directory if needed
97
+ parent_dir = os.path.dirname(self._path)
98
+ if parent_dir and not os.path.exists(parent_dir):
99
+ os.makedirs(parent_dir, exist_ok=True)
100
+
101
+ with open(self._path, "wb") as f:
102
+ for chunk in data:
103
+ f.write(chunk)
@@ -0,0 +1,78 @@
1
+ from dataclasses import dataclass
2
+ from typing import Optional
3
+
4
+ from omegaconf import MISSING
5
+ from .gaussian_model import FrameReader, FrameWriter
6
+ from .bytes import BytesReader, BytesWriter
7
+
8
+
9
+ @dataclass
10
+ class FrameReaderConfig:
11
+ """Configuration for reading GaussianModel frame sequences."""
12
+ first_frame_path: str = MISSING
13
+ subsequent_format: str = MISSING
14
+ start_index: int = 2
15
+ sh_degree: int = 3
16
+ max_frames: Optional[int] = None
17
+ device: Optional[str] = None
18
+
19
+
20
+ @dataclass
21
+ class FrameWriterConfig:
22
+ """Configuration for writing GaussianModel frame sequences."""
23
+ first_frame_path: str = MISSING
24
+ subsequent_format: str = MISSING
25
+ start_index: int = 2
26
+ overwrite: bool = False
27
+
28
+
29
+ def build_frame_reader(config: FrameReaderConfig) -> FrameReader:
30
+ """Build a FrameReader from configuration."""
31
+ return FrameReader(
32
+ first_frame_path=config.first_frame_path,
33
+ subsequent_format=config.subsequent_format,
34
+ start_index=config.start_index,
35
+ sh_degree=config.sh_degree,
36
+ max_frames=config.max_frames,
37
+ device=config.device,
38
+ )
39
+
40
+
41
+ def build_frame_writer(config: FrameWriterConfig) -> FrameWriter:
42
+ """Build a FrameWriter from configuration."""
43
+ return FrameWriter(
44
+ first_frame_path=config.first_frame_path,
45
+ subsequent_format=config.subsequent_format,
46
+ start_index=config.start_index,
47
+ overwrite=config.overwrite,
48
+ )
49
+
50
+
51
+ @dataclass
52
+ class BytesReaderConfig:
53
+ """Configuration for reading bytes data from a file."""
54
+ path: str = MISSING
55
+ chunk_size: int = BytesReader.DEFAULT_CHUNK_SIZE
56
+
57
+
58
+ @dataclass
59
+ class BytesWriterConfig:
60
+ """Configuration for writing bytes data to a file."""
61
+ path: str = MISSING
62
+ overwrite: bool = False
63
+
64
+
65
+ def build_bytes_reader(config: BytesReaderConfig) -> BytesReader:
66
+ """Build a BytesReader from configuration."""
67
+ return BytesReader(
68
+ path=config.path,
69
+ chunk_size=config.chunk_size,
70
+ )
71
+
72
+
73
+ def build_bytes_writer(config: BytesWriterConfig) -> BytesWriter:
74
+ """Build a BytesWriter from configuration."""
75
+ return BytesWriter(
76
+ path=config.path,
77
+ overwrite=config.overwrite,
78
+ )
@@ -0,0 +1,127 @@
1
+ """
2
+ Frame reader and writer classes for GaussianModel sequences.
3
+ """
4
+
5
+ import os
6
+ from typing import Iterator
7
+
8
+ import torch
9
+ from gaussian_splatting import GaussianModel
10
+
11
+
12
+ class FrameReader:
13
+ """
14
+ Reader for GaussianModel frames from a series of files.
15
+
16
+ Example:
17
+ reader = FrameReader(
18
+ first_frame_path="data/frame_0000.ply",
19
+ subsequent_format="data/frame_{:04d}.ply",
20
+ start_index=1,
21
+ device="cuda"
22
+ )
23
+ for frame in reader.read():
24
+ process(frame)
25
+ """
26
+
27
+ def __init__(
28
+ self,
29
+ first_frame_path: str,
30
+ subsequent_format: str,
31
+ start_index: int,
32
+ sh_degree: int = 3,
33
+ max_frames: int | None = None,
34
+ device: str | torch.device | None = None,
35
+ ):
36
+ self._first_frame_path = first_frame_path
37
+ self._subsequent_format = subsequent_format
38
+ self._start_index = start_index
39
+ self._sh_degree = sh_degree
40
+ self._max_frames = max_frames
41
+ self._device = device
42
+
43
+ def to(self, device: str | torch.device | None) -> "FrameReader":
44
+ """Set the device for loaded models.
45
+
46
+ Args:
47
+ device: The device to move models to after loading.
48
+ Can be a string like "cuda" or "cpu", a torch.device, or None.
49
+
50
+ Returns:
51
+ self for method chaining.
52
+ """
53
+ self._device = device
54
+ return self
55
+
56
+ def read(self) -> Iterator[GaussianModel]:
57
+ """Read GaussianModel frames from the file sequence."""
58
+ model = GaussianModel(sh_degree=self._sh_degree)
59
+ model.load_ply(self._first_frame_path)
60
+ if self._device is not None:
61
+ model.to(self._device)
62
+ yield model
63
+
64
+ frame_count = 1
65
+ if self._max_frames is not None and frame_count >= self._max_frames:
66
+ return
67
+
68
+ index = self._start_index
69
+ while True:
70
+ path = self._subsequent_format.format(index)
71
+ model = GaussianModel(sh_degree=self._sh_degree)
72
+ model.load_ply(path)
73
+ if self._device is not None:
74
+ model.to(self._device)
75
+ yield model
76
+ frame_count += 1
77
+ if self._max_frames is not None and frame_count >= self._max_frames:
78
+ break
79
+ index += 1
80
+
81
+
82
+ class FrameWriter:
83
+ """
84
+ Writer for GaussianModel frames to a series of files.
85
+
86
+ Example:
87
+ writer = FrameWriter(
88
+ first_frame_path="output/frame_0000.ply",
89
+ subsequent_format="output/frame_{:04d}.ply",
90
+ start_index=1
91
+ )
92
+ writer.write(frame_iterator)
93
+ """
94
+
95
+ def __init__(
96
+ self,
97
+ first_frame_path: str,
98
+ subsequent_format: str,
99
+ start_index: int,
100
+ overwrite: bool = False,
101
+ ):
102
+ self._first_frame_path = first_frame_path
103
+ self._subsequent_format = subsequent_format
104
+ self._start_index = start_index
105
+ self._overwrite = overwrite
106
+
107
+ def write(self, frames: Iterator[GaussianModel]) -> None:
108
+ """Write GaussianModel frames to the file sequence."""
109
+ is_first = True
110
+ index = self._start_index
111
+
112
+ for frame in frames:
113
+ if is_first:
114
+ path = self._first_frame_path
115
+ is_first = False
116
+ else:
117
+ path = self._subsequent_format.format(index)
118
+ index += 1
119
+
120
+ if not self._overwrite and os.path.exists(path):
121
+ raise FileExistsError(f"File already exists: {path}")
122
+
123
+ parent_dir = os.path.dirname(path)
124
+ if parent_dir and not os.path.exists(parent_dir):
125
+ os.makedirs(parent_dir, exist_ok=True)
126
+
127
+ frame.save_ply(path)
@@ -0,0 +1,33 @@
1
+ """复制 cameras.json 和 cfg_args 文件从输入路径到输出路径"""
2
+
3
+ import shutil
4
+ from pathlib import Path
5
+ import argparse
6
+
7
+
8
+ def copy_configs(src: Path, dst: Path):
9
+ dst.mkdir(parents=True, exist_ok=True)
10
+ for name in ["cameras.json", "cfg_args"]:
11
+ if (src / name).exists():
12
+ shutil.copy2(src / name, dst / name)
13
+ print(f"{src / name} -> {dst / name}")
14
+
15
+
16
+ if __name__ == "__main__":
17
+ p = argparse.ArgumentParser()
18
+ p.add_argument("--first-frame-in")
19
+ p.add_argument("--first-frame-out")
20
+ p.add_argument("--subsequent-format-in")
21
+ p.add_argument("--subsequent-format-out")
22
+ p.add_argument("--max-frames", type=int)
23
+ args = p.parse_args()
24
+
25
+ if args.first_frame_in:
26
+ copy_configs(Path(args.first_frame_in), Path(args.first_frame_out))
27
+
28
+ if args.subsequent_format_in:
29
+ for i in range(2, args.max_frames + 1):
30
+ copy_configs(
31
+ Path(args.subsequent_format_in.format(i)),
32
+ Path(args.subsequent_format_out.format(i)),
33
+ )
@@ -0,0 +1,34 @@
1
+ from abc import ABC, abstractmethod
2
+ from dataclasses import dataclass
3
+ from typing import Self
4
+
5
+
6
+ @dataclass
7
+ class Payload(ABC):
8
+ """
9
+ Abstract base class for frame payload data.
10
+
11
+ This represents the intermediate data structure after processing a frame,
12
+ before serialization. Subclasses should define specific fields for their
13
+ encoding scheme.
14
+
15
+ The name "Payload" reflects its role as the processed data that will be
16
+ passed to a serializer for conversion to bytes.
17
+ """
18
+
19
+ @abstractmethod
20
+ def to(self, device) -> Self:
21
+ """
22
+ Move the Payload to the specified device for further processing.
23
+
24
+ Subclasses must implement this method to handle device transfer
25
+ (e.g., moving tensors to GPU/CPU).
26
+
27
+ Args:
28
+ device: The target device (e.g., 'cpu', 'cuda', torch.device).
29
+
30
+ Returns:
31
+ A new Payload instance on the target device, or self if already
32
+ on the target device.
33
+ """
34
+ pass
@@ -0,0 +1,42 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Iterator
3
+
4
+ from .payload import Payload
5
+
6
+
7
+ class AbstractSerializer(ABC):
8
+ """
9
+ Abstract base class for serializing Payload objects to bytes.
10
+
11
+ Subclasses must implement `serialize_frame` and `flush` methods to define
12
+ the specific serialization logic for Payload objects.
13
+ """
14
+
15
+ @abstractmethod
16
+ def serialize_frame(self, payload: Payload) -> Iterator[bytes]:
17
+ """
18
+ Serialize a single Payload object to bytes.
19
+
20
+ Args:
21
+ payload: A Payload instance to serialize.
22
+
23
+ Yields:
24
+ Serialized byte chunks. May yield zero, one, or multiple chunks.
25
+ When the iterator is exhausted, all data for this payload has been
26
+ serialized.
27
+ """
28
+ pass
29
+
30
+ @abstractmethod
31
+ def flush(self) -> Iterator[bytes]:
32
+ """
33
+ Flush any remaining buffered data.
34
+
35
+ This method should be called after all payloads have been serialized
36
+ to ensure any remaining buffered data is output.
37
+
38
+ Yields:
39
+ Remaining buffered byte chunks. May yield zero, one, or multiple
40
+ chunks until all buffered data has been flushed.
41
+ """
42
+ pass
@@ -0,0 +1,15 @@
1
+ from .interface import (
2
+ VQInterframeCodecConfig,
3
+ VQInterframeCodecContext,
4
+ VQKeyframePayload,
5
+ VQInterframePayload,
6
+ VQInterframeCodecInterface,
7
+ )
8
+
9
+ __all__ = [
10
+ 'VQInterframeCodecConfig',
11
+ 'VQInterframeCodecContext',
12
+ 'VQKeyframePayload',
13
+ 'VQInterframePayload',
14
+ 'VQInterframeCodecInterface',
15
+ ]