Пример #1
0
class __JPEG__(ImageMetrics):
    SEGMENT_HEADER = Con.Struct('segment_header',
                                Con.Const(Con.Byte('header'), 0xFF),
                                Con.Byte('type'),
                                Con.If(
        lambda ctx: ctx['type'] not in (0xD8, 0xD9),
        Con.UBInt16('length')))

    APP0 = Con.Struct('JFIF_segment_marker',
                      Con.String('identifier', 5),
                      Con.Byte('major_version'),
                      Con.Byte('minor_version'),
                      Con.Byte('density_units'),
                      Con.UBInt16('x_density'),
                      Con.UBInt16('y_density'),
                      Con.Byte('thumbnail_width'),
                      Con.Byte('thumbnail_height'))

    SOF = Con.Struct('start_of_frame',
                     Con.Byte('data_precision'),
                     Con.UBInt16('image_height'),
                     Con.UBInt16('image_width'),
                     Con.Byte('components'))

    def __init__(self, width, height, bits_per_pixel):
        ImageMetrics.__init__(self, width, height, bits_per_pixel,
                              0, u'image/jpeg')

    @classmethod
    def parse(cls, file):
        try:
            header = cls.SEGMENT_HEADER.parse_stream(file)
            if (header.type != 0xD8):
                raise InvalidJPEG(_(u'Invalid JPEG header'))

            segment = cls.SEGMENT_HEADER.parse_stream(file)
            while (segment.type != 0xD9):
                if (segment.type == 0xDA):
                    break

                if (segment.type in (0xC0, 0xC1, 0xC2, 0xC3,
                                     0xC5, 0XC5, 0xC6, 0xC7,
                                     0xC9, 0xCA, 0xCB, 0xCD,
                                     0xCE, 0xCF)):  # start of frame
                    segment_data = cStringIO.StringIO(
                        file.read(segment.length - 2))
                    frame0 = cls.SOF.parse_stream(segment_data)
                    segment_data.close()

                    return __JPEG__(width=frame0.image_width,
                                    height=frame0.image_height,
                                    bits_per_pixel=(frame0.data_precision *
                                                    frame0.components))
                else:
                    file.seek(segment.length - 2, 1)

                segment = cls.SEGMENT_HEADER.parse_stream(file)

            raise InvalidJPEG(_(u'Start of frame not found'))
        except Con.ConstError:
            raise InvalidJPEG(_(u"Invalid JPEG segment marker at 0x%X") % \
                                  (file.tell()))
Пример #2
0
class DiscID:
    """An object representing a 32 bit FreeDB disc ID value."""

    DISCID = Con.Struct('discid', Con.UBInt8('digit_sum'),
                        Con.UBInt16('length'), Con.UBInt8('track_count'))

    def __init__(self, tracks=[], offsets=None, length=None, lead_in=150):
        """Fields are as follows:

        tracks  - a list of track lengths in CD frames
        offsets - a list of track offsets in CD frames
        length  - the length of the entire disc in CD frames
        lead_in - the location of the first track on the CD, in frames

        These fields are all optional.
        One will presumably fill them with data later in that event.
        """

        self.tracks = tracks
        self.__offsets__ = offsets
        self.__length__ = length
        self.__lead_in__ = lead_in

    @classmethod
    def from_cdda(cls, cdda):
        """Given a CDDA object, returns a populated DiscID.

        May raise ValueError if there are no audio tracks on the CD."""

        tracks = list(cdda)
        if (len(tracks) < 1):
            raise ValueError(_(u"no audio tracks in CDDA object"))

        return cls(tracks=[t.length() for t in tracks],
                   offsets=[t.offset() for t in tracks],
                   length=cdda.length(),
                   lead_in=tracks[0].offset())

    def add(self, track):
        """Adds a new track length, in CD frames."""

        self.tracks.append(track)

    def offsets(self):
        """Returns a list of calculated offset integers, from track lengths."""

        if (self.__offsets__ is None):
            offsets = [self.__lead_in__]

            for track in self.tracks[0:-1]:
                offsets.append(track + offsets[-1])

            return offsets
        else:
            return self.__offsets__

    def length(self):
        """Returns the total length of the disc, in seconds."""

        if (self.__length__ is None):
            return sum(self.tracks)
        else:
            return self.__length__

    def idsuffix(self):
        """Returns a FreeDB disc ID suffix string.

        This is for making server queries."""

        return str(len(self.tracks)) + " " + \
               " ".join([str(offset) for offset in self.offsets()]) + \
               " " + str((self.length() + self.__lead_in__) / 75)

    def __str__(self):
        def __count_digits__(i):
            if (i == 0):
                return 0
            else:
                return (i % 10) + __count_digits__(i / 10)

        disc_id = Con.Container()

        disc_id.track_count = len(self.tracks)
        disc_id.length = self.length() / 75
        disc_id.digit_sum = sum(
            [__count_digits__(o / 75) for o in self.offsets()]) % 0xFF

        return DiscID.DISCID.build(disc_id).encode('hex')

    def freedb_id(self):
        """Returns the entire FreeDB disc ID, including suffix."""

        return str(self) + " " + self.idsuffix()

    def toxmcd(self, output):
        """Writes a newly created XMCD file to output.

        Its values are populated from this DiscID's fields."""

        output.write(
            XMCD.from_tracks([
                DummyAudioFile(length, None, i + 1)
                for (i, length) in enumerate(self.tracks)
            ]).to_string())