Dolby metadata integration

This commit is contained in:
Jamie Hardt
2022-11-24 22:05:31 -08:00
parent b6acdb1f7f
commit e83603cb47
2 changed files with 50 additions and 15 deletions

View File

@@ -12,10 +12,11 @@ from struct import unpack
from dataclasses import dataclass
from typing import Optional, Tuple, Any
from io import BytesIO
class SegmentType(IntEnum):
"""
Metadata segment type.
"""
EndMarker = 0x0
DolbyE = 0x1
@@ -78,6 +79,9 @@ class DolbyDigitalPlusMetadata:
class DolbySurroundEncodingMode(Enum):
"""
Dolby surround endcoding mode.
"""
RESERVED = 0b11
IN_USE = 0b10
NOT_IN_USE = 0b01
@@ -169,13 +173,13 @@ class DolbyDigitalPlusMetadata:
RESERVED = 0b11
class LanguageCode(Enum):
class LanguageCode(int):
"""
§ 4.3.4.1
Per ATSC/A52 § 5.4.2.12 this is not in use and always 0xFF
"""
NONE = 0xff
Per ATSC/A52 § 5.4.2.12, this is not in use and always 0xFF.
"""
pass
class MixLevel(int):
@@ -204,6 +208,8 @@ class DolbyDigitalPlusMetadata:
class PreferredDownMixMode(Enum):
"""
Indicates the creating engineer's preference of what the receiver should
downmix.
§ 4.3.8.1
"""
NOT_INDICATED = 0b00
@@ -214,6 +220,7 @@ class DolbyDigitalPlusMetadata:
class SurroundEXMode(IntEnum):
"""
Dolby Surround-EX mode.
`dsurexmod` § 4.3.9.1
"""
NOT_INDICATED = 0b00
@@ -332,10 +339,15 @@ class DolbyDigitalPlusMetadata:
#: Dolby Headphone mode
dolby_headphone_encoded: HeadphoneMode
ad_converter_type: ADConverterType
compression_profile: RFCompressionProfile
dynamic_range: RFCompressionProfile
stream_type: StreamDependency
#: Indicates if this stream can be decoded independently or not
stream_dependency: StreamDependency
#: Data rate of this bitstream in kilobits per second
datarate_kbps: int
@staticmethod
@@ -460,11 +472,11 @@ class DolbyDigitalPlusMetadata:
ad_converter_type=ad_converter_type,
compression_profile=compression,
dynamic_range=dynamic_range,
stream_type=stream_info,
stream_dependency=stream_info,
datarate_kbps=data_rate)
class WavDolbyChunkReader:
class WavDolbyMetadataReader:
"""
Reads Dolby bitstream metadata.
"""
@@ -477,8 +489,23 @@ class WavDolbyChunkReader:
#: not recognized).
segment_list: Tuple[SegmentType | int, bool, Any]
version: str
def __init__(self, dbmd_data) -> None:
self.segment_list = []
h = BytesIO(dbmd_data)
v_vec = []
for _ in range(4):
b = h.read(1)
v_vec.insert(0, str(unpack("B", b[0:1])))
self.version = ".".join(v_vec)

View File

@@ -12,6 +12,7 @@ from .wave_ixml_reader import WavIXMLFormat
from .wave_bext_reader import WavBextReader
from .wave_info_reader import WavInfoChunkReader
from .wave_adm_reader import WavADMReader
from .wave_dbmd_reader import WavDolbyMetadataReader
#: Calculated statistics about the audio data.
WavDataDescriptor = namedtuple('WavDataDescriptor', 'byte_count frame_count')
@@ -63,20 +64,23 @@ class WavInfoReader:
self.path = absolute_path
#: Wave audio data format
self.fmt :Optional[WavAudioFormat] = None
":class:`wavinfo.wave_reader.WavAudioFormat`"
#: Broadcast-Wave metadata
self.bext :Optional[WavBextReader] = None
":class:`wavinfo.wave_bext_reader.WavBextReader` with Broadcast-WAV metadata"
#: iXML metadata
self.ixml :Optional[WavIXMLFormat] = None
":class:`wavinfo.wave_ixml_reader.WavIXMLFormat` with iXML metadata"
#: ADM Audio Definiton Model metadata
self.adm :Optional[WavADMReader]= None
":class:`wavinfo.wave_axml_reader.WavADMReader` with ADM metadata"
#: Dolby Bitstream Metadata
self.dolby :Optional[WavDolbyMetadataReader] = None
#: RIFF INFO Metadata
self.info :Optional[WavInfoChunkReader]= None
":class:`wavinfo.wave_info_reader.WavInfoChunkReader` with RIFF INFO metadata"
with open(path, 'rb') as f:
self.get_wav_info(f)
@@ -92,6 +96,7 @@ class WavInfoReader:
self.ixml = self._get_ixml(wavfile)
self.adm = self._get_adm(wavfile)
self.info = self._get_info(wavfile, encoding=self.info_encoding)
self.dolby = self._get_dbmd(wavfile)
self.data = self._describe_data()
def _find_chunk_data(self, ident, from_stream, default_none=False):
@@ -150,9 +155,13 @@ class WavInfoReader:
chna = self._find_chunk_data(b'chna', f, default_none=True)
return WavADMReader(axml_data=axml, chna_data=chna) if axml and chna else None
def _get_dbmd(self, f):
dbmd_data = self._find_chunk_data(b'dbmd', f, default_none=True)
return WavDolbyMetadataReader(dbmd_data=dbmd_data) if dbmd_data else None
def _get_ixml(self, f):
ixml_data = self._find_chunk_data(b'iXML', f, default_none=True)
return None if ixml_data is None else WavIXMLFormat(ixml_data.rstrip(b'\0'))
return WavIXMLFormat(ixml_data.rstrip(b'\0')) if ixml_data else None
def walk(self) -> Generator[str,str,Any]:
"""
@@ -176,6 +185,5 @@ class WavInfoReader:
for key in dict.keys():
yield scope, key, dict[key]
def __repr__(self):
return 'WavInfoReader({}, {}, {})'.format(self.path, self.info_encoding, self.bext_encoding)