Spaces:
Runtime error
Runtime error
| #! /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))) |