class_docstr: Glue together encoder transform, entropy model and decoder transform
code: def compress_video(
      self, video: VideoData, force_cpu: bool = False
  ) -> Tuple[Tensor, List]:
      self._prepare_for_compression()
  
      assert (
          video.video_tensor.dim() == 5
          and video.video_tensor.shape[0] == 1
      ), f"expected batch 1 video of shape [1, T, 3, H, W], got {video.shape}"
  
      if not self._on_cpu() and force_cpu:
          raise ValueError("Compression not supported on GPU.")
  
      frames_shape = video.spatial_shape
      x = self._pad(video.video_tensor, frames_shape)
      x = Scenes(self.analysis_transform(x))
  
      # `encode_and_decode_scenes` compresses to strings if training we are in eval
      # mode and _code_to_strings is set to True
      network_outs = self.encode_and_decode_scenes(
          x, cache=None
      )  # no cache in eval
      rec_scenes = []
      bits = []
      for i, res in enumerate(network_outs):
          rec_scenes.append(res.reconstruction)  # [1, C, H, W]
          if i == 0:
              assert res.likelihood.dim() == 3
              bits.append(self.compute_rate(res.likelihood).item())
          else:
              assert res.likelihood.dim() == 1
              bits.append(res.likelihood.item())
  
      # Stack recosntructions to get [B, T, C, H, W]; sum to get total bits
      return torch.stack(rec_scenes, dim=1), bits
  
imports: import itertools
  import math
  from typing import Generator, List, NamedTuple, Optional, Sequence, Tuple, Union
  import torch
  import torch.nn
  import utils.memoize
  from datamodules.video_data_api import Scenes, VideoData
  from neural.entropy_model import PreviousLatent, VCTEntropyModel
  from torch import Tensor
label_desc: Use image node_id 14 for calling the VCTPipeline obj's underlying member method code with example usage: obj.compress_video(video, force_cpu) and returns: torch,  bits