Ejemplo n.º 1
0
    def __save_existing(self, fileobj, atoms, path, ilst_data, padding_func):
        # Replace the old ilst atom.
        ilst = path[-1]
        offset = ilst.offset
        length = ilst.length

        # Use adjacent free atom if there is one
        free = _find_padding(path)
        if free is not None:
            offset = min(offset, free.offset)
            length += free.length

        # Always add a padding atom to make things easier
        padding_overhead = len(Atom.render(b"free", b""))
        content_size = get_size(fileobj) - (offset + length)
        padding_size = length - (len(ilst_data) + padding_overhead)
        info = PaddingInfo(padding_size, content_size)
        new_padding = info._get_padding(padding_func)
        # Limit padding size so we can be sure the free atom overhead is as we
        # calculated above (see Atom.render)
        new_padding = min(0xFFFFFFFF, new_padding)

        ilst_data += Atom.render(b"free", b"\x00" * new_padding)

        resize_bytes(fileobj, length, len(ilst_data), offset)
        delta = len(ilst_data) - length

        fileobj.seek(offset)
        fileobj.write(ilst_data)
        self.__update_parents(fileobj, path[:-1], delta)
        self.__update_offsets(fileobj, atoms, delta, offset)
Ejemplo n.º 2
0
    def __save_new(self, fileobj, atoms, ilst_data, padding_func):
        hdlr = Atom.render(b"hdlr", b"\x00" * 8 + b"mdirappl" + b"\x00" * 9)
        meta_data = b"\x00\x00\x00\x00" + hdlr + ilst_data

        try:
            path = atoms.path(b"moov", b"udta")
        except KeyError:
            path = atoms.path(b"moov")

        offset = path[-1]._dataoffset

        # ignoring some atom overhead... but we don't have padding left anyway
        # and padding_size is guaranteed to be less than zero
        content_size = get_size(fileobj) - offset
        padding_size = -len(meta_data)
        assert padding_size < 0
        info = PaddingInfo(padding_size, content_size)
        new_padding = info._get_padding(padding_func)
        new_padding = min(0xFFFFFFFF, new_padding)

        free = Atom.render(b"free", b"\x00" * new_padding)
        meta = Atom.render(b"meta", meta_data + free)
        if path[-1].name != b"udta":
            # moov.udta not found -- create one
            data = Atom.render(b"udta", meta)
        else:
            data = meta

        insert_bytes(fileobj, len(data), offset)
        fileobj.seek(offset)
        fileobj.write(data)
        self.__update_parents(fileobj, path, len(data))
        self.__update_offsets(fileobj, atoms, len(data), offset)
Ejemplo n.º 3
0
    def save(self, filething=None, padding=None):

        values = []
        items = sorted(self.items(), key=lambda kv: _item_sort_key(*kv))
        for key, value in items:
            try:
                values.append(self._render(key, value))
            except (TypeError, ValueError) as s:
                reraise(MP4MetadataValueError, s, sys.exc_info()[2])

        for key, failed in iteritems(self._failed_atoms):
            # don't write atoms back if we have added a new one with
            # the same name, this excludes freeform which can have
            # multiple atoms with the same key (most parsers seem to be able
            # to handle that)
            if key in self:
                assert _key2name(key) != b"----"
                continue
            for data in failed:
                values.append(Atom.render(_key2name(key), data))

        data = Atom.render(b"ilst", b"".join(values))

        # Find the old atoms.
        try:
            atoms = Atoms(filething.fileobj)
        except AtomError as err:
            reraise(error, err, sys.exc_info()[2])

        self.__save(filething.fileobj, atoms, data, padding)
Ejemplo n.º 4
0
 def __fixed_render_cover(self, key, value):
     atom_data = []
     for cover in value:
         try:
             imageformat = cover.imageformat
         except AttributeError:
             imageformat = MP4Cover.FORMAT_JPEG
         atom_data.append(Atom.render(b"data", struct.pack(">2I", imageformat, 0) + bytes(cover)))
     return Atom.render(_key2name(key), b"".join(atom_data))
Ejemplo n.º 5
0
 def __fixed_render_cover(self, key, value):
     atom_data = []
     for cover in value:
         try:
             imageformat = cover.imageformat
         except AttributeError:
             imageformat = MP4Cover.FORMAT_JPEG
         atom_data.append(Atom.render(
             b"data", struct.pack(">2I", imageformat, 0) + bytes(cover)))
     return Atom.render(_key2name(key), b"".join(atom_data))
Ejemplo n.º 6
0
 def __render_data(self, key, version, flags, value):
     if isinstance(value, bytes):
         return Atom.render(
             _key2name(key),
             Atom.render(
                 b"data",
                 struct.pack(">2I", version << 24 | flags, 0) + value))
     return Atom.render(
         _key2name(key), b"".join([
             Atom.render(
                 b"data",
                 struct.pack(">2I", version << 24 | flags, 0) + data)
             for data in value
         ]))
Ejemplo n.º 7
0
    def _parse_stsd(self, atom, fileobj):
        """Sets channels, bits_per_sample, sample_rate and optionally bitrate.

        Can raise MP4StreamInfoError.
        """

        assert atom.name == b"stsd"

        ok, data = atom.read(fileobj)
        if not ok:
            raise MP4StreamInfoError("Invalid stsd")

        try:
            version, flags, data = parse_full_atom(data)
        except ValueError as e:
            raise MP4StreamInfoError(e)

        if version != 0:
            raise MP4StreamInfoError("Unsupported stsd version")

        try:
            num_entries, offset = cdata.uint32_be_from(data, 0)
        except cdata.error as e:
            raise MP4StreamInfoError(e)

        if num_entries == 0:
            return

        # look at the first entry if there is one
        entry_fileobj = cBytesIO(data[offset:])
        try:
            entry_atom = Atom(entry_fileobj)
        except AtomError as e:
            raise MP4StreamInfoError(e)

        try:
            entry = AudioSampleEntry(entry_atom, entry_fileobj)
        except ASEntryError as e:
            raise MP4StreamInfoError(e)
        else:
            self.channels = entry.channels
            self.bits_per_sample = entry.sample_size
            self.sample_rate = entry.sample_rate
            self.bitrate = entry.bitrate
            self.codec = entry.codec
            self.codec_description = entry.codec_description
Ejemplo n.º 8
0
    def __render_freeform(self, key, value):
        if isinstance(value, bytes):
            value = [value]

        dummy, mean, name = _key2name(key).split(b":", 2)
        mean = struct.pack(">I4sI", len(mean) + 12, b"mean", 0) + mean
        name = struct.pack(">I4sI", len(name) + 12, b"name", 0) + name

        data = b""
        ret = b""
        for v in value:
            flags = AtomDataType.UTF8
            version = 0
            if isinstance(v, MP4FreeForm):
                flags = v.dataformat
                version = v.version

            # for every element we give render once
            data = struct.pack(">I4s2I",
                               len(v) + 16, b"data", version << 24 | flags, 0)
            data += v
            ret += Atom.render(b"----", mean + name + data)

        return ret