Spaces:
Sleeping
Sleeping
#! /usr/bin/env python | |
# -*- coding: utf-8 -*- | |
# Copyright 2016 Google Inc. All rights reserved. | |
# | |
# Licensed under the Apache License, Version 2.0 (the "License"); | |
# you may not use this file except in compliance with the License. | |
# You may obtain a copy of the License at | |
# | |
# http://www.apache.org/licenses/LICENSE-2.0 | |
# | |
# Unless required by applicable law or agreed to in writing, software | |
# distributed under the License is distributed on an "AS IS" BASIS, | |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
# See the License for the specific language governing permissions and | |
# limitations under the License. | |
"""MPEG SA3D box processing classes. | |
Enables the injection of an SA3D MPEG-4. The SA3D box specification | |
conforms to that outlined in docs/spatial-audio-rfc.md | |
""" | |
import struct | |
from spatialmedia.mpeg import box | |
from spatialmedia.mpeg import constants | |
def load(fh, position=None, end=None): | |
""" Loads the SA3D box located at position in an mp4 file. | |
Args: | |
fh: file handle, input file handle. | |
position: int or None, current file position. | |
Returns: | |
new_box: box, SA3D box loaded from the file location or None. | |
""" | |
if position is None: | |
position = fh.tell() | |
fh.seek(position) | |
new_box = SA3DBox() | |
new_box.position = position | |
size = struct.unpack(">I", fh.read(4))[0] | |
name = fh.read(4) | |
if (name != constants.TAG_SA3D): | |
print("Error: box is not an SA3D box.") | |
return None | |
if (position + size > end): | |
print("Error: SA3D box size exceeds bounds.") | |
return None | |
new_box.content_size = size - new_box.header_size | |
new_box.version = struct.unpack(">B", fh.read(1))[0] | |
new_box.ambisonic_type = struct.unpack(">B", fh.read(1))[0] | |
new_box.head_locked_stereo = (new_box.ambisonic_type & int('10000000', 2) != 0) | |
new_box.ambisonic_type = new_box.ambisonic_type & int('01111111', 2) | |
new_box.ambisonic_order = struct.unpack(">I", fh.read(4))[0] | |
new_box.ambisonic_channel_ordering = struct.unpack(">B", fh.read(1))[0] | |
new_box.ambisonic_normalization = struct.unpack(">B", fh.read(1))[0] | |
new_box.num_channels = struct.unpack(">I", fh.read(4))[0] | |
for i in range(0, new_box.num_channels): | |
new_box.channel_map.append( | |
struct.unpack(">I", fh.read(4))[0]) | |
return new_box | |
class SA3DBox(box.Box): | |
ambisonic_types = {'periphonic': 0} | |
ambisonic_orderings = {'ACN': 0} | |
ambisonic_normalizations = {'SN3D': 0} | |
def __init__(self): | |
box.Box.__init__(self) | |
self.name = constants.TAG_SA3D | |
self.header_size = 8 | |
self.version = 0 | |
self.ambisonic_type = 0 | |
self.head_locked_stereo = False | |
self.ambisonic_order = 0 | |
self.ambisonic_channel_ordering = 0 | |
self.ambisonic_normalization = 0 | |
self.num_channels = 0 | |
self.channel_map = list() | |
def create(num_channels, audio_metadata): | |
new_box = SA3DBox() | |
new_box.header_size = 8 | |
new_box.name = constants.TAG_SA3D | |
new_box.version = 0 # uint8 | |
new_box.content_size += 1 # uint8 | |
new_box.ambisonic_type = SA3DBox.ambisonic_types[ | |
audio_metadata["ambisonic_type"]] | |
new_box.head_locked_stereo = audio_metadata["head_locked_stereo"] | |
new_box.content_size += 1 # uint8 | |
new_box.ambisonic_order = audio_metadata["ambisonic_order"] | |
new_box.content_size += 4 # uint32 | |
new_box.ambisonic_channel_ordering = SA3DBox.ambisonic_orderings[ | |
audio_metadata["ambisonic_channel_ordering"]] | |
new_box.content_size += 1 # uint8 | |
new_box.ambisonic_normalization = SA3DBox.ambisonic_normalizations[ | |
audio_metadata["ambisonic_normalization"]] | |
new_box.content_size += 1 # uint8 | |
new_box.num_channels = num_channels | |
new_box.content_size += 4 # uint32 | |
channel_map = audio_metadata["channel_map"] | |
for channel_element in channel_map: | |
new_box.channel_map.append(channel_element) | |
new_box.content_size += 4 # uint32 | |
return new_box | |
def ambisonic_type_name(self): | |
return next((key for key,value in SA3DBox.ambisonic_types.items() | |
if value==self.ambisonic_type)) | |
def ambisonic_channel_ordering_name(self): | |
return next((key for key,value in SA3DBox.ambisonic_orderings.items() | |
if value==self.ambisonic_channel_ordering)) | |
def ambisonic_normalization_name(self): | |
return next((key for key,value in SA3DBox.ambisonic_normalizations.items() | |
if value==self.ambisonic_normalization)) | |
def print_box(self, console): | |
""" Prints the contents of this spatial audio (SA3D) box to the | |
console. | |
""" | |
ambisonic_type = self.ambisonic_type_name() | |
channel_ordering = self.ambisonic_channel_ordering_name() | |
ambisonic_normalization = self.ambisonic_normalization_name() | |
console("\t\tAmbisonic Type: %s" % ambisonic_type) | |
console("\t\tContains Head-Locked Stereo: %r" % self.head_locked_stereo) | |
console("\t\tAmbisonic Order: %d" % self.ambisonic_order) | |
console("\t\tAmbisonic Channel Ordering: %s" % channel_ordering) | |
console("\t\tAmbisonic Normalization: %s" % ambisonic_normalization) | |
console("\t\tNumber of Channels: %d" % self.num_channels) | |
console("\t\tChannel Map: %s" % str(self.channel_map)) | |
def get_metadata_string(self): | |
""" Outputs a concise single line audio metadata string. """ | |
metadata = "%s, %s, %s, Order %d, %d Channel(s), Channel Map: %s" \ | |
% (self.ambisonic_normalization_name(),\ | |
self.ambisonic_channel_ordering_name(),\ | |
self.ambisonic_type_name(),\ | |
self.ambisonic_order,\ | |
self.num_channels,\ | |
str(self.channel_map)) | |
return metadata | |
def save(self, in_fh, out_fh, delta): | |
if (self.header_size == 16): | |
out_fh.write(struct.pack(">I", 1)) | |
out_fh.write(struct.pack(">Q", self.size())) | |
out_fh.write(self.name) | |
elif(self.header_size == 8): | |
out_fh.write(struct.pack(">I", self.size())) | |
out_fh.write(self.name) | |
ambisonic_type = ( | |
self.ambisonic_type | int('10000000', 2) if | |
self.head_locked_stereo else self.ambisonic_type & int('01111111', 2)) | |
out_fh.write(struct.pack(">B", self.version)) | |
out_fh.write(struct.pack(">B", ambisonic_type)) | |
out_fh.write(struct.pack(">I", self.ambisonic_order)) | |
out_fh.write(struct.pack(">B", self.ambisonic_channel_ordering)) | |
out_fh.write(struct.pack(">B", self.ambisonic_normalization)) | |
out_fh.write(struct.pack(">I", self.num_channels)) | |
for i in self.channel_map: | |
if (i != None): | |
out_fh.write(struct.pack(">I", int(i))) |