2727from cai .core import insert_xmp_key
2828from cai .jumbf import create_codestream_superbox
2929from cai .jumbf import create_json_superbox
30+ from cai .jumbf import get_app11_marker_segment_headers
3031from cai .jumbf import json_to_bytes
3132
3233'''Starling CLI tool to generate CAI metadata.
@@ -87,9 +88,21 @@ def main():
8788 # read media content if injection is enabled
8889 with open (args .inject , 'rb' ) as f :
8990 raw_bytes = f .read ()
91+
92+ # get the label of the last Claim
9093 parent_claim = get_xmp_tag (raw_bytes )
9194 print ('parent_claim: ' , parent_claim )
9295
96+ # get App11 marker segment headers
97+ app11_headers = get_app11_marker_segment_headers (raw_bytes )
98+ has_app11_headers = True if len (app11_headers ) > 0 else False
99+ if has_app11_headers :
100+ print ('Find App11 marker segments' )
101+ for i in range (1 , len (app11_headers ) + 1 ):
102+ print ('\t {0}' .format (app11_headers [i ]))
103+ else :
104+ print ('No existing App11 marker segment.' )
105+
93106 # create CAI metadata
94107 assertions = []
95108 for filepath , label in zip (assertion_filepaths , assertion_labels ):
@@ -104,37 +117,154 @@ def main():
104117 raise Exception (
105118 'Unknown assertion type {0} from {1}' .format (fileext , filepath ))
106119
120+ # private key for signature
107121 if key_filepath != '' :
108122 with open (key_filepath , 'rb' ) as f :
109123 key = f .read ()
110124 else :
111125 key = []
112126
127+ # create a new Store
113128 cai_store = CaiStore (label = store_label ,
114129 assertions = assertions ,
115130 recorder = recorder ,
116131 parent_claim = parent_claim ,
117132 key = key )
118- cai_claim_block = CaiClaimBlock ()
119- cai_claim_block .content_boxes .append (cai_store )
120- cai_segment = App11Box ()
121- cai_segment .payload = cai_claim_block .convert_bytes ()
133+ if has_app11_headers :
134+ # get last segment header information
135+ header_number = len (app11_headers )
136+ last_le = app11_headers [header_number ]['le' ]
137+ last_en = app11_headers [header_number ]['en' ]
138+ last_z = app11_headers [header_number ]['z' ]
139+ last_lbox = app11_headers [header_number ]['lbox' ]
140+ last_tbox = app11_headers [header_number ]['tbox' ]
141+ last_offset = app11_headers [header_number ]['offset' ]
142+ print ('Last Le: {0}, En: {1}, Z: {2}, LBox: {3}, TBox: {4}, offset: {5}' .format (
143+ last_le , last_en , last_z , last_lbox , last_tbox , last_offset ))
122144
123- # output CAI metadata
124- if len (args .output ) == 0 :
125- print (cai_segment .convert_bytes ().hex ())
145+ # re-construct Claim Block payload
146+ claim_block_payload = bytearray ()
147+ for i in range (1 , header_number + 1 ):
148+ payload_start = app11_headers [i ]['offset' ] + 20
149+ payload_end = payload_start + (app11_headers [i ]['le' ] - 18 )
150+ payload = raw_bytes [payload_start : payload_end ]
151+ claim_block_payload += payload
152+ print ('Claim Block payload size (Description/Content Boxes): {}' .format (len (claim_block_payload )))
153+
154+ print ('Claim Block' )
155+ print ('\t Superbox' )
156+ print ('\t \t LBox: {0}, TBox: {1}' .format (last_lbox , last_tbox ))
157+
158+ print ('\t Description Box' )
159+ offset_d = 0
160+ lbox_d = int .from_bytes (claim_block_payload [offset_d : offset_d + 4 ], byteorder = 'big' )
161+ tbox_d = claim_block_payload [offset_d + 4 : offset_d + 8 ].decode ('utf-8' )
162+ print ('\t \t offset: {}' .format (offset_d ))
163+ print ('\t \t LBox: {0}, TBox: {1}' .format (lbox_d , tbox_d ))
164+ print ('\t \t Type: {}' .format (claim_block_payload [offset_d + 8 : offset_d + 24 ].hex ()))
165+ print ('\t \t Toggles: {}' .format (claim_block_payload [offset_d + 24 : offset_d + 25 ].hex ()))
166+ print ('\t \t Label: {}' .format (claim_block_payload [offset_d + 25 : lbox_d ].decode ('utf-8' )))
167+
168+ print ('\t Content Box (Stores)' )
169+ print ('\t \t Store #1' )
170+ print ('\t \t Superbox' )
171+ offset_s = lbox_d
172+ lbox_s = int .from_bytes (claim_block_payload [offset_s : offset_s + 4 ], byteorder = 'big' )
173+ tbox_s = claim_block_payload [offset_s + 4 : offset_s + 8 ].decode ('utf-8' )
174+ print ('\t \t \t offset: {}' .format (offset_s ))
175+ print ('\t \t \t LBox: {0}, TBox: {1}' .format (lbox_s , tbox_s ))
176+ print ('\t \t Description Box' )
177+ offset_d = offset_s + 8
178+ lbox_d = int .from_bytes (claim_block_payload [offset_d : offset_d + 4 ], byteorder = 'big' )
179+ tbox_d = claim_block_payload [offset_d + 4 : offset_d + 8 ].decode ('utf-8' )
180+ print ('\t \t \t offset: {}' .format (offset_d ))
181+ print ('\t \t \t LBox: {0}, TBox: {1}' .format (lbox_d , tbox_d ))
182+ print ('\t \t \t Type: {}' .format (claim_block_payload [offset_d + 8 : offset_d + 24 ].hex ()))
183+ print ('\t \t \t Toggles: {}' .format (claim_block_payload [offset_d + 24 : offset_d + 25 ].hex ()))
184+ print ('\t \t \t Label: {}' .format (claim_block_payload [offset_d + 25 : offset_d + lbox_d ].decode ('utf-8' )))
185+ print ('\t \t Content Box' )
186+ offset_c = offset_d + lbox_d
187+ lbox_c = int .from_bytes (claim_block_payload [offset_c : offset_c + 4 ], byteorder = 'big' )
188+ tbox_c = claim_block_payload [offset_c + 4 : offset_c + 8 ].decode ('utf-8' )
189+ print ('\t \t \t offset: {}' .format (offset_c ))
190+ print ('\t \t \t LBox: {0}, TBox: {1}' .format (lbox_c , tbox_c ))
191+ print ('\t \t \t Type: {}' .format (claim_block_payload [offset_c + 8 : offset_c + 24 ].hex ()))
192+
193+ print ('\t \t Store #2' )
194+ print ('\t \t Superbox' )
195+ offset_s = offset_s + lbox_s
196+ lbox_s = int .from_bytes (claim_block_payload [offset_s : offset_s + 4 ], byteorder = 'big' )
197+ tbox_s = claim_block_payload [offset_s + 4 : offset_s + 8 ].decode ('utf-8' )
198+ print ('\t \t \t offset: {}' .format (offset_s ))
199+ print ('\t \t \t LBox: {0}, TBox: {1}' .format (lbox_s , tbox_s ))
200+ print ('\t \t Description Box' )
201+ offset_d = offset_s + 8
202+ lbox_d = int .from_bytes (claim_block_payload [offset_d : offset_d + 4 ], byteorder = 'big' )
203+ tbox_d = claim_block_payload [offset_d + 4 : offset_d + 8 ].decode ('utf-8' )
204+ print ('\t \t \t offset: {}' .format (offset_d ))
205+ print ('\t \t \t LBox: {0}, TBox: {1}' .format (lbox_d , tbox_d ))
206+ print ('\t \t \t Type: {}' .format (claim_block_payload [offset_d + 8 : offset_d + 24 ].hex ()))
207+ print ('\t \t \t Toggles: {}' .format (claim_block_payload [offset_d + 24 : offset_d + 25 ].hex ()))
208+ print ('\t \t \t Label: {}' .format (claim_block_payload [offset_d + 25 : offset_d + lbox_d ].decode ('utf-8' )))
209+
210+ print ('\t \t Store #3 (newly created)' )
211+ print ('\t \t Superbox' )
212+ store_bytes = cai_store .convert_bytes ()
213+ offset_s = 0
214+ lbox_s = int .from_bytes (store_bytes [offset_s : offset_s + 4 ], byteorder = 'big' )
215+ tbox_s = store_bytes [offset_s + 4 : offset_s + 8 ].decode ('utf-8' )
216+ print ('\t \t \t offset: {}' .format (offset_s ))
217+ print ('\t \t \t LBox: {0}, TBox: {1}' .format (lbox_s , tbox_s ))
218+ print ('\t \t Description Box' )
219+ offset_d = offset_s + 8
220+ lbox_d = int .from_bytes (store_bytes [offset_d : offset_d + 4 ], byteorder = 'big' )
221+ tbox_d = store_bytes [offset_d + 4 : offset_d + 8 ].decode ('utf-8' )
222+ print ('\t \t \t LBox: {0}, TBox: {1}' .format (lbox_d , tbox_d ))
223+ print ('\t \t \t Type: {}' .format (store_bytes [offset_d + 8 : offset_d + 24 ].hex ()))
224+ print ('\t \t \t Toggles: {}' .format (store_bytes [offset_d + 24 : offset_d + 25 ].hex ()))
225+ print ('\t \t \t Label: {}' .format (store_bytes [offset_d + 25 : offset_d + lbox_d ].decode ('utf-8' )))
226+
227+ # append new Store bytes
228+ updated_claim_block_payload = claim_block_payload + store_bytes
229+ updated_lbox = last_lbox + len (store_bytes )
230+ #updated_tbox = last_tbox
231+ updated_claim_block_bytes = updated_lbox .to_bytes (4 , byteorder = 'big' ) + b'jumb' + updated_claim_block_payload
232+ updated_app11_segment = App11Box (en = last_en )
233+ updated_app11_segment .payload = updated_claim_block_bytes
234+
235+ update_range_s = app11_headers [1 ]['offset' ]
236+ update_range_e = app11_headers [1 ]['offset' ] + last_lbox + 16
237+ print ('Update range: {0} - {1}' .format (update_range_s , update_range_e ))
238+
239+ if len (args .inject ) > 0 :
240+ fname , fext = os .path .splitext (args .inject )
241+ fpath = fname + '-cai' + fext
242+ with open (fpath , 'wb' ) as f :
243+ data_bytes = raw_bytes [:update_range_s ] + updated_app11_segment .convert_bytes () + raw_bytes [update_range_e :]
244+ cai_data_bytes = insert_xmp_key (data_bytes , store_label = store_label )
245+ f .write (cai_data_bytes )
126246 else :
127- with open (args .output , 'w' ) as f :
128- f .write (cai_segment .convert_bytes ().hex ())
129-
130- # inject CAI metadata
131- if len (args .inject ) > 0 :
132- fname , fext = os .path .splitext (args .inject )
133- fpath = fname + '-cai' + fext
134- with open (fpath , 'wb' ) as f :
135- data_bytes = raw_bytes [0 :2 ] + cai_segment .convert_bytes () + raw_bytes [2 :]
136- cai_data_bytes = insert_xmp_key (data_bytes , store_label = store_label )
137- f .write (cai_data_bytes )
247+ # create a new Claim Block Box
248+ cai_claim_block = CaiClaimBlock ()
249+ cai_claim_block .content_boxes .append (cai_store )
250+ cai_segment = App11Box ()
251+ cai_segment .payload = cai_claim_block .convert_bytes ()
252+
253+ # inject CAI metadata
254+ if len (args .inject ) > 0 :
255+ fname , fext = os .path .splitext (args .inject )
256+ fpath = fname + '-cai' + fext
257+ with open (fpath , 'wb' ) as f :
258+ data_bytes = raw_bytes [0 :2 ] + cai_segment .convert_bytes () + raw_bytes [2 :]
259+ cai_data_bytes = insert_xmp_key (data_bytes , store_label = store_label )
260+ f .write (cai_data_bytes )
261+
262+ # output CAI metadata
263+ #if len(args.output) == 0:
264+ # print(cai_segment.convert_bytes().hex())
265+ #else:
266+ # with open(args.output, 'w') as f:
267+ # f.write(cai_segment.convert_bytes().hex())
138268
139269
140270if __name__ == "__main__" :
0 commit comments