Example #1
0
    def process_video_track(self, elements):
        track = core.VideoStream()
        # Defaults
        track.codec = u'Unknown'
        track.fps = 0
        self.set_track_defaults(track)

        for elem in elements:
            elem_id = elem.get_id()
            if elem_id == MATROSKA_CODEC_ID:
                track.codec = elem.get_str()

            elif elem_id == MATROSKA_FRAME_DURATION_ID:
                try:
                    track.fps = 1 / (pow(10, -9) * (elem.get_value()))
                except ZeroDivisionError:
                    pass

            elif elem_id == MATROSKA_VIDEO_SETTINGS_ID:
                d_width = d_height = None
                for settings_elem in self.process_one_level(elem):
                    settings_elem_id = settings_elem.get_id()
                    if settings_elem_id == MATROSKA_VIDEO_WIDTH_ID:
                        track.width = settings_elem.get_value()
                    elif settings_elem_id == MATROSKA_VIDEO_HEIGHT_ID:
                        track.height = settings_elem.get_value()
                    elif settings_elem_id == MATROSKA_VIDEO_DISPLAY_WIDTH_ID:
                        d_width = settings_elem.get_value()
                    elif settings_elem_id == MATROSKA_VIDEO_DISPLAY_HEIGHT_ID:
                        d_height = settings_elem.get_value()
                    elif settings_elem_id == MATROSKA_VIDEO_INTERLACED_ID:
                        value = int(settings_elem.get_value())
                        self._set('interlaced', value)

                if None not in [d_width, d_height]:
                    track.aspect = float(d_width) / d_height

            else:
                self.process_track_common(elem, track)

        # convert codec information
        # http://haali.cs.msu.ru/mkv/codecs.pdf
        if track.codec in FOURCCMap:
            track.codec = FOURCCMap[track.codec]
        elif '/' in track.codec and track.codec.split(
                '/')[0] + '/' in FOURCCMap:
            track.codec = FOURCCMap[track.codec.split('/')[0] + '/']
        elif track.codec.endswith('FOURCC') and len(track.codec_private
                                                    or '') == 40:
            track.codec = track.codec_private[16:20]
        elif track.codec.startswith('V_REAL/'):
            track.codec = track.codec[7:]
        elif track.codec.startswith('V_'):
            # FIXME: add more video codecs here
            track.codec = track.codec[2:]

        track.id = len(self.video)
        self.video.append(track)
        return track
Example #2
0
    def parseDisc(self, device):
        type = None
        if self.is_disc(device) != 2:
            raise core.ParseError()

        f = open(device)
        try:
            # read CD-XA001 at byte 1024 in sector 16
            f.seek(2048 * 16 + 1024, 0)
            if f.read(8) != 'CD-XA001':
                raise core.ParseError()
            # read VIDEO_CD at sector 150
            f.seek(2048 * 150, 0)
            typebuffer = f.read(8)
            if typebuffer != 'VIDEO_CD' and typebuffer != 'SUPERVCD':
                raise core.ParseError()
            # Read some bytes of the ISO9660 part to better guess VCD or SVCD.
            # Maybe this code is not needed and VIDEO_CD and SUPERVCD are enough.
            f.seek(2048 * 16, 0)
            iso9660 = f.read(2048 * 16)
            if typebuffer == 'VIDEO_CD' and iso9660.find('MPEGAV') > 0:
                type = 'VCD'
            elif typebuffer == 'SUPERVCD' and \
                 (iso9660.find('SVCD') > 0 or iso9660.find('MPEG2') > 0):
                type = 'SVCD'
            else:
                raise core.ParseError()
        finally:
            f.close()

        # read the tracks to generate the title list
        device = open(device)
        (first, last) = cdrom.audiocd_toc_header(device)

        lmin = 0
        lsec = 0

        num = 0
        for i in range(first, last + 2):
            if i == last + 1:
                min, sec, frames = cdrom.audiocd_leadout(device)
            else:
                min, sec, frames = cdrom.audiocd_toc_entry(device, i)
            if num:
                vi = core.VideoStream()
                # XXX add more static information here, it's also possible
                # XXX to scan for more informations like fps
                # XXX Settings to MPEG1/2 is a wild guess, maybe the track
                # XXX isn't playable at all (e.g. the menu)
                if type == 'VCD':
                    vi.codec = 'MPEG1'
                else:
                    vi.codec = 'MPEG2'
                vi.length = (min - lmin) * 60 + (sec - lsec)
                self.tracks.append(vi)
            num += 1
            lmin, lsec = min, sec
        device.close()
Example #3
0
    def __init__(self, file):
        core.AVContainer.__init__(self)
        self.sequence_header_offset = 0
        self.mpeg_version = 2

        # detect TS (fast scan)
        if not self.isTS(file):
            # detect system mpeg (many infos)
            if not self.isMPEG(file):
                # detect PES
                if not self.isPES(file):
                    # Maybe it's MPEG-ES
                    if self.isES(file):
                        # If isES() succeeds, we needn't do anything further.
                        return
                    if file.name.lower().endswith('mpeg') or \
                             file.name.lower().endswith('mpg'):
                        # This has to be an mpeg file. It could be a bad
                        # recording from an ivtv based hardware encoder with
                        # same bytes missing at the beginning.
                        # Do some more digging...
                        if not self.isMPEG(file, force=True) or \
                           not self.video or not self.audio:
                            # does not look like an mpeg at all
                            raise ParseError()
                    else:
                        # no mpeg at all
                        raise ParseError()

        self.mime = 'video/mpeg'
        if not self.video:
            self.video.append(core.VideoStream())

        if self.sequence_header_offset <= 0:
            return

        self.progressive(file)

        for vi in self.video:
            vi.width, vi.height = self.dxy(file)
            vi.fps, vi.aspect = self.framerate_aspect(file)
            vi.bitrate = self.bitrate(file)
            if self.length:
                vi.length = self.length

        if not self.type:
            self.type = 'MPEG Video'

        # set fourcc codec for video and audio
        vc, ac = 'MP2V', 'MP2A'
        if self.mpeg_version == 1:
            vc, ac = 'MPEG', 0x0050
        for v in self.video:
            v.codec = vc
        for a in self.audio:
            if not a.codec:
                a.codec = ac
Example #4
0
    def _parseSTRF(self, t, strh):
        fccType = strh['fccType']
        retval = {}
        if fccType == 'auds':
            (
                retval['wFormatTag'],
                retval['nChannels'],
                retval['nSamplesPerSec'],
                retval['nAvgBytesPerSec'],
                retval['nBlockAlign'],
                retval['nBitsPerSample'],
            ) = struct.unpack('<HHHHHH', t[0:12])
            ai = core.AudioStream()
            ai.samplerate = retval['nSamplesPerSec']
            ai.channels = retval['nChannels']
            # FIXME: Bitrate calculation is completely wrong.
            #ai.samplebits = retval['nBitsPerSample']
            #ai.bitrate = retval['nAvgBytesPerSec'] * 8

            # TODO: set code if possible
            # http://www.stats.uwa.edu.au/Internal/Specs/DXALL/FileSpec/\
            #    Languages
            # ai.language = strh['wLanguage']
            ai.codec = retval['wFormatTag']
            self.audio.append(ai)
        elif fccType == 'vids':
            v = struct.unpack('<IIIHH', t[0:16])
            (
                retval['biSize'],
                retval['biWidth'],
                retval['biHeight'],
                retval['biPlanes'],
                retval['biBitCount'],
            ) = v
            v = struct.unpack('IIIII', t[20:40])
            (
                retval['biSizeImage'],
                retval['biXPelsPerMeter'],
                retval['biYPelsPerMeter'],
                retval['biClrUsed'],
                retval['biClrImportant'],
            ) = v
            vi = core.VideoStream()
            vi.codec = t[16:20]
            vi.width = retval['biWidth']
            vi.height = retval['biHeight']
            # FIXME: Bitrate calculation is completely wrong.
            #vi.bitrate = strh['dwRate']
            vi.fps = float(strh['dwRate']) / strh['dwScale']
            vi.length = strh['dwLength'] / vi.fps
            self.video.append(vi)
        return retval
Example #5
0
 def _read_header(self, object_id, s):
     if object_id == 'PROP':
         prop = struct.unpack('>9IHH', s)
         log.debug('PROP: %r' % prop)
     if object_id == 'MDPR':
         mdpr = struct.unpack('>H7I', s[:30])
         log.debug('MDPR: %r' % mdpr)
         self.length = mdpr[7] / 1000.0
         (stream_name_size, ) = struct.unpack('>B', s[30:31])
         stream_name = s[31:31 + stream_name_size]
         pos = 31 + stream_name_size
         (mime_type_size, ) = struct.unpack('>B', s[pos:pos + 1])
         mime = s[pos + 1:pos + 1 + mime_type_size]
         pos += mime_type_size + 1
         (type_specific_len, ) = struct.unpack('>I', s[pos:pos + 4])
         type_specific = s[pos + 4:pos + 4 + type_specific_len]
         pos += 4 + type_specific_len
         if mime[:5] == 'audio':
             ai = core.AudioStream()
             ai.id = mdpr[0]
             ai.bitrate = mdpr[2]
             self.audio.append(ai)
         elif mime[:5] == 'video':
             vi = core.VideoStream()
             vi.id = mdpr[0]
             vi.bitrate = mdpr[2]
             self.video.append(vi)
         else:
             log.debug('Unknown: %r' % mime)
     if object_id == 'CONT':
         pos = 0
         (title_len, ) = struct.unpack('>H', s[pos:pos + 2])
         self.title = s[2:title_len + 2]
         pos += title_len + 2
         (author_len, ) = struct.unpack('>H', s[pos:pos + 2])
         self.artist = s[pos + 2:pos + author_len + 2]
         pos += author_len + 2
         (copyright_len, ) = struct.unpack('>H', s[pos:pos + 2])
         self.copyright = s[pos + 2:pos + copyright_len + 2]
         pos += copyright_len + 2
         (comment_len, ) = struct.unpack('>H', s[pos:pos + 2])
         self.comment = s[pos + 2:pos + comment_len + 2]
Example #6
0
    def isES(self, file):
        file.seek(0, 0)
        try:
            header = struct.unpack('>LL', file.read(8))
        except (struct.error, IOError):
            return False

        if header[0] != 0x1B3:
            return False

        # Is an mpeg video elementary stream

        self.mime = 'video/mpeg'
        video = core.VideoStream()
        video.width = header[1] >> 20
        video.height = (header[1] >> 8) & 0xfff
        if header[1] & 0xf < len(FRAME_RATE):
            video.fps = FRAME_RATE[header[1] & 0xf]
        if (header[1] >> 4) & 0xf < len(ASPECT_RATIO):
            # FIXME: Empirically the aspect looks like PAR rather than DAR
            video.aspect = ASPECT_RATIO[(header[1] >> 4) & 0xf]
        self.video.append(video)
        return True
Example #7
0
class MPEG4(core.AVContainer):
    """
    Parser for the MP4 container format. This format is mostly
    identical to Apple Quicktime and 3GP files. It maps to mp4, mov,
    qt and some other extensions.
    """
    table_mapping = {'QTUDTA': QTUDTA}

    def __init__(self, file):
        core.AVContainer.__init__(self)
        self._references = []

        self.mime = 'video/quicktime'
        self.type = 'Quicktime Video'
        h = file.read(8)
        try:
            (size, type) = struct.unpack('>I4s', h)
        except struct.error:
            # EOF.
            raise ParseError()

        if type == 'ftyp':
            # file type information
            if size >= 12:
                # this should always happen
                if file.read(4) != 'qt  ':
                    # not a quicktime movie, it is a mpeg4 container
                    self.mime = 'video/mp4'
                    self.type = 'MPEG-4 Video'
                size -= 4
            file.seek(size - 8, 1)
            h = file.read(8)
            (size, type) = struct.unpack('>I4s', h)

        while type in ['mdat', 'skip']:
            # movie data at the beginning, skip
            file.seek(size - 8, 1)
            h = file.read(8)
            (size, type) = struct.unpack('>I4s', h)

        if not type in ['moov', 'wide', 'free']:
            log.debug(u'invalid header: %r' % type)
            raise ParseError()

        # Extended size
        if size == 1:
            size = struct.unpack('>Q', file.read(8))

        # Back over the atom header we just read, since _readatom expects the
        # file position to be at the start of an atom.
        file.seek(-8, 1)
        while self._readatom(file):
            pass

        if self._references:
            self._set('references', self._references)


    def _readatom(self, file):
        s = file.read(8)
        if len(s) < 8:
            return 0

        atomsize, atomtype = struct.unpack('>I4s', s)
        if not str(atomtype).decode('latin1').isalnum():
            # stop at nonsense data
            return 0

        log.debug(u'%r [%X]' % (atomtype, atomsize))

        if atomtype == 'udta':
            # Userdata (Metadata)
            pos = 0
            tabl = {}
            i18ntabl = {}
            atomdata = file.read(atomsize - 8)
            while pos < atomsize - 12:
                (datasize, datatype) = struct.unpack('>I4s', atomdata[pos:pos + 8])
                if ord(datatype[0]) == 169:
                    # i18n Metadata...
                    mypos = 8 + pos
                    while mypos + 4 < datasize + pos:
                        # first 4 Bytes are i18n header
                        (tlen, lang) = struct.unpack('>HH', atomdata[mypos:mypos + 4])
                        i18ntabl[lang] = i18ntabl.get(lang, {})
                        l = atomdata[mypos + 4:mypos + tlen + 4]
                        i18ntabl[lang][datatype[1:]] = l
                        mypos += tlen + 4
                elif datatype == 'WLOC':
                    # Drop Window Location
                    pass
                else:
                    if ord(atomdata[pos + 8:pos + datasize][0]) > 1:
                        tabl[datatype] = atomdata[pos + 8:pos + datasize]
                pos += datasize
            if len(i18ntabl.keys()) > 0:
                for k in i18ntabl.keys():
                    if QTLANGUAGES.has_key(k) and QTLANGUAGES[k] == 'en':
                        self._appendtable('QTUDTA', i18ntabl[k])
                        self._appendtable('QTUDTA', tabl)
            else:
                log.debug(u'NO i18')
                self._appendtable('QTUDTA', tabl)

        elif atomtype == 'trak':
            atomdata = file.read(atomsize - 8)
            pos = 0
            trackinfo = {}
            tracktype = None
            while pos < atomsize - 8:
                (datasize, datatype) = struct.unpack('>I4s', atomdata[pos:pos + 8])

                if datatype == 'tkhd':
                    tkhd = struct.unpack('>6I8x4H36xII', atomdata[pos + 8:pos + datasize])
                    trackinfo['width'] = tkhd[10] >> 16
                    trackinfo['height'] = tkhd[11] >> 16
                    trackinfo['id'] = tkhd[3]

                    try:
                        # XXX Timestamp of Seconds is since January 1st 1904!
                        # XXX 2082844800 is the difference between Unix and
                        # XXX Apple time. FIXME to work on Apple, too
                        self.timestamp = int(tkhd[1]) - 2082844800
                    except Exception, e:
                        log.exception(u'There was trouble extracting timestamp')

                elif datatype == 'mdia':
                    pos += 8
                    datasize -= 8
                    log.debug(u'--> mdia information')

                    while datasize:
                        mdia = struct.unpack('>I4s', atomdata[pos:pos + 8])
                        if mdia[1] == 'mdhd':
                            # Parse based on version of mdhd header.  See
                            # http://wiki.multimedia.cx/index.php?title=QuickTime_container#mdhd
                            ver = ord(atomdata[pos + 8])
                            if ver == 0:
                                mdhd = struct.unpack('>IIIIIhh', atomdata[pos + 8:pos + 8 + 24])
                            elif ver == 1:
                                mdhd = struct.unpack('>IQQIQhh', atomdata[pos + 8:pos + 8 + 36])
                            else:
                                mdhd = None

                            if mdhd:
                                # duration / time scale
                                trackinfo['length'] = mdhd[4] / mdhd[3]
                                if mdhd[5] in QTLANGUAGES:
                                    trackinfo['language'] = QTLANGUAGES[mdhd[5]]
                                # mdhd[6] == quality
                                self.length = max(self.length, mdhd[4] / mdhd[3])
                        elif mdia[1] == 'minf':
                            # minf has only atoms inside
                            pos -= (mdia[0] - 8)
                            datasize += (mdia[0] - 8)
                        elif mdia[1] == 'stbl':
                            # stbl has only atoms inside
                            pos -= (mdia[0] - 8)
                            datasize += (mdia[0] - 8)
                        elif mdia[1] == 'hdlr':
                            hdlr = struct.unpack('>I4s4s', atomdata[pos + 8:pos + 8 + 12])
                            if hdlr[1] == 'mhlr':
                                if hdlr[2] == 'vide':
                                    tracktype = 'video'
                                if hdlr[2] == 'soun':
                                    tracktype = 'audio'
                        elif mdia[1] == 'stsd':
                            stsd = struct.unpack('>2I', atomdata[pos + 8:pos + 8 + 8])
                            if stsd[1] > 0:
                                codec = atomdata[pos + 16:pos + 16 + 8]
                                codec = struct.unpack('>I4s', codec)
                                trackinfo['codec'] = codec[1]
                                if codec[1] == 'jpeg':
                                    tracktype = 'image'
                        elif mdia[1] == 'dinf':
                            dref = struct.unpack('>I4s', atomdata[pos + 8:pos + 8 + 8])
                            log.debug(u'  --> %r, %r (useless)' % mdia)
                            if dref[1] == 'dref':
                                num = struct.unpack('>I', atomdata[pos + 20:pos + 20 + 4])[0]
                                rpos = pos + 20 + 4
                                for ref in range(num):
                                    # FIXME: do somthing if this references
                                    ref = struct.unpack('>I3s', atomdata[rpos:rpos + 7])
                                    data = atomdata[rpos + 7:rpos + ref[0]]
                                    rpos += ref[0]
                        else:
                            if mdia[1].startswith('st'):
                                log.debug(u'  --> %r, %r (sample)' % mdia)
                            elif mdia[1] == 'vmhd' and not tracktype:
                                # indicates that this track is video
                                tracktype = 'video'
                            elif mdia[1] in ['vmhd', 'smhd'] and not tracktype:
                                # indicates that this track is audio
                                tracktype = 'audio'
                            else:
                                log.debug(u'  --> %r, %r (unknown)' % mdia)

                        pos += mdia[0]
                        datasize -= mdia[0]

                elif datatype == 'udta':
                    log.debug(u'udta: %r' % struct.unpack('>I4s', atomdata[:8]))
                else:
                    if datatype == 'edts':
                        log.debug(u'--> %r [%d] (edit list)' % \
                                  (datatype, datasize))
                    else:
                        log.debug(u'--> %r [%d] (unknown)' % \
                                  (datatype, datasize))
                pos += datasize

            info = None
            if tracktype == 'video':
                info = core.VideoStream()
                self.video.append(info)
            if tracktype == 'audio':
                info = core.AudioStream()
                self.audio.append(info)
            if info:
                for key, value in trackinfo.items():
                    setattr(info, key, value)
Example #8
0
    def _parseHeader(self, header, granule):
        headerlen = len(header)
        flags = ord(header[0])

        if headerlen >= 30 and header[1:7] == 'vorbis':
            ai = core.AudioStream()
            ai.version, ai.channels, ai.samplerate, bitrate_max, ai.bitrate, \
                        bitrate_min, blocksize, framing = \
                        struct.unpack('<IBIiiiBB', header[7:7 + 23])
            ai.codec = 'Vorbis'
            #ai.granule = granule
            #ai.length = granule / ai.samplerate
            self.audio.append(ai)
            self.all_streams.append(ai)

        elif headerlen >= 7 and header[1:7] == 'theora':
            # Theora Header
            # XXX Finish Me
            vi = core.VideoStream()
            vi.codec = 'theora'
            self.video.append(vi)
            self.all_streams.append(vi)

        elif headerlen >= 142 and \
                 header[1:36] == 'Direct Show Samples embedded in Ogg':
            # Old Directshow format
            # XXX Finish Me
            vi = core.VideoStream()
            vi.codec = 'dshow'
            self.video.append(vi)
            self.all_streams.append(vi)

        elif flags & PACKET_TYPE_BITS == PACKET_TYPE_HEADER and \
                 headerlen >= struct.calcsize(STREAM_HEADER_VIDEO) + 1:
            # New Directshow Format
            htype = header[1:9]

            if htype[:5] == 'video':
                sh = header[9:struct.calcsize(STREAM_HEADER_VIDEO) + 9]
                streamheader = struct.unpack(STREAM_HEADER_VIDEO, sh)
                vi = core.VideoStream()
                (type, ssize, timeunit, samplerate, vi.length, buffersize, \
                 vi.bitrate, vi.width, vi.height) = streamheader

                vi.width /= 65536
                vi.height /= 65536
                # XXX length, bitrate are very wrong
                vi.codec = type
                vi.fps = 10000000 / timeunit
                self.video.append(vi)
                self.all_streams.append(vi)

            elif htype[:5] == 'audio':
                sha = header[9:struct.calcsize(STREAM_HEADER_AUDIO) + 9]
                streamheader = struct.unpack(STREAM_HEADER_AUDIO, sha)
                ai = core.AudioStream()
                (type, ssize, timeunit, ai.samplerate, ai.length, buffersize, \
                 ai.bitrate, ai.channels, bloc, ai.bitrate) = streamheader
                self.samplerate = ai.samplerate
                log.debug(u'Samplerate %d' % self.samplerate)
                self.audio.append(ai)
                self.all_streams.append(ai)

            elif htype[:4] == 'text':
                subtitle = core.Subtitle()
                # FIXME: add more info
                self.subtitles.append(subtitle)
                self.all_streams.append(subtitle)

        else:
            log.debug(u'Unknown Header')
Example #9
0
    def __init__(self, file):
        core.AVContainer.__init__(self)
        self.mime = 'video/flv'
        self.type = 'Flash Video'
        data = file.read(13)
        if len(data) < 13 or struct.unpack('>3sBBII', data)[0] != 'FLV':
            raise ParseError()

        for _ in range(10):
            if self.audio and self.video:
                break
            data = file.read(11)
            if len(data) < 11:
                break
            chunk = struct.unpack('>BH4BI', data)
            size = (chunk[1] << 8) + chunk[2]

            if chunk[0] == FLV_TAG_TYPE_AUDIO:
                flags = ord(file.read(1))
                if not self.audio:
                    a = core.AudioStream()
                    a.channels = (flags & FLV_AUDIO_CHANNEL_MASK) + 1
                    srate = (flags & FLV_AUDIO_SAMPLERATE_MASK)
                    a.samplerate = (
                        44100 << (srate >> FLV_AUDIO_SAMPLERATE_OFFSET) >> 3)
                    codec = (flags & FLV_AUDIO_CODECID_MASK
                             ) >> FLV_AUDIO_CODECID_OFFSET
                    if codec < len(FLV_AUDIO_CODECID):
                        a.codec = FLV_AUDIO_CODECID[codec]
                    self.audio.append(a)

                file.seek(size - 1, 1)

            elif chunk[0] == FLV_TAG_TYPE_VIDEO:
                flags = ord(file.read(1))
                if not self.video:
                    v = core.VideoStream()
                    codec = (flags & FLV_VIDEO_CODECID_MASK) - 2
                    if codec < len(FLV_VIDEO_CODECID):
                        v.codec = FLV_VIDEO_CODECID[codec]
                    # width and height are in the meta packet, but I have
                    # no file with such a packet inside. So maybe we have
                    # to decode some parts of the video.
                    self.video.append(v)

                file.seek(size - 1, 1)

            elif chunk[0] == FLV_TAG_TYPE_META:
                log.info('metadata %r', str(chunk))
                metadata = file.read(size)
                try:
                    while metadata:
                        length, value = self._parse_value(metadata)
                        if isinstance(value, dict):
                            log.info('metadata: %r', value)
                            if value.get('creator'):
                                self.copyright = value.get('creator')
                            if value.get('width'):
                                self.width = value.get('width')
                            if value.get('height'):
                                self.height = value.get('height')
                            if value.get('duration'):
                                self.length = value.get('duration')
                            self._appendtable('FLVINFO', value)
                        if not length:
                            # parse error
                            break
                        metadata = metadata[length:]
                except (IndexError, struct.error, TypeError):
                    pass
            else:
                log.info('unkown %r', str(chunk))
                file.seek(size, 1)

            file.seek(4, 1)
Example #10
0
    def ReadPESHeader(self, offset, buffer, id=0):
        """
        Parse a PES header.
        Since it starts with 0x00 0x00 0x01 like 'normal' mpegs, this
        function will return (0, None) when it is no PES header or
        (packet length, timestamp position (maybe None))

        http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
        """
        if not buffer[0:3] == '\x00\x00\x01':
            return 0, None

        packet_length = (ord(buffer[4]) << 8) + ord(buffer[5]) + 6
        align = ord(buffer[6]) & 4
        header_length = ord(buffer[8])

        # PES ID (starting with 001)
        if ord(buffer[3]) & 0xE0 == 0xC0:
            id = id or ord(buffer[3]) & 0x1F
            for a in self.audio:
                if a.id == id:
                    break
            else:
                self.audio.append(core.AudioStream())
                self.audio[-1]._set('id', id)

        elif ord(buffer[3]) & 0xF0 == 0xE0:
            id = id or ord(buffer[3]) & 0xF
            for v in self.video:
                if v.id == id:
                    break
            else:
                self.video.append(core.VideoStream())
                self.video[-1]._set('id', id)

            # new mpeg starting
            if buffer[header_length + 9:header_length + 13] == \
                   '\x00\x00\x01\xB3' and not self.sequence_header_offset:
                # yes, remember offset for later use
                self.sequence_header_offset = offset + header_length + 9
        elif ord(buffer[3]) == 189 or ord(buffer[3]) == 191:
            # private stream. we don't know, but maybe we can guess later
            id = id or ord(buffer[3]) & 0xF
            if align and \
                   buffer[header_length + 9:header_length + 11] == '\x0b\x77':
                # AC3 stream
                for a in self.audio:
                    if a.id == id:
                        break
                else:
                    self.audio.append(core.AudioStream())
                    self.audio[-1]._set('id', id)
                    self.audio[-1].codec = 0x2000  # AC3

        else:
            # unknown content
            pass

        ptsdts = ord(buffer[7]) >> 6

        if ptsdts and ptsdts == ord(buffer[9]) >> 4:
            if ord(buffer[9]) >> 4 != ptsdts:
                log.warning(u'WARNING: bad PTS/DTS, please contact us')
                return packet_length, None

            # timestamp = self.ReadPTS(buffer[9:14])
            high = ((ord(buffer[9]) & 0xF) >> 1)
            med = (ord(buffer[10]) << 7) + (ord(buffer[11]) >> 1)
            low = (ord(buffer[12]) << 7) + (ord(buffer[13]) >> 1)
            return packet_length, 9

        return packet_length, None
Example #11
0
    def ReadHeader(self, buffer, offset):
        """
        Handle MPEG header in buffer on position offset
        Return None on error, new offset or 0 if the new offset can't be scanned
        """
        if buffer[offset:offset + 3] != '\x00\x00\x01':
            return None

        id = ord(buffer[offset + 3])

        if id == PADDING_PKT:
            return offset + (ord(buffer[offset + 4]) << 8) + \
                   ord(buffer[offset + 5]) + 6

        if id == PACK_PKT:
            if ord(buffer[offset + 4]) & 0xF0 == 0x20:
                self.type = 'MPEG-1 Video'
                self.get_time = self.ReadSCRMpeg1
                self.mpeg_version = 1
                return offset + 12
            elif (ord(buffer[offset + 4]) & 0xC0) == 0x40:
                self.type = 'MPEG-2 Video'
                self.get_time = self.ReadSCRMpeg2
                return offset + (ord(buffer[offset + 13]) & 0x07) + 14
            else:
                # I have no idea what just happened, but for some DVB
                # recordings done with mencoder this points to a
                # PACK_PKT describing something odd. Returning 0 here
                # (let's hope there are no extensions in the header)
                # fixes it.
                return 0

        if 0xC0 <= id <= 0xDF:
            # code for audio stream
            for a in self.audio:
                if a.id == id:
                    break
            else:
                self.audio.append(core.AudioStream())
                self.audio[-1]._set('id', id)
            return 0

        if 0xE0 <= id <= 0xEF:
            # code for video stream
            for v in self.video:
                if v.id == id:
                    break
            else:
                self.video.append(core.VideoStream())
                self.video[-1]._set('id', id)
            return 0

        if id == SEQ_HEAD:
            # sequence header, remember that position for later use
            self.sequence_header_offset = offset
            return 0

        if id in [PRIVATE_STREAM1, PRIVATE_STREAM2]:
            # private stream. we don't know, but maybe we can guess later
            add = ord(buffer[offset + 8])
            # if (ord(buffer[offset+6]) & 4) or 1:
            # id = ord(buffer[offset+10+add])
            if buffer[offset + 11 + add:offset + 15 +
                      add].find('\x0b\x77') != -1:
                # AC3 stream
                for a in self.audio:
                    if a.id == id:
                        break
                else:
                    self.audio.append(core.AudioStream())
                    self.audio[-1]._set('id', id)
                    self.audio[-1].codec = 0x2000  # AC3
            return 0

        if id == SYS_PKT:
            return 0

        if id == EXT_START:
            return 0

        return 0
Example #12
0
    def _getnextheader(self, s):
        r = struct.unpack('<16sQ', s[:24])
        (guidstr, objsize) = r
        guid = self._parseguid(guidstr)
        if guid == GUIDS['ASF_File_Properties_Object']:
            log.debug('File Properties Object')
            val = struct.unpack('<16s6Q4I', s[24:24 + 80])
            (fileid, size, date, packetcount, duration, senddur, preroll, flags, minpack, maxpack, maxbr) = val
            # FIXME: parse date to timestamp
            self.length = duration / 10000000.0

        elif guid == GUIDS['ASF_Stream_Properties_Object']:
            log.debug('Stream Properties Object [%d]' % objsize)
            streamtype = self._parseguid(s[24:40])
            errortype = self._parseguid(s[40:56])
            offset, typelen, errorlen, flags = struct.unpack('<QIIH', s[56:74])
            strno = flags & 0x7f
            encrypted = flags >> 15
            if encrypted:
                self._set('encrypted', True)
            if streamtype == GUIDS['ASF_Video_Media']:
                vi = core.VideoStream()
                vi.width, vi.height, depth, codec, = struct.unpack('<4xII2xH4s', s[89:89 + 20])
                vi.codec = codec
                vi.id = strno
                self.video.append(vi)
            elif streamtype == GUIDS['ASF_Audio_Media']:
                ai = core.AudioStream()
                twocc, ai.channels, ai.samplerate, bitrate, block, \
                ai.samplebits, = struct.unpack('<HHIIHH', s[78:78 + 16])
                ai.bitrate = 8 * bitrate
                ai.codec = twocc
                ai.id = strno
                self.audio.append(ai)

            self._apply_extinfo(strno)

        elif guid == GUIDS['ASF_Extended_Stream_Properties_Object']:
            streamid, langid, frametime = struct.unpack('<HHQ', s[72:84])
            (bitrate,) = struct.unpack('<I', s[40:40 + 4])
            if streamid not in self._extinfo:
                self._extinfo[streamid] = [None, None, None, {}]
            if frametime == 0:
                # Problaby VFR, report as 1000fps (which is what MPlayer does)
                frametime = 10000.0
            self._extinfo[streamid][:3] = [bitrate, 10000000.0 / frametime, langid]
            self._apply_extinfo(streamid)

        elif guid == GUIDS['ASF_Header_Extension_Object']:
            log.debug('ASF_Header_Extension_Object %d' % objsize)
            size = struct.unpack('<I', s[42:46])[0]
            data = s[46:46 + size]
            while len(data):
                log.debug('Sub:')
                h = self._getnextheader(data)
                data = data[h[1]:]

        elif guid == GUIDS['ASF_Codec_List_Object']:
            log.debug('List Object')
            pass

        elif guid == GUIDS['ASF_Error_Correction_Object']:
            log.debug('Error Correction')
            pass

        elif guid == GUIDS['ASF_Content_Description_Object']:
            log.debug('Content Description Object')
            val = struct.unpack('<5H', s[24:24 + 10])
            pos = 34
            strings = []
            for i in val:
                ss = s[pos:pos + i].replace('\0', '').lstrip().rstrip()
                strings.append(ss)
                pos += i

            # Set empty strings to None
            strings = [x or None for x in strings]
            self.title, self.artist, self.copyright, self.caption, rating = strings

        elif guid == GUIDS['ASF_Extended_Content_Description_Object']:
            (count,) = struct.unpack('<H', s[24:26])
            pos = 26
            descriptor = {}
            for i in range(0, count):
                # Read additional content descriptors
                d = self._parsekv(s[pos:])
                pos += d[0]
                descriptor[d[1]] = d[2]
            self._appendtable('ASFDESCRIPTOR', descriptor)

        elif guid == GUIDS['ASF_Metadata_Object']:
            (count,) = struct.unpack('<H', s[24:26])
            pos = 26
            streams = {}
            for i in range(0, count):
                # Read additional content descriptors
                size, key, value, strno = self._parsekv2(s[pos:])
                if strno not in streams:
                    streams[strno] = {}
                streams[strno][key] = value
                pos += size

            for strno, metadata in streams.items():
                if strno not in self._extinfo:
                    self._extinfo[strno] = [None, None, None, {}]
                self._extinfo[strno][3].update(metadata)
                self._apply_extinfo(strno)

        elif guid == GUIDS['ASF_Language_List_Object']:
            count = struct.unpack('<H', s[24:26])[0]
            pos = 26
            for i in range(0, count):
                idlen = struct.unpack('<B', s[pos:pos + 1])[0]
                idstring = s[pos + 1:pos + 1 + idlen]
                idstring = unicode(idstring, 'utf-16').replace('\0', '')
                log.debug('Language: %d/%d: %r' % (i + 1, count, idstring))
                self._languages.append(idstring)
                pos += 1 + idlen

        elif guid == GUIDS['ASF_Stream_Bitrate_Properties_Object']:
            # This record contains stream bitrate with payload overhead.  For
            # audio streams, we should have the average bitrate from
            # ASF_Stream_Properties_Object.  For video streams, we get it from
            # ASF_Extended_Stream_Properties_Object.  So this record is not
            # used.
            pass

        elif guid == GUIDS['ASF_Content_Encryption_Object'] or \
                        guid == GUIDS['ASF_Extended_Content_Encryption_Object']:
            self._set('encrypted', True)
        else:
            # Just print the type:
            for h in GUIDS.keys():
                if GUIDS[h] == guid:
                    log.debug('Unparsed %r [%d]' % (h, objsize))
                    break
            else:
                u = "%.8X-%.4X-%.4X-%.2X%.2X-%s" % guid
                log.debug('unknown: len=%d [%d]' % (len(u), objsize))
        return r