def check_subtitle_cpl_duplicated_uuid(self, playlist, asset, folder): """ Issue when using the same UUID for Subtitle XML and MXF. This can cause issue on certain hardware, eg. Dolby server using a version prior to 2.8.18, see patch notes extract below : Fixed an error where the server did not extract SMPTE timedtext (as in subtitles/captions) from the MXF file that was incorrectly created using the same universally unique identifier (UUID) for the MXF file and the main XML inside the MXF files. [DCPLYR-3418] """ st_dict = self.st_util.get_subtitle_xml(asset, folder) if not st_dict: return st_uuid = self.st_util.get_subtitle_uuid(st_dict) _, asset = asset if self.dcp.schema == 'SMPTE': mxf_uuid = asset['Probe'].get('AssetUUID', "") if st_uuid == mxf_uuid: raise CheckException( "Using the same UUID for Subtitle ID and MXF UUID can " "cause issue on Dolby server prior to 2.8.18 firmware.")
def check_cpl_reel_coherence(self, playlist): """ CPL reel attributes shall be coherents across all reels. """ cpl = playlist['Info']['CompositionPlaylist'] coherence_keys = [ 'EditRate', 'FrameRate', 'HighFrameRate', 'ScreenAspectRatio', 'Stereoscopic', 'Resolution', 'DecompositionLevels', 'Precincts', 'ChannelCount', 'ChannelFormat', 'ChannelConfiguration', 'SoundLanguage', ] for k in coherence_keys: if cpl[k] == "Mixed": raise CheckException( "{} is not coherent for all reels".format(k))
def check_dcnc_field_claim_audio(self, playlist, fields): """ Audio format from CPL and ContentTitleText shall match. """ # NOTE : MXF track count don't seems to be related to the actual # number of audio channels (there could be metadata and/or reserved # tracks). # TODO : SMPTE 428-12 add SoundField UL structure that could be used # to have a more meanigful check audio_format = fields['AudioType'].get('Channels') audio_map = DCP_SETTINGS['sound']['format_channels'] sounds = list( list_cpl_assets(playlist, filters=['Sound'], required_keys=['Probe'])) if sounds and audio_format: _, asset = sounds[0] asset_cc = asset['Probe']['ChannelCount'] cpl_cc = audio_map.get(audio_format) if cpl_cc and asset_cc < cpl_cc: raise CheckException( "ContentTitle claims {} audio but CPL contains only " " {} channels".format(audio_format, asset_cc))
def check_certif_date(self, cert, index): """ Certificate date validation. Reference : SMPTE 430-2-2017 6.2 9 """ # 9. Check time validity # Note : Date are formatted in ASN.1 Time YYYYMMDDhhmmssZ time_format = '%Y%m%d%H%M%SZ' if self.context_time == 'NOW': validity_time = datetime.now() elif self.context_time != '': validity_time = datetime.strptime(self.context_time, time_format) if self.context_time: not_before_str = cert.get_notBefore().decode("utf-8") not_before = datetime.strptime(not_before_str, time_format) not_after_str = cert.get_notAfter().decode("utf-8") not_after = datetime.strptime(not_after_str, '%Y%m%d%H%M%SZ') if validity_time < not_before or validity_time > not_after: raise CheckException("Certificate is not valid at this time")
def check_subtitle_cpl_font_ref(self, playlist, asset, folder): """ Subtitle font references check. Reference : SMPTE ST 428-7-2014 5.11.1 Interop TI Subtitle Spec 1.1 2.7 """ st_dict = self.st_util.get_subtitle_xml(asset, folder) if not st_dict: return if self.dcp.schema == 'SMPTE': font_id = self.st_util.get_subtitle_elem(st_dict, 'LoadFont@ID') font_ref = keys_by_name_dict(st_dict, 'Font@ID') else: font_id = self.st_util.get_subtitle_elem(st_dict, 'LoadFont@Id') font_ref = keys_by_name_dict(st_dict, 'Font@Id') for ref in font_ref: if ref != font_id: raise CheckException( "Subtitle reference unknown font {} (loaded {})".format( ref, font_id))
def metadata_cmp_pair( self, playlist, metadata, type_a, type_b, cmp=operator.eq, message="" ): for reel in playlist['Info']['CompositionPlaylist']['ReelList']: metadatas = { k: v[metadata] for k, v in six.iteritems(reel['Assets']) if v.get(metadata) and k in [type_a, type_b] } vals = list(metadatas.values()) if len(vals) == 2 and not cmp(vals[0], vals[1]): what = "{} / {} {} mismatch for Reel {}".format( type_a, type_b, metadata, reel['Position']) if message: what += ", {}".format(message) raise CheckException(what)
def check_picture_cpl_encoding(self, playlist, asset): """ Picture wavelet transform levels SMPTE compliance. """ resolutions = self.settings['resolutions'] levels_map = { '2K': self.settings['dwt_levels_2k'], '4K': self.settings['dwt_levels_4k'], } _, asset = asset if 'Probe' in asset and self.dcp.schema == 'SMPTE': levels = asset['Probe']['DecompositionLevels'] resolution = asset['Probe']['Resolution'] resolution_name = '' for k, v in six.iteritems(resolutions): if resolution in v: resolution_name = k break is_dci = resolution_name in levels_map if is_dci and levels_map[resolution_name] != levels: raise CheckException( "Picture must have {} wavelet transform levels, {}" " found".format(levels_map[resolution_name], levels))
def check_assets_cpl_hash(self, playlist, asset): """ CPL assets Hash shall be present alongside KeyId (encrypted). """ if 'KeyId' in asset and 'Hash' not in asset: raise CheckException("Encrypted asset must have a Hash element")
def check_cpl_referenced_by_pkl(self, playlist): """ CPL shall be present in PKL. """ cpl = playlist['Info']['CompositionPlaylist'] pkl_id = cpl.get('PKLId') if not pkl_id: raise CheckException("CPL is not referenced in any PKL")
def check_sign_issuer_serial(self, source): """ XML signature serial number check. """ sig = source['Signer']['X509Data']['X509IssuerSerial'] # Signer Serial number if sig['X509SerialNumber'] != self.cert_list[-1].get_serial_number(): raise CheckException("Invalid Signer Serial Number")
def check_certif_version(self, cert, index): """ Certificate version check (X509 v3). """ if cert.get_version() != crypto.x509.Version.v3.value: raise CheckException("Invalid certificate version")
def check_assets_am_uuid(self, am, asset): """ AssetMap UUIDs validation. """ uuid, _, _ = asset if not check_uuid(uuid): raise CheckException( "Invalid uuid found : {}".format(uuid))
def check_assets_pkl_referenced_by_assetamp(self, pkl, asset): """ PKL assets shall be present in AssetMap. """ uuid, _, _ = asset # Note : dcp._list_asset is directly extracted from Assetmap if uuid not in self.dcp._list_asset.keys(): raise CheckException("Not present in Assetmap")
def check_dcnc_field_claim_standard(self, playlist, fields): """ DCP Standard coherence check. """ standard = fields['Standard'].get('Schema') if standard and standard != self.dcp.schema: raise CheckException("ContentTitle claims {} but DCP schema is {}".format( standard, self.dcp.schema))