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. | |
| """MPEG4 processing classes. | |
| Functions for loading MP4/MOV files and manipulating boxes. | |
| """ | |
| from spatialmedia.mpeg import box | |
| from spatialmedia.mpeg import constants | |
| from spatialmedia.mpeg import container | |
| def load(fh): | |
| """Load the mpeg4 file structure of a file. | |
| Args: | |
| fh: file handle, input file handle. | |
| position: int, current file position. | |
| size: int, maximum size. This is used to ensure correct box sizes. | |
| return: | |
| mpeg4, the loaded mpeg4 structure. | |
| """ | |
| fh.seek(0, 2) | |
| size = fh.tell() | |
| contents = container.load_multiple(fh, 0, size) | |
| if not contents: | |
| print("Error, failed to load .mp4 file.") | |
| return None | |
| elif len(contents) == 0: | |
| print("Error, no boxes found.") | |
| return None | |
| loaded_mpeg4 = Mpeg4Container() | |
| loaded_mpeg4.contents = contents | |
| for element in loaded_mpeg4.contents: | |
| if (element.name == constants.TAG_MOOV): | |
| loaded_mpeg4.moov_box = element | |
| if (element.name == constants.TAG_FREE): | |
| loaded_mpeg4.free_box = element | |
| if (element.name == constants.TAG_MDAT | |
| and not loaded_mpeg4.first_mdat_box): | |
| loaded_mpeg4.first_mdat_box = element | |
| if (element.name == constants.TAG_FTYP): | |
| loaded_mpeg4.ftyp_box = element | |
| if not loaded_mpeg4.moov_box: | |
| print("Error, file does not contain moov box.") | |
| return None | |
| if not loaded_mpeg4.first_mdat_box: | |
| print("Error, file does not contain mdat box.") | |
| return None | |
| loaded_mpeg4.first_mdat_position = \ | |
| loaded_mpeg4.first_mdat_box.position | |
| loaded_mpeg4.first_mdat_position += \ | |
| loaded_mpeg4.first_mdat_box.header_size | |
| loaded_mpeg4.content_size = 0 | |
| for element in loaded_mpeg4.contents: | |
| loaded_mpeg4.content_size += element.size() | |
| return loaded_mpeg4 | |
| class Mpeg4Container(container.Container): | |
| """Specialized behaviour for the root mpeg4 container.""" | |
| def __init__(self): | |
| self.contents = list() | |
| self.content_size = 0 | |
| self.header_size = 0 | |
| self.moov_box = None | |
| self.free_box = None | |
| self.first_mdat_box = None | |
| self.ftyp_box = None | |
| self.first_mdat_position = None | |
| self.padding = 0 | |
| def merge(self, element): | |
| """Mpeg4 containers do not support merging.""" | |
| print("Cannot merge mpeg4 files") | |
| exit(0) | |
| def print_structure(self): | |
| """Print mpeg4 file structure recursively.""" | |
| print("mpeg4 [{}]".format(self.content_size)) | |
| size = len(self.contents) | |
| for i in range(size): | |
| next_indent = " βββ" | |
| if i == (size - 1): | |
| next_indent = " βββ" | |
| self.contents[i].print_structure(next_indent) | |
| def save(self, in_fh, out_fh): | |
| """Save mpeg4 filecontent to file. | |
| Args: | |
| in_fh: file handle, source file handle for uncached contents. | |
| out_fh: file handle, destination file hand for saved file. | |
| """ | |
| self.resize() | |
| new_position = 0 | |
| for element in self.contents: | |
| if element.name == constants.TAG_MDAT: | |
| new_position += element.header_size | |
| break | |
| new_position += element.size() | |
| delta = new_position - self.first_mdat_position | |
| for element in self.contents: | |
| element.save(in_fh, out_fh, delta) |