360-video-injector / spatialmedia /mpeg /mpeg4_container.py
ender
HF Ready
a92043d
#! /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)