Module: Robocap::SDK::Mp4Cenc

Defined in:
lib/robocap/sdk/mp4_cenc.rb

Constant Summary collapse

CEKA_TAG =
'cenc_cek_wrapped_b64'
CUSTOMER_ID_TAG =
'cenc_customer_id'
CUSTOMER_ID_FALLBACK_TAG =
'username'
KID_TAG =
'cenc_kid_hex'
CENC_WRAPPED_ALGO_TAG =
'cenc_wrapped_algo'
CENC_STRIP_TAGS_ON_DECRYPT =
[
  CEKA_TAG,
  CENC_WRAPPED_ALGO_TAG,
].freeze

Class Method Summary collapse

Class Method Details

.has_cenc_tags(mp4_path, ffprobe_executable: nil) ⇒ Object



105
106
107
108
109
110
111
# File 'lib/robocap/sdk/mp4_cenc.rb', line 105

def has_cenc_tags(mp4_path, ffprobe_executable: nil)
  tags = read_format_tags(mp4_path, ffprobe_executable: ffprobe_executable)
  return false unless tags[CEKA_TAG] && !tags[CEKA_TAG].empty?
  has_customer_id_source?(tags)
rescue Error
  false
end

.load_cenc_metadata(mp4_path, ffprobe_executable: nil) ⇒ Object



99
100
101
102
103
# File 'lib/robocap/sdk/mp4_cenc.rb', line 99

def (mp4_path, ffprobe_executable: nil)
  (
    read_format_tags(mp4_path, ffprobe_executable: ffprobe_executable),
  )
end

.parse_cenc_metadata_from_tags(tags) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/robocap/sdk/mp4_cenc.rb', line 66

def (tags)
  unless tags[CEKA_TAG] && !tags[CEKA_TAG].empty?
    raise Error.new(
      code: ErrorCode::ERR_CENC_TAGS_MISSING,
      message: "Missing CENC tags: #{CEKA_TAG}",
    )
  end

  customer_id = resolve_customer_id(tags)

  begin
    cek_wrapped = Base64.strict_decode64(tags[CEKA_TAG])
  rescue ArgumentError
    raise Error.new(
      code: ErrorCode::ERR_CENC_TAGS_MISSING,
      message: 'Invalid cenc_cek_wrapped_b64 Base64',
    )
  end

  unless cek_wrapped.bytesize == Config::RSA_2048_CIPHERTEXT_BYTES
    raise Error.new(
      code: ErrorCode::ERR_CENC_CEKA_WRAP,
      message: "Wrapped CEK must be #{Config::RSA_2048_CIPHERTEXT_BYTES} bytes",
    )
  end

  kid = tags[KID_TAG]
  kid = kid.strip.downcase if kid
  kid = nil if kid && kid.empty?

  CencMp4Metadata.new(customer_id: customer_id, cek_wrapped: cek_wrapped, kid_hex: kid)
end

.read_format_tags(mp4_path, ffprobe_executable: nil) ⇒ Object



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/robocap/sdk/mp4_cenc.rb', line 29

def read_format_tags(mp4_path, ffprobe_executable: nil)
  path = Pathname(mp4_path).expand_path
  unless path.file?
    raise Error.new(
      code: ErrorCode::ERR_CENC_TAGS_MISSING,
      message: "MP4 file not found: #{path}",
    )
  end
  exe = FfmpegCli.resolve_ffprobe_executable(ffprobe_executable)
  stdout, stderr, status = FfmpegCli.open3_capture3(
    exe, '-v', 'error', '-show_format', '-print_format', 'json', path.to_s,
  )
  unless status.exitstatus.zero?
    raise Error.new(
      code: ErrorCode::ERR_CENC_FFPROBE_FAILED,
      message: "ffprobe failed: #{stderr}",
    )
  end

  payload = parse_ffprobe_json(stdout)
  fmt = payload['format']
  unless fmt.is_a?(Hash)
    raise Error.new(
      code: ErrorCode::ERR_CENC_TAGS_MISSING,
      message: 'ffprobe output missing format section',
    )
  end
  tags = fmt['tags']
  unless tags.is_a?(Hash)
    raise Error.new(
      code: ErrorCode::ERR_CENC_TAGS_MISSING,
      message: 'MP4 has no format metadata tags',
    )
  end
  tags.transform_keys(&:to_s).transform_values(&:to_s)
end